diff options
Diffstat (limited to 'drivers/tty')
156 files changed, 23783 insertions, 9046 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 0ecf22b6a38..b24aa010f68 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -1,3 +1,14 @@ +config TTY + bool "Enable TTY" if EXPERT + default y + ---help--- + Allows you to remove TTY support which can save space, and + blocks features that require TTY from inclusion in the kernel. + TTY is required for any text terminals or serial port + communication. Most users should leave this enabled. + +if TTY + config VT bool "Virtual terminal" if EXPERT depends on !S390 && !UML @@ -355,7 +366,7 @@ config TRACE_SINK "Trace data router for MIPI P1149.7 cJTAG standard". config PPC_EPAPR_HV_BYTECHAN - tristate "ePAPR hypervisor byte channel driver" + bool "ePAPR hypervisor byte channel driver" depends on PPC select EPAPR_PARAVIRT help @@ -388,3 +399,24 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE If the number you specify is not a valid byte channel handle, then there simply will be no early console output. This is true also if you don't boot under a hypervisor at all. + +config GOLDFISH_TTY + tristate "Goldfish TTY Driver" + depends on GOLDFISH + help + Console and system TTY driver for the Goldfish virtual platform. + +config DA_TTY + bool "DA TTY" + depends on METAG_DA + select SERIAL_NONSTANDARD + help + This enables a TTY on a Dash channel. + +config DA_CONSOLE + bool "DA Console" + depends on DA_TTY + help + This enables a console on a Dash channel. + +endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 2953059530e..58ad1c05b7f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -1,5 +1,5 @@ -obj-y += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ - tty_buffer.o tty_port.o tty_mutex.o +obj-$(CONFIG_TTY) += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \ + tty_buffer.o tty_port.o tty_mutex.o tty_ldsem.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o obj-$(CONFIG_AUDIT) += tty_audit.o @@ -27,5 +27,7 @@ obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o +obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o +obj-$(CONFIG_DA_TTY) += metag_da.o obj-y += ipwireless/ diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 9d7d00cdfec..979e7c3ea2c 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -251,7 +251,6 @@ static void receive_chars(struct serial_state *info) { int status; int serdatr; - struct tty_struct *tty = info->tport.tty; unsigned char ch, flag; struct async_icount *icount; int oe = 0; @@ -314,7 +313,7 @@ static void receive_chars(struct serial_state *info) #endif flag = TTY_BREAK; if (info->tport.flags & ASYNC_SAK) - do_SAK(tty); + do_SAK(info->tport.tty); } else if (status & UART_LSR_PE) flag = TTY_PARITY; else if (status & UART_LSR_FE) @@ -328,10 +327,10 @@ static void receive_chars(struct serial_state *info) oe = 1; } } - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(&info->tport, ch, flag); if (oe == 1) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_flip_buffer_push(tty); + tty_insert_flip_char(&info->tport, 0, TTY_OVERRUN); + tty_flip_buffer_push(&info->tport); out: return; } @@ -394,11 +393,6 @@ static void check_modem_status(struct serial_state *info) icount->dsr++; if (dstatus & SER_DCD) { icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((port->flags & ASYNC_HARDPPS_CD) && - !(status & SER_DCD)) - hardpps(); -#endif } if (dstatus & SER_CTS) icount->cts++; @@ -1099,7 +1093,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state, state->custom_divisor = new_serial.custom_divisor; port->close_delay = new_serial.close_delay * HZ/100; port->closing_wait = new_serial.closing_wait * HZ/100; - tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: if (port->flags & ASYNC_INITIALIZED) { @@ -1254,6 +1248,8 @@ static int rs_ioctl(struct tty_struct *tty, struct async_icount cprev, cnow; /* kernel counter temps */ void __user *argp = (void __user *)arg; unsigned long flags; + DEFINE_WAIT(wait); + int ret; if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; @@ -1294,25 +1290,33 @@ static int rs_ioctl(struct tty_struct *tty, cprev = info->icount; local_irq_restore(flags); while (1) { - interruptible_sleep_on(&info->tport.delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; + prepare_to_wait(&info->tport.delta_msr_wait, + &wait, TASK_INTERRUPTIBLE); local_irq_save(flags); cnow = info->icount; /* atomic copy */ local_irq_restore(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { + ret = -EIO; /* no change => error */ + break; + } if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { - return 0; + ret = 0; + break; + } + schedule(); + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; } cprev = cnow; } - /* NOTREACHED */ + finish_wait(&info->tport.delta_msr_wait, &wait); + return ret; case TIOCSERGWILD: case TIOCSERSWILD: @@ -1528,7 +1532,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV; - tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; retval = startup(tty, info); if (retval) { @@ -1791,8 +1795,6 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); - platform_set_drvdata(pdev, NULL); - return error; } @@ -1804,19 +1806,7 @@ static struct platform_driver amiga_serial_driver = { }, }; -static int __init amiga_serial_init(void) -{ - return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe); -} - -module_init(amiga_serial_init); - -static void __exit amiga_serial_exit(void) -{ - platform_driver_unregister(&amiga_serial_driver); -} - -module_exit(amiga_serial_exit); +module_platform_driver_probe(amiga_serial_driver, amiga_serial_probe); #if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE) @@ -1875,6 +1865,9 @@ static struct console sercons = { */ static int __init amiserial_console_init(void) { + if (!MACH_IS_AMIGA) + return -ENODEV; + register_console(&sercons); return 0; } diff --git a/drivers/tty/bfin_jtag_comm.c b/drivers/tty/bfin_jtag_comm.c index 1cfcdbf1d0c..8096fcbe2dc 100644 --- a/drivers/tty/bfin_jtag_comm.c +++ b/drivers/tty/bfin_jtag_comm.c @@ -95,18 +95,16 @@ bfin_jc_emudat_manager(void *arg) /* if incoming data is ready, eat it */ if (bfin_read_DBGSTAT() & EMUDIF) { - if (tty != NULL) { - uint32_t emudat = bfin_read_emudat(); - if (inbound_len == 0) { - pr_debug("incoming length: 0x%08x\n", emudat); - inbound_len = emudat; - } else { - size_t num_chars = (4 <= inbound_len ? 4 : inbound_len); - pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); - inbound_len -= num_chars; - tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars); - tty_flip_buffer_push(tty); - } + uint32_t emudat = bfin_read_emudat(); + if (inbound_len == 0) { + pr_debug("incoming length: 0x%08x\n", emudat); + inbound_len = emudat; + } else { + size_t num_chars = (4 <= inbound_len ? 4 : inbound_len); + pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); + inbound_len -= num_chars; + tty_insert_flip_string(&port, (unsigned char *)&emudat, num_chars); + tty_flip_buffer_push(&port); } } @@ -351,7 +349,7 @@ bfin_jc_early_write(struct console *co, const char *buf, unsigned int count) bfin_jc_straight_buffer_write(buf, count); } -static struct __initdata console bfin_jc_early_console = { +static struct console bfin_jc_early_console __initdata = { .name = "early_BFJC", .write = bfin_jc_early_write, .flags = CON_ANYTIME | CON_PRINTBUFFER, diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index b09c8d1f9a6..a57bb5ab761 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -441,7 +441,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, void __iomem *base_addr) { struct cyclades_port *info; - struct tty_struct *tty; + struct tty_port *port; int len, index = cinfo->bus_index; u8 ivr, save_xir, channel, save_car, data, char_count; @@ -452,22 +452,11 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, save_xir = readb(base_addr + (CyRIR << index)); channel = save_xir & CyIRChannel; info = &cinfo->ports[channel + chip * 4]; + port = &info->port; save_car = cyy_readb(info, CyCAR); cyy_writeb(info, CyCAR, save_xir); ivr = cyy_readb(info, CyRIVR) & CyIVRMask; - tty = tty_port_tty_get(&info->port); - /* if there is nowhere to put the data, discard it */ - if (tty == NULL) { - if (ivr == CyIVRRxEx) { /* exception */ - data = cyy_readb(info, CyRDSR); - } else { /* normal character reception */ - char_count = cyy_readb(info, CyRDCR); - while (char_count--) - data = cyy_readb(info, CyRDSR); - } - goto end; - } /* there is an open port for this data */ if (ivr == CyIVRRxEx) { /* exception */ data = cyy_readb(info, CyRDSR); @@ -484,40 +473,45 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, if (data & info->ignore_status_mask) { info->icount.rx++; - tty_kref_put(tty); return; } - if (tty_buffer_request_room(tty, 1)) { + if (tty_buffer_request_room(port, 1)) { if (data & info->read_status_mask) { if (data & CyBREAK) { - tty_insert_flip_char(tty, + tty_insert_flip_char(port, cyy_readb(info, CyRDSR), TTY_BREAK); info->icount.rx++; - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); + if (port->flags & ASYNC_SAK) { + struct tty_struct *tty = + tty_port_tty_get(port); + if (tty) { + do_SAK(tty); + tty_kref_put(tty); + } + } } else if (data & CyFRAME) { - tty_insert_flip_char(tty, + tty_insert_flip_char(port, cyy_readb(info, CyRDSR), TTY_FRAME); info->icount.rx++; info->idle_stats.frame_errs++; } else if (data & CyPARITY) { /* Pieces of seven... */ - tty_insert_flip_char(tty, + tty_insert_flip_char(port, cyy_readb(info, CyRDSR), TTY_PARITY); info->icount.rx++; info->idle_stats.parity_errs++; } else if (data & CyOVERRUN) { - tty_insert_flip_char(tty, 0, + tty_insert_flip_char(port, 0, TTY_OVERRUN); info->icount.rx++; /* If the flip buffer itself is overflowing, we still lose the next incoming character. */ - tty_insert_flip_char(tty, + tty_insert_flip_char(port, cyy_readb(info, CyRDSR), TTY_FRAME); info->icount.rx++; @@ -527,12 +521,12 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, /* } else if(data & CyTIMEOUT) { */ /* } else if(data & CySPECHAR) { */ } else { - tty_insert_flip_char(tty, 0, + tty_insert_flip_char(port, 0, TTY_NORMAL); info->icount.rx++; } } else { - tty_insert_flip_char(tty, 0, TTY_NORMAL); + tty_insert_flip_char(port, 0, TTY_NORMAL); info->icount.rx++; } } else { @@ -552,10 +546,10 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, info->mon.char_max = char_count; info->mon.char_last = char_count; #endif - len = tty_buffer_request_room(tty, char_count); + len = tty_buffer_request_room(port, char_count); while (len--) { data = cyy_readb(info, CyRDSR); - tty_insert_flip_char(tty, data, TTY_NORMAL); + tty_insert_flip_char(port, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; #ifdef CY_16Y_HACK @@ -564,9 +558,8 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, } info->idle_stats.recv_idle = jiffies; } - tty_schedule_flip(tty); - tty_kref_put(tty); -end: + tty_schedule_flip(port); + /* end of service */ cyy_writeb(info, CyRIR, save_xir & 0x3f); cyy_writeb(info, CyCAR, save_car); @@ -924,10 +917,11 @@ cyz_issue_cmd(struct cyclades_card *cinfo, return 0; } /* cyz_issue_cmd */ -static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) +static void cyz_handle_rx(struct cyclades_port *info) { struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; + struct tty_port *port = &info->port; unsigned int char_count; int len; #ifdef BLOCKMOVE @@ -946,80 +940,77 @@ static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) else char_count = rx_put - rx_get + rx_bufsize; - if (char_count) { + if (!char_count) + return; + #ifdef CY_ENABLE_MONITORING - info->mon.int_count++; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; #endif - if (tty == NULL) { - /* flush received characters */ - new_rx_get = (new_rx_get + char_count) & - (rx_bufsize - 1); - info->rflush_count++; - } else { + #ifdef BLOCKMOVE - /* we'd like to use memcpy(t, f, n) and memset(s, c, count) - for performance, but because of buffer boundaries, there - may be several steps to the operation */ - while (1) { - len = tty_prepare_flip_string(tty, &buf, - char_count); - if (!len) - break; + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while (1) { + len = tty_prepare_flip_string(port, &buf, + char_count); + if (!len) + break; - len = min_t(unsigned int, min(len, char_count), - rx_bufsize - new_rx_get); + len = min_t(unsigned int, min(len, char_count), + rx_bufsize - new_rx_get); - memcpy_fromio(buf, cinfo->base_addr + - rx_bufaddr + new_rx_get, len); + memcpy_fromio(buf, cinfo->base_addr + + rx_bufaddr + new_rx_get, len); - new_rx_get = (new_rx_get + len) & - (rx_bufsize - 1); - char_count -= len; - info->icount.rx += len; - info->idle_stats.recv_bytes += len; - } + new_rx_get = (new_rx_get + len) & + (rx_bufsize - 1); + char_count -= len; + info->icount.rx += len; + info->idle_stats.recv_bytes += len; + } #else - len = tty_buffer_request_room(tty, char_count); - while (len--) { - data = readb(cinfo->base_addr + rx_bufaddr + - new_rx_get); - new_rx_get = (new_rx_get + 1) & - (rx_bufsize - 1); - tty_insert_flip_char(tty, data, TTY_NORMAL); - info->idle_stats.recv_bytes++; - info->icount.rx++; - } + len = tty_buffer_request_room(port, char_count); + while (len--) { + data = readb(cinfo->base_addr + rx_bufaddr + + new_rx_get); + new_rx_get = (new_rx_get + 1) & + (rx_bufsize - 1); + tty_insert_flip_char(port, data, TTY_NORMAL); + info->idle_stats.recv_bytes++; + info->icount.rx++; + } #endif #ifdef CONFIG_CYZ_INTR - /* Recalculate the number of chars in the RX buffer and issue - a cmd in case it's higher than the RX high water mark */ - rx_put = readl(&buf_ctrl->rx_put); - if (rx_put >= rx_get) - char_count = rx_put - rx_get; - else - char_count = rx_put - rx_get + rx_bufsize; - if (char_count >= readl(&buf_ctrl->rx_threshold) && - !timer_pending(&cyz_rx_full_timer[ - info->line])) - mod_timer(&cyz_rx_full_timer[info->line], - jiffies + 1); + /* Recalculate the number of chars in the RX buffer and issue + a cmd in case it's higher than the RX high water mark */ + rx_put = readl(&buf_ctrl->rx_put); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + if (char_count >= readl(&buf_ctrl->rx_threshold) && + !timer_pending(&cyz_rx_full_timer[ + info->line])) + mod_timer(&cyz_rx_full_timer[info->line], + jiffies + 1); #endif - info->idle_stats.recv_idle = jiffies; - tty_schedule_flip(tty); - } - /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, new_rx_get); - } + info->idle_stats.recv_idle = jiffies; + tty_schedule_flip(&info->port); + + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, new_rx_get); } -static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) +static void cyz_handle_tx(struct cyclades_port *info) { struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; + struct tty_struct *tty; u8 data; unsigned int char_count; #ifdef BLOCKMOVE @@ -1039,63 +1030,63 @@ static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) else char_count = tx_get - tx_put - 1; - if (char_count) { - - if (tty == NULL) - goto ztxdone; + if (!char_count) + return; + + tty = tty_port_tty_get(&info->port); + if (tty == NULL) + goto ztxdone; - if (info->x_char) { /* send special char */ - data = info->x_char; + if (info->x_char) { /* send special char */ + data = info->x_char; - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - info->x_char = 0; - char_count--; - info->icount.tx++; - } + cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + info->x_char = 0; + char_count--; + info->icount.tx++; + } #ifdef BLOCKMOVE - while (0 < (small_count = min_t(unsigned int, - tx_bufsize - tx_put, min_t(unsigned int, - (SERIAL_XMIT_SIZE - info->xmit_tail), - min_t(unsigned int, info->xmit_cnt, - char_count))))) { - - memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + - tx_put), - &info->port.xmit_buf[info->xmit_tail], - small_count); - - tx_put = (tx_put + small_count) & (tx_bufsize - 1); - char_count -= small_count; - info->icount.tx += small_count; - info->xmit_cnt -= small_count; - info->xmit_tail = (info->xmit_tail + small_count) & - (SERIAL_XMIT_SIZE - 1); - } + while (0 < (small_count = min_t(unsigned int, + tx_bufsize - tx_put, min_t(unsigned int, + (SERIAL_XMIT_SIZE - info->xmit_tail), + min_t(unsigned int, info->xmit_cnt, + char_count))))) { + + memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), + &info->port.xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); + char_count -= small_count; + info->icount.tx += small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = (info->xmit_tail + small_count) & + (SERIAL_XMIT_SIZE - 1); + } #else - while (info->xmit_cnt && char_count) { - data = info->port.xmit_buf[info->xmit_tail]; - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) & - (SERIAL_XMIT_SIZE - 1); - - cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); - tx_put = (tx_put + 1) & (tx_bufsize - 1); - char_count--; - info->icount.tx++; - } + while (info->xmit_cnt && char_count) { + data = info->port.xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) & + (SERIAL_XMIT_SIZE - 1); + + cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); + char_count--; + info->icount.tx++; + } #endif - tty_wakeup(tty); + tty_wakeup(tty); + tty_kref_put(tty); ztxdone: - /* Update tx_put */ - cy_writel(&buf_ctrl->tx_put, tx_put); - } + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); } static void cyz_handle_cmd(struct cyclades_card *cinfo) { struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; - struct tty_struct *tty; struct cyclades_port *info; __u32 channel, param, fw_ver; __u8 cmd; @@ -1108,23 +1099,20 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - tty = tty_port_tty_get(&info->port); - if (tty == NULL) - continue; switch (cmd) { case C_CM_PR_ERROR: - tty_insert_flip_char(tty, 0, TTY_PARITY); + tty_insert_flip_char(&info->port, 0, TTY_PARITY); info->icount.rx++; special_count++; break; case C_CM_FR_ERROR: - tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_insert_flip_char(&info->port, 0, TTY_FRAME); info->icount.rx++; special_count++; break; case C_CM_RXBRK: - tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_insert_flip_char(&info->port, 0, TTY_BREAK); info->icount.rx++; special_count++; break; @@ -1137,7 +1125,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); else - tty_hangup(tty); + tty_port_tty_hangup(&info->port, false); } break; case C_CM_MCTS: @@ -1166,7 +1154,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_rx(info, tty); + cyz_handle_rx(info); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: @@ -1176,7 +1164,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_tx(info, tty); + cyz_handle_tx(info); break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: @@ -1188,8 +1176,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) if (delta_count) wake_up_interruptible(&info->port.delta_msr_wait); if (special_count) - tty_schedule_flip(tty); - tty_kref_put(tty); + tty_schedule_flip(&info->port); } } @@ -1255,17 +1242,11 @@ static void cyz_poll(unsigned long arg) cyz_handle_cmd(cinfo); for (port = 0; port < cinfo->nports; port++) { - struct tty_struct *tty; - info = &cinfo->ports[port]; - tty = tty_port_tty_get(&info->port); - /* OK to pass NULL to the handle functions below. - They need to drop the data in that case. */ if (!info->throttle) - cyz_handle_rx(info, tty); - cyz_handle_tx(info, tty); - tty_kref_put(tty); + cyz_handle_rx(info); + cyz_handle_tx(info); } /* poll every 'cyz_polling_cycle' period */ expires = jiffies + cyz_polling_cycle; @@ -2728,6 +2709,8 @@ cy_ioctl(struct tty_struct *tty, break; #ifndef CONFIG_CYZ_INTR case CYZSETPOLLCYCLE: + if (arg > LONG_MAX / HZ) + return -ENODEV; cyz_polling_cycle = (arg * HZ) / 1000; break; case CYZGETPOLLCYCLE: diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index c117d775a22..0419b69e270 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -32,6 +32,7 @@ #include <linux/poll.h> #include <asm/epapr_hcalls.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/console.h> @@ -371,22 +372,17 @@ console_initcall(ehv_bc_console_init); static irqreturn_t ehv_bc_tty_rx_isr(int irq, void *data) { struct ehv_bc_data *bc = data; - struct tty_struct *ttys = tty_port_tty_get(&bc->port); unsigned int rx_count, tx_count, len; int count; char buffer[EV_BYTE_CHANNEL_MAX_BYTES]; int ret; - /* ttys could be NULL during a hangup */ - if (!ttys) - return IRQ_HANDLED; - /* Find out how much data needs to be read, and then ask the TTY layer * if it can handle that much. We want to ensure that every byte we * read from the byte channel will be accepted by the TTY layer. */ ev_byte_channel_poll(bc->handle, &rx_count, &tx_count); - count = tty_buffer_request_room(ttys, rx_count); + count = tty_buffer_request_room(&bc->port, rx_count); /* 'count' is the maximum amount of data the TTY layer can accept at * this time. However, during testing, I was never able to get 'count' @@ -407,7 +403,7 @@ static irqreturn_t ehv_bc_tty_rx_isr(int irq, void *data) */ /* Pass the received data to the tty layer. */ - ret = tty_insert_flip_string(ttys, buffer, len); + ret = tty_insert_flip_string(&bc->port, buffer, len); /* 'ret' is the number of bytes that the TTY layer accepted. * If it's not equal to 'len', then it means the buffer is @@ -422,9 +418,7 @@ static irqreturn_t ehv_bc_tty_rx_isr(int irq, void *data) } /* Tell the tty layer that we're done. */ - tty_flip_buffer_push(ttys); - - tty_kref_put(ttys); + tty_flip_buffer_push(&bc->port); return IRQ_HANDLED; } @@ -479,13 +473,9 @@ static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc) static irqreturn_t ehv_bc_tty_tx_isr(int irq, void *data) { struct ehv_bc_data *bc = data; - struct tty_struct *ttys = tty_port_tty_get(&bc->port); ehv_bc_tx_dequeue(bc); - if (ttys) { - tty_wakeup(ttys); - tty_kref_put(ttys); - } + tty_port_tty_wakeup(&bc->port); return IRQ_HANDLED; } @@ -870,6 +860,7 @@ error: */ static void __exit ehv_bc_exit(void) { + platform_driver_unregister(&ehv_bc_tty_driver); tty_unregister_driver(ehv_bc_driver); put_tty_driver(ehv_bc_driver); kfree(bcs); diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c new file mode 100644 index 00000000000..09495f515fa --- /dev/null +++ b/drivers/tty/goldfish.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2012 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/goldfish.h> + +enum { + GOLDFISH_TTY_PUT_CHAR = 0x00, + GOLDFISH_TTY_BYTES_READY = 0x04, + GOLDFISH_TTY_CMD = 0x08, + + GOLDFISH_TTY_DATA_PTR = 0x10, + GOLDFISH_TTY_DATA_LEN = 0x14, + GOLDFISH_TTY_DATA_PTR_HIGH = 0x18, + + GOLDFISH_TTY_CMD_INT_DISABLE = 0, + GOLDFISH_TTY_CMD_INT_ENABLE = 1, + GOLDFISH_TTY_CMD_WRITE_BUFFER = 2, + GOLDFISH_TTY_CMD_READ_BUFFER = 3, +}; + +struct goldfish_tty { + struct tty_port port; + spinlock_t lock; + void __iomem *base; + u32 irq; + int opencount; + struct console console; +}; + +static DEFINE_MUTEX(goldfish_tty_lock); +static struct tty_driver *goldfish_tty_driver; +static u32 goldfish_tty_line_count = 8; +static u32 goldfish_tty_current_line_count; +static struct goldfish_tty *goldfish_ttys; + +static void goldfish_tty_do_write(int line, const char *buf, unsigned count) +{ + unsigned long irq_flags; + struct goldfish_tty *qtty = &goldfish_ttys[line]; + void __iomem *base = qtty->base; + spin_lock_irqsave(&qtty->lock, irq_flags); + gf_write64((u64)buf, base + GOLDFISH_TTY_DATA_PTR, + base + GOLDFISH_TTY_DATA_PTR_HIGH); + writel(count, base + GOLDFISH_TTY_DATA_LEN); + writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD); + spin_unlock_irqrestore(&qtty->lock, irq_flags); +} + +static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct goldfish_tty *qtty = &goldfish_ttys[pdev->id]; + void __iomem *base = qtty->base; + unsigned long irq_flags; + unsigned char *buf; + u32 count; + + count = readl(base + GOLDFISH_TTY_BYTES_READY); + if (count == 0) + return IRQ_NONE; + + count = tty_prepare_flip_string(&qtty->port, &buf, count); + spin_lock_irqsave(&qtty->lock, irq_flags); + gf_write64((u64)buf, base + GOLDFISH_TTY_DATA_PTR, + base + GOLDFISH_TTY_DATA_PTR_HIGH); + writel(count, base + GOLDFISH_TTY_DATA_LEN); + writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD); + spin_unlock_irqrestore(&qtty->lock, irq_flags); + tty_schedule_flip(&qtty->port); + return IRQ_HANDLED; +} + +static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, + port); + writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD); + return 0; +} + +static void goldfish_tty_shutdown(struct tty_port *port) +{ + struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, + port); + writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD); +} + +static int goldfish_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; + return tty_port_open(&qtty->port, tty, filp); +} + +static void goldfish_tty_close(struct tty_struct *tty, struct file *filp) +{ + tty_port_close(tty->port, tty, filp); +} + +static void goldfish_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static int goldfish_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + goldfish_tty_do_write(tty->index, buf, count); + return count; +} + +static int goldfish_tty_write_room(struct tty_struct *tty) +{ + return 0x10000; +} + +static int goldfish_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct goldfish_tty *qtty = &goldfish_ttys[tty->index]; + void __iomem *base = qtty->base; + return readl(base + GOLDFISH_TTY_BYTES_READY); +} + +static void goldfish_tty_console_write(struct console *co, const char *b, + unsigned count) +{ + goldfish_tty_do_write(co->index, b, count); +} + +static struct tty_driver *goldfish_tty_console_device(struct console *c, + int *index) +{ + *index = c->index; + return goldfish_tty_driver; +} + +static int goldfish_tty_console_setup(struct console *co, char *options) +{ + if ((unsigned)co->index > goldfish_tty_line_count) + return -ENODEV; + if (goldfish_ttys[co->index].base == 0) + return -ENODEV; + return 0; +} + +static struct tty_port_operations goldfish_port_ops = { + .activate = goldfish_tty_activate, + .shutdown = goldfish_tty_shutdown +}; + +static const struct tty_operations goldfish_tty_ops = { + .open = goldfish_tty_open, + .close = goldfish_tty_close, + .hangup = goldfish_tty_hangup, + .write = goldfish_tty_write, + .write_room = goldfish_tty_write_room, + .chars_in_buffer = goldfish_tty_chars_in_buffer, +}; + +static int goldfish_tty_create_driver(void) +{ + int ret; + struct tty_driver *tty; + + goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * + goldfish_tty_line_count, GFP_KERNEL); + if (goldfish_ttys == NULL) { + ret = -ENOMEM; + goto err_alloc_goldfish_ttys_failed; + } + tty = alloc_tty_driver(goldfish_tty_line_count); + if (tty == NULL) { + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + tty->driver_name = "goldfish"; + tty->name = "ttyGF"; + tty->type = TTY_DRIVER_TYPE_SERIAL; + tty->subtype = SERIAL_TYPE_NORMAL; + tty->init_termios = tty_std_termios; + tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(tty, &goldfish_tty_ops); + ret = tty_register_driver(tty); + if (ret) + goto err_tty_register_driver_failed; + + goldfish_tty_driver = tty; + return 0; + +err_tty_register_driver_failed: + put_tty_driver(tty); +err_alloc_tty_driver_failed: + kfree(goldfish_ttys); + goldfish_ttys = NULL; +err_alloc_goldfish_ttys_failed: + return ret; +} + +static void goldfish_tty_delete_driver(void) +{ + tty_unregister_driver(goldfish_tty_driver); + put_tty_driver(goldfish_tty_driver); + goldfish_tty_driver = NULL; + kfree(goldfish_ttys); + goldfish_ttys = NULL; +} + +static int goldfish_tty_probe(struct platform_device *pdev) +{ + struct goldfish_tty *qtty; + int ret = -EINVAL; + int i; + struct resource *r; + struct device *ttydev; + void __iomem *base; + u32 irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) + return -EINVAL; + + base = ioremap(r->start, 0x1000); + if (base == NULL) + pr_err("goldfish_tty: unable to remap base\n"); + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (r == NULL) + goto err_unmap; + + irq = r->start; + + if (pdev->id >= goldfish_tty_line_count) + goto err_unmap; + + mutex_lock(&goldfish_tty_lock); + if (goldfish_tty_current_line_count == 0) { + ret = goldfish_tty_create_driver(); + if (ret) + goto err_create_driver_failed; + } + goldfish_tty_current_line_count++; + + qtty = &goldfish_ttys[pdev->id]; + spin_lock_init(&qtty->lock); + tty_port_init(&qtty->port); + qtty->port.ops = &goldfish_port_ops; + qtty->base = base; + qtty->irq = irq; + + writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD); + + ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, + "goldfish_tty", pdev); + if (ret) + goto err_request_irq_failed; + + + ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, + pdev->id, &pdev->dev); + if (IS_ERR(ttydev)) { + ret = PTR_ERR(ttydev); + goto err_tty_register_device_failed; + } + + strcpy(qtty->console.name, "ttyGF"); + qtty->console.write = goldfish_tty_console_write; + qtty->console.device = goldfish_tty_console_device; + qtty->console.setup = goldfish_tty_console_setup; + qtty->console.flags = CON_PRINTBUFFER; + qtty->console.index = pdev->id; + register_console(&qtty->console); + + mutex_unlock(&goldfish_tty_lock); + return 0; + + tty_unregister_device(goldfish_tty_driver, i); +err_tty_register_device_failed: + free_irq(irq, pdev); +err_request_irq_failed: + goldfish_tty_current_line_count--; + if (goldfish_tty_current_line_count == 0) + goldfish_tty_delete_driver(); +err_create_driver_failed: + mutex_unlock(&goldfish_tty_lock); +err_unmap: + iounmap(base); + return ret; +} + +static int goldfish_tty_remove(struct platform_device *pdev) +{ + struct goldfish_tty *qtty; + + mutex_lock(&goldfish_tty_lock); + + qtty = &goldfish_ttys[pdev->id]; + unregister_console(&qtty->console); + tty_unregister_device(goldfish_tty_driver, pdev->id); + iounmap(qtty->base); + qtty->base = 0; + free_irq(qtty->irq, pdev); + goldfish_tty_current_line_count--; + if (goldfish_tty_current_line_count == 0) + goldfish_tty_delete_driver(); + mutex_unlock(&goldfish_tty_lock); + return 0; +} + +static struct platform_driver goldfish_tty_platform_driver = { + .probe = goldfish_tty_probe, + .remove = goldfish_tty_remove, + .driver = { + .name = "goldfish_tty" + } +}; + +module_platform_driver(goldfish_tty_platform_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index f47b734c6a7..8902f9b4df7 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -1,3 +1,5 @@ +if TTY + config HVC_DRIVER bool help @@ -119,3 +121,4 @@ config HVCS which will also be compiled when this driver is built as a module. +endif # TTY diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 13ee53bd0bf..4fcec1d793a 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -31,6 +31,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/major.h> +#include <linux/atomic.h> #include <linux/sysrq.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -70,6 +71,9 @@ static struct task_struct *hvc_task; /* Picks up late kicks after list walk but before schedule() */ static int hvc_kicked; +/* hvc_init is triggered from hvc_alloc, i.e. only when actually used */ +static atomic_t hvc_needs_init __read_mostly = ATOMIC_INIT(-1); + static int hvc_init(void); #ifdef CONFIG_MAGIC_SYSRQ @@ -186,7 +190,7 @@ static struct tty_driver *hvc_console_device(struct console *c, int *index) return hvc_driver; } -static int __init hvc_console_setup(struct console *co, char *options) +static int hvc_console_setup(struct console *co, char *options) { if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) return -ENODEV; @@ -361,7 +365,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) tty->driver_data = NULL; tty_port_put(&hp->port); printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); - } + } else + /* We are ready... raise DTR/RTS */ + if (C_BAUD(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 1); + /* Force wakeup of the polling thread */ hvc_kick(); @@ -393,6 +402,10 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) /* We are done with the tty pointer now. */ tty_port_tty_set(&hp->port, NULL); + if (C_HUPCL(tty)) + if (hp->ops->dtr_rts) + hp->ops->dtr_rts(hp, 0); + if (hp->ops->notifier_del) hp->ops->notifier_del(hp, hp->data); @@ -629,7 +642,7 @@ int hvc_poll(struct hvc_struct *hp) /* Read data if any */ for (;;) { - int count = tty_buffer_request_room(tty, N_INBUF); + int count = tty_buffer_request_room(&hp->port, N_INBUF); /* If flip is full, just reschedule a later read */ if (count == 0) { @@ -672,7 +685,7 @@ int hvc_poll(struct hvc_struct *hp) } } #endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(tty, buf[i], 0); + tty_insert_flip_char(&hp->port, buf[i], 0); } read_total += n; @@ -691,7 +704,7 @@ int hvc_poll(struct hvc_struct *hp) a minimum for performance. */ timeout = MIN_TIMEOUT; - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&hp->port); } tty_kref_put(tty); @@ -747,10 +760,17 @@ static int khvcd(void *unused) if (poll_mask == 0) schedule(); else { + unsigned long j_timeout; + if (timeout < MAX_TIMEOUT) timeout += (timeout >> 6) + 1; - msleep_interruptible(timeout); + /* + * We don't use msleep_interruptible otherwise + * "kick" will fail to wake us up + */ + j_timeout = msecs_to_jiffies(timeout) + 1; + schedule_timeout_interruptible(j_timeout); } } __set_current_state(TASK_RUNNING); @@ -779,7 +799,7 @@ static int hvc_tiocmset(struct tty_struct *tty, } #ifdef CONFIG_CONSOLE_POLL -int hvc_poll_init(struct tty_driver *driver, int line, char *options) +static int hvc_poll_init(struct tty_driver *driver, int line, char *options) { return 0; } @@ -842,7 +862,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, int i; /* We wait until a driver actually comes along */ - if (!hvc_driver) { + if (atomic_inc_not_zero(&hvc_needs_init)) { int err = hvc_init(); if (err) return ERR_PTR(err); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h index 674d23cb919..91310198082 100644 --- a/drivers/tty/hvc/hvc_console.h +++ b/drivers/tty/hvc/hvc_console.h @@ -75,6 +75,9 @@ struct hv_ops { /* tiocmget/set implementation */ int (*tiocmget)(struct hvc_struct *hp); int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear); + + /* Callbacks to handle tty ports */ + void (*dtr_rts)(struct hvc_struct *hp, int raise); }; /* Register a vterm and a slot index for use as a console (console_init) */ diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c index 44fbebab507..809920d80a6 100644 --- a/drivers/tty/hvc/hvc_dcc.c +++ b/drivers/tty/hvc/hvc_dcc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -8,20 +8,11 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> #include <linux/init.h> -#include <linux/moduleparam.h> -#include <linux/types.h> +#include <asm/dcc.h> #include <asm/processor.h> #include "hvc_console.h" @@ -30,35 +21,6 @@ #define DCC_STATUS_RX (1 << 30) #define DCC_STATUS_TX (1 << 29) -static inline u32 __dcc_getstatus(void) -{ - u32 __ret; - asm volatile("mrc p14, 0, %0, c0, c1, 0 @ read comms ctrl reg" - : "=r" (__ret) : : "cc"); - - return __ret; -} - - -static inline char __dcc_getchar(void) -{ - char __c; - - asm volatile("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" - : "=r" (__c)); - isb(); - - return __c; -} - -static inline void __dcc_putchar(char c) -{ - asm volatile("mcr p14, 0, %0, c0, c5, 0 @ write a char" - : /* no output register */ - : "r" (c)); - isb(); -} - static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) { int i; @@ -86,6 +48,21 @@ static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) return i; } +static bool hvc_dcc_check(void) +{ + unsigned long time = jiffies + (HZ / 10); + + /* Write a test character to check if it is handled */ + __dcc_putchar('\n'); + + while (time_is_after_jiffies(time)) { + if (!(__dcc_getstatus() & DCC_STATUS_TX)) + return true; + } + + return false; +} + static const struct hv_ops hvc_dcc_get_put_ops = { .get_chars = hvc_dcc_get_chars, .put_chars = hvc_dcc_put_chars, @@ -93,6 +70,9 @@ static const struct hv_ops hvc_dcc_get_put_ops = { static int __init hvc_dcc_console_init(void) { + if (!hvc_dcc_check()) + return -ENODEV; + hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); return 0; } @@ -100,6 +80,9 @@ console_initcall(hvc_dcc_console_init); static int __init hvc_dcc_init(void) { + if (!hvc_dcc_check()) + return -ENODEV; + hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); return 0; } diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index b6f7d52f7c3..ea74460f363 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -77,6 +77,7 @@ struct hvc_iucv_private { struct list_head tty_outqueue; /* outgoing IUCV messages */ struct list_head tty_inqueue; /* incoming IUCV messages */ struct device *dev; /* device structure */ + u8 info_path[16]; /* IUCV path info (dev attr) */ }; struct iucv_tty_buffer { @@ -126,7 +127,7 @@ static struct iucv_handler hvc_iucv_handler = { * This function returns the struct hvc_iucv_private instance that corresponds * to the HVC virtual terminal number specified as parameter @num. */ -struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) +static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) { if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices)) return NULL; @@ -656,21 +657,64 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) } /** + * hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS + * @hp: Pointer the HVC device (struct hvc_struct) + * @raise: Non-zero to raise or zero to lower DTR/RTS lines + * + * This routine notifies the HVC back-end to raise or lower DTR/RTS + * lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to + * drop the IUCV connection (similar to hang up the modem). + */ +static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + /* Raising the DTR/RTS is ignored as IUCV connections can be + * established at any times. + */ + if (raise) + return; + + priv = hvc_iucv_get_private(hp->vtermno); + if (!priv) + return; + + /* Lowering the DTR/RTS lines disconnects an established IUCV + * connection. + */ + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + priv->iucv_state = IUCV_DISCONN; + spin_unlock_bh(&priv->lock); + + /* Sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. * @hp: Pointer to the HVC device (struct hvc_struct) * @id: Additional data (originally passed to hvc_alloc): * the index of an struct hvc_iucv_private instance. * * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. + * closed. The function cleans up tty resources. The clean-up of the IUCV + * connection is done in hvc_iucv_dtr_rts() and depends on the HUPCL termios + * control setting. * * Locking: struct hvc_iucv_private->lock */ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) { struct hvc_iucv_private *priv; - struct iucv_path *path; priv = hvc_iucv_get_private(id); if (!priv) @@ -679,17 +723,11 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) flush_sndbuf_sync(priv); spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + priv->tty_state = TTY_CLOSED; + priv->sndbuf_len = 0; spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } } /** @@ -735,18 +773,37 @@ static int hvc_iucv_filter_connreq(u8 ipvmid[8]) static int hvc_iucv_path_pending(struct iucv_path *path, u8 ipvmid[8], u8 ipuser[16]) { - struct hvc_iucv_private *priv; + struct hvc_iucv_private *priv, *tmp; + u8 wildcard[9] = "lnxhvc "; + int i, rc, find_unused; u8 nuser_data[16]; u8 vm_user_id[9]; - int i, rc; + ASCEBC(wildcard, sizeof(wildcard)); + find_unused = !memcmp(wildcard, ipuser, 8); + + /* First, check if the pending path request is managed by this + * IUCV handler: + * - find a disconnected device if ipuser contains the wildcard + * - find the device that matches the terminal ID in ipuser + */ priv = NULL; - for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i] && - (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) { - priv = hvc_iucv_table[i]; + for (i = 0; i < hvc_iucv_devices; i++) { + tmp = hvc_iucv_table[i]; + if (!tmp) + continue; + + if (find_unused) { + spin_lock(&tmp->lock); + if (tmp->iucv_state == IUCV_DISCONN) + priv = tmp; + spin_unlock(&tmp->lock); + + } else if (!memcmp(tmp->srv_name, ipuser, 8)) + priv = tmp; + if (priv) break; - } + } if (!priv) return -ENODEV; @@ -789,6 +846,10 @@ static int hvc_iucv_path_pending(struct iucv_path *path, priv->path = path; priv->iucv_state = IUCV_CONNECTED; + /* store path information */ + memcpy(priv->info_path, ipvmid, 8); + memcpy(priv->info_path + 8, ipuser + 8, 8); + /* flush buffered output data... */ schedule_delayed_work(&priv->sndbuf_work, 5); @@ -923,6 +984,49 @@ static int hvc_iucv_pm_restore_thaw(struct device *dev) return 0; } +static ssize_t hvc_iucv_dev_termid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hvc_iucv_private *priv = dev_get_drvdata(dev); + size_t len; + + len = sizeof(priv->srv_name); + memcpy(buf, priv->srv_name, len); + EBCASC(buf, len); + buf[len++] = '\n'; + return len; +} + +static ssize_t hvc_iucv_dev_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hvc_iucv_private *priv = dev_get_drvdata(dev); + return sprintf(buf, "%u:%u\n", priv->iucv_state, priv->tty_state); +} + +static ssize_t hvc_iucv_dev_peer_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hvc_iucv_private *priv = dev_get_drvdata(dev); + char vmid[9], ipuser[9]; + + memset(vmid, 0, sizeof(vmid)); + memset(ipuser, 0, sizeof(ipuser)); + + spin_lock_bh(&priv->lock); + if (priv->iucv_state == IUCV_CONNECTED) { + memcpy(vmid, priv->info_path, 8); + memcpy(ipuser, priv->info_path + 8, 8); + } + spin_unlock_bh(&priv->lock); + EBCASC(ipuser, 8); + + return sprintf(buf, "%s:%s\n", vmid, ipuser); +} + /* HVC operations */ static const struct hv_ops hvc_iucv_ops = { @@ -931,6 +1035,7 @@ static const struct hv_ops hvc_iucv_ops = { .notifier_add = hvc_iucv_notifier_add, .notifier_del = hvc_iucv_notifier_del, .notifier_hangup = hvc_iucv_notifier_hangup, + .dtr_rts = hvc_iucv_dtr_rts, }; /* Suspend / resume device operations */ @@ -947,6 +1052,25 @@ static struct device_driver hvc_iucv_driver = { .pm = &hvc_iucv_pm_ops, }; +/* IUCV HVC device attributes */ +static DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL); +static DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL); +static DEVICE_ATTR(peer, 0640, hvc_iucv_dev_peer_show, NULL); +static struct attribute *hvc_iucv_dev_attrs[] = { + &dev_attr_termid.attr, + &dev_attr_state.attr, + &dev_attr_peer.attr, + NULL, +}; +static struct attribute_group hvc_iucv_dev_attr_group = { + .attrs = hvc_iucv_dev_attrs, +}; +static const struct attribute_group *hvc_iucv_dev_attr_groups[] = { + &hvc_iucv_dev_attr_group, + NULL, +}; + + /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance * @id: hvc_iucv_table index @@ -1008,6 +1132,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) priv->dev->bus = &iucv_bus; priv->dev->parent = iucv_root; priv->dev->driver = &hvc_iucv_driver; + priv->dev->groups = hvc_iucv_dev_attr_groups; priv->dev->release = (void (*)(struct device *)) kfree; rc = device_register(priv->dev); if (rc) { @@ -1316,8 +1441,7 @@ out_error_memory: mempool_destroy(hvc_iucv_mempool); kmem_cache_destroy(hvc_iucv_buffer_cache); out_error: - if (hvc_iucv_filter) - kfree(hvc_iucv_filter); + kfree(hvc_iucv_filter); hvc_iucv_devices = 0; /* ensure that we do not provide any device */ return rc; } @@ -1328,7 +1452,7 @@ out_error: */ static int __init hvc_iucv_config(char *val) { - return strict_strtoul(val, 10, &hvc_iucv_devices); + return kstrtoul(val, 10, &hvc_iucv_devices); } diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index cd69b48f6df..a585079b4b3 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -61,6 +61,7 @@ static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES]; /* For early boot console */ static struct hvc_opal_priv hvc_opal_boot_priv; static u32 hvc_opal_boot_termno; +static bool hvc_opal_event_registered; static const struct hv_ops hvc_opal_raw_ops = { .get_chars = opal_get_chars, @@ -161,6 +162,18 @@ static const struct hv_ops hvc_opal_hvsi_ops = { .tiocmset = hvc_opal_hvsi_tiocmset, }; +static int hvc_opal_console_event(struct notifier_block *nb, + unsigned long events, void *change) +{ + if (events & OPAL_EVENT_CONSOLE_INPUT) + hvc_kick(); + return 0; +} + +static struct notifier_block hvc_opal_console_nb = { + .notifier_call = hvc_opal_console_event, +}; + static int hvc_opal_probe(struct platform_device *dev) { const struct hv_ops *ops; @@ -170,6 +183,7 @@ static int hvc_opal_probe(struct platform_device *dev) unsigned int termno, boot = 0; const __be32 *reg; + if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) { proto = HV_PROTOCOL_RAW; ops = &hvc_opal_raw_ops; @@ -213,12 +227,18 @@ static int hvc_opal_probe(struct platform_device *dev) dev->dev.of_node->full_name, boot ? " (boot console)" : ""); - /* We don't do IRQ yet */ + /* We don't do IRQ ... */ hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&dev->dev, hp); + /* ... but we use OPAL event to kick the console */ + if (!hvc_opal_event_registered) { + opal_notifier_register(&hvc_opal_console_nb); + hvc_opal_event_registered = true; + } + return 0; } @@ -255,13 +275,7 @@ static int __init hvc_opal_init(void) /* Register as a vio device to receive callbacks */ return platform_driver_register(&hvc_opal_driver); } -module_init(hvc_opal_init); - -static void __exit hvc_opal_exit(void) -{ - platform_driver_unregister(&hvc_opal_driver); -} -module_exit(hvc_opal_exit); +device_initcall(hvc_opal_init); static void udbg_opal_putc(char c) { @@ -329,7 +343,7 @@ static void udbg_init_opal_common(void) void __init hvc_opal_init_early(void) { struct device_node *stdout_node = NULL; - const u32 *termno; + const __be32 *termno; const char *name = NULL; const struct hv_ops *ops; u32 index; @@ -371,7 +385,7 @@ void __init hvc_opal_init_early(void) if (!stdout_node) return; termno = of_get_property(stdout_node, "reg", NULL); - index = termno ? *termno : 0; + index = termno ? be32_to_cpup(termno) : 0; if (index >= MAX_NR_HVC_CONSOLES) return; hvc_opal_privs[index] = &hvc_opal_boot_priv; diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c index 0069bb86ba4..08c87920b74 100644 --- a/drivers/tty/hvc/hvc_rtas.c +++ b/drivers/tty/hvc/hvc_rtas.c @@ -102,17 +102,7 @@ static int __init hvc_rtas_init(void) return 0; } -module_init(hvc_rtas_init); - -/* This will tear down the tty portion of the driver */ -static void __exit hvc_rtas_exit(void) -{ - /* Really the fun isn't over until the worker thread breaks down and - * the tty cleans up */ - if (hvc_rtas_dev) - hvc_remove(hvc_rtas_dev); -} -module_exit(hvc_rtas_exit); +device_initcall(hvc_rtas_init); /* This will happen prior to module init. There is no tty at this time? */ static int __init hvc_rtas_console_init(void) diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c index 7a84a059547..df374860037 100644 --- a/drivers/tty/hvc/hvc_tile.c +++ b/drivers/tty/hvc/hvc_tile.c @@ -18,16 +18,46 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> #include <linux/moduleparam.h> +#include <linux/platform_device.h> #include <linux/types.h> +#include <asm/setup.h> +#include <arch/sim_def.h> + #include <hv/hypervisor.h> #include "hvc_console.h" +static int use_sim_console; +static int __init sim_console(char *str) +{ + use_sim_console = 1; + return 0; +} +early_param("sim_console", sim_console); + +int tile_console_write(const char *buf, int count) +{ + if (unlikely(use_sim_console)) { + int i; + for (i = 0; i < count; ++i) + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | + (buf[i] << _SIM_CONTROL_OPERATOR_BITS)); + __insn_mtspr(SPR_SIM_CONTROL, SIM_CONTROL_PUTC | + (SIM_PUTC_FLUSH_BINARY << + _SIM_CONTROL_OPERATOR_BITS)); + return 0; + } else { + return hv_console_write((HV_VirtAddr)buf, count); + } +} + static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) { - return hv_console_write((HV_VirtAddr)buf, count); + return tile_console_write(buf, count); } static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) @@ -44,25 +74,132 @@ static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) return i; } +#ifdef __tilegx__ +/* + * IRQ based callbacks. + */ +static int hvc_tile_notifier_add_irq(struct hvc_struct *hp, int irq) +{ + int rc; + int cpu = raw_smp_processor_id(); /* Choose an arbitrary cpu */ + HV_Coord coord = { .x = cpu_x(cpu), .y = cpu_y(cpu) }; + + rc = notifier_add_irq(hp, irq); + if (rc) + return rc; + + /* + * Request that the hypervisor start sending us interrupts. + * If the hypervisor returns an error, we still return 0, so that + * we can fall back to polling. + */ + if (hv_console_set_ipi(KERNEL_PL, irq, coord) < 0) + notifier_del_irq(hp, irq); + + return 0; +} + +static void hvc_tile_notifier_del_irq(struct hvc_struct *hp, int irq) +{ + HV_Coord coord = { 0, 0 }; + + /* Tell the hypervisor to stop sending us interrupts. */ + hv_console_set_ipi(KERNEL_PL, -1, coord); + + notifier_del_irq(hp, irq); +} + +static void hvc_tile_notifier_hangup_irq(struct hvc_struct *hp, int irq) +{ + hvc_tile_notifier_del_irq(hp, irq); +} +#endif + static const struct hv_ops hvc_tile_get_put_ops = { .get_chars = hvc_tile_get_chars, .put_chars = hvc_tile_put_chars, +#ifdef __tilegx__ + .notifier_add = hvc_tile_notifier_add_irq, + .notifier_del = hvc_tile_notifier_del_irq, + .notifier_hangup = hvc_tile_notifier_hangup_irq, +#endif +}; + + +#ifdef __tilegx__ +static int hvc_tile_probe(struct platform_device *pdev) +{ + struct hvc_struct *hp; + int tile_hvc_irq; + + /* Create our IRQ and register it. */ + tile_hvc_irq = irq_alloc_hwirq(-1); + if (!tile_hvc_irq) + return -ENXIO; + + tile_irq_activate(tile_hvc_irq, TILE_IRQ_PERCPU); + hp = hvc_alloc(0, tile_hvc_irq, &hvc_tile_get_put_ops, 128); + if (IS_ERR(hp)) { + irq_free_hwirq(tile_hvc_irq); + return PTR_ERR(hp); + } + dev_set_drvdata(&pdev->dev, hp); + + return 0; +} + +static int hvc_tile_remove(struct platform_device *pdev) +{ + int rc; + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); + + rc = hvc_remove(hp); + if (rc == 0) + irq_free_hwirq(hp->data); + + return rc; +} + +static void hvc_tile_shutdown(struct platform_device *pdev) +{ + struct hvc_struct *hp = dev_get_drvdata(&pdev->dev); + + hvc_tile_notifier_del_irq(hp, hp->data); +} + +static struct platform_device hvc_tile_pdev = { + .name = "hvc-tile", + .id = 0, +}; + +static struct platform_driver hvc_tile_driver = { + .probe = hvc_tile_probe, + .remove = hvc_tile_remove, + .shutdown = hvc_tile_shutdown, + .driver = { + .name = "hvc-tile", + .owner = THIS_MODULE, + } }; +#endif static int __init hvc_tile_console_init(void) { - extern void disable_early_printk(void); hvc_instantiate(0, 0, &hvc_tile_get_put_ops); add_preferred_console("hvc", 0, NULL); - disable_early_printk(); return 0; } console_initcall(hvc_tile_console_init); static int __init hvc_tile_init(void) { - struct hvc_struct *s; - s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); - return IS_ERR(s) ? PTR_ERR(s) : 0; +#ifndef __tilegx__ + struct hvc_struct *hp; + hp = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); + return PTR_ERR_OR_ZERO(hp); +#else + platform_device_register(&hvc_tile_pdev); + return platform_driver_register(&hvc_tile_driver); +#endif } device_initcall(hvc_tile_init); diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c index 72228276fe3..9cf573d06a2 100644 --- a/drivers/tty/hvc/hvc_udbg.c +++ b/drivers/tty/hvc/hvc_udbg.c @@ -80,14 +80,7 @@ static int __init hvc_udbg_init(void) return 0; } -module_init(hvc_udbg_init); - -static void __exit hvc_udbg_exit(void) -{ - if (hvc_udbg_dev) - hvc_remove(hvc_udbg_dev); -} -module_exit(hvc_udbg_exit); +device_initcall(hvc_udbg_init); static int __init hvc_udbg_console_init(void) { diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 0c629807610..b594abfbf21 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -48,6 +48,7 @@ #include <asm/prom.h> #include <asm/hvsi.h> #include <asm/udbg.h> +#include <asm/machdep.h> #include "hvc_console.h" @@ -404,7 +405,7 @@ module_exit(hvc_vio_exit); void __init hvc_vio_init_early(void) { struct device_node *stdout_node; - const u32 *termno; + const __be32 *termno; const char *name; const struct hv_ops *ops; @@ -429,7 +430,7 @@ void __init hvc_vio_init_early(void) termno = of_get_property(stdout_node, "reg", NULL); if (termno == NULL) goto out; - hvterm_priv0.termno = *termno; + hvterm_priv0.termno = of_read_number(termno, 1); spin_lock_init(&hvterm_priv0.buf_lock); hvterm_privs[0] = &hvterm_priv0; @@ -457,7 +458,9 @@ void __init hvc_vio_init_early(void) if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) goto out; #endif - add_preferred_console("hvc", 0, NULL); + /* Check whether the user has requested a different console. */ + if (!strstr(cmd_line, "console=")) + add_preferred_console("hvc", 0, NULL); hvc_instantiate(0, 0, ops); out: of_node_put(stdout_node); diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 19843ec3f80..2dc2831840c 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -183,7 +183,7 @@ static int dom0_write_console(uint32_t vtermno, const char *str, int len) { int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); if (rc < 0) - return 0; + return rc; return len; } @@ -208,7 +208,7 @@ static int xen_hvm_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -230,7 +230,7 @@ static int xen_hvm_console_init(void) if (r < 0 || v == 0) goto err; mfn = v; - info->intf = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE); + info->intf = xen_remap(mfn << PAGE_SHIFT, PAGE_SIZE); if (info->intf == NULL) goto err; info->vtermno = HVC_COOKIE; @@ -257,7 +257,7 @@ static int xen_pv_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } else if (info->intf != NULL) { @@ -284,7 +284,7 @@ static int xen_initial_domain_console_init(void) info = vtermno_to_xencons(HVC_COOKIE); if (!info) { - info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO); + info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL); if (!info) return -ENOMEM; } @@ -561,18 +561,7 @@ static int __init xen_hvc_init(void) #endif return r; } - -static void __exit xen_hvc_fini(void) -{ - struct xencons_info *entry, *next; - - if (list_empty(&xenconsoles)) - return; - - list_for_each_entry_safe(entry, next, &xenconsoles, list) { - xen_console_remove(entry); - } -} +device_initcall(xen_hvc_init); static int xen_cons_init(void) { @@ -598,10 +587,6 @@ static int xen_cons_init(void) hvc_instantiate(HVC_COOKIE, 0, ops); return 0; } - - -module_init(xen_hvc_init); -module_exit(xen_hvc_fini); console_initcall(xen_cons_init); #ifdef CONFIG_EARLY_PRINTK @@ -636,12 +621,28 @@ struct console xenboot_console = { .name = "xenboot", .write = xenboot_write_console, .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, + .index = -1, }; #endif /* CONFIG_EARLY_PRINTK */ void xen_raw_console_write(const char *str) { - dom0_write_console(0, str, strlen(str)); + ssize_t len = strlen(str); + int rc = 0; + + if (xen_domain()) { + rc = dom0_write_console(0, str, len); +#ifdef CONFIG_X86 + if (rc == -ENOSYS && xen_hvm_domain()) + goto outb_print; + + } else if (xen_cpuid_base()) { + int i; +outb_print: + for (i = 0; i < len; i++) + outb(str[i], 0xe9); +#endif + } } void xen_raw_printk(const char *fmt, ...) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 87763573395..81e939e90c4 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -609,11 +609,11 @@ static int hvcs_io(struct hvcs_struct *hvcsd) /* remove the read masks */ hvcsd->todo_mask &= ~(HVCS_READ_MASK); - if (tty_buffer_request_room(tty, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) { + if (tty_buffer_request_room(&hvcsd->port, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) { got = hvc_get_chars(unit_address, &buf[0], HVCS_BUFF_LEN); - tty_insert_flip_string(tty, buf, got); + tty_insert_flip_string(&hvcsd->port, buf, got); } /* Give the TTY time to process the data we just sent. */ @@ -623,7 +623,7 @@ static int hvcs_io(struct hvcs_struct *hvcsd) spin_unlock_irqrestore(&hvcsd->lock, flags); /* This is synch because tty->low_latency == 1 */ if(got) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&hvcsd->port); if (!got) { /* Do this _after_ the flip_buffer_push */ @@ -881,17 +881,12 @@ static struct vio_driver hvcs_vio_driver = { /* Only called from hvcs_get_pi please */ static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) { - int clclength; - hvcsd->p_unit_address = pi->unit_address; hvcsd->p_partition_ID = pi->partition_ID; - clclength = strlen(&pi->location_code[0]); - if (clclength > HVCS_CLC_LENGTH) - clclength = HVCS_CLC_LENGTH; /* copy the null-term char too */ - strncpy(&hvcsd->p_location_code[0], - &pi->location_code[0], clclength + 1); + strlcpy(&hvcsd->p_location_code[0], + &pi->location_code[0], sizeof(hvcsd->p_location_code)); } /* diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 68357a6e4de..41901997c0d 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -329,8 +329,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) } } -static void hvsi_insert_chars(struct hvsi_struct *hp, struct tty_struct *tty, - const char *buf, int len) +static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) { int i; @@ -346,7 +345,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, struct tty_struct *tty, continue; } #endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(tty, c, 0); + tty_insert_flip_char(&hp->port, c, 0); } } @@ -359,8 +358,7 @@ static void hvsi_insert_chars(struct hvsi_struct *hp, struct tty_struct *tty, * revisited. */ #define TTY_THRESHOLD_THROTTLE 128 -static bool hvsi_recv_data(struct hvsi_struct *hp, struct tty_struct *tty, - const uint8_t *packet) +static bool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet) { const struct hvsi_header *header = (const struct hvsi_header *)packet; const uint8_t *data = packet + sizeof(struct hvsi_header); @@ -377,7 +375,7 @@ static bool hvsi_recv_data(struct hvsi_struct *hp, struct tty_struct *tty, datalen = TTY_THRESHOLD_THROTTLE; } - hvsi_insert_chars(hp, tty, data, datalen); + hvsi_insert_chars(hp, data, datalen); if (overflow > 0) { /* @@ -438,9 +436,7 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, case VS_DATA_PACKET_HEADER: if (!is_open(hp)) break; - if (tty == NULL) - break; /* no tty buffer to put data in */ - flip = hvsi_recv_data(hp, tty, packet); + flip = hvsi_recv_data(hp, packet); break; case VS_CONTROL_PACKET_HEADER: hvsi_recv_control(hp, packet, tty, handshake); @@ -469,17 +465,17 @@ static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, compact_inbuf(hp, packet); if (flip) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&hp->port); return 1; } -static void hvsi_send_overflow(struct hvsi_struct *hp, struct tty_struct *tty) +static void hvsi_send_overflow(struct hvsi_struct *hp) { pr_debug("%s: delivering %i bytes overflow\n", __func__, hp->n_throttle); - hvsi_insert_chars(hp, tty, hp->throttle_buf, hp->n_throttle); + hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); hp->n_throttle = 0; } @@ -514,8 +510,8 @@ static irqreturn_t hvsi_interrupt(int irq, void *arg) if (tty && hp->n_throttle && !test_bit(TTY_THROTTLED, &tty->flags)) { /* we weren't hung up and we weren't throttled, so we can * deliver the rest now */ - hvsi_send_overflow(hp, tty); - tty_flip_buffer_push(tty); + hvsi_send_overflow(hp); + tty_flip_buffer_push(&hp->port); } spin_unlock_irqrestore(&hp->lock, flags); @@ -865,7 +861,6 @@ static void hvsi_write_worker(struct work_struct *work) { struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); - struct tty_struct *tty; unsigned long flags; #ifdef DEBUG static long start_j = 0; @@ -899,11 +894,7 @@ static void hvsi_write_worker(struct work_struct *work) start_j = 0; #endif /* DEBUG */ wake_up_all(&hp->emptyq); - tty = tty_port_tty_get(&hp->port); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } + tty_port_tty_wakeup(&hp->port); } out: @@ -1001,8 +992,8 @@ static void hvsi_unthrottle(struct tty_struct *tty) spin_lock_irqsave(&hp->lock, flags); if (hp->n_throttle) { - hvsi_send_overflow(hp, tty); - tty_flip_buffer_push(tty); + hvsi_send_overflow(hp); + tty_flip_buffer_push(&hp->port); } spin_unlock_irqrestore(&hp->lock, flags); @@ -1187,9 +1178,7 @@ static int __init hvsi_console_init(void) hvsi_wait = poll_for_state; /* no irqs yet; must poll */ /* search device tree for vty nodes */ - for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); - vty != NULL; - vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { + for_each_compatible_node(vty, "serial", "hvterm-protocol") { struct hvsi_struct *hp; const uint32_t *vtermno, *irq; diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index 3396eb9d57a..7ae6c293e51 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -1,5 +1,4 @@ #include <linux/types.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/console.h> @@ -9,7 +8,7 @@ static int hvsi_send_packet(struct hvsi_priv *pv, struct hvsi_header *packet) { - packet->seqno = atomic_inc_return(&pv->seqno); + packet->seqno = cpu_to_be16(atomic_inc_return(&pv->seqno)); /* Assumes that always succeeds, works in practice */ return pv->put_chars(pv->termno, (char *)packet, packet->len); @@ -28,7 +27,7 @@ static void hvsi_start_handshake(struct hvsi_priv *pv) /* Send version query */ q.hdr.type = VS_QUERY_PACKET_HEADER; q.hdr.len = sizeof(struct hvsi_query); - q.verb = VSV_SEND_VERSION_NUMBER; + q.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); hvsi_send_packet(pv, &q.hdr); } @@ -40,7 +39,7 @@ static int hvsi_send_close(struct hvsi_priv *pv) ctrl.hdr.type = VS_CONTROL_PACKET_HEADER; ctrl.hdr.len = sizeof(struct hvsi_control); - ctrl.verb = VSV_CLOSE_PROTOCOL; + ctrl.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); return hvsi_send_packet(pv, &ctrl.hdr); } @@ -69,14 +68,14 @@ static void hvsi_got_control(struct hvsi_priv *pv) { struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf; - switch (pkt->verb) { + switch (be16_to_cpu(pkt->verb)) { case VSV_CLOSE_PROTOCOL: /* We restart the handshaking */ hvsi_start_handshake(pv); break; case VSV_MODEM_CTL_UPDATE: /* Transition of carrier detect */ - hvsi_cd_change(pv, pkt->word & HVSI_TSCD); + hvsi_cd_change(pv, be32_to_cpu(pkt->word) & HVSI_TSCD); break; } } @@ -87,7 +86,7 @@ static void hvsi_got_query(struct hvsi_priv *pv) struct hvsi_query_response r; /* We only handle version queries */ - if (pkt->verb != VSV_SEND_VERSION_NUMBER) + if (be16_to_cpu(pkt->verb) != VSV_SEND_VERSION_NUMBER) return; pr_devel("HVSI@%x: Got version query, sending response...\n", @@ -96,7 +95,7 @@ static void hvsi_got_query(struct hvsi_priv *pv) /* Send version response */ r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; r.hdr.len = sizeof(struct hvsi_query_response); - r.verb = VSV_SEND_VERSION_NUMBER; + r.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); r.u.version = HVSI_VERSION; r.query_seqno = pkt->hdr.seqno; hvsi_send_packet(pv, &r.hdr); @@ -112,7 +111,7 @@ static void hvsi_got_response(struct hvsi_priv *pv) switch(r->verb) { case VSV_SEND_MODEM_CTL_STATUS: - hvsi_cd_change(pv, r->u.mctrl_word & HVSI_TSCD); + hvsi_cd_change(pv, be32_to_cpu(r->u.mctrl_word) & HVSI_TSCD); pv->mctrl_update = 1; break; } @@ -265,8 +264,7 @@ int hvsilib_read_mctrl(struct hvsi_priv *pv) pv->mctrl_update = 0; q.hdr.type = VS_QUERY_PACKET_HEADER; q.hdr.len = sizeof(struct hvsi_query); - q.hdr.seqno = atomic_inc_return(&pv->seqno); - q.verb = VSV_SEND_MODEM_CTL_STATUS; + q.verb = cpu_to_be16(VSV_SEND_MODEM_CTL_STATUS); rc = hvsi_send_packet(pv, &q.hdr); if (rc <= 0) { pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc); @@ -304,9 +302,9 @@ int hvsilib_write_mctrl(struct hvsi_priv *pv, int dtr) ctrl.hdr.type = VS_CONTROL_PACKET_HEADER, ctrl.hdr.len = sizeof(struct hvsi_control); - ctrl.verb = VSV_SET_MODEM_CTL; - ctrl.mask = HVSI_TSDTR; - ctrl.word = dtr ? HVSI_TSDTR : 0; + ctrl.verb = cpu_to_be16(VSV_SET_MODEM_CTL); + ctrl.mask = cpu_to_be32(HVSI_TSDTR); + ctrl.word = cpu_to_be32(dtr ? HVSI_TSDTR : 0); return hvsi_send_packet(pv, &ctrl.hdr); } @@ -341,8 +339,8 @@ void hvsilib_establish(struct hvsi_priv *pv) pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); - /* Try for up to 200s */ - for (timeout = 0; timeout < 20; timeout++) { + /* Try for up to 400ms */ + for (timeout = 0; timeout < 40; timeout++) { if (pv->established) goto established; if (!hvsi_get_packet(pv)) diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index b4ba0670dc5..2c14842541d 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -646,7 +646,7 @@ static void queue_received_packet(struct ipw_hardware *hw, (*assem) = pool_allocate(hw, *assem, length); if (!(*assem)) { printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": no memory for incomming data packet, dropped!\n"); + ": no memory for incoming data packet, dropped!\n"); return; } (*assem)->protocol = protocol; @@ -670,7 +670,7 @@ static void queue_received_packet(struct ipw_hardware *hw, packet = pool_allocate(hw, NULL, length); if (!packet) { printk(KERN_ERR IPWIRELESS_PCCARD_NAME - ": no memory for incomming ctrl packet, dropped!\n"); + ": no memory for incoming ctrl packet, dropped!\n"); return; } packet->protocol = protocol; @@ -1732,8 +1732,7 @@ void ipwireless_hardware_free(struct ipw_hardware *hw) flush_work(&hw->work_rx); for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) - if (hw->packet_assembler[i] != NULL) - kfree(hw->packet_assembler[i]); + kfree(hw->packet_assembler[i]); for (i = 0; i < NL_NUM_OF_PRIORITIES; i++) list_for_each_entry_safe(tp, tq, &hw->tx_queue[i], queue) { diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c index 2cde13ddf9f..17ee3bf0926 100644 --- a/drivers/tty/ipwireless/tty.c +++ b/drivers/tty/ipwireless/tty.c @@ -15,7 +15,6 @@ * Copyright (C) 2007 David Sterba */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> @@ -106,7 +105,7 @@ static int ipw_open(struct tty_struct *linux_tty, struct file *filp) tty->port.tty = linux_tty; linux_tty->driver_data = tty; - linux_tty->low_latency = 1; + tty->port.low_latency = 1; if (tty->tty_type == TTYTYPE_MODEM) ipwireless_ppp_open(tty->network); @@ -160,15 +159,9 @@ static void ipw_close(struct tty_struct *linux_tty, struct file *filp) void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, unsigned int length) { - struct tty_struct *linux_tty; int work = 0; mutex_lock(&tty->ipw_tty_mutex); - linux_tty = tty->port.tty; - if (linux_tty == NULL) { - mutex_unlock(&tty->ipw_tty_mutex); - return; - } if (!tty->port.count) { mutex_unlock(&tty->ipw_tty_mutex); @@ -176,18 +169,15 @@ void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, } mutex_unlock(&tty->ipw_tty_mutex); - work = tty_insert_flip_string(linux_tty, data, length); + work = tty_insert_flip_string(&tty->port, data, length); if (work != length) printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": %d chars not inserted to flip buffer!\n", length - work); - /* - * This may sleep if ->low_latency is set - */ if (work) - tty_flip_buffer_push(linux_tty); + tty_flip_buffer_push(&tty->port); } static void ipw_write_packet_sent_callback(void *callback_data, diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index 3205b2e9090..858291ca889 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -634,10 +634,10 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) break; case 1: /* Received Break !!! */ - tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_insert_flip_char(&port->port, 0, TTY_BREAK); if (port->port.flags & ASYNC_SAK) do_SAK(tty); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->port); break; case 2: /* Statistics */ @@ -650,15 +650,15 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) break; } } else { /* Data Packet */ - - count = tty_prepare_flip_string(tty, &rp, byte_count & ~1); + count = tty_prepare_flip_string(&port->port, &rp, + byte_count & ~1); pr_debug("%s: Can rx %d of %d bytes.\n", __func__, count, byte_count); word_count = count >> 1; insw(base, rp, word_count); byte_count -= (word_count << 1); if (count & 0x0001) { - tty_insert_flip_char(tty, inw(base) & 0xff, + tty_insert_flip_char(&port->port, inw(base) & 0xff, TTY_NORMAL); byte_count -= 2; } @@ -671,7 +671,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) byte_count -= 2; } } - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->port); } outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c new file mode 100644 index 00000000000..7332e2ca461 --- /dev/null +++ b/drivers/tty/metag_da.c @@ -0,0 +1,677 @@ +/* + * dashtty.c - tty driver for Dash channels interface. + * + * Copyright (C) 2007,2008,2012 Imagination Technologies + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include <linux/atomic.h> +#include <linux/completion.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/serial.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/uaccess.h> + +#include <asm/da.h> + +/* Channel error codes */ +#define CONAOK 0 +#define CONERR 1 +#define CONBAD 2 +#define CONPRM 3 +#define CONADR 4 +#define CONCNT 5 +#define CONCBF 6 +#define CONCBE 7 +#define CONBSY 8 + +/* Default channel for the console */ +#define CONSOLE_CHANNEL 1 + +#define NUM_TTY_CHANNELS 6 + +/* Auto allocate */ +#define DA_TTY_MAJOR 0 + +/* A speedy poll rate helps the userland debug process connection response. + * But, if you set it too high then no other userland processes get much + * of a look in. + */ +#define DA_TTY_POLL (HZ / 50) + +/* + * A short put delay improves latency but has a high throughput overhead + */ +#define DA_TTY_PUT_DELAY (HZ / 100) + +static atomic_t num_channels_need_poll = ATOMIC_INIT(0); + +static struct timer_list poll_timer; + +static struct tty_driver *channel_driver; + +static struct timer_list put_timer; +static struct task_struct *dashtty_thread; + +#define RX_BUF_SIZE 1024 + +enum { + INCHR = 1, + OUTCHR, + RDBUF, + WRBUF, + RDSTAT +}; + +/** + * struct dashtty_port - Wrapper struct for dashtty tty_port. + * @port: TTY port data + * @rx_lock: Lock for rx_buf. + * This protects between the poll timer and user context. + * It's also held during read SWITCH operations. + * @rx_buf: Read buffer + * @xmit_lock: Lock for xmit_*, and port.xmit_buf. + * This protects between user context and kernel thread. + * It's also held during write SWITCH operations. + * @xmit_cnt: Size of xmit buffer contents + * @xmit_head: Head of xmit buffer where data is written + * @xmit_tail: Tail of xmit buffer where data is read + * @xmit_empty: Completion for xmit buffer being empty + */ +struct dashtty_port { + struct tty_port port; + spinlock_t rx_lock; + void *rx_buf; + struct mutex xmit_lock; + unsigned int xmit_cnt; + unsigned int xmit_head; + unsigned int xmit_tail; + struct completion xmit_empty; +}; + +static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS]; + +static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0); +static wait_queue_head_t dashtty_waitqueue; + +/* + * Low-level DA channel access routines + */ +static int chancall(int in_bios_function, int in_channel, + int in_arg2, void *in_arg3, + void *in_arg4) +{ + register int bios_function asm("D1Ar1") = in_bios_function; + register int channel asm("D0Ar2") = in_channel; + register int arg2 asm("D1Ar3") = in_arg2; + register void *arg3 asm("D0Ar4") = in_arg3; + register void *arg4 asm("D1Ar5") = in_arg4; + register int bios_call asm("D0Ar6") = 3; + register int result asm("D0Re0"); + + asm volatile ( + "MSETL [A0StP++], %6,%4,%2\n\t" + "ADD A0StP, A0StP, #8\n\t" + "SWITCH #0x0C30208\n\t" + "GETD %0, [A0StP+#-8]\n\t" + "SUB A0StP, A0StP, #(4*6)+8\n\t" + : "=d" (result) /* outs */ + : "d" (bios_function), + "d" (channel), + "d" (arg2), + "d" (arg3), + "d" (arg4), + "d" (bios_call) /* ins */ + : "memory"); + + return result; +} + +/* + * Attempts to fetch count bytes from channel and returns actual count. + */ +static int fetch_data(unsigned int channel) +{ + struct dashtty_port *dport = &dashtty_ports[channel]; + int received = 0; + + spin_lock_bh(&dport->rx_lock); + /* check the port isn't being shut down */ + if (!dport->rx_buf) + goto unlock; + if (chancall(RDBUF, channel, RX_BUF_SIZE, + (void *)dport->rx_buf, &received) == CONAOK) { + if (received) { + int space; + unsigned char *cbuf; + + space = tty_prepare_flip_string(&dport->port, &cbuf, + received); + + if (space <= 0) + goto unlock; + + memcpy(cbuf, dport->rx_buf, space); + tty_flip_buffer_push(&dport->port); + } + } +unlock: + spin_unlock_bh(&dport->rx_lock); + + return received; +} + +/** + * find_channel_to_poll() - Returns number of the next channel to poll. + * Returns: The number of the next channel to poll, or -1 if none need + * polling. + */ +static int find_channel_to_poll(void) +{ + static int last_polled_channel; + int last = last_polled_channel; + int chan; + struct dashtty_port *dport; + + for (chan = last + 1; ; ++chan) { + if (chan >= NUM_TTY_CHANNELS) + chan = 0; + + dport = &dashtty_ports[chan]; + if (dport->rx_buf) { + last_polled_channel = chan; + return chan; + } + + if (chan == last) + break; + } + return -1; +} + +/** + * put_channel_data() - Write out a block of channel data. + * @chan: DA channel number. + * + * Write a single block of data out to the debug adapter. If the circular buffer + * is wrapped then only the first block is written. + * + * Returns: 1 if the remote buffer was too full to accept data. + * 0 otherwise. + */ +static int put_channel_data(unsigned int chan) +{ + struct dashtty_port *dport; + struct tty_struct *tty; + int number_written; + unsigned int count = 0; + + dport = &dashtty_ports[chan]; + mutex_lock(&dport->xmit_lock); + if (dport->xmit_cnt) { + count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail), + dport->xmit_cnt); + chancall(WRBUF, chan, count, + dport->port.xmit_buf + dport->xmit_tail, + &number_written); + dport->xmit_cnt -= number_written; + if (!dport->xmit_cnt) { + /* reset pointers to avoid wraps */ + dport->xmit_head = 0; + dport->xmit_tail = 0; + complete(&dport->xmit_empty); + } else { + dport->xmit_tail += number_written; + if (dport->xmit_tail >= SERIAL_XMIT_SIZE) + dport->xmit_tail -= SERIAL_XMIT_SIZE; + } + atomic_sub(number_written, &dashtty_xmit_cnt); + } + mutex_unlock(&dport->xmit_lock); + + /* if we've made more data available, wake up tty */ + if (count && number_written) { + tty = tty_port_tty_get(&dport->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + } + + /* did the write fail? */ + return count && !number_written; +} + +/** + * put_data() - Kernel thread to write out blocks of channel data to DA. + * @arg: Unused. + * + * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the + * channels to write out any buffered data. If any of the channels stall due to + * the remote buffer being full, a hold off happens to allow the debugger to + * drain the buffer. + */ +static int put_data(void *arg) +{ + unsigned int chan, stall; + + __set_current_state(TASK_RUNNING); + while (!kthread_should_stop()) { + /* + * For each channel see if there's anything to transmit in the + * port's xmit_buf. + */ + stall = 0; + for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan) + stall += put_channel_data(chan); + + /* + * If some of the buffers are full, hold off for a short while + * to allow them to empty. + */ + if (stall) + msleep(25); + + wait_event_interruptible(dashtty_waitqueue, + atomic_read(&dashtty_xmit_cnt)); + } + + return 0; +} + +/* + * This gets called every DA_TTY_POLL and polls the channels for data + */ +static void dashtty_timer(unsigned long ignored) +{ + int channel; + + /* If there are no ports open do nothing and don't poll again. */ + if (!atomic_read(&num_channels_need_poll)) + return; + + channel = find_channel_to_poll(); + + /* Did we find a channel to poll? */ + if (channel >= 0) + fetch_data(channel); + + mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL); +} + +static void add_poll_timer(struct timer_list *poll_timer) +{ + setup_timer(poll_timer, dashtty_timer, 0); + poll_timer->expires = jiffies + DA_TTY_POLL; + + /* + * Always attach the timer to the boot CPU. The DA channels are per-CPU + * so all polling should be from a single CPU. + */ + add_timer_on(poll_timer, 0); +} + +static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct dashtty_port *dport = container_of(port, struct dashtty_port, + port); + void *rx_buf; + + /* Allocate the buffer we use for writing data */ + if (tty_port_alloc_xmit_buf(port) < 0) + goto err; + + /* Allocate the buffer we use for reading data */ + rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL); + if (!rx_buf) + goto err_free_xmit; + + spin_lock_bh(&dport->rx_lock); + dport->rx_buf = rx_buf; + spin_unlock_bh(&dport->rx_lock); + + /* + * Don't add the poll timer if we're opening a console. This + * avoids the overhead of polling the Dash but means it is not + * possible to have a login on /dev/console. + * + */ + if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (atomic_inc_return(&num_channels_need_poll) == 1) + add_poll_timer(&poll_timer); + + return 0; +err_free_xmit: + tty_port_free_xmit_buf(port); +err: + return -ENOMEM; +} + +static void dashtty_port_shutdown(struct tty_port *port) +{ + struct dashtty_port *dport = container_of(port, struct dashtty_port, + port); + void *rx_buf; + unsigned int count; + + /* stop reading */ + if (dport != &dashtty_ports[CONSOLE_CHANNEL]) + if (atomic_dec_and_test(&num_channels_need_poll)) + del_timer_sync(&poll_timer); + + mutex_lock(&dport->xmit_lock); + count = dport->xmit_cnt; + mutex_unlock(&dport->xmit_lock); + if (count) { + /* + * There's still data to write out, so wake and wait for the + * writer thread to drain the buffer. + */ + del_timer(&put_timer); + wake_up_interruptible(&dashtty_waitqueue); + wait_for_completion(&dport->xmit_empty); + } + + /* Null the read buffer (timer could still be running!) */ + spin_lock_bh(&dport->rx_lock); + rx_buf = dport->rx_buf; + dport->rx_buf = NULL; + spin_unlock_bh(&dport->rx_lock); + /* Free the read buffer */ + kfree(rx_buf); + + /* Free the write buffer */ + tty_port_free_xmit_buf(port); +} + +static const struct tty_port_operations dashtty_port_ops = { + .activate = dashtty_port_activate, + .shutdown = dashtty_port_shutdown, +}; + +static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + return tty_port_install(&dashtty_ports[tty->index].port, driver, tty); +} + +static int dashtty_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(tty->port, tty, filp); +} + +static void dashtty_close(struct tty_struct *tty, struct file *filp) +{ + return tty_port_close(tty->port, tty, filp); +} + +static void dashtty_hangup(struct tty_struct *tty) +{ + int channel; + struct dashtty_port *dport; + + channel = tty->index; + dport = &dashtty_ports[channel]; + + /* drop any data in the xmit buffer */ + mutex_lock(&dport->xmit_lock); + if (dport->xmit_cnt) { + atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt); + dport->xmit_cnt = 0; + dport->xmit_head = 0; + dport->xmit_tail = 0; + complete(&dport->xmit_empty); + } + mutex_unlock(&dport->xmit_lock); + + tty_port_hangup(tty->port); +} + +/** + * dashtty_put_timer() - Delayed wake up of kernel thread. + * @ignored: unused + * + * This timer function wakes up the kernel thread if any data exists in the + * buffers. It is used to delay the expensive writeout until the writer has + * stopped writing. + */ +static void dashtty_put_timer(unsigned long ignored) +{ + if (atomic_read(&dashtty_xmit_cnt)) + wake_up_interruptible(&dashtty_waitqueue); +} + +static int dashtty_write(struct tty_struct *tty, const unsigned char *buf, + int total) +{ + int channel, count, block; + struct dashtty_port *dport; + + /* Determine the channel */ + channel = tty->index; + dport = &dashtty_ports[channel]; + + /* + * Write to output buffer. + * + * The reason that we asynchronously write the buffer is because if we + * were to write the buffer synchronously then because DA channels are + * per-CPU the buffer would be written to the channel of whatever CPU + * we're running on. + * + * What we actually want to happen is have all input and output done on + * one CPU. + */ + mutex_lock(&dport->xmit_lock); + /* work out how many bytes we can write to the xmit buffer */ + total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt)); + atomic_add(total, &dashtty_xmit_cnt); + dport->xmit_cnt += total; + /* write the actual bytes (may need splitting if it wraps) */ + for (count = total; count; count -= block) { + block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head)); + memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block); + dport->xmit_head += block; + if (dport->xmit_head >= SERIAL_XMIT_SIZE) + dport->xmit_head -= SERIAL_XMIT_SIZE; + buf += block; + } + count = dport->xmit_cnt; + /* xmit buffer no longer empty? */ + if (count) + reinit_completion(&dport->xmit_empty); + mutex_unlock(&dport->xmit_lock); + + if (total) { + /* + * If the buffer is full, wake up the kthread, otherwise allow + * some more time for the buffer to fill up a bit before waking + * it. + */ + if (count == SERIAL_XMIT_SIZE) { + del_timer(&put_timer); + wake_up_interruptible(&dashtty_waitqueue); + } else { + mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY); + } + } + return total; +} + +static int dashtty_write_room(struct tty_struct *tty) +{ + struct dashtty_port *dport; + int channel; + int room; + + channel = tty->index; + dport = &dashtty_ports[channel]; + + /* report the space in the xmit buffer */ + mutex_lock(&dport->xmit_lock); + room = SERIAL_XMIT_SIZE - dport->xmit_cnt; + mutex_unlock(&dport->xmit_lock); + + return room; +} + +static int dashtty_chars_in_buffer(struct tty_struct *tty) +{ + struct dashtty_port *dport; + int channel; + int chars; + + channel = tty->index; + dport = &dashtty_ports[channel]; + + /* report the number of bytes in the xmit buffer */ + mutex_lock(&dport->xmit_lock); + chars = dport->xmit_cnt; + mutex_unlock(&dport->xmit_lock); + + return chars; +} + +static const struct tty_operations dashtty_ops = { + .install = dashtty_install, + .open = dashtty_open, + .close = dashtty_close, + .hangup = dashtty_hangup, + .write = dashtty_write, + .write_room = dashtty_write_room, + .chars_in_buffer = dashtty_chars_in_buffer, +}; + +static int __init dashtty_init(void) +{ + int ret; + int nport; + struct dashtty_port *dport; + + if (!metag_da_enabled()) + return -ENODEV; + + channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS, + TTY_DRIVER_REAL_RAW); + if (IS_ERR(channel_driver)) + return PTR_ERR(channel_driver); + + channel_driver->driver_name = "metag_da"; + channel_driver->name = "ttyDA"; + channel_driver->major = DA_TTY_MAJOR; + channel_driver->minor_start = 0; + channel_driver->type = TTY_DRIVER_TYPE_SERIAL; + channel_driver->subtype = SERIAL_TYPE_NORMAL; + channel_driver->init_termios = tty_std_termios; + channel_driver->init_termios.c_cflag |= CLOCAL; + + tty_set_operations(channel_driver, &dashtty_ops); + for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { + dport = &dashtty_ports[nport]; + tty_port_init(&dport->port); + dport->port.ops = &dashtty_port_ops; + spin_lock_init(&dport->rx_lock); + mutex_init(&dport->xmit_lock); + /* the xmit buffer starts empty, i.e. completely written */ + init_completion(&dport->xmit_empty); + complete(&dport->xmit_empty); + } + + setup_timer(&put_timer, dashtty_put_timer, 0); + + init_waitqueue_head(&dashtty_waitqueue); + dashtty_thread = kthread_create(put_data, NULL, "ttyDA"); + if (IS_ERR(dashtty_thread)) { + pr_err("Couldn't create dashtty thread\n"); + ret = PTR_ERR(dashtty_thread); + goto err_destroy_ports; + } + /* + * Bind the writer thread to the boot CPU so it can't migrate. + * DA channels are per-CPU and we want all channel I/O to be on a single + * predictable CPU. + */ + kthread_bind(dashtty_thread, 0); + wake_up_process(dashtty_thread); + + ret = tty_register_driver(channel_driver); + + if (ret < 0) { + pr_err("Couldn't install dashtty driver: err %d\n", + ret); + goto err_stop_kthread; + } + + return 0; + +err_stop_kthread: + kthread_stop(dashtty_thread); +err_destroy_ports: + for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { + dport = &dashtty_ports[nport]; + tty_port_destroy(&dport->port); + } + put_tty_driver(channel_driver); + return ret; +} + +static void dashtty_exit(void) +{ + int nport; + struct dashtty_port *dport; + + del_timer_sync(&put_timer); + kthread_stop(dashtty_thread); + del_timer_sync(&poll_timer); + tty_unregister_driver(channel_driver); + for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { + dport = &dashtty_ports[nport]; + tty_port_destroy(&dport->port); + } + put_tty_driver(channel_driver); +} + +module_init(dashtty_init); +module_exit(dashtty_exit); + +#ifdef CONFIG_DA_CONSOLE + +static void dash_console_write(struct console *co, const char *s, + unsigned int count) +{ + int actually_written; + + chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written); +} + +static struct tty_driver *dash_console_device(struct console *c, int *index) +{ + *index = c->index; + return channel_driver; +} + +struct console dash_console = { + .name = "ttyDA", + .write = dash_console_write, + .device = dash_console_device, + .flags = CON_PRINTBUFFER, + .index = 1, +}; + +#endif diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index f9d28503bde..1deaca4674e 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -913,16 +913,12 @@ 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) { - struct tty_struct *tty = tty_port_tty_get( - &brd->ports[a].port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - } + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) + tty_port_tty_hangup(&brd->ports[a].port, false); + for (a = 0; a < MAX_PORTS_PER_BOARD; a++) tty_port_destroy(&brd->ports[a].port); + while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1365,7 +1361,6 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { - struct tty_struct *tty; unsigned long flags; dcd = !!dcd; @@ -1373,10 +1368,8 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) if (dcd != p->DCDState) { p->DCDState = dcd; spin_unlock_irqrestore(&p->port.lock, flags); - tty = tty_port_tty_get(&p->port); - if (tty && !C_CLOCAL(tty) && !dcd) - tty_hangup(tty); - tty_kref_put(tty); + if (!dcd) + tty_port_tty_hangup(&p->port, true); } else spin_unlock_irqrestore(&p->port.lock, flags); @@ -1405,7 +1398,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, if (inited && !test_bit(TTY_THROTTLED, &tty->flags) && MoxaPortRxQueue(p) > 0) { /* RX */ MoxaPortReadData(p); - tty_schedule_flip(tty); + tty_schedule_flip(&p->port); } } else { clear_bit(EMPTYWAIT, &p->statusflags); @@ -1429,8 +1422,8 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, goto put; if (tty && (intr & IntrBreak) && !I_IGNBRK(tty)) { /* BREAK */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); + tty_insert_flip_char(&p->port, 0, TTY_BREAK); + tty_schedule_flip(&p->port); } if (intr & IntrLine) @@ -1966,7 +1959,7 @@ static int MoxaPortReadData(struct moxa_port *port) ofs = baseAddr + DynPage_addr + bufhead + head; len = (tail >= head) ? (tail - head) : (rx_mask + 1 - head); - len = tty_prepare_flip_string(tty, &dst, + len = tty_prepare_flip_string(&port->port, &dst, min(len, count)); memcpy_fromio(dst, ofs, len); head = (head + len) & rx_mask; @@ -1978,7 +1971,7 @@ static int MoxaPortReadData(struct moxa_port *port) while (count > 0) { writew(pageno, baseAddr + Control_reg); ofs = baseAddr + DynPage_addr + pageofs; - len = tty_prepare_flip_string(tty, &dst, + len = tty_prepare_flip_string(&port->port, &dst, min(Page_size - pageofs, count)); memcpy_fromio(dst, ofs, len); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 40113868bec..4c4a2367456 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -1084,6 +1084,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); + } mxser_shutdown_port(port); clear_bit(ASYNCB_INITIALIZED, &port->flags); mutex_unlock(&port->mutex); @@ -1264,7 +1268,7 @@ static int mxser_set_serial_info(struct tty_struct *tty, (new_serial.flags & ASYNC_FLAGS)); port->close_delay = new_serial.close_delay * HZ / 100; port->closing_wait = new_serial.closing_wait * HZ / 100; - tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (new_serial.baud_base != info->baud_base || new_serial.custom_divisor != @@ -1614,8 +1618,12 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) if (ip->type == PORT_16550A) me->fifo[p] = 1; - opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); - opmode &= OP_MODE_MASK; + if (ip->board->chip_flag == MOXA_MUST_MU860_HWID) { + opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2); + opmode &= OP_MODE_MASK; + } else { + opmode = RS232_MODE; + } me->iftype[p] = opmode; mutex_unlock(&port->mutex); } @@ -1672,6 +1680,9 @@ static int mxser_ioctl(struct tty_struct *tty, int shiftbit; unsigned char val, mask; + if (info->board->chip_flag != MOXA_MUST_MU860_HWID) + return -EFAULT; + p = tty->index % 4; if (cmd == MOXA_SET_OP_MODE) { if (get_user(opmode, (int __user *) argp)) @@ -2079,7 +2090,7 @@ static void mxser_receive_chars(struct tty_struct *tty, } while (gdl--) { ch = inb(port->ioaddr + UART_RX); - tty_insert_flip_char(tty, ch, 0); + tty_insert_flip_char(&port->port, ch, 0); cnt++; } goto end_intr; @@ -2118,7 +2129,7 @@ intr_old: } else flag = TTY_BREAK; } - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(&port->port, ch, flag); cnt++; if (cnt >= recv_room) { if (!port->ldisc_stop_rx) @@ -2145,7 +2156,7 @@ end_intr: * recursive locking. */ spin_unlock(&port->slock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->port); spin_lock(&port->slock); } @@ -2364,7 +2375,6 @@ static void mxser_release_vector(struct mxser_board *brd) static void mxser_release_ISA_res(struct mxser_board *brd) { - free_irq(brd->irq, brd); release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); mxser_release_vector(brd); } @@ -2430,6 +2440,7 @@ static void mxser_board_remove(struct mxser_board *brd) tty_unregister_device(mxvar_sdriver, brd->idx + i); tty_port_destroy(&brd->ports[i].port); } + free_irq(brd->irq, brd); } static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) @@ -2554,6 +2565,7 @@ static int mxser_probe(struct pci_dev *pdev, struct mxser_board *brd; unsigned int i, j; unsigned long ioaddress; + struct device *tty_dev; int retval = -EINVAL; for (i = 0; i < MXSER_BOARDS; i++) @@ -2637,13 +2649,25 @@ static int mxser_probe(struct pci_dev *pdev, if (retval) goto err_rel3; - for (i = 0; i < brd->info->nports; i++) - tty_port_register_device(&brd->ports[i].port, mxvar_sdriver, - brd->idx + i, &pdev->dev); + for (i = 0; i < brd->info->nports; i++) { + tty_dev = tty_port_register_device(&brd->ports[i].port, + mxvar_sdriver, brd->idx + i, &pdev->dev); + if (IS_ERR(tty_dev)) { + retval = PTR_ERR(tty_dev); + for (; i > 0; i--) + tty_unregister_device(mxvar_sdriver, + brd->idx + i - 1); + goto err_relbrd; + } + } pci_set_drvdata(pdev, brd); return 0; +err_relbrd: + for (i = 0; i < brd->info->nports; i++) + tty_port_destroy(&brd->ports[i].port); + free_irq(brd->irq, brd); err_rel3: pci_release_region(pdev, 3); err_zero: @@ -2665,7 +2689,6 @@ static void mxser_remove(struct pci_dev *pdev) mxser_board_remove(brd); - free_irq(pdev->irq, brd); pci_release_region(pdev, 2); pci_release_region(pdev, 3); pci_disable_device(pdev); @@ -2683,6 +2706,7 @@ static struct pci_driver mxser_driver = { static int __init mxser_module_init(void) { struct mxser_board *brd; + struct device *tty_dev; unsigned int b, i, m; int retval; @@ -2728,14 +2752,29 @@ static int __init mxser_module_init(void) /* mxser_initbrd will hook ISR. */ if (mxser_initbrd(brd, NULL) < 0) { + mxser_release_ISA_res(brd); brd->info = NULL; continue; } brd->idx = m * MXSER_PORTS_PER_BOARD; - for (i = 0; i < brd->info->nports; i++) - tty_port_register_device(&brd->ports[i].port, + for (i = 0; i < brd->info->nports; i++) { + tty_dev = tty_port_register_device(&brd->ports[i].port, mxvar_sdriver, brd->idx + i, NULL); + if (IS_ERR(tty_dev)) { + for (; i > 0; i--) + tty_unregister_device(mxvar_sdriver, + brd->idx + i - 1); + for (i = 0; i < brd->info->nports; i++) + tty_port_destroy(&brd->ports[i].port); + free_irq(brd->irq, brd); + mxser_release_ISA_res(brd); + brd->info = NULL; + break; + } + } + if (brd->info == NULL) + continue; m++; } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index dcc0430a49c..2ebe47b78a3 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -194,6 +194,7 @@ struct gsm_control { struct gsm_mux { struct tty_struct *tty; /* The tty our ldisc is bound to */ spinlock_t lock; + struct mutex mutex; unsigned int num; struct kref ref; @@ -807,7 +808,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) int h = dlci->adaption - 1; total_size = 0; - while(1) { + while (1) { len = kfifo_len(dlci->fifo); if (len == 0) return total_size; @@ -827,8 +828,8 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) switch (dlci->adaption) { case 1: /* Unstructured */ break; - case 2: /* Unstructed with modem bits. Always one byte as we never - send inline break data */ + case 2: /* Unstructed with modem bits. + Always one byte as we never send inline break data */ *dp++ = gsm_encode_modem(dlci); break; } @@ -968,7 +969,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) unsigned long flags; int sweep; - if (dlci->constipated) + if (dlci->constipated) return; spin_lock_irqsave(&dlci->gsm->tx_lock, flags); @@ -981,7 +982,7 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci) gsm_dlci_data_output(dlci->gsm, dlci); } if (sweep) - gsm_dlci_data_sweep(dlci->gsm); + gsm_dlci_data_sweep(dlci->gsm); spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); } @@ -1067,9 +1068,9 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) if (!(tty->termios.c_cflag & CLOCAL)) tty_hangup(tty); - if (brk & 0x01) - tty_insert_flip_char(tty, 0, TTY_BREAK); } + if (brk & 0x01) + tty_insert_flip_char(&dlci->port, 0, TTY_BREAK); dlci->modem_rx = mlines; } @@ -1089,6 +1090,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) { unsigned int addr = 0; unsigned int modem = 0; + unsigned int brk = 0; struct gsm_dlci *dlci; int len = clen; u8 *dp = data; @@ -1115,6 +1117,16 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) if (len == 0) return; } + len--; + if (len > 0) { + while (gsm_read_ea(&brk, *dp++) == 0) { + len--; + if (len == 0) + return; + } + modem <<= 7; + modem |= (brk & 0x7f); + } tty = tty_port_tty_get(&dlci->port); gsm_process_modem(tty, dlci, modem, clen); if (tty) { @@ -1137,8 +1149,8 @@ static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) { - struct tty_struct *tty; - unsigned int addr = 0 ; + struct tty_port *port; + unsigned int addr = 0; u8 bits; int len = clen; u8 *dp = data; @@ -1160,19 +1172,18 @@ static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) bits = *dp; if ((bits & 1) == 0) return; - /* See if we have an uplink tty */ - tty = tty_port_tty_get(&gsm->dlci[addr]->port); - if (tty) { - if (bits & 2) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - if (bits & 4) - tty_insert_flip_char(tty, 0, TTY_PARITY); - if (bits & 8) - tty_insert_flip_char(tty, 0, TTY_FRAME); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } + port = &gsm->dlci[addr]->port; + + if (bits & 2) + tty_insert_flip_char(port, 0, TTY_OVERRUN); + if (bits & 4) + tty_insert_flip_char(port, 0, TTY_PARITY); + if (bits & 8) + tty_insert_flip_char(port, 0, TTY_FRAME); + + tty_flip_buffer_push(port); + gsm_control_reply(gsm, CMD_RLS, data, clen); } @@ -1419,11 +1430,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci) pr_debug("DLCI %d goes closed.\n", dlci->addr); dlci->state = DLCI_CLOSED; if (dlci->addr != 0) { - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } + tty_port_tty_hangup(&dlci->port, false); kfifo_reset(dlci->fifo); } else dlci->gsm->dead = 1; @@ -1545,36 +1552,37 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int clen) { /* krefs .. */ struct tty_port *port = &dlci->port; - struct tty_struct *tty = tty_port_tty_get(port); + struct tty_struct *tty; unsigned int modem = 0; int len = clen; if (debug & 16) - pr_debug("%d bytes for tty %p\n", len, tty); - if (tty) { - switch (dlci->adaption) { - /* Unsupported types */ - /* Packetised interruptible data */ - case 4: - break; - /* Packetised uininterruptible voice/data */ - case 3: - break; - /* Asynchronous serial with line state in each frame */ - case 2: - while (gsm_read_ea(&modem, *data++) == 0) { - len--; - if (len == 0) - return; - } + pr_debug("%d bytes for tty\n", len); + switch (dlci->adaption) { + /* Unsupported types */ + /* Packetised interruptible data */ + case 4: + break; + /* Packetised uininterruptible voice/data */ + case 3: + break; + /* Asynchronous serial with line state in each frame */ + case 2: + while (gsm_read_ea(&modem, *data++) == 0) { + len--; + if (len == 0) + return; + } + tty = tty_port_tty_get(port); + if (tty) { gsm_process_modem(tty, dlci, modem, clen); - /* Line state will go via DLCI 0 controls only */ - case 1: - default: - tty_insert_flip_string(tty, data, len); - tty_flip_buffer_push(tty); + tty_kref_put(tty); } - tty_kref_put(tty); + /* Line state will go via DLCI 0 controls only */ + case 1: + default: + tty_insert_flip_string(port, data, len); + tty_flip_buffer_push(port); } } @@ -1689,6 +1697,8 @@ static inline void dlci_put(struct gsm_dlci *dlci) tty_port_put(&dlci->port); } +static void gsm_destroy_network(struct gsm_dlci *dlci); + /** * gsm_dlci_release - release DLCI * @dlci: DLCI to destroy @@ -1702,9 +1712,16 @@ static void gsm_dlci_release(struct gsm_dlci *dlci) { struct tty_struct *tty = tty_port_tty_get(&dlci->port); if (tty) { + mutex_lock(&dlci->mutex); + gsm_destroy_network(dlci); + mutex_unlock(&dlci->mutex); + tty_vhangup(tty); + + tty_port_tty_set(&dlci->port, NULL); tty_kref_put(tty); } + dlci->state = DLCI_CLOSED; dlci_put(dlci); } @@ -1732,10 +1749,11 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - if (gsm->encoding == 0){ - /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. - In this case it contain the last piece of data - required to generate final CRC */ + if (gsm->encoding == 0) { + /* WARNING: gsm->received_fcs is used for + gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); } if (gsm->fcs != GOOD_FCS) { @@ -2010,7 +2028,7 @@ static void gsm_error(struct gsm_mux *gsm, * and then shut down each device hanging up the channels as we go. */ -void gsm_cleanup_mux(struct gsm_mux *gsm) +static void gsm_cleanup_mux(struct gsm_mux *gsm) { int i; struct gsm_dlci *dlci = gsm->dlci[0]; @@ -2045,15 +2063,16 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) dlci->state == DLCI_CLOSED); } /* Free up any link layer users */ + mutex_lock(&gsm->mutex); for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); + mutex_unlock(&gsm->mutex); /* Now wipe the queues */ list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); INIT_LIST_HEAD(&gsm->tx_list); } -EXPORT_SYMBOL_GPL(gsm_cleanup_mux); /** * gsm_activate_mux - generic GSM setup @@ -2064,7 +2083,7 @@ EXPORT_SYMBOL_GPL(gsm_cleanup_mux); * finally kick off connecting to DLCI 0 on the modem. */ -int gsm_activate_mux(struct gsm_mux *gsm) +static int gsm_activate_mux(struct gsm_mux *gsm) { struct gsm_dlci *dlci; int i = 0; @@ -2100,7 +2119,6 @@ int gsm_activate_mux(struct gsm_mux *gsm) gsm->dead = 0; /* Tty opens are now permissible */ return 0; } -EXPORT_SYMBOL_GPL(gsm_activate_mux); /** * gsm_free_mux - free up a mux @@ -2108,13 +2126,12 @@ EXPORT_SYMBOL_GPL(gsm_activate_mux); * * Dispose of allocated resources for a dead mux */ -void gsm_free_mux(struct gsm_mux *gsm) +static void gsm_free_mux(struct gsm_mux *gsm) { kfree(gsm->txframe); kfree(gsm->buf); kfree(gsm); } -EXPORT_SYMBOL_GPL(gsm_free_mux); /** * gsm_free_muxr - free up a mux @@ -2144,7 +2161,7 @@ static inline void mux_put(struct gsm_mux *gsm) * Creates a new mux ready for activation. */ -struct gsm_mux *gsm_alloc_mux(void) +static struct gsm_mux *gsm_alloc_mux(void) { struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); if (gsm == NULL) @@ -2161,6 +2178,7 @@ struct gsm_mux *gsm_alloc_mux(void) return NULL; } spin_lock_init(&gsm->lock); + mutex_init(&gsm->mutex); kref_init(&gsm->ref); INIT_LIST_HEAD(&gsm->tx_list); @@ -2176,7 +2194,6 @@ struct gsm_mux *gsm_alloc_mux(void) return gsm; } -EXPORT_SYMBOL_GPL(gsm_alloc_mux); /** * gsmld_output - write to link @@ -2260,14 +2277,15 @@ static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *f; int i; char buf[64]; - char flags; + char flags = TTY_NORMAL; if (debug & 4) print_hex_dump_bytes("gsmld_receive: ", DUMP_PREFIX_OFFSET, cp, count); for (i = count, dp = cp, f = fp; i; i--, dp++) { - flags = *f++; + if (f) + flags = *f++; switch (flags) { case TTY_NORMAL: gsm->receive(gsm, *dp); @@ -2702,7 +2720,7 @@ static void gsm_mux_rx_netchar(struct gsm_dlci *dlci, return; } -int gsm_change_mtu(struct net_device *net, int new_mtu) +static int gsm_change_mtu(struct net_device *net, int new_mtu) { struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net); if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu)) @@ -2896,25 +2914,37 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) gsm = gsm_mux[mux]; if (gsm->dead) return -EL2HLT; - /* If DLCI 0 is not yet fully open return an error. This is ok from a locking - perspective as we don't have to worry about this if DLCI0 is lost */ - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + /* If DLCI 0 is not yet fully open return an error. + This is ok from a locking + perspective as we don't have to worry about this + if DLCI0 is lost */ + mutex_lock(&gsm->mutex); + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) { + mutex_unlock(&gsm->mutex); return -EL2NSYNC; + } dlci = gsm->dlci[line]; if (dlci == NULL) { alloc = true; dlci = gsm_dlci_alloc(gsm, line); } - if (dlci == NULL) + if (dlci == NULL) { + mutex_unlock(&gsm->mutex); return -ENOMEM; + } ret = tty_port_install(&dlci->port, driver, tty); if (ret) { if (alloc) dlci_put(dlci); + mutex_unlock(&gsm->mutex); return ret; } + dlci_get(dlci); + dlci_get(gsm->dlci[0]); + mux_get(gsm); tty->driver_data = dlci; + mutex_unlock(&gsm->mutex); return 0; } @@ -2925,9 +2955,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) struct tty_port *port = &dlci->port; port->count++; - dlci_get(dlci); - dlci_get(dlci->gsm->dlci[0]); - mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2947,24 +2974,29 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) if (dlci == NULL) return; + if (dlci->state == DLCI_CLOSED) + return; mutex_lock(&dlci->mutex); gsm_destroy_network(dlci); mutex_unlock(&dlci->mutex); gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - goto out; + 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); + } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); -out: - dlci_put(dlci); - dlci_put(gsm->dlci[0]); - mux_put(gsm); + return; } static void gsmtty_hangup(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return; tty_port_hangup(&dlci->port); gsm_dlci_begin_close(dlci); } @@ -2972,9 +3004,12 @@ static void gsmtty_hangup(struct tty_struct *tty) static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, int len) { + int sent; struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; /* Stuff the bytes into the fifo queue */ - int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); + sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); /* Need to kick the channel */ gsm_dlci_data_kick(dlci); return sent; @@ -2983,18 +3018,24 @@ static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, static int gsmtty_write_room(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; return TX_SIZE - kfifo_len(dlci->fifo); } static int gsmtty_chars_in_buffer(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; return kfifo_len(dlci->fifo); } static void gsmtty_flush_buffer(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return; /* Caution needed: If we implement reliable transport classes then the data being transmitted can't simply be junked once it has first hit the stack. Until then we can just blow it @@ -3013,6 +3054,8 @@ static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) static int gsmtty_tiocmget(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; return dlci->modem_rx; } @@ -3022,6 +3065,8 @@ static int gsmtty_tiocmset(struct tty_struct *tty, struct gsm_dlci *dlci = tty->driver_data; unsigned int modem_tx = dlci->modem_tx; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; modem_tx &= ~clear; modem_tx |= set; @@ -3040,6 +3085,8 @@ static int gsmtty_ioctl(struct tty_struct *tty, struct gsm_netconfig nc; int index; + if (dlci->state == DLCI_CLOSED) + return -EINVAL; switch (cmd) { case GSMIOC_ENABLE_NET: if (copy_from_user(&nc, (void __user *)arg, sizeof(nc))) @@ -3066,6 +3113,9 @@ static int gsmtty_ioctl(struct tty_struct *tty, static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) { + struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return; /* For the moment its fixed. In actual fact the speed information for the virtual channel can be propogated in both directions by the RPN control message. This however rapidly gets nasty as we @@ -3077,6 +3127,8 @@ static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) static void gsmtty_throttle(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return; if (tty->termios.c_cflag & CRTSCTS) dlci->modem_tx &= ~TIOCM_DTR; dlci->throttled = 1; @@ -3087,6 +3139,8 @@ static void gsmtty_throttle(struct tty_struct *tty) static void gsmtty_unthrottle(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; + if (dlci->state == DLCI_CLOSED) + return; if (tty->termios.c_cflag & CRTSCTS) dlci->modem_tx |= TIOCM_DTR; dlci->throttled = 0; @@ -3098,6 +3152,8 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) { struct gsm_dlci *dlci = tty->driver_data; int encode = 0; /* Off */ + if (dlci->state == DLCI_CLOSED) + return -EINVAL; if (state == -1) /* "On indefinitely" - we can't encode this properly */ @@ -3110,6 +3166,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } +static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + struct gsm_mux *gsm = dlci->gsm; + + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); + driver->ttys[tty->index] = NULL; +} /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { @@ -3129,6 +3195,7 @@ static const struct tty_operations gsmtty_ops = { .tiocmget = gsmtty_tiocmget, .tiocmset = gsmtty_tiocmset, .break_ctl = gsmtty_break_ctl, + .remove = gsmtty_remove, }; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 1b2db9a3038..644ddb841d9 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -848,13 +848,11 @@ static struct n_hdlc *n_hdlc_alloc(void) { struct n_hdlc_buf *buf; int i; - struct n_hdlc *n_hdlc = kmalloc(sizeof(*n_hdlc), GFP_KERNEL); + struct n_hdlc *n_hdlc = kzalloc(sizeof(*n_hdlc), GFP_KERNEL); if (!n_hdlc) return NULL; - memset(n_hdlc, 0, sizeof(*n_hdlc)); - n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); @@ -952,8 +950,6 @@ static char hdlc_register_ok[] __initdata = KERN_INFO "N_HDLC line discipline registered.\n"; static char hdlc_register_fail[] __initdata = KERN_ERR "error registering line discipline: %d\n"; -static char hdlc_init_fail[] __initdata = - KERN_INFO "N_HDLC: init failure %d\n"; static int __init n_hdlc_init(void) { @@ -973,8 +969,6 @@ static int __init n_hdlc_init(void) else printk(hdlc_register_fail, status); - if (status) - printk(hdlc_init_fail, status); return status; } /* end of init_module() */ diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 1e6405070ce..8b157d68a03 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1244,7 +1244,7 @@ static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, { struct r3964_info *pInfo = tty->disc_data; const unsigned char *p; - char *f, flags = 0; + char *f, flags = TTY_NORMAL; int i; for (i = count, p = cp, f = fp; i; i--, p++) { diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 19083efa231..f44f1ba762c 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -49,6 +49,8 @@ #include <linux/file.h> #include <linux/uaccess.h> #include <linux/module.h> +#include <linux/ratelimit.h> +#include <linux/vmalloc.h> /* number of characters left in xmit buffer before select has we have room */ @@ -73,36 +75,83 @@ #define ECHO_OP_SET_CANON_COL 0x81 #define ECHO_OP_ERASE_TAB 0x82 +#define ECHO_COMMIT_WATERMARK 256 +#define ECHO_BLOCK 256 +#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) + + +#undef N_TTY_TRACE +#ifdef N_TTY_TRACE +# define n_tty_trace(f, args...) trace_printk(f, ##args) +#else +# define n_tty_trace(f, args...) +#endif + struct n_tty_data { - unsigned int column; + /* producer-published */ + size_t read_head; + size_t canon_head; + size_t echo_head; + size_t echo_commit; + size_t echo_mark; + DECLARE_BITMAP(char_map, 256); + + /* private to n_tty_receive_overrun (single-threaded) */ unsigned long overrun_time; int num_overrun; + /* non-atomic */ + bool no_room; + + /* must hold exclusive termios_rwsem to reset these */ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; - unsigned char echo_overrun:1; + unsigned char push:1; - DECLARE_BITMAP(process_char_map, 256); + /* shared by producer and consumer */ + char read_buf[N_TTY_BUF_SIZE]; DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + unsigned char echo_buf[N_TTY_BUF_SIZE]; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; + int minimum_to_wake; - unsigned char *echo_buf; - unsigned int echo_pos; - unsigned int echo_cnt; + /* consumer-published */ + size_t read_tail; + size_t line_start; - int canon_data; - unsigned long canon_head; + /* protected by output lock */ + unsigned int column; unsigned int canon_column; + size_t echo_tail; struct mutex atomic_read_lock; struct mutex output_lock; - struct mutex echo_lock; - spinlock_t read_lock; }; +static inline size_t read_cnt(struct n_tty_data *ldata) +{ + return ldata->read_head - ldata->read_tail; +} + +static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { @@ -112,30 +161,18 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) +static int receive_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; - int old_left; - /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on * its flags, e.g. parity error). */ - left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - read_cnt(ldata) * 3 - 1; } else - left = N_TTY_BUF_SIZE - ldata->read_cnt - 1; + left = N_TTY_BUF_SIZE - read_cnt(ldata) - 1; /* * If we are doing input canonicalization, and there are no @@ -144,120 +181,208 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = ldata->icanon && !ldata->canon_data; - old_left = tty->receive_room; - tty->receive_room = left; + left = ldata->icanon && ldata->canon_head == ldata->read_tail; + + return left; +} + +/** + * n_tty_set_room - receive space + * @tty: terminal + * + * Re-schedules the flip buffer work if space just became available. + * + * Caller holds exclusive termios_rwsem + * or + * n_tty_read()/consumer path: + * holds non-exclusive termios_rwsem + */ + +static void n_tty_set_room(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) { + if (unlikely(ldata->no_room) && receive_room(tty)) { + ldata->no_room = 0; + WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); - schedule_work(&tty->port->buf.work); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); + queue_work(system_unbound_wq, &tty->port->buf.work); } } -static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) +static ssize_t chars_in_buffer(struct tty_struct *tty) { - if (ldata->read_cnt < N_TTY_BUF_SIZE) { - ldata->read_buf[ldata->read_head] = c; - ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1); - ldata->read_cnt++; - } + struct n_tty_data *ldata = tty->disc_data; + ssize_t n = 0; + + if (!ldata->icanon) + n = read_cnt(ldata); + else + n = ldata->canon_head - ldata->read_tail; + return n; } /** - * put_tty_queue - add character to tty - * @c: character - * @ldata: n_tty data + * n_tty_write_wakeup - asynchronous I/O notifier + * @tty: tty device * - * Add a character to the tty read_buf queue. This is done under the - * read_lock to serialize character addition and also to protect us - * against parallel reads or flushes + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up */ -static void put_tty_queue(unsigned char c, struct n_tty_data *ldata) +static void n_tty_write_wakeup(struct tty_struct *tty) { - unsigned long flags; + if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) + kill_fasync(&tty->fasync, SIGIO, POLL_OUT); +} + +static void n_tty_check_throttle(struct tty_struct *tty) +{ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) + return; /* - * The problem of stomping on the buffers ends here. - * Why didn't anyone see this one coming? --AJK - */ - spin_lock_irqsave(&ldata->read_lock, flags); - put_tty_queue_nolock(c, ldata); - spin_unlock_irqrestore(&ldata->read_lock, flags); + * Check the remaining room for the input canonicalization + * mode. We don't want to throttle the driver if we're in + * canonical mode and don't have a newline yet! + */ + while (1) { + int throttled; + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE) + break; + throttled = tty_throttle_safe(tty); + if (!throttled) + break; + } + __tty_set_flow_change(tty, 0); +} + +static void n_tty_check_unthrottle(struct tty_struct *tty) +{ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && + tty->link->ldisc->ops->write_wakeup == n_tty_write_wakeup) { + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + return; + if (!tty->count) + return; + n_tty_set_room(tty); + n_tty_write_wakeup(tty->link); + if (waitqueue_active(&tty->link->write_wait)) + wake_up_interruptible_poll(&tty->link->write_wait, POLLOUT); + return; + } + + /* If there is enough space in the read buffer now, let the + * low-level driver know. We use chars_in_buffer() to + * check the buffer, as it now knows about canonical mode. + * Otherwise, if the driver is throttled and the line is + * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, + * we won't get any more characters. + */ + + while (1) { + int unthrottled; + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + if (!tty->count) + break; + n_tty_set_room(tty); + unthrottled = tty_unthrottle_safe(tty); + if (!unthrottled) + break; + } + __tty_set_flow_change(tty, 0); } /** - * check_unthrottle - allow new receive data - * @tty; tty device + * put_tty_queue - add character to tty + * @c: character + * @ldata: n_tty data * - * Check whether to call the driver unthrottle functions + * Add a character to the tty read_buf queue. * - * Can sleep, may be called under the atomic_read_lock mutex but - * this is not guaranteed. + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * modifies read_head + * + * read_head is only considered 'published' if canonical mode is + * not active. */ -static void check_unthrottle(struct tty_struct *tty) + +static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) { - if (tty->count) - tty_unthrottle(tty); + *read_buf_addr(ldata, ldata->read_head++) = c; } /** * reset_buffer_flags - reset buffer state * @tty: terminal to reset * - * Reset the read buffer counters, clear the flags, - * and make sure the driver is unthrottled. Called - * from n_tty_open() and n_tty_flush_buffer(). + * Reset the read buffer counters and clear the flags. + * Called from n_tty_open() and n_tty_flush_buffer(). * - * Locking: tty_read_lock for read fields. + * Locking: caller holds exclusive termios_rwsem + * (or locking is not required) */ -static void reset_buffer_flags(struct tty_struct *tty) +static void reset_buffer_flags(struct n_tty_data *ldata) { - struct n_tty_data *ldata = tty->disc_data; - unsigned long flags; + ldata->read_head = ldata->canon_head = ldata->read_tail = 0; + ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; + ldata->echo_mark = 0; + ldata->line_start = 0; - spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; - spin_unlock_irqrestore(&ldata->read_lock, flags); + ldata->erasing = 0; + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->push = 0; +} - mutex_lock(&ldata->echo_lock); - ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; - mutex_unlock(&ldata->echo_lock); +static void n_tty_packet_mode_flush(struct tty_struct *tty) +{ + unsigned long flags; - ldata->canon_head = ldata->canon_data = ldata->erasing = 0; - bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - n_tty_set_room(tty); + spin_lock_irqsave(&tty->ctrl_lock, flags); + if (tty->link->packet) { + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + if (waitqueue_active(&tty->link->read_wait)) + wake_up_interruptible(&tty->link->read_wait); + } + spin_unlock_irqrestore(&tty->ctrl_lock, flags); } /** * n_tty_flush_buffer - clean input queue * @tty: terminal device * - * Flush the input buffer. Called when the line discipline is - * being closed, when the tty layer wants the buffer flushed (eg - * at hangup) or when the N_TTY line discipline internally has to - * clean the pending queue (for example some signals). + * Flush the input buffer. Called when the tty layer wants the + * buffer flushed (eg at hangup) or when the N_TTY line discipline + * internally has to clean the pending queue (for example some signals). + * + * Holds termios_rwsem to exclude producer/consumer while + * buffer indices are reset. * - * Locking: ctrl_lock, read_lock. + * Locking: ctrl_lock, exclusive termios_rwsem */ static void n_tty_flush_buffer(struct tty_struct *tty) { - unsigned long flags; - /* clear everything and unthrottle the driver */ - reset_buffer_flags(tty); - - if (!tty->link) - return; + down_write(&tty->termios_rwsem); + reset_buffer_flags(tty->disc_data); + n_tty_set_room(tty); - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link->packet) { - tty->ctrl_status |= TIOCPKT_FLUSHREAD; - wake_up_interruptible(&tty->link->read_wait); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + if (tty->link) + n_tty_packet_mode_flush(tty); + up_write(&tty->termios_rwsem); } /** @@ -267,24 +392,18 @@ static void n_tty_flush_buffer(struct tty_struct *tty) * Report the number of characters buffered to be delivered to user * at this instant in time. * - * Locking: read_lock + * Locking: exclusive termios_rwsem */ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { - struct n_tty_data *ldata = tty->disc_data; - unsigned long flags; - ssize_t n = 0; + ssize_t n; - spin_lock_irqsave(&ldata->read_lock, flags); - if (!ldata->icanon) { - n = ldata->read_cnt; - } else if (ldata->canon_data) { - n = (ldata->canon_head > ldata->read_tail) ? - ldata->canon_head - ldata->read_tail : - ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); - } - spin_unlock_irqrestore(&ldata->read_lock, flags); + WARN_ONCE(1, "%s is deprecated and scheduled for removal.", __func__); + + down_write(&tty->termios_rwsem); + n = chars_in_buffer(tty); + up_write(&tty->termios_rwsem); return n; } @@ -529,33 +648,23 @@ break_out: * are prioritized. Also, when control characters are echoed with a * prefixed "^", the pair is treated atomically and thus not separated. * - * Locking: output_lock to protect column state and space left, - * echo_lock to protect the echo buffer + * Locking: callers must hold output_lock */ -static void process_echoes(struct tty_struct *tty) +static size_t __process_echoes(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - int space, nr; + int space, old_space; + size_t tail; unsigned char c; - unsigned char *cp, *buf_end; - if (!ldata->echo_cnt) - return; + old_space = space = tty_write_room(tty); - mutex_lock(&ldata->output_lock); - mutex_lock(&ldata->echo_lock); - - space = tty_write_room(tty); - - buf_end = ldata->echo_buf + N_TTY_BUF_SIZE; - cp = ldata->echo_buf + ldata->echo_pos; - nr = ldata->echo_cnt; - while (nr > 0) { - c = *cp; + tail = ldata->echo_tail; + while (ldata->echo_commit != tail) { + c = echo_buf(ldata, tail); if (c == ECHO_OP_START) { unsigned char op; - unsigned char *opp; int no_space_left = 0; /* @@ -563,18 +672,13 @@ static void process_echoes(struct tty_struct *tty) * operation, get the next byte, which is either the * op code or a control character value. */ - opp = cp + 1; - if (opp == buf_end) - opp -= N_TTY_BUF_SIZE; - op = *opp; + op = echo_buf(ldata, tail + 1); switch (op) { unsigned int num_chars, num_bs; case ECHO_OP_ERASE_TAB: - if (++opp == buf_end) - opp -= N_TTY_BUF_SIZE; - num_chars = *opp; + num_chars = echo_buf(ldata, tail + 2); /* * Determine how many columns to go back @@ -600,21 +704,18 @@ static void process_echoes(struct tty_struct *tty) if (ldata->column > 0) ldata->column--; } - cp += 3; - nr -= 3; + tail += 3; break; case ECHO_OP_SET_CANON_COL: ldata->canon_column = ldata->column; - cp += 2; - nr -= 2; + tail += 2; break; case ECHO_OP_MOVE_BACK_COL: if (ldata->column > 0) ldata->column--; - cp += 2; - nr -= 2; + tail += 2; break; case ECHO_OP_START: @@ -626,8 +727,7 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, ECHO_OP_START); ldata->column++; space--; - cp += 2; - nr -= 2; + tail += 2; break; default: @@ -648,15 +748,13 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, op ^ 0100); ldata->column += 2; space -= 2; - cp += 2; - nr -= 2; + tail += 2; } if (no_space_left) break; } else { - if (O_OPOST(tty) && - !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { int retval = do_output_char(c, tty, space); if (retval < 0) break; @@ -667,80 +765,96 @@ static void process_echoes(struct tty_struct *tty) tty_put_char(tty, c); space -= 1; } - cp += 1; - nr -= 1; + tail += 1; } - - /* When end of circular buffer reached, wrap around */ - if (cp >= buf_end) - cp -= N_TTY_BUF_SIZE; } - if (nr == 0) { - ldata->echo_pos = 0; - ldata->echo_cnt = 0; - ldata->echo_overrun = 0; - } else { - int num_processed = ldata->echo_cnt - nr; - ldata->echo_pos += num_processed; - ldata->echo_pos &= N_TTY_BUF_SIZE - 1; - ldata->echo_cnt = nr; - if (num_processed > 0) - ldata->echo_overrun = 0; + /* If the echo buffer is nearly full (so that the possibility exists + * of echo overrun before the next commit), then discard enough + * data at the tail to prevent a subsequent overrun */ + while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { + if (echo_buf(ldata, tail) == ECHO_OP_START) { + if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) + tail += 3; + else + tail += 2; + } else + tail++; } - mutex_unlock(&ldata->echo_lock); + ldata->echo_tail = tail; + return old_space - space; +} + +static void commit_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t nr, old, echoed; + size_t head; + + head = ldata->echo_head; + ldata->echo_mark = head; + old = ldata->echo_commit - ldata->echo_tail; + + /* Process committed echoes if the accumulated # of bytes + * is over the threshold (and try again each time another + * block is accumulated) */ + nr = head - ldata->echo_tail; + if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = head; + echoed = __process_echoes(tty); + mutex_unlock(&ldata->output_lock); + + if (echoed && tty->ops->flush_chars) + tty->ops->flush_chars(tty); +} + +static void process_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t echoed; + + if (ldata->echo_mark == ldata->echo_tail) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = ldata->echo_mark; + echoed = __process_echoes(tty); mutex_unlock(&ldata->output_lock); - if (tty->ops->flush_chars) + if (echoed && tty->ops->flush_chars) tty->ops->flush_chars(tty); } +/* NB: echo_mark and echo_head should be equivalent here */ +static void flush_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if ((!L_ECHO(tty) && !L_ECHONL(tty)) || + ldata->echo_commit == ldata->echo_head) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = ldata->echo_head; + __process_echoes(tty); + mutex_unlock(&ldata->output_lock); +} + /** * add_echo_byte - add a byte to the echo buffer * @c: unicode byte to echo * @ldata: n_tty data * * Add a character or operation byte to the echo buffer. - * - * Should be called under the echo lock to protect the echo buffer. */ -static void add_echo_byte(unsigned char c, struct n_tty_data *ldata) +static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) { - int new_byte_pos; - - if (ldata->echo_cnt == N_TTY_BUF_SIZE) { - /* Circular buffer is already at capacity */ - new_byte_pos = ldata->echo_pos; - - /* - * Since the buffer start position needs to be advanced, - * be sure to step by a whole operation byte group. - */ - if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) { - if (ldata->echo_buf[(ldata->echo_pos + 1) & - (N_TTY_BUF_SIZE - 1)] == - ECHO_OP_ERASE_TAB) { - ldata->echo_pos += 3; - ldata->echo_cnt -= 2; - } else { - ldata->echo_pos += 2; - ldata->echo_cnt -= 1; - } - } else { - ldata->echo_pos++; - } - ldata->echo_pos &= N_TTY_BUF_SIZE - 1; - - ldata->echo_overrun = 1; - } else { - new_byte_pos = ldata->echo_pos + ldata->echo_cnt; - new_byte_pos &= N_TTY_BUF_SIZE - 1; - ldata->echo_cnt++; - } - - ldata->echo_buf[new_byte_pos] = c; + *echo_buf_addr(ldata, ldata->echo_head++) = c; } /** @@ -748,16 +862,12 @@ static void add_echo_byte(unsigned char c, struct n_tty_data *ldata) * @ldata: n_tty data * * Add an operation to the echo buffer to move back one column. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_move_back_col(struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); - mutex_unlock(&ldata->echo_lock); } /** @@ -766,16 +876,12 @@ static void echo_move_back_col(struct n_tty_data *ldata) * * Add an operation to the echo buffer to set the canon column * to the current column. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_set_canon_col(struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); - mutex_unlock(&ldata->echo_lock); } /** @@ -791,15 +897,11 @@ static void echo_set_canon_col(struct n_tty_data *ldata) * of input. This information will be used later, along with * canon column (if applicable), to go back the correct number * of columns. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_erase_tab(unsigned int num_chars, int after_tab, struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); - add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_ERASE_TAB, ldata); @@ -811,8 +913,6 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, num_chars |= 0x80; add_echo_byte(num_chars, ldata); - - mutex_unlock(&ldata->echo_lock); } /** @@ -824,20 +924,16 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, * L_ECHO(tty) is true. Called from the driver receive_buf path. * * This variant does not treat control characters specially. - * - * Locking: echo_lock to protect the echo buffer */ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) { - mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_START, ldata); } else { add_echo_byte(c, ldata); } - mutex_unlock(&ldata->echo_lock); } /** @@ -850,16 +946,12 @@ static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) * * This variant tags control characters to be echoed as "^X" * (where X is the letter representing the control char). - * - * Locking: echo_lock to protect the echo buffer */ static void echo_char(unsigned char c, struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - mutex_lock(&ldata->echo_lock); - if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(ECHO_OP_START, ldata); @@ -868,8 +960,6 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(ECHO_OP_START, ldata); add_echo_byte(c, ldata); } - - mutex_unlock(&ldata->echo_lock); } /** @@ -894,17 +984,22 @@ static inline void finish_erasing(struct n_tty_data *ldata) * present in the stream from the driver layer. Handles the complexities * of UTF-8 multibyte symbols. * - * Locking: read_lock for tty buffers + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * modifies read_head + * + * Modifying the read_head is not considered a publish in this context + * because canonical mode is active -- only canon_head publishes */ static void eraser(unsigned char c, struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; enum { ERASE, WERASE, KILL } kill_type; - int head, seen_alnums, cnt; - unsigned long flags; + size_t head; + size_t cnt; + int seen_alnums; - /* FIXME: locking needed ? */ if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return; @@ -915,19 +1010,11 @@ static void eraser(unsigned char c, struct tty_struct *tty) kill_type = WERASE; else { if (!L_ECHO(tty)) { - spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & - (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&ldata->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & - (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&ldata->read_lock, flags); finish_erasing(ldata); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ @@ -939,14 +1026,13 @@ static void eraser(unsigned char c, struct tty_struct *tty) } seen_alnums = 0; - /* FIXME: Locking ?? */ while (ldata->read_head != ldata->canon_head) { head = ldata->read_head; /* erase a single possibly multibyte character */ do { - head = (head - 1) & (N_TTY_BUF_SIZE-1); - c = ldata->read_buf[head]; + head--; + c = read_buf(ldata, head); } while (is_continuation(c, tty) && head != ldata->canon_head); /* do not partially erase */ @@ -960,11 +1046,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) else if (seen_alnums) break; } - cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); - spin_lock_irqsave(&ldata->read_lock, flags); + cnt = ldata->read_head - head; ldata->read_head = head; - ldata->read_cnt -= cnt; - spin_unlock_irqrestore(&ldata->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) { @@ -974,9 +1057,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); while (--cnt > 0) { - head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(ldata->read_buf[head], - ldata); + head++; + echo_char_raw(read_buf(ldata, head), ldata); echo_move_back_col(ldata); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { @@ -984,7 +1066,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } else if (c == '\t') { unsigned int num_chars = 0; int after_tab = 0; - unsigned long tail = ldata->read_head; + size_t tail = ldata->read_head; /* * Count the columns used for characters @@ -994,8 +1076,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) * number of columns. */ while (tail != ldata->canon_head) { - tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = ldata->read_buf[tail]; + tail--; + c = read_buf(ldata, tail); if (c == '\t') { after_tab = 1; break; @@ -1031,23 +1113,19 @@ static void eraser(unsigned char c, struct tty_struct *tty) * isig - handle the ISIG optio * @sig: signal * @tty: terminal - * @flush: force flush * - * Called when a signal is being sent due to terminal input. This - * may caus terminal flushing to take place according to the termios - * settings and character used. Called from the driver receive_buf - * path so serialized. + * Called when a signal is being sent due to terminal input. + * Called from the driver receive_buf path so serialized. * - * Locking: ctrl_lock, read_lock (both via flush buffer) + * Locking: ctrl_lock */ -static inline void isig(int sig, struct tty_struct *tty, int flush) +static void isig(int sig, struct tty_struct *tty) { - if (tty->pgrp) - kill_pgrp(tty->pgrp, sig, 1); - if (flush || !L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, sig, 1); + put_pid(tty_pgrp); } } @@ -1058,17 +1136,28 @@ static inline void isig(int sig, struct tty_struct *tty, int flush) * An RS232 break event has been hit in the incoming bitstream. This * can cause a variety of events depending upon the termios settings. * - * Called from the receive_buf path so single threaded. + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes read_head via put_tty_queue() + * + * Note: may get exclusive termios_rwsem if flushing input buffer */ -static inline void n_tty_receive_break(struct tty_struct *tty) +static void n_tty_receive_break(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; if (I_IGNBRK(tty)) return; if (I_BRKINT(tty)) { - isig(SIGINT, tty, 1); + isig(SIGINT, tty); + if (!L_NOFLSH(tty)) { + /* flushing needs exclusive termios_rwsem */ + up_read(&tty->termios_rwsem); + n_tty_flush_buffer(tty); + tty_driver_flush_buffer(tty); + down_read(&tty->termios_rwsem); + } return; } if (I_PARMRK(tty)) { @@ -1076,7 +1165,8 @@ static inline void n_tty_receive_break(struct tty_struct *tty) put_tty_queue('\0', ldata); } put_tty_queue('\0', ldata); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); } /** @@ -1092,7 +1182,7 @@ static inline void n_tty_receive_break(struct tty_struct *tty) * private. */ -static inline void n_tty_receive_overrun(struct tty_struct *tty) +static void n_tty_receive_overrun(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; char buf[64]; @@ -1114,24 +1204,50 @@ static inline void n_tty_receive_overrun(struct tty_struct *tty) * @c: character * * Process a parity error and queue the right data to indicate - * the error case if necessary. Locking as per n_tty_receive_buf. + * the error case if necessary. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes read_head via put_tty_queue() */ -static inline void n_tty_receive_parity_error(struct tty_struct *tty, - unsigned char c) +static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; - if (I_IGNPAR(tty)) - return; - if (I_PARMRK(tty)) { - put_tty_queue('\377', ldata); - put_tty_queue('\0', ldata); - put_tty_queue(c, ldata); - } else if (I_INPCK(tty)) - put_tty_queue('\0', ldata); - else + if (I_INPCK(tty)) { + if (I_IGNPAR(tty)) + return; + if (I_PARMRK(tty)) { + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); + put_tty_queue(c, ldata); + } else + put_tty_queue('\0', ldata); + } else put_tty_queue(c, ldata); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); +} + +static void +n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) +{ + if (!L_NOFLSH(tty)) { + /* flushing needs exclusive termios_rwsem */ + up_read(&tty->termios_rwsem); + n_tty_flush_buffer(tty); + tty_driver_flush_buffer(tty); + down_read(&tty->termios_rwsem); + } + if (I_IXON(tty)) + start_tty(tty); + if (L_ECHO(tty)) { + echo_char(c, tty); + commit_echoes(tty); + } else + process_echoes(tty); + isig(signal, tty); + return; } /** @@ -1142,123 +1258,53 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, * Process an individual character of input received from the driver. * This is serialized with respect to itself by the rules for the * driver above. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes canon_head if canonical mode is active + * otherwise, publishes read_head via put_tty_queue() + * + * Returns 1 if LNEXT was received, else returns 0 */ -static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) +static int +n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) { struct n_tty_data *ldata = tty->disc_data; - unsigned long flags; - int parmrk; - - if (ldata->raw) { - put_tty_queue(c, ldata); - return; - } - - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - - if (L_EXTPROC(tty)) { - put_tty_queue(c, ldata); - return; - } - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && - I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && - c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } - - if (tty->closing) { - if (I_IXON(tty)) { - if (c == START_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } else if (c == STOP_CHAR(tty)) - stop_tty(tty); - } - return; - } - - /* - * If the previous character was LNEXT, or we know that this - * character is not one of the characters that we'll have to - * handle specially, do shortcut processing to speed things - * up. - */ - if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { - ldata->lnext = 0; - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { - /* beep if no space */ - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } - if (L_ECHO(tty)) { - finish_erasing(ldata); - /* Record the column of first canon char. */ - if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(ldata); - echo_char(c, tty); - process_echoes(tty); - } - if (parmrk) - put_tty_queue(c, ldata); - put_tty_queue(c, ldata); - return; - } if (I_IXON(tty)) { if (c == START_CHAR(tty)) { start_tty(tty); process_echoes(tty); - return; + return 0; } if (c == STOP_CHAR(tty)) { stop_tty(tty); - return; + return 0; } } if (L_ISIG(tty)) { - int signal; - signal = SIGINT; - if (c == INTR_CHAR(tty)) - goto send_signal; - signal = SIGQUIT; - if (c == QUIT_CHAR(tty)) - goto send_signal; - signal = SIGTSTP; - if (c == SUSP_CHAR(tty)) { -send_signal: - /* - * Note that we do not use isig() here because we want - * the order to be: - * 1) flush, 2) echo, 3) signal - */ - if (!L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - } - if (I_IXON(tty)) - start_tty(tty); - if (L_ECHO(tty)) { - echo_char(c, tty); - process_echoes(tty); - } - if (tty->pgrp) - kill_pgrp(tty->pgrp, signal, 1); - return; + if (c == INTR_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGINT, c); + return 0; + } else if (c == QUIT_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGQUIT, c); + return 0; + } else if (c == SUSP_CHAR(tty)) { + n_tty_receive_signal_char(tty, SIGTSTP, c); + return 0; } } + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (c == '\r') { if (I_IGNCR(tty)) - return; + return 0; if (I_ICRNL(tty)) c = '\n'; } else if (c == '\n' && I_INLCR(tty)) @@ -1268,8 +1314,8 @@ send_signal: if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); - process_echoes(tty); - return; + commit_echoes(tty); + return 0; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { ldata->lnext = 1; @@ -1278,54 +1324,37 @@ send_signal: if (L_ECHOCTL(tty)) { echo_char_raw('^', ldata); echo_char_raw('\b', ldata); - process_echoes(tty); + commit_echoes(tty); } } - return; + return 1; } - if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && - L_IEXTEN(tty)) { - unsigned long tail = ldata->canon_head; + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { + size_t tail = ldata->canon_head; finish_erasing(ldata); echo_char(c, tty); echo_char_raw('\n', ldata); while (tail != ldata->read_head) { - echo_char(ldata->read_buf[tail], tty); - tail = (tail+1) & (N_TTY_BUF_SIZE-1); + echo_char(read_buf(ldata, tail), tty); + tail++; } - process_echoes(tty); - return; + commit_echoes(tty); + return 0; } if (c == '\n') { - if (ldata->read_cnt >= N_TTY_BUF_SIZE) { - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } if (L_ECHO(tty) || L_ECHONL(tty)) { echo_char_raw('\n', ldata); - process_echoes(tty); + commit_echoes(tty); } goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (ldata->read_cnt >= N_TTY_BUF_SIZE) - return; - if (ldata->canon_head != ldata->read_head) - set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } if ((c == EOL_CHAR(tty)) || (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) - ? 1 : 0; - if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } /* * XXX are EOL_CHAR and EOL2_CHAR echoed?!? */ @@ -1334,36 +1363,26 @@ send_signal: if (ldata->canon_head == ldata->read_head) echo_set_canon_col(ldata); echo_char(c, tty); - process_echoes(tty); + commit_echoes(tty); } /* * XXX does PARMRK doubling happen for * EOL_CHAR and EOL2_CHAR? */ - if (parmrk) + if (c == (unsigned char) '\377' && I_PARMRK(tty)) put_tty_queue(c, ldata); handle_newline: - spin_lock_irqsave(&ldata->read_lock, flags); - set_bit(ldata->read_head, ldata->read_flags); - put_tty_queue_nolock(c, ldata); + set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags); + put_tty_queue(c, ldata); ldata->canon_head = ldata->read_head; - ldata->canon_data++; - spin_unlock_irqrestore(&ldata->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); - return; + return 0; } } - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { - /* beep if no space */ - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } if (L_ECHO(tty)) { finish_erasing(ldata); if (c == '\n') @@ -1374,29 +1393,122 @@ handle_newline: echo_set_canon_col(ldata); echo_char(c, tty); } - process_echoes(tty); + commit_echoes(tty); } - if (parmrk) + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) put_tty_queue(c, ldata); put_tty_queue(c, ldata); + return 0; } +static inline void +n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; -/** - * n_tty_write_wakeup - asynchronous I/O notifier - * @tty: tty device - * - * Required for the ptys, serial driver etc. since processes - * that attach themselves to the master and rely on ASYNC - * IO must be woken up - */ + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + put_tty_queue(c, ldata); + put_tty_queue(c, ldata); +} -static void n_tty_write_wakeup(struct tty_struct *tty) +static void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) - kill_fasync(&tty->fasync, SIGIO, POLL_OUT); + n_tty_receive_char_inline(tty, c); +} + +static inline void +n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + put_tty_queue(c, ldata); +} + +static void n_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) +{ + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + + if (I_IXON(tty)) { + if (c == STOP_CHAR(tty)) + stop_tty(tty); + else if (c == START_CHAR(tty) || + (tty->stopped && !tty->flow_stopped && I_IXANY(tty) && + c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && + c != SUSP_CHAR(tty))) { + start_tty(tty); + process_echoes(tty); + } + } +} + +static void +n_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) +{ + char buf[64]; + + switch (flag) { + case TTY_BREAK: + n_tty_receive_break(tty); + break; + case TTY_PARITY: + case TTY_FRAME: + n_tty_receive_parity_error(tty, c); + break; + case TTY_OVERRUN: + n_tty_receive_overrun(tty); + break; + default: + printk(KERN_ERR "%s: unknown flag %d\n", + tty_name(tty, buf), flag); + break; + } +} + +static void +n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) +{ + struct n_tty_data *ldata = tty->disc_data; + + ldata->lnext = 0; + if (likely(flag == TTY_NORMAL)) { + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + n_tty_receive_char(tty, c); + } else + n_tty_receive_char_flagged(tty, c, flag); } /** @@ -1410,86 +1522,209 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * been received. This function must be called from soft contexts * not from interrupt context. The driver is responsible for making * calls one at a time and in order (or using flush_to_ldisc) + * + * n_tty_receive_buf()/producer path: + * claims non-exclusive termios_rwsem + * publishes read_head and canon_head */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static void +n_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct n_tty_data *ldata = tty->disc_data; - const unsigned char *p; - char *f, flags = TTY_NORMAL; - int i; - char buf[64]; - unsigned long cpuflags; - - if (ldata->real_raw) { - spin_lock_irqsave(&ldata->read_lock, cpuflags); - i = min(N_TTY_BUF_SIZE - ldata->read_cnt, - N_TTY_BUF_SIZE - ldata->read_head); - i = min(count, i); - memcpy(ldata->read_buf + ldata->read_head, cp, i); - ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); - ldata->read_cnt += i; - cp += i; - count -= i; - - i = min(N_TTY_BUF_SIZE - ldata->read_cnt, - N_TTY_BUF_SIZE - ldata->read_head); - i = min(count, i); - memcpy(ldata->read_buf + ldata->read_head, cp, i); - ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); - ldata->read_cnt += i; - spin_unlock_irqrestore(&ldata->read_lock, cpuflags); - } else { - for (i = count, p = cp, f = fp; i; i--, p++) { - if (f) - flags = *f++; - switch (flags) { - case TTY_NORMAL: - n_tty_receive_char(tty, *p); - break; - case TTY_BREAK: - n_tty_receive_break(tty); - break; - case TTY_PARITY: - case TTY_FRAME: - n_tty_receive_parity_error(tty, *p); - break; - case TTY_OVERRUN: - n_tty_receive_overrun(tty); - break; - default: - printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flags); - break; + size_t n, head; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head); + n = min_t(size_t, count, n); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + cp += n; + count -= n; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = N_TTY_BUF_SIZE - max(read_cnt(ldata), head); + n = min_t(size_t, count, n); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; +} + +static void +n_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + put_tty_queue(*cp++, ldata); + else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + n_tty_receive_char_closing(tty, *cp++); + else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + if (L_EXTPROC(tty)) { + put_tty_queue(c, ldata); + continue; + } + if (!test_bit(c, ldata->char_map)) + n_tty_receive_char_inline(tty, c); + else if (n_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; } + } else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void +n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (!test_bit(c, ldata->char_map)) + n_tty_receive_char_fast(tty, c); + else if (n_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } else + n_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + + if (ldata->real_raw) + n_tty_receive_buf_real_raw(tty, cp, fp, count); + else if (ldata->raw || (L_EXTPROC(tty) && !preops)) + n_tty_receive_buf_raw(tty, cp, fp, count); + else if (tty->closing && !L_EXTPROC(tty)) + n_tty_receive_buf_closing(tty, cp, fp, count); + else { + if (ldata->lnext) { + char flag = TTY_NORMAL; + + if (fp) + flag = *fp++; + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; } + + if (!preops && !I_PARMRK(tty)) + n_tty_receive_buf_fast(tty, cp, fp, count); + else + n_tty_receive_buf_standard(tty, cp, fp, count); + + flush_echoes(tty); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } - n_tty_set_room(tty); - - if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (read_cnt(ldata) >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); } +} - /* - * Check the remaining room for the input canonicalization - * mode. We don't want to throttle the driver if we're in - * canonical mode and don't have a newline yet! - */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) - tty_throttle(tty); +static int +n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count, int flow) +{ + struct n_tty_data *ldata = tty->disc_data; + int room, n, rcvd = 0; + + down_read(&tty->termios_rwsem); + + while (1) { + room = receive_room(tty); + n = min(count, room); + if (!n) { + if (flow && !room) + ldata->no_room = 1; + break; + } + __receive_buf(tty, cp, fp, n); + cp += n; + if (fp) + fp += n; + count -= n; + rcvd += n; + } - /* FIXME: there is a tiny race here if the receive room check runs - before the other work executes and empties the buffer (upping - the receiving room and unthrottling. We then throttle and get - stuck. This has been observed and traced down by Vincent Pillet/ - We need to address this when we sort out out the rx path locking */ + tty->receive_room = room; + n_tty_check_throttle(tty); + up_read(&tty->termios_rwsem); + + return rcvd; +} + +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + n_tty_receive_buf_common(tty, cp, fp, count, 0); +} + +static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + return n_tty_receive_buf_common(tty, cp, fp, count, 1); } int is_ignored(int sig) @@ -1509,72 +1744,67 @@ int is_ignored(int sig) * guaranteed that this function will not be re-entered or in progress * when the ldisc is closed. * - * Locking: Caller holds tty->termios_mutex + * Locking: Caller holds tty->termios_rwsem */ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { struct n_tty_data *ldata = tty->disc_data; - int canon_change = 1; - if (old) - canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; - if (canon_change) { + if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - ldata->canon_head = ldata->read_tail; - ldata->canon_data = 0; + ldata->line_start = ldata->read_tail; + if (!L_ICANON(tty) || !read_cnt(ldata)) { + ldata->canon_head = ldata->read_tail; + ldata->push = 0; + } else { + set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1), + ldata->read_flags); + ldata->canon_head = ldata->read_head; + ldata->push = 1; + } ldata->erasing = 0; + ldata->lnext = 0; } - if (canon_change && !L_ICANON(tty) && ldata->read_cnt) - wake_up_interruptible(&tty->read_wait); - ldata->icanon = (L_ICANON(tty) != 0); - if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - ldata->raw = 1; - ldata->real_raw = 1; - n_tty_set_room(tty); - return; - } + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - bitmap_zero(ldata->process_char_map, 256); + bitmap_zero(ldata->char_map, 256); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', ldata->process_char_map); + set_bit('\r', ldata->char_map); if (I_INLCR(tty)) - set_bit('\n', ldata->process_char_map); + set_bit('\n', ldata->char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), ldata->process_char_map); - set_bit(KILL_CHAR(tty), ldata->process_char_map); - set_bit(EOF_CHAR(tty), ldata->process_char_map); - set_bit('\n', ldata->process_char_map); - set_bit(EOL_CHAR(tty), ldata->process_char_map); + set_bit(ERASE_CHAR(tty), ldata->char_map); + set_bit(KILL_CHAR(tty), ldata->char_map); + set_bit(EOF_CHAR(tty), ldata->char_map); + set_bit('\n', ldata->char_map); + set_bit(EOL_CHAR(tty), ldata->char_map); if (L_IEXTEN(tty)) { - set_bit(WERASE_CHAR(tty), - ldata->process_char_map); - set_bit(LNEXT_CHAR(tty), - ldata->process_char_map); - set_bit(EOL2_CHAR(tty), - ldata->process_char_map); + set_bit(WERASE_CHAR(tty), ldata->char_map); + set_bit(LNEXT_CHAR(tty), ldata->char_map); + set_bit(EOL2_CHAR(tty), ldata->char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - ldata->process_char_map); + ldata->char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), ldata->process_char_map); - set_bit(STOP_CHAR(tty), ldata->process_char_map); + set_bit(START_CHAR(tty), ldata->char_map); + set_bit(STOP_CHAR(tty), ldata->char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), ldata->process_char_map); - set_bit(QUIT_CHAR(tty), ldata->process_char_map); - set_bit(SUSP_CHAR(tty), ldata->process_char_map); + set_bit(INTR_CHAR(tty), ldata->char_map); + set_bit(QUIT_CHAR(tty), ldata->char_map); + set_bit(SUSP_CHAR(tty), ldata->char_map); } - clear_bit(__DISABLED_CHAR, ldata->process_char_map); + clear_bit(__DISABLED_CHAR, ldata->char_map); ldata->raw = 0; ldata->real_raw = 0; } else { @@ -1587,9 +1817,20 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) ldata->real_raw = 0; } n_tty_set_room(tty); + /* + * Fix tty hang when I_IXON(tty) is cleared, but the tty + * been stopped by STOP_CHAR(tty) before it. + */ + if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) { + start_tty(tty); + process_echoes(tty); + } + /* The termios change make the tty ready for I/O */ - wake_up_interruptible(&tty->write_wait); - wake_up_interruptible(&tty->read_wait); + if (waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + if (waitqueue_active(&tty->read_wait)) + wake_up_interruptible(&tty->read_wait); } /** @@ -1606,10 +1847,10 @@ static void n_tty_close(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; - n_tty_flush_buffer(tty); - kfree(ldata->read_buf); - kfree(ldata->echo_buf); - kfree(ldata); + if (tty->link) + n_tty_packet_mode_flush(tty); + + vfree(ldata); tty->disc_data = NULL; } @@ -1627,51 +1868,43 @@ static int n_tty_open(struct tty_struct *tty) { struct n_tty_data *ldata; - ldata = kzalloc(sizeof(*ldata), GFP_KERNEL); + /* Currently a malloc failure here can panic */ + ldata = vmalloc(sizeof(*ldata)); if (!ldata) goto err; ldata->overrun_time = jiffies; mutex_init(&ldata->atomic_read_lock); mutex_init(&ldata->output_lock); - mutex_init(&ldata->echo_lock); - spin_lock_init(&ldata->read_lock); - - /* These are ugly. Currently a malloc failure here can panic */ - ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!ldata->read_buf || !ldata->echo_buf) - goto err_free_bufs; tty->disc_data = ldata; - reset_buffer_flags(tty); - tty_unthrottle(tty); + reset_buffer_flags(tty->disc_data); ldata->column = 0; - n_tty_set_termios(tty, NULL); - tty->minimum_to_wake = 1; + ldata->canon_column = 0; + ldata->minimum_to_wake = 1; + ldata->num_overrun = 0; + ldata->no_room = 0; + ldata->lnext = 0; tty->closing = 0; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + n_tty_set_termios(tty, NULL); + tty_unthrottle(tty); return 0; -err_free_bufs: - kfree(ldata->read_buf); - kfree(ldata->echo_buf); - kfree(ldata); err: return -ENOMEM; } -static inline int input_available_p(struct tty_struct *tty, int amt) +static inline int input_available_p(struct tty_struct *tty, int poll) { struct n_tty_data *ldata = tty->disc_data; + int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1; - tty_flush_to_ldisc(tty); - if (ldata->icanon && !L_EXTPROC(tty)) { - if (ldata->canon_data) - return 1; - } else if (ldata->read_cnt >= (amt ? amt : 1)) - return 1; - - return 0; + if (ldata->icanon && !L_EXTPROC(tty)) + return ldata->canon_head != ldata->read_tail; + else + return read_cnt(ldata) >= amt; } /** @@ -1689,6 +1922,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * * Called under the ldata->atomic_read_lock sem * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published */ static int copy_from_read_buf(struct tty_struct *tty, @@ -1699,34 +1935,126 @@ static int copy_from_read_buf(struct tty_struct *tty, struct n_tty_data *ldata = tty->disc_data; int retval; size_t n; - unsigned long flags; bool is_eof; + size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); retval = 0; - spin_lock_irqsave(&ldata->read_lock, flags); - n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); + n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail); n = min(*nr, n); - spin_unlock_irqrestore(&ldata->read_lock, flags); if (n) { - retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); + retval = copy_to_user(*b, read_buf_addr(ldata, tail), n); n -= retval; - is_eof = n == 1 && - ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, + is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty); + tty_audit_add_data(tty, read_buf_addr(ldata, tail), n, ldata->icanon); - spin_lock_irqsave(&ldata->read_lock, flags); - ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); - ldata->read_cnt -= n; + ldata->read_tail += n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata)) n = 0; - spin_unlock_irqrestore(&ldata->read_lock, flags); *b += n; *nr -= n; } return retval; } +/** + * canon_copy_from_read_buf - copy read data in canonical mode + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function for n_tty_read. It is only called when ICANON is on; + * it copies one line of input up to and including the line-delimiting + * character into the user-space buffer. + * + * NB: When termios is changed from non-canonical to canonical mode and + * the read buffer contains data, n_tty_set_termios() simulates an EOF + * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer. + * This causes data already processed as input to be immediately available + * as input although a newline has not been received. + * + * Called under the atomic_read_lock mutex + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int canon_copy_from_read_buf(struct tty_struct *tty, + unsigned char __user **b, + size_t *nr) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t n, size, more, c; + size_t eol; + size_t tail; + int ret, found = 0; + bool eof_push = 0; + + /* N.B. avoid overrun if nr == 0 */ + n = min(*nr, read_cnt(ldata)); + if (!n) + return 0; + + tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); + size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); + + n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n", + __func__, *nr, tail, n, size); + + eol = find_next_bit(ldata->read_flags, size, tail); + more = n - (size - tail); + if (eol == N_TTY_BUF_SIZE && more) { + /* scan wrapped without finding set bit */ + eol = find_next_bit(ldata->read_flags, more, 0); + if (eol != more) + found = 1; + } else if (eol != size) + found = 1; + + size = N_TTY_BUF_SIZE - tail; + n = eol - tail; + if (n > 4096) + n += 4096; + n += found; + c = n; + + if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) { + n--; + eof_push = !n && ldata->read_tail != ldata->line_start; + } + + n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n", + __func__, eol, found, n, c, size, more); + + if (n > size) { + ret = copy_to_user(*b, read_buf_addr(ldata, tail), size); + if (ret) + return -EFAULT; + ret = copy_to_user(*b + size, ldata->read_buf, n - size); + } else + ret = copy_to_user(*b, read_buf_addr(ldata, tail), n); + + if (ret) + return -EFAULT; + *b += n; + *nr -= n; + + if (found) + clear_bit(eol, ldata->read_flags); + smp_mb__after_atomic(); + ldata->read_tail += c; + + if (found) { + if (!ldata->push) + ldata->line_start = ldata->read_tail; + else + ldata->push = 0; + tty_audit_push(tty); + } + return eof_push ? -EAGAIN : 0; +} + extern ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *); @@ -1739,10 +2067,9 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, * and if appropriate send any needed signals and return a negative * error code if action should be taken. * - * FIXME: - * Locking: None - redirected write test is safe, testing - * current->signal should possibly lock current->sighand - * pgrp locking ? + * Locking: redirected write test is safe + * current->signal->tty check is safe + * ctrl_lock to safely reference tty->pgrp */ static int job_control(struct tty_struct *tty, struct file *file) @@ -1752,19 +2079,22 @@ static int job_control(struct tty_struct *tty, struct file *file) /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_op->write != redirected_tty_write && - current->signal->tty == tty) { - if (!tty->pgrp) - printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); - else if (task_pgrp(current) != tty->pgrp) { - if (is_ignored(SIGTTIN) || - is_current_pgrp_orphaned()) - return -EIO; - kill_pgrp(task_pgrp(current), SIGTTIN, 1); - set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; - } + if (file->f_op->write == redirected_tty_write || + current->signal->tty != tty) + return 0; + + spin_lock_irq(&tty->ctrl_lock); + if (!tty->pgrp) + printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); + else if (task_pgrp(current) != tty->pgrp) { + spin_unlock_irq(&tty->ctrl_lock); + if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) + return -EIO; + kill_pgrp(task_pgrp(current), SIGTTIN, 1); + set_thread_flag(TIF_SIGPENDING); + return -ERESTARTSYS; } + spin_unlock_irq(&tty->ctrl_lock); return 0; } @@ -1782,6 +2112,10 @@ static int job_control(struct tty_struct *tty, struct file *file) * a hangup. Always called in user context, may sleep. * * This code must be sure never to sleep through a hangup. + * + * n_tty_read()/consumer path: + * claims non-exclusive termios_rwsem + * publishes read_tail */ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, @@ -1793,47 +2127,44 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int c; int minimum, time; ssize_t retval = 0; - ssize_t size; long timeout; unsigned long flags; int packet; -do_it_again: c = job_control(tty, file); if (c < 0) return c; + /* + * Internal serialization of reads. + */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&ldata->atomic_read_lock)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) + return -ERESTARTSYS; + } + + down_read(&tty->termios_rwsem); + minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; if (!ldata->icanon) { - time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); if (time) - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; else if (!waitqueue_active(&tty->read_wait) || - (tty->minimum_to_wake > minimum)) - tty->minimum_to_wake = minimum; + (ldata->minimum_to_wake > minimum)) + ldata->minimum_to_wake = minimum; } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } - tty->minimum_to_wake = minimum = 1; + timeout = (HZ / 10) * TIME_CHAR(tty); + ldata->minimum_to_wake = minimum = 1; } } - /* - * Internal serialization of reads. - */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&ldata->atomic_read_lock)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&ldata->atomic_read_lock)) - return -ERESTARTSYS; - } packet = tty->packet; add_wait_queue(&tty->read_wait, &wait); @@ -1860,31 +2191,40 @@ do_it_again: TASK_RUNNING. */ set_current_state(TASK_INTERRUPTIBLE); - if (((minimum - (b - buf)) < tty->minimum_to_wake) && + if (((minimum - (b - buf)) < ldata->minimum_to_wake) && ((minimum - (b - buf)) >= 1)) - tty->minimum_to_wake = (minimum - (b - buf)); + ldata->minimum_to_wake = (minimum - (b - buf)); 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); + tty_flush_to_ldisc(tty); + down_read(&tty->termios_rwsem); + if (!input_available_p(tty, 0)) { + retval = -EIO; + break; + } + } else { + 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; + } + n_tty_set_room(tty); + up_read(&tty->termios_rwsem); + + timeout = schedule_timeout(timeout); + + down_read(&tty->termios_rwsem); + continue; } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); - timeout = schedule_timeout(timeout); - continue; } __set_current_state(TASK_RUNNING); @@ -1899,45 +2239,11 @@ do_it_again: } if (ldata->icanon && !L_EXTPROC(tty)) { - /* N.B. avoid overrun if nr == 0 */ - spin_lock_irqsave(&ldata->read_lock, flags); - while (nr && ldata->read_cnt) { - int eol; - - eol = test_and_clear_bit(ldata->read_tail, - ldata->read_flags); - c = ldata->read_buf[ldata->read_tail]; - ldata->read_tail = ((ldata->read_tail+1) & - (N_TTY_BUF_SIZE-1)); - ldata->read_cnt--; - if (eol) { - /* this test should be redundant: - * we shouldn't be reading data if - * canon_data is 0 - */ - if (--ldata->canon_data < 0) - ldata->canon_data = 0; - } - spin_unlock_irqrestore(&ldata->read_lock, flags); - - if (!eol || (c != __DISABLED_CHAR)) { - if (tty_put_user(tty, c, b++)) { - retval = -EFAULT; - b--; - spin_lock_irqsave(&ldata->read_lock, flags); - break; - } - nr--; - } - if (eol) { - tty_audit_push(tty); - spin_lock_irqsave(&ldata->read_lock, flags); - break; - } - spin_lock_irqsave(&ldata->read_lock, flags); - } - spin_unlock_irqrestore(&ldata->read_lock, flags); - if (retval) + retval = canon_copy_from_read_buf(tty, &b, &nr); + if (retval == -EAGAIN) { + retval = 0; + continue; + } else if (retval) break; } else { int uncopied; @@ -1951,39 +2257,26 @@ do_it_again: } } - /* If there is enough space in the read buffer now, let the - * low-level driver know. We use n_tty_chars_in_buffer() to - * check the buffer, as it now knows about canonical mode. - * Otherwise, if the driver is throttled and the line is - * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, - * we won't get any more characters. - */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); - check_unthrottle(tty); - } + n_tty_check_unthrottle(tty); if (b - buf >= minimum) break; if (time) timeout = time; } - mutex_unlock(&ldata->atomic_read_lock); - remove_wait_queue(&tty->read_wait, &wait); + n_tty_set_room(tty); + up_read(&tty->termios_rwsem); + remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = minimum; + ldata->minimum_to_wake = minimum; + + mutex_unlock(&ldata->atomic_read_lock); __set_current_state(TASK_RUNNING); - size = b - buf; - if (size) { - retval = size; - if (nr) - clear_bit(TTY_PUSH, &tty->flags); - } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; + if (b - buf) + retval = b - buf; - n_tty_set_room(tty); return retval; } @@ -2024,6 +2317,8 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, return retval; } + down_read(&tty->termios_rwsem); + /* Write out any echoed characters that are still pending */ process_echoes(tty); @@ -2038,7 +2333,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EIO; break; } - if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + if (O_OPOST(tty)) { while (nr > 0) { ssize_t num = process_output_block(tty, b, nr); if (num < 0) { @@ -2059,8 +2354,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, if (tty->ops->flush_chars) tty->ops->flush_chars(tty); } else { + struct n_tty_data *ldata = tty->disc_data; + while (nr > 0) { + mutex_lock(&ldata->output_lock); c = tty->ops->write(tty, b, nr); + mutex_unlock(&ldata->output_lock); if (c < 0) { retval = c; goto break_out; @@ -2077,13 +2376,18 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, retval = -EAGAIN; break; } + up_read(&tty->termios_rwsem); + schedule(); + + down_read(&tty->termios_rwsem); } break_out: __set_current_state(TASK_RUNNING); remove_wait_queue(&tty->write_wait, &wait); if (b - buf != nr && tty->fasync) set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + up_read(&tty->termios_rwsem); return (b - buf) ? b - buf : retval; } @@ -2104,11 +2408,12 @@ break_out: static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { + struct n_tty_data *ldata = tty->disc_data; unsigned int mask = 0; poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); - if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) + if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; @@ -2118,9 +2423,9 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - tty->minimum_to_wake = MIN_CHAR(tty); + ldata->minimum_to_wake = MIN_CHAR(tty); else - tty->minimum_to_wake = 1; + ldata->minimum_to_wake = 1; } if (tty->ops->write && !tty_is_writelocked(tty) && tty_chars_in_buffer(tty) < WAKEUP_CHARS && @@ -2131,19 +2436,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, static unsigned long inq_canon(struct n_tty_data *ldata) { - int nr, head, tail; + size_t nr, head, tail; - if (!ldata->canon_data) + if (ldata->canon_head == ldata->read_tail) return 0; head = ldata->canon_head; tail = ldata->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); + nr = head - tail; /* Skip EOF-chars.. */ while (head != tail) { - if (test_bit(tail, ldata->read_flags) && - ldata->read_buf[tail] == __DISABLED_CHAR) + if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && + read_buf(ldata, tail) == __DISABLED_CHAR) nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); + tail++; } return nr; } @@ -2158,16 +2463,30 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, case TIOCOUTQ: return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: - /* FIXME: Locking */ - retval = ldata->read_cnt; + down_write(&tty->termios_rwsem); if (L_ICANON(tty)) retval = inq_canon(ldata); + else + retval = read_cnt(ldata); + up_write(&tty->termios_rwsem); return put_user(retval, (unsigned int __user *) arg); default: return n_tty_ioctl_helper(tty, file, cmd, arg); } } +static void n_tty_fasync(struct tty_struct *tty, int on) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!waitqueue_active(&tty->read_wait)) { + if (on) + ldata->minimum_to_wake = 1; + else if (!tty->fasync) + ldata->minimum_to_wake = N_TTY_BUF_SIZE; + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -2181,14 +2500,16 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, - .write_wakeup = n_tty_write_wakeup + .write_wakeup = n_tty_write_wakeup, + .fasync = n_tty_fasync, + .receive_buf2 = n_tty_receive_buf2, }; /** * n_tty_inherit_ops - inherit N_TTY methods * @ops: struct tty_ldisc_ops where to save N_TTY methods * - * Used by a generic struct tty_ldisc_ops to easily inherit N_TTY + * Enables a 'subclass' line discipline to 'inherit' N_TTY * methods. */ diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index a0c69ab0439..cd042936955 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -791,7 +791,6 @@ static int send_data(enum port_type index, struct nozomi *dc) const u8 toggle = port->toggle_ul; void __iomem *addr = port->ul_addr[toggle]; const u32 ul_size = port->ul_size[toggle]; - struct tty_struct *tty = tty_port_tty_get(&port->port); /* Get data from tty and place in buf for now */ size = kfifo_out(&port->fifo_ul, dc->send_buf, @@ -799,7 +798,6 @@ static int send_data(enum port_type index, struct nozomi *dc) if (size == 0) { DBG4("No more data to send, disable link:"); - tty_kref_put(tty); return 0; } @@ -809,10 +807,8 @@ static int send_data(enum port_type index, struct nozomi *dc) write_mem32(addr, (u32 *) &size, 4); write_mem32(addr + 4, (u32 *) dc->send_buf, size); - if (tty) - tty_wakeup(tty); + tty_port_tty_wakeup(&port->port); - tty_kref_put(tty); return 1; } @@ -827,15 +823,10 @@ static int receive_data(enum port_type index, struct nozomi *dc) struct tty_struct *tty = tty_port_tty_get(&port->port); int i, ret; - if (unlikely(!tty)) { - DBG1("tty not open for port: %d?", index); - return 1; - } - read_mem32((u32 *) &size, addr, 4); /* DBG1( "%d bytes port: %d", size, index); */ - if (test_bit(TTY_THROTTLED, &tty->flags)) { + if (tty && test_bit(TTY_THROTTLED, &tty->flags)) { DBG1("No room in tty, don't read data, don't ack interrupt, " "disable interrupt"); @@ -855,13 +846,14 @@ static int receive_data(enum port_type index, struct nozomi *dc) read_mem32((u32 *) buf, addr + offset, RECEIVE_BUF_MAX); if (size == 1) { - tty_insert_flip_char(tty, buf[0], TTY_NORMAL); + tty_insert_flip_char(&port->port, buf[0], TTY_NORMAL); size = 0; } else if (size < RECEIVE_BUF_MAX) { - size -= tty_insert_flip_string(tty, (char *) buf, size); + size -= tty_insert_flip_string(&port->port, + (char *)buf, size); } else { - i = tty_insert_flip_string(tty, \ - (char *) buf, RECEIVE_BUF_MAX); + i = tty_insert_flip_string(&port->port, + (char *)buf, RECEIVE_BUF_MAX); size -= i; offset += i; } @@ -967,7 +959,7 @@ static int receive_flow_control(struct nozomi *dc) dev_err(&dc->pdev->dev, "ERROR: flow control received for non-existing port\n"); return 0; - }; + } DBG1("0x%04X->0x%04X", *((u16 *)&dc->port[port].ctrl_dl), *((u16 *)&ctrl_dl)); @@ -1033,7 +1025,7 @@ static enum ctrl_port_type port2ctrl(enum port_type port, dev_err(&dc->pdev->dev, "ERROR: send flow control " \ "received for non-existing port\n"); - }; + } return CTRL_ERROR; } @@ -1276,15 +1268,11 @@ static irqreturn_t interrupt_handler(int irq, void *dev_id) exit_handler: spin_unlock(&dc->spin_mutex); - for (a = 0; a < NOZOMI_MAX_PORTS; a++) { - struct tty_struct *tty; - if (test_and_clear_bit(a, &dc->flip)) { - tty = tty_port_tty_get(&dc->port[a].port); - if (tty) - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - } + + for (a = 0; a < NOZOMI_MAX_PORTS; a++) + if (test_and_clear_bit(a, &dc->flip)) + tty_flip_buffer_push(&dc->port[a].port); + return IRQ_HANDLED; none: spin_unlock(&dc->spin_mutex); @@ -1513,12 +1501,9 @@ static void tty_exit(struct nozomi *dc) DBG1(" "); - for (i = 0; i < MAX_PORT; ++i) { - struct tty_struct *tty = tty_port_tty_get(&dc->port[i].port); - if (tty && list_empty(&tty->hangup_work.entry)) - tty_hangup(tty); - tty_kref_put(tty); - } + for (i = 0; i < MAX_PORT; ++i) + tty_port_tty_hangup(&dc->port[i].port, false); + /* Racy below - surely should wait for scheduled work to be done or complete off a hangup method ? */ while (dc->open_ttys) @@ -1687,12 +1672,6 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, rval = kfifo_in(&port->fifo_ul, (unsigned char *)buffer, count); - /* notify card */ - if (unlikely(dc == NULL)) { - DBG1("No device context?"); - goto exit; - } - spin_lock_irqsave(&dc->spin_mutex, flags); /* CTS is only valid on the modem channel */ if (port == &(dc->port[PORT_MDM])) { @@ -1708,7 +1687,6 @@ static int ntty_write(struct tty_struct *tty, const unsigned char *buffer, } spin_unlock_irqrestore(&dc->spin_mutex, flags); -exit: return rval; } @@ -1827,7 +1805,7 @@ static int ntty_ioctl(struct tty_struct *tty, default: DBG1("ERR: 0x%08X, %d", cmd, cmd); break; - }; + } return rval; } diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 79ff3a5e925..25c9bc78372 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -38,16 +38,18 @@ 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)) + return; if (tty->count > 2) return; } + set_bit(TTY_IO_ERROR, &tty->flags); wake_up_interruptible(&tty->read_wait); wake_up_interruptible(&tty->write_wait); tty->packet = 0; /* Review - krefs on tty_link ?? */ if (!tty->link) return; - tty->link->packet = 0; set_bit(TTY_OTHER_CLOSED, &tty->link->flags); wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); @@ -55,9 +57,10 @@ static void pty_close(struct tty_struct *tty, struct file *filp) set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { - mutex_lock(&devpts_mutex); - devpts_pty_kill(tty->link->driver_data); - mutex_unlock(&devpts_mutex); + mutex_lock(&devpts_mutex); + if (tty->link->driver_data) + devpts_pty_kill(tty->link->driver_data); + mutex_unlock(&devpts_mutex); } #endif tty_unlock(tty); @@ -86,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty) * pty_space - report space left for writing * @to: tty we are writing into * - * The tty buffers allow 64K but we sneak a peak and clip at 8K this - * allows a lot of overspill room for echo and other fun messes to - * be handled properly + * Limit the buffer space used by ptys to 8k. */ static int pty_space(struct tty_struct *to) { - int n = 8192 - to->port->buf.memory_used; - if (n < 0) - return 0; - return n; + int n = tty_buffer_space_avail(to->port); + return min(n, 8192); } /** @@ -120,12 +119,10 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) if (c > 0) { /* Stuff the data into the input queue of the other end */ - c = tty_insert_flip_string(to, buf, c); + c = tty_insert_flip_string(to->port, buf, c); /* And shovel */ - if (c) { - tty_flip_buffer_push(to); - tty_wakeup(tty); - } + if (c) + tty_flip_buffer_push(to->port); } return c; } @@ -241,24 +238,24 @@ static void pty_flush_buffer(struct tty_struct *tty) static int pty_open(struct tty_struct *tty, struct file *filp) { - int retval = -ENODEV; - if (!tty || !tty->link) - goto out; + return -ENODEV; - retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; if (test_bit(TTY_PTY_LOCK, &tty->link->flags)) goto out; - if (tty->link->count != 1) + if (tty->driver->subtype == PTY_TYPE_SLAVE && tty->link->count != 1) goto out; + clear_bit(TTY_IO_ERROR, &tty->flags); clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); - retval = 0; + return 0; + out: - return retval; + set_bit(TTY_IO_ERROR, &tty->flags); + return -EIO; } static void pty_set_termios(struct tty_struct *tty, @@ -284,7 +281,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) struct tty_struct *pty = tty->link; /* For a PTY we need to lock the tty side */ - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; @@ -311,7 +308,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; pty->winsize = *ws; /* Never used so will go away soon */ done: - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return 0; } @@ -399,15 +396,8 @@ err: return retval; } -/* this is called once with whichever end is closed last */ -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - devpts_kill_index(tty->driver_data, tty->index); -} - static void pty_cleanup(struct tty_struct *tty) { - tty->port->itty = NULL; tty_port_put(tty->port); } @@ -621,6 +611,12 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) { } +/* this is called once with whichever end is closed last */ +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + devpts_kill_index(tty->driver_data, tty->index); +} + static const struct tty_operations ptm_unix98_ops = { .lookup = ptm_unix98_lookup, .install = pty_unix98_install, @@ -663,7 +659,7 @@ static const struct tty_operations pty_unix98_ops = { * Allocate a unix98 pty master device from the ptmx driver. * * Locking: tty_mutex protects the init_dev work. tty->count should - * protect the rest. + * protect the rest. * allocated_ptys_lock handles the list of free pty numbers */ @@ -676,6 +672,9 @@ static int ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); + /* We refuse fsnotify events on ptmx, since it's a shared resource */ + filp->f_mode |= FMODE_NONOTIFY; + retval = tty_alloc_file(filp); if (retval) return retval; @@ -704,6 +703,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) mutex_unlock(&tty_mutex); set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ + tty->driver_data = inode; tty_add_file(tty, filp); @@ -714,14 +714,13 @@ static int ptmx_open(struct inode *inode, struct file *filp) retval = PTR_ERR(slave_inode); goto err_release; } + tty->link->driver_data = slave_inode; retval = ptm_driver->ops->open(tty, filp); if (retval) goto err_release; tty_unlock(tty); - tty->driver_data = inode; - tty->link->driver_data = slave_inode; return 0; err_release: tty_unlock(tty); @@ -797,7 +796,7 @@ static void __init unix98_pty_init(void) cdev_init(&ptmx_cdev, &ptmx_fops); if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) - panic("Couldn't register /dev/ptmx driver\n"); + panic("Couldn't register /dev/ptmx driver"); device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); } diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index e42009a0052..383c4c79663 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -55,7 +55,7 @@ #undef REV_PCI_ORDER #undef ROCKET_DEBUG_IO -#define POLL_PERIOD HZ/100 /* Polling period .01 seconds (10ms) */ +#define POLL_PERIOD (HZ/100) /* Polling period .01 seconds (10ms) */ /****** Kernel includes ******/ @@ -150,12 +150,14 @@ static Word_t aiop_intr_bits[AIOP_CTL_SIZE] = { AIOP_INTR_BIT_3 }; +#ifdef CONFIG_PCI static Word_t upci_aiop_intr_bits[AIOP_CTL_SIZE] = { UPCI_AIOP_INTR_BIT_0, UPCI_AIOP_INTR_BIT_1, UPCI_AIOP_INTR_BIT_2, UPCI_AIOP_INTR_BIT_3 }; +#endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, @@ -227,7 +229,6 @@ static unsigned long nextLineNumber; static int __init init_ISA(int i); static void rp_wait_until_sent(struct tty_struct *tty, int timeout); static void rp_flush_buffer(struct tty_struct *tty); -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model); static unsigned char GetLineNumber(int ctrl, int aiop, int ch); static unsigned char SetLineNumber(int ctrl, int aiop, int ch); static void rp_start(struct tty_struct *tty); @@ -241,11 +242,6 @@ static void sDisInterrupts(CHANNEL_T * ChP, Word_t Flags); static void sModemReset(CONTROLLER_T * CtlP, int chan, int on); static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on); static int sWriteTxPrioByte(CHANNEL_T * ChP, Byte_t Data); -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd); static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t * AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly); @@ -315,9 +311,8 @@ static inline int rocket_paranoia_check(struct r_port *info, * that receive data is present on a serial port. Pulls data from FIFO, moves it into the * tty layer. */ -static void rp_do_receive(struct r_port *info, - struct tty_struct *tty, - CHANNEL_t * cp, unsigned int ChanStatus) +static void rp_do_receive(struct r_port *info, CHANNEL_t *cp, + unsigned int ChanStatus) { unsigned int CharNStat; int ToRecv, wRecv, space; @@ -379,7 +374,8 @@ static void rp_do_receive(struct r_port *info, flag = TTY_OVERRUN; else flag = TTY_NORMAL; - tty_insert_flip_char(tty, CharNStat & 0xff, flag); + tty_insert_flip_char(&info->port, CharNStat & 0xff, + flag); ToRecv--; } @@ -399,7 +395,7 @@ static void rp_do_receive(struct r_port *info, * characters at time by doing repeated word IO * transfer. */ - space = tty_prepare_flip_string(tty, &cbuf, ToRecv); + space = tty_prepare_flip_string(&info->port, &cbuf, ToRecv); if (space < ToRecv) { #ifdef ROCKET_DEBUG_RECEIVE printk(KERN_INFO "rp_do_receive:insufficient space ToRecv=%d space=%d\n", ToRecv, space); @@ -415,7 +411,7 @@ static void rp_do_receive(struct r_port *info, cbuf[ToRecv - 1] = sInB(sGetTxRxDataIO(cp)); } /* Push the data up to the tty layer */ - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&info->port); } /* @@ -449,7 +445,7 @@ static void rp_do_transmit(struct r_port *info) /* Loop sending data to FIFO until done or FIFO full */ while (1) { - if (tty->stopped || tty->hw_stopped) + if (tty->stopped) break; c = min(info->xmit_fifo_room, info->xmit_cnt); c = min(c, XMIT_BUF_SIZE - info->xmit_tail); @@ -494,7 +490,6 @@ static void rp_do_transmit(struct r_port *info) static void rp_handle_port(struct r_port *info) { CHANNEL_t *cp; - struct tty_struct *tty; unsigned int IntMask, ChanStatus; if (!info) @@ -505,12 +500,7 @@ static void rp_handle_port(struct r_port *info) "info->flags & NOT_INIT\n"); return; } - tty = tty_port_tty_get(&info->port); - if (!tty) { - printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " - "tty==NULL\n"); - return; - } + cp = &info->channel; IntMask = sGetChanIntID(cp) & info->intmask; @@ -519,7 +509,7 @@ static void rp_handle_port(struct r_port *info) #endif ChanStatus = sGetChanStatus(cp); if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ - rp_do_receive(info, tty, cp, ChanStatus); + rp_do_receive(info, cp, ChanStatus); } if (IntMask & DELTA_CD) { /* CD change */ #if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) @@ -530,7 +520,7 @@ static void rp_handle_port(struct r_port *info) #ifdef ROCKET_DEBUG_HANGUP printk(KERN_INFO "CD drop, calling hangup.\n"); #endif - tty_hangup(tty); + tty_port_tty_hangup(&info->port, false); } info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0; wake_up_interruptible(&info->port.open_wait); @@ -543,7 +533,6 @@ static void rp_handle_port(struct r_port *info) printk(KERN_INFO "DSR change...\n"); } #endif - tty_kref_put(tty); } /* @@ -1113,15 +1102,12 @@ static void rp_set_termios(struct tty_struct *tty, /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios.c_cflag & CBAUD)) { - if (!tty->hw_stopped || !(tty->termios.c_cflag & CRTSCTS)) - sSetRTS(cp); + sSetRTS(cp); sSetDTR(cp); } - if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios.c_cflag & CRTSCTS)) rp_start(tty); - } } static int rp_break(struct tty_struct *tty, int break_state) @@ -1577,10 +1563,10 @@ static int rp_put_char(struct tty_struct *tty, unsigned char ch) spin_lock_irqsave(&info->slock, flags); cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0) + if (!tty->stopped && info->xmit_fifo_room == 0) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); - if (tty->stopped || tty->hw_stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { + if (tty->stopped || info->xmit_fifo_room == 0 || info->xmit_cnt != 0) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= XMIT_BUF_SIZE - 1; info->xmit_cnt++; @@ -1621,14 +1607,14 @@ static int rp_write(struct tty_struct *tty, #endif cp = &info->channel; - if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room < count) + if (!tty->stopped && info->xmit_fifo_room < count) info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); /* * If the write queue for the port is empty, and there is FIFO space, stuff bytes * into FIFO. Use the write queue for temp storage. */ - if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { + if (!tty->stopped && info->xmit_cnt == 0 && info->xmit_fifo_room > 0) { c = min(count, info->xmit_fifo_room); b = buf; @@ -1676,7 +1662,7 @@ static int rp_write(struct tty_struct *tty, retval += c; } - if ((retval > 0) && !tty->stopped && !tty->hw_stopped) + if ((retval > 0) && !tty->stopped) set_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); end: @@ -1758,12 +1744,172 @@ static void rp_flush_buffer(struct tty_struct *tty) #ifdef CONFIG_PCI -static struct pci_device_id __used rocket_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, +static const struct pci_device_id rocket_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4QUAD) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8OCTA) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP8INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8J) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4J) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8SNI) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16SNI) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP16INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_CRP16INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_URP32INTF) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP4) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RPP8) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_232) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP2_422) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP6M) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP4M) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_8PORT) }, + { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_UPCI_RM3_4PORT) }, { } }; MODULE_DEVICE_TABLE(pci, rocket_pci_ids); +/* Resets the speaker controller on RocketModem II and III devices */ +static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) +{ + ByteIO_t addr; + + /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ + if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { + addr = CtlP->AiopIO[0] + 0x4F; + sOutB(addr, 0); + } + + /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ + if ((model == MODEL_UPCI_RM3_8PORT) + || (model == MODEL_UPCI_RM3_4PORT)) { + addr = CtlP->AiopIO[0] + 0x88; + sOutB(addr, 0); + } +} + +/*************************************************************************** +Function: sPCIInitController +Purpose: Initialization of controller global registers and controller + structure. +Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, + IRQNum,Frequency,PeriodicOnly) + CONTROLLER_T *CtlP; Ptr to controller structure + int CtlNum; Controller number + ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. + This list must be in the order the AIOPs will be found on the + controller. Once an AIOP in the list is not found, it is + assumed that there are no more AIOPs on the controller. + int AiopIOListSize; Number of addresses in AiopIOList + int IRQNum; Interrupt Request number. Can be any of the following: + 0: Disable global interrupts + 3: IRQ 3 + 4: IRQ 4 + 5: IRQ 5 + 9: IRQ 9 + 10: IRQ 10 + 11: IRQ 11 + 12: IRQ 12 + 15: IRQ 15 + Byte_t Frequency: A flag identifying the frequency + of the periodic interrupt, can be any one of the following: + FREQ_DIS - periodic interrupt disabled + FREQ_137HZ - 137 Hertz + FREQ_69HZ - 69 Hertz + FREQ_34HZ - 34 Hertz + FREQ_17HZ - 17 Hertz + FREQ_9HZ - 9 Hertz + FREQ_4HZ - 4 Hertz + If IRQNum is set to 0 the Frequency parameter is + overidden, it is forced to a value of FREQ_DIS. + int PeriodicOnly: 1 if all interrupts except the periodic + interrupt are to be blocked. + 0 is both the periodic interrupt and + other channel interrupts are allowed. + If IRQNum is set to 0 the PeriodicOnly parameter is + overidden, it is forced to a value of 0. +Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller + initialization failed. + +Comments: + If periodic interrupts are to be disabled but AIOP interrupts + are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. + + If interrupts are to be completely disabled set IRQNum to 0. + + Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an + invalid combination. + + This function performs initialization of global interrupt modes, + but it does not actually enable global interrupts. To enable + and disable global interrupts use functions sEnGlobalInt() and + sDisGlobalInt(). Enabling of global interrupts is normally not + done until all other initializations are complete. + + Even if interrupts are globally enabled, they must also be + individually enabled for each channel that is to generate + interrupts. + +Warnings: No range checking on any of the parameters is done. + + No context switches are allowed while executing this function. + + After this function all AIOPs on the controller are disabled, + they can be enabled with sEnAiop(). +*/ +static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, + ByteIO_t * AiopIOList, int AiopIOListSize, + WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, + int PeriodicOnly, int altChanRingIndicator, + int UPCIRingInd) +{ + int i; + ByteIO_t io; + + CtlP->AltChanRingIndicator = altChanRingIndicator; + CtlP->UPCIRingInd = UPCIRingInd; + CtlP->CtlNum = CtlNum; + CtlP->CtlID = CTLID_0001; /* controller release 1 */ + CtlP->BusType = isPCI; /* controller release 1 */ + + if (ConfigIO) { + CtlP->isUPCI = 1; + CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; + CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; + CtlP->AiopIntrBits = upci_aiop_intr_bits; + } else { + CtlP->isUPCI = 0; + CtlP->PCIIO = + (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); + CtlP->AiopIntrBits = aiop_intr_bits; + } + + sPCIControllerEOI(CtlP); /* clear EOI if warm init */ + /* Init AIOPs */ + CtlP->NumAiop = 0; + for (i = 0; i < AiopIOListSize; i++) { + io = AiopIOList[i]; + CtlP->AiopIO[i] = (WordIO_t) io; + CtlP->AiopIntChanIO[i] = io + _INT_CHAN; + + CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ + if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ + break; /* done looking for AIOPs */ + + CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ + sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ + sOutB(io + _INDX_DATA, sClockPrescale); + CtlP->NumAiop++; /* bump count of AIOPs */ + } + + if (CtlP->NumAiop == 0) + return (-1); + else + return (CtlP->NumAiop); +} + /* * Called when a PCI card is found. Retrieves and stores model information, * init's aiopic and serial port hardware. @@ -1781,7 +1927,8 @@ static __init int register_PCI(int i, struct pci_dev *dev) WordIO_t ConfigIO = 0; ByteIO_t UPCIRingInd = 0; - if (!dev || pci_enable_device(dev)) + if (!dev || !pci_match_id(rocket_pci_ids, dev) || + pci_enable_device(dev)) return 0; rcktpt_io_addr[i] = pci_resource_start(dev, 0); @@ -2508,126 +2655,6 @@ static int sInitController(CONTROLLER_T * CtlP, int CtlNum, ByteIO_t MudbacIO, } /*************************************************************************** -Function: sPCIInitController -Purpose: Initialization of controller global registers and controller - structure. -Call: sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize, - IRQNum,Frequency,PeriodicOnly) - CONTROLLER_T *CtlP; Ptr to controller structure - int CtlNum; Controller number - ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. - This list must be in the order the AIOPs will be found on the - controller. Once an AIOP in the list is not found, it is - assumed that there are no more AIOPs on the controller. - int AiopIOListSize; Number of addresses in AiopIOList - int IRQNum; Interrupt Request number. Can be any of the following: - 0: Disable global interrupts - 3: IRQ 3 - 4: IRQ 4 - 5: IRQ 5 - 9: IRQ 9 - 10: IRQ 10 - 11: IRQ 11 - 12: IRQ 12 - 15: IRQ 15 - Byte_t Frequency: A flag identifying the frequency - of the periodic interrupt, can be any one of the following: - FREQ_DIS - periodic interrupt disabled - FREQ_137HZ - 137 Hertz - FREQ_69HZ - 69 Hertz - FREQ_34HZ - 34 Hertz - FREQ_17HZ - 17 Hertz - FREQ_9HZ - 9 Hertz - FREQ_4HZ - 4 Hertz - If IRQNum is set to 0 the Frequency parameter is - overidden, it is forced to a value of FREQ_DIS. - int PeriodicOnly: 1 if all interrupts except the periodic - interrupt are to be blocked. - 0 is both the periodic interrupt and - other channel interrupts are allowed. - If IRQNum is set to 0 the PeriodicOnly parameter is - overidden, it is forced to a value of 0. -Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller - initialization failed. - -Comments: - If periodic interrupts are to be disabled but AIOP interrupts - are allowed, set Frequency to FREQ_DIS and PeriodicOnly to 0. - - If interrupts are to be completely disabled set IRQNum to 0. - - Setting Frequency to FREQ_DIS and PeriodicOnly to 1 is an - invalid combination. - - This function performs initialization of global interrupt modes, - but it does not actually enable global interrupts. To enable - and disable global interrupts use functions sEnGlobalInt() and - sDisGlobalInt(). Enabling of global interrupts is normally not - done until all other initializations are complete. - - Even if interrupts are globally enabled, they must also be - individually enabled for each channel that is to generate - interrupts. - -Warnings: No range checking on any of the parameters is done. - - No context switches are allowed while executing this function. - - After this function all AIOPs on the controller are disabled, - they can be enabled with sEnAiop(). -*/ -static int sPCIInitController(CONTROLLER_T * CtlP, int CtlNum, - ByteIO_t * AiopIOList, int AiopIOListSize, - WordIO_t ConfigIO, int IRQNum, Byte_t Frequency, - int PeriodicOnly, int altChanRingIndicator, - int UPCIRingInd) -{ - int i; - ByteIO_t io; - - CtlP->AltChanRingIndicator = altChanRingIndicator; - CtlP->UPCIRingInd = UPCIRingInd; - CtlP->CtlNum = CtlNum; - CtlP->CtlID = CTLID_0001; /* controller release 1 */ - CtlP->BusType = isPCI; /* controller release 1 */ - - if (ConfigIO) { - CtlP->isUPCI = 1; - CtlP->PCIIO = ConfigIO + _PCI_9030_INT_CTRL; - CtlP->PCIIO2 = ConfigIO + _PCI_9030_GPIO_CTRL; - CtlP->AiopIntrBits = upci_aiop_intr_bits; - } else { - CtlP->isUPCI = 0; - CtlP->PCIIO = - (WordIO_t) ((ByteIO_t) AiopIOList[0] + _PCI_INT_FUNC); - CtlP->AiopIntrBits = aiop_intr_bits; - } - - sPCIControllerEOI(CtlP); /* clear EOI if warm init */ - /* Init AIOPs */ - CtlP->NumAiop = 0; - for (i = 0; i < AiopIOListSize; i++) { - io = AiopIOList[i]; - CtlP->AiopIO[i] = (WordIO_t) io; - CtlP->AiopIntChanIO[i] = io + _INT_CHAN; - - CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ - if (CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ - break; /* done looking for AIOPs */ - - CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t) io); /* num channels in AIOP */ - sOutW((WordIO_t) io + _INDX_ADDR, _CLK_PRE); /* clock prescaler */ - sOutB(io + _INDX_DATA, sClockPrescale); - CtlP->NumAiop++; /* bump count of AIOPs */ - } - - if (CtlP->NumAiop == 0) - return (-1); - else - return (CtlP->NumAiop); -} - -/*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) @@ -3116,25 +3143,6 @@ static void sPCIModemReset(CONTROLLER_T * CtlP, int chan, int on) sOutB(addr + chan, 0); /* apply or remove reset */ } -/* Resets the speaker controller on RocketModem II and III devices */ -static void rmSpeakerReset(CONTROLLER_T * CtlP, unsigned long model) -{ - ByteIO_t addr; - - /* RocketModem II speaker control is at the 8th port location of offset 0x40 */ - if ((model == MODEL_RP4M) || (model == MODEL_RP6M)) { - addr = CtlP->AiopIO[0] + 0x4F; - sOutB(addr, 0); - } - - /* RocketModem III speaker control is at the 1st port location of offset 0x80 */ - if ((model == MODEL_UPCI_RM3_8PORT) - || (model == MODEL_UPCI_RM3_4PORT)) { - addr = CtlP->AiopIO[0] + 0x88; - sOutB(addr, 0); - } -} - /* Returns the line number given the controller (board), aiop and channel number */ static unsigned char GetLineNumber(int ctrl, int aiop, int ch) { diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index a44345a2dbb..c7e8b60b617 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -85,7 +85,6 @@ static void serial21285_enable_ms(struct uart_port *port) static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, flag, rxs, max_count = 256; status = *CSR_UARTFLG; @@ -115,7 +114,7 @@ static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) status = *CSR_UARTFLG; } - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index f99a84526f8..5dc9c4bfa66 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -14,7 +14,6 @@ * 2.4/2.5 port David McCullough */ -#include <asm/dbg.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/serial.h> @@ -262,8 +261,7 @@ static void rs_start(struct tty_struct *tty) local_irq_restore(flags); } -static void receive_chars(struct m68k_serial *info, struct tty_struct *tty, - unsigned short rx) +static void receive_chars(struct m68k_serial *info, unsigned short rx) { m68328_uart *uart = &uart_addr[info->line]; unsigned char ch, flag; @@ -293,9 +291,6 @@ static void receive_chars(struct m68k_serial *info, struct tty_struct *tty, } } - if(!tty) - goto clear_and_exit; - flag = TTY_NORMAL; if (rx & URX_PARITY_ERROR) @@ -305,15 +300,12 @@ static void receive_chars(struct m68k_serial *info, struct tty_struct *tty, else if (rx & URX_FRAME_ERROR) flag = TTY_FRAME; - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(&info->tport, ch, flag); #ifndef CONFIG_XCOPILOT_BUGS } while((rx = uart->urx.w) & URX_DATA_READY); #endif - tty_schedule_flip(tty); - -clear_and_exit: - return; + tty_schedule_flip(&info->tport); } static void transmit_chars(struct m68k_serial *info, struct tty_struct *tty) @@ -367,11 +359,11 @@ irqreturn_t rs_interrupt(int irq, void *dev_id) tx = uart->utx.w; if (rx & URX_DATA_READY) - receive_chars(info, tty, rx); + receive_chars(info, rx); if (tx & UTX_TX_AVAIL) transmit_chars(info, tty); #else - receive_chars(info, tty, rx); + receive_chars(info, rx); #endif tty_kref_put(tty); @@ -637,8 +629,7 @@ static void rs_flush_chars(struct tty_struct *tty) /* Enable transmitter */ local_irq_save(flags); - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) { + if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) { local_irq_restore(flags); return; } @@ -704,7 +695,7 @@ static int rs_write(struct tty_struct * tty, total += c; } - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + if (info->xmit_cnt && !tty->stopped) { /* Enable transmitter */ local_irq_disable(); #ifndef USE_INTS @@ -985,10 +976,8 @@ static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) change_speed(info, tty); if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } @@ -1009,7 +998,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) + if (serial_paranoia_check(info, tty->name, "rs_close")) return; local_irq_save(flags); diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 12caa1292b7..1ebf8538b4f 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -12,6 +12,35 @@ */ #include <linux/serial_8250.h> +#include <linux/dmaengine.h> + +struct uart_8250_dma { + dma_filter_fn fn; + void *rx_param; + void *tx_param; + + int rx_chan_id; + int tx_chan_id; + + struct dma_slave_config rxconf; + struct dma_slave_config txconf; + + struct dma_chan *rxchan; + struct dma_chan *txchan; + + dma_addr_t rx_addr; + dma_addr_t tx_addr; + + dma_cookie_t rx_cookie; + dma_cookie_t tx_cookie; + + void *rx_buf; + + size_t rx_size; + size_t tx_size; + + unsigned char tx_running:1; +}; struct old_serial_port { unsigned int uart; @@ -88,13 +117,6 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) * is cleared, the machine locks up with endless interrupts. */ #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) #else #define ALPHA_KLUDGE_MCR 0 #endif @@ -143,3 +165,24 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt) return 0; } #endif + +#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_request_dma(struct uart_8250_port *); +extern void serial8250_release_dma(struct uart_8250_port *); +#else +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) +{ + return -1; +} +static inline int serial8250_request_dma(struct uart_8250_port *p) +{ + return -1; +} +static inline void serial8250_release_dma(struct uart_8250_port *p) { } +#endif diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250_core.c index f9320437a64..7a91c6d1eb7 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -239,13 +239,6 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE, }, - [PORT_RM9000] = { - .name = "RM9000", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, [PORT_OCTEON] = { .name = "OCTEON", .fifo_size = 64, @@ -308,7 +301,28 @@ static const struct serial8250_config uart_config[] = { }, [PORT_8250_CIR] = { .name = "CIR port" - } + }, + [PORT_ALTR_16550_F32] = { + .name = "Altera 16550 FIFO32", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_ALTR_16550_F64] = { + .name = "Altera 16550 FIFO64", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_ALTR_16550_F128] = { + .name = "Altera 16550 FIFO128", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, }; /* Uart divisor latch read */ @@ -324,9 +338,9 @@ static void default_serial_dl_write(struct uart_8250_port *up, int value) serial_out(up, UART_DLM, value >> 8 & 0xff); } -#ifdef CONFIG_MIPS_ALCHEMY +#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) -/* Au1x00 UART hardware has a weird register layout */ +/* Au1x00/RT288x UART hardware has a weird register layout */ static const u8 au_io_in_map[] = { [UART_RX] = 0, [UART_IER] = 2, @@ -370,56 +384,6 @@ static void au_serial_dl_write(struct uart_8250_port *up, int value) #endif -#ifdef CONFIG_SERIAL_8250_RM9K - -static const u8 - regmap_in[8] = { - [UART_RX] = 0x00, - [UART_IER] = 0x0c, - [UART_IIR] = 0x14, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }, - regmap_out[8] = { - [UART_TX] = 0x04, - [UART_IER] = 0x0c, - [UART_FCR] = 0x18, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }; - -static unsigned int rm9k_serial_in(struct uart_port *p, int offset) -{ - offset = regmap_in[offset] << p->regshift; - return readl(p->membase + offset); -} - -static void rm9k_serial_out(struct uart_port *p, int offset, int value) -{ - offset = regmap_out[offset] << p->regshift; - writel(value, p->membase + offset); -} - -static int rm9k_serial_dl_read(struct uart_8250_port *up) -{ - return ((__raw_readl(up->port.membase + 0x10) << 8) | - (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff; -} - -static void rm9k_serial_dl_write(struct uart_8250_port *up, int value) -{ - __raw_writel(value, up->port.membase + 0x08); - __raw_writel(value >> 8, up->port.membase + 0x10); -} - -#endif - static unsigned int hub6_serial_in(struct uart_port *p, int offset) { offset = offset << p->regshift; @@ -497,16 +461,7 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = mem32_serial_out; break; -#ifdef CONFIG_SERIAL_8250_RM9K - case UPIO_RM9000: - p->serial_in = rm9k_serial_in; - p->serial_out = rm9k_serial_out; - up->dl_read = rm9k_serial_dl_read; - up->dl_write = rm9k_serial_dl_write; - break; -#endif - -#ifdef CONFIG_MIPS_ALCHEMY +#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; @@ -600,7 +555,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) */ if ((p->port.type == PORT_XR17V35X) || (p->port.type == PORT_XR17D15X)) { - serial_out(p, UART_EXAR_SLEEP, 0xff); + serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0); return; } @@ -1341,7 +1296,9 @@ static void serial8250_start_tx(struct uart_port *port) struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); - if (!(up->ier & UART_IER_THRI)) { + if (up->dma && !serial8250_tx_dma(up)) { + return; + } else if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_port_out(port, UART_IER, up->ier); @@ -1349,9 +1306,7 @@ static void serial8250_start_tx(struct uart_port *port) unsigned char lsr; lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((port->type == PORT_RM9000) ? - (lsr & UART_LSR_THRE) : - (lsr & UART_LSR_TEMT)) + if (lsr & UART_LSR_TEMT) serial8250_tx_chars(up); } } @@ -1397,7 +1352,6 @@ unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr) { struct uart_port *port = &up->port; - struct tty_struct *tty = port->state->port.tty; unsigned char ch; int max_count = 256; char flag; @@ -1462,7 +1416,7 @@ ignore_char: lsr = serial_in(up, UART_LSR); } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); spin_unlock(&port->lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); return lsr; } @@ -1547,6 +1501,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned long flags; struct uart_8250_port *up = container_of(port, struct uart_8250_port, port); + int dma_err = 0; if (iir & UART_IIR_NO_INT) return 0; @@ -1557,10 +1512,15 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) DEBUG_INTR("status = %x...", status); - if (status & (UART_LSR_DR | UART_LSR_BI)) - status = serial8250_rx_chars(up, status); + if (status & (UART_LSR_DR | UART_LSR_BI)) { + if (up->dma) + dma_err = serial8250_rx_dma(up, iir); + + if (!up->dma || dma_err) + status = serial8250_rx_chars(up, status); + } serial8250_modem_status(up); - if (status & UART_LSR_THRE) + if (!up->dma && (status & UART_LSR_THRE)) serial8250_tx_chars(up); spin_unlock_irqrestore(&port->lock, flags); @@ -1734,6 +1694,10 @@ static int serial_link_irq_chain(struct uart_8250_port *up) static void serial_unlink_irq_chain(struct uart_8250_port *up) { + /* + * yes, some broken gcc emit "warning: 'i' may be used uninitialized" + * but no, we are not going to take a patch that assigns NULL below. + */ struct irq_info *i; struct hlist_node *n; struct hlist_head *h; @@ -1962,13 +1926,8 @@ static void serial8250_put_poll_char(struct uart_port *port, wait_for_xmitr(up, BOTH_EMPTY); /* * Send the character out. - * If a LF, also do CR... */ serial_port_out(port, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up, BOTH_EMPTY); - serial_port_out(port, UART_TX, 13); - } /* * Finally, wait for transmitter to become empty @@ -1991,9 +1950,12 @@ static int serial8250_startup(struct uart_port *port) if (port->type == PORT_8250_CIR) return -ENODEV; - port->fifosize = uart_config[up->port.type].fifo_size; - up->tx_loadsz = uart_config[up->port.type].tx_loadsz; - up->capabilities = uart_config[up->port.type].flags; + if (!port->fifosize) + port->fifosize = uart_config[port->type].fifo_size; + if (!up->tx_loadsz) + up->tx_loadsz = uart_config[port->type].tx_loadsz; + if (!up->capabilities) + up->capabilities = uart_config[port->type].flags; up->mcr = 0; if (port->iotype != up->cur_iotype) @@ -2198,6 +2160,18 @@ dont_test_tx_en: up->msr_saved_flags = 0; /* + * Request DMA channels for both RX and TX. + */ + if (up->dma) { + retval = serial8250_request_dma(up); + if (retval) { + pr_warn_ratelimited("ttyS%d - failed to request DMA\n", + serial_index(port)); + up->dma = NULL; + } + } + + /* * Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here. @@ -2230,6 +2204,9 @@ static void serial8250_shutdown(struct uart_port *port) up->ier = 0; serial_port_out(port, UART_IER, 0); + if (up->dma) + serial8250_release_dma(up); + spin_lock_irqsave(&port->lock, flags); if (port->flags & UPF_FOURPORT) { /* reset interrupts on the AST Fourport board */ @@ -2344,7 +2321,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { fcr = uart_config[port->type].fcr; - if (baud < 2400 || fifo_bug) { + if ((baud < 2400 && !up->dma) || fifo_bug) { fcr &= ~UART_FCR_TRIGGER_MASK; fcr |= UART_FCR_TRIGGER_1; } @@ -2356,9 +2333,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * the trigger, or the MCR RTS bit is cleared. In the case where * the remote UART is not using CTS auto flow control, we must * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. + * UART to respond. IOW, at least 32 bytes of FIFO. Also enable + * AFE if hw flow control is supported */ - if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) { + if ((up->capabilities & UART_CAP_AFE && (port->fifosize >= 32)) || + (port->flags & UPF_HARD_FLOW)) { up->mcr &= ~UART_MCR_AFE; if (termios->c_cflag & CRTSCTS) up->mcr |= UART_MCR_AFE; @@ -2378,7 +2357,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= UART_LSR_BI; /* @@ -2455,6 +2434,24 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, serial_dl_write(up, quot); /* + * XR17V35x UARTs have an extra fractional divisor register (DLD) + * + * We need to recalculate all of the registers, because DLM and DLL + * are already rounded to a whole integer. + * + * When recalculating we use a 32x clock instead of a 16x clock to + * allow 1-bit for rounding in the fractional part. + */ + if (up->port.type == PORT_XR17V35X) { + unsigned int baud_x32 = (port->uartclk * 2) / baud; + u16 quot = baud_x32 / 32; + u8 quot_frac = DIV_ROUND_CLOSEST(baud_x32 % 32, 2); + + serial_dl_write(up, quot); + serial_port_out(port, 0x2, quot_frac & 0xf); + } + + /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR * is written without DLAB set, this mode will be disabled. */ @@ -2692,6 +2689,10 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (port->type == PORT_16550A && port->iotype == UPIO_AU) up->bugs |= UART_BUG_NOMSR; + /* HW bugs may trigger IRQ while IIR == NO_INT */ + if (port->type == PORT_TEGRA) + up->bugs |= UART_BUG_NOMSR; + if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); @@ -2826,9 +2827,12 @@ static void serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) { up->port.type = type; - up->port.fifosize = uart_config[type].fifo_size; - up->capabilities = uart_config[type].flags; - up->tx_loadsz = uart_config[type].tx_loadsz; + if (!up->port.fifosize) + up->port.fifosize = uart_config[type].fifo_size; + if (!up->tx_loadsz) + up->tx_loadsz = uart_config[type].tx_loadsz; + if (!up->capabilities) + up->capabilities = uart_config[type].flags; } static void __init @@ -2879,14 +2883,10 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) touch_nmi_watchdog(); - local_irq_save(flags); - if (port->sysrq) { - /* serial8250_handle_irq() already took the lock */ - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else - spin_lock(&port->lock); + if (port->sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); /* * First save the IER then disable the interrupts @@ -2918,8 +2918,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) serial8250_modem_status(up); if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static int __init serial8250_console_setup(struct console *co, char *options) @@ -3081,7 +3080,7 @@ void serial8250_resume_port(int line) */ static int serial8250_probe(struct platform_device *dev) { - struct plat_serial8250_port *p = dev->dev.platform_data; + struct plat_serial8250_port *p = dev_get_platdata(&dev->dev); struct uart_8250_port uart; int ret, i, irqflag = 0; @@ -3262,6 +3261,14 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->bugs = up->bugs; uart->port.mapbase = up->port.mapbase; uart->port.private_data = up->port.private_data; + uart->port.fifosize = up->port.fifosize; + uart->tx_loadsz = up->tx_loadsz; + uart->capabilities = up->capabilities; + + /* Take tx_loadsz from fifosize if it wasn't set separately */ + if (uart->port.fifosize && !uart->tx_loadsz) + uart->tx_loadsz = uart->port.fifosize; + if (up->port.dev) uart->port.dev = up->port.dev; @@ -3287,6 +3294,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->dl_read = up->dl_read; if (up->dl_write) uart->dl_write = up->dl_write; + if (up->dma) + uart->dma = up->dma; if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, @@ -3430,3 +3439,34 @@ module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); #endif MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); + +#ifdef CONFIG_SERIAL_8250_DEPRECATED_OPTIONS +#ifndef MODULE +/* This module was renamed to 8250_core in 3.7. Keep the old "8250" name + * working as well for the module options so we don't break people. We + * need to keep the names identical and the convenient macros will happily + * refuse to let us do that by failing the build with redefinition errors + * of global variables. So we stick them inside a dummy function to avoid + * those conflicts. The options still get parsed, and the redefined + * MODULE_PARAM_PREFIX lets us keep the "8250." syntax alive. + * + * This is hacky. I'm sorry. + */ +static void __used s8250_options(void) +{ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "8250_core." + + module_param_cb(share_irqs, ¶m_ops_uint, &share_irqs, 0644); + module_param_cb(nr_uarts, ¶m_ops_uint, &nr_uarts, 0644); + module_param_cb(skip_txen_test, ¶m_ops_uint, &skip_txen_test, 0644); +#ifdef CONFIG_SERIAL_8250_RSA + __module_param_call(MODULE_PARAM_PREFIX, probe_rsa, + ¶m_array_ops, .arr = &__param_arr_probe_rsa, + 0444, -1); +#endif +} +#else +MODULE_ALIAS("8250_core"); +#endif +#endif diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c new file mode 100644 index 00000000000..148ffe4c232 --- /dev/null +++ b/drivers/tty/serial/8250/8250_dma.c @@ -0,0 +1,244 @@ +/* + * 8250_dma.c - DMA Engine API support for 8250.c + * + * Copyright (C) 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_reg.h> +#include <linux/dma-mapping.h> + +#include "8250.h" + +static void __dma_tx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct circ_buf *xmit = &p->port.state->xmit; + unsigned long flags; + + dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + spin_lock_irqsave(&p->port.lock, flags); + + dma->tx_running = 0; + + xmit->tail += dma->tx_size; + xmit->tail &= UART_XMIT_SIZE - 1; + p->port.icount.tx += dma->tx_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&p->port); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) + serial8250_tx_dma(p); + + spin_unlock_irqrestore(&p->port.lock, flags); +} + +static void __dma_rx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + int count; + + dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, + dma->rx_size, DMA_FROM_DEVICE); + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + dmaengine_terminate_all(dma->rxchan); + + count = dma->rx_size - state.residue; + + tty_insert_flip_string(tty_port, dma->rx_buf, count); + p->port.icount.rx += count; + + tty_flip_buffer_push(tty_port); +} + +int serial8250_tx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct circ_buf *xmit = &p->port.state->xmit; + struct dma_async_tx_descriptor *desc; + + if (uart_tx_stopped(&p->port) || dma->tx_running || + uart_circ_empty(xmit)) + return 0; + + dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + desc = dmaengine_prep_slave_single(dma->txchan, + dma->tx_addr + xmit->tail, + dma->tx_size, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EBUSY; + + dma->tx_running = 1; + + desc->callback = __dma_tx_complete; + desc->callback_param = p; + + dma->tx_cookie = dmaengine_submit(desc); + + dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + dma_async_issue_pending(dma->txchan); + + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_tx_dma); + +int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) +{ + struct uart_8250_dma *dma = p->dma; + struct dma_async_tx_descriptor *desc; + struct dma_tx_state state; + int dma_status; + + dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + + 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_status == DMA_IN_PROGRESS) { + dmaengine_pause(dma->rxchan); + __dma_rx_complete(p); + } + return -ETIMEDOUT; + default: + break; + } + + if (dma_status) + return 0; + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -EBUSY; + + desc->callback = __dma_rx_complete; + desc->callback_param = p; + + 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); + + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_rx_dma); + +int serial8250_request_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + dma_cap_mask_t mask; + + /* Default slave configuration parameters */ + dma->rxconf.direction = DMA_DEV_TO_MEM; + dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->rxconf.src_addr = p->port.mapbase + UART_RX; + + dma->txconf.direction = DMA_MEM_TO_DEV; + dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma->txconf.dst_addr = p->port.mapbase + UART_TX; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Get a channel for RX */ + dma->rxchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->rx_param, + p->port.dev, "rx"); + if (!dma->rxchan) + return -ENODEV; + + dmaengine_slave_config(dma->rxchan, &dma->rxconf); + + /* Get a channel for TX */ + dma->txchan = dma_request_slave_channel_compat(mask, + dma->fn, dma->tx_param, + p->port.dev, "tx"); + if (!dma->txchan) { + dma_release_channel(dma->rxchan); + return -ENODEV; + } + + dmaengine_slave_config(dma->txchan, &dma->txconf); + + /* RX buffer */ + if (!dma->rx_size) + dma->rx_size = PAGE_SIZE; + + dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_buf) + goto err; + + /* TX buffer */ + dma->tx_addr = dma_map_single(dma->txchan->device->dev, + p->port.state->xmit.buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + 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); + goto err; + } + + dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); + + return 0; +err: + dma_release_channel(dma->rxchan); + dma_release_channel(dma->txchan); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(serial8250_request_dma); + +void serial8250_release_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + + if (!dma) + return; + + /* Release RX resources */ + dmaengine_terminate_all(dma->rxchan); + dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf, + dma->rx_addr); + dma_release_channel(dma->rxchan); + dma->rxchan = NULL; + + /* Release TX resources */ + dmaengine_terminate_all(dma->txchan); + dma_unmap_single(dma->txchan->device->dev, dma->tx_addr, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_release_channel(dma->txchan); + dma->txchan = NULL; + dma->tx_running = 0; + + dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); +} +EXPORT_SYMBOL_GPL(serial8250_release_dma); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 096d2ef48b3..51b307aab75 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -2,6 +2,7 @@ * Synopsys DesignWare 8250 driver. * * Copyright 2011 Picochip, Jamie Iles. + * Copyright 2013 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,7 +14,6 @@ * raised, the LCR needs to be rewritten and the uart status register read. */ #include <linux/device.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/module.h> #include <linux/serial_8250.h> @@ -24,51 +24,195 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> + +#include <asm/byteorder.h> + +#include "8250.h" + +/* Offsets for the DesignWare specific registers */ +#define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_CPR 0xf4 /* Component Parameter Register */ +#define DW_UART_UCV 0xf8 /* UART Component Version */ + +/* Component Parameter Register bits */ +#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) +#define DW_UART_CPR_AFCE_MODE (1 << 4) +#define DW_UART_CPR_THRE_MODE (1 << 5) +#define DW_UART_CPR_SIR_MODE (1 << 6) +#define DW_UART_CPR_SIR_LP_MODE (1 << 7) +#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) +#define DW_UART_CPR_FIFO_ACCESS (1 << 9) +#define DW_UART_CPR_FIFO_STAT (1 << 10) +#define DW_UART_CPR_SHADOW (1 << 11) +#define DW_UART_CPR_ENCODED_PARMS (1 << 12) +#define DW_UART_CPR_DMA_EXTRA (1 << 13) +#define DW_UART_CPR_FIFO_MODE (0xff << 16) +/* Helper for fifo size calculation */ +#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) + struct dw8250_data { - int last_lcr; - int line; + u8 usr_reg; + int last_mcr; + int line; + struct clk *clk; + struct uart_8250_dma dma; +}; + +struct dw8250_acpi_desc { + void (*set_termios)(struct uart_port *p, struct ktermios *termios, + struct ktermios *old); }; +#define BYT_PRV_CLK 0x800 +#define BYT_PRV_CLK_EN (1 << 0) +#define BYT_PRV_CLK_M_VAL_SHIFT 1 +#define BYT_PRV_CLK_N_VAL_SHIFT 16 +#define BYT_PRV_CLK_UPDATE (1 << 31) + +static void byt_set_termios(struct uart_port *p, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + unsigned int m, n; + u32 reg; + + /* + * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the + * dividers must be adjusted. + * + * uartclk = (m / n) * 100 MHz, where m <= n + */ + switch (baud) { + case 500000: + case 1000000: + case 2000000: + case 4000000: + m = 64; + n = 100; + p->uartclk = 64000000; + break; + case 3500000: + m = 56; + n = 100; + p->uartclk = 56000000; + break; + case 1500000: + case 3000000: + m = 48; + n = 100; + p->uartclk = 48000000; + break; + case 2500000: + m = 40; + n = 100; + p->uartclk = 40000000; + break; + default: + m = 2304; + n = 3125; + p->uartclk = 73728000; + } + + /* Reset the clock */ + reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); + writel(reg, p->membase + BYT_PRV_CLK); + reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; + writel(reg, p->membase + BYT_PRV_CLK); + + serial8250_do_set_termios(p, termios, old); +} + +static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) +{ + struct dw8250_data *d = p->private_data; + + /* If reading MSR, report CTS asserted when auto-CTS/RTS enabled */ + if (offset == UART_MSR && d->last_mcr & UART_MCR_AFE) { + value |= UART_MSR_CTS; + value &= ~UART_MSR_DCTS; + } + + return value; +} + +static void dw8250_force_idle(struct uart_port *p) +{ + serial8250_clear_and_reinit_fifos(container_of + (p, struct uart_8250_port, port)); + (void)p->serial_in(p, UART_RX); +} + static void dw8250_serial_out(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; - if (offset == UART_LCR) - d->last_lcr = value; + if (offset == UART_MCR) + d->last_mcr = value; + + writeb(value, p->membase + (offset << p->regshift)); - offset <<= p->regshift; - writeb(value, p->membase + offset); + /* Make sure LCR write wasn't ignored */ + if (offset == UART_LCR) { + int tries = 1000; + while (tries--) { + unsigned int lcr = p->serial_in(p, UART_LCR); + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + return; + dw8250_force_idle(p); + writeb(value, p->membase + (UART_LCR << p->regshift)); + } + dev_err(p->dev, "Couldn't set LCR to %d\n", value); + } } static unsigned int dw8250_serial_in(struct uart_port *p, int offset) { - offset <<= p->regshift; + unsigned int value = readb(p->membase + (offset << p->regshift)); + + return dw8250_modify_msr(p, offset, value); +} - return readb(p->membase + offset); +/* Read Back (rb) version to ensure register access ording. */ +static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value) +{ + dw8250_serial_out(p, offset, value); + dw8250_serial_in(p, UART_LCR); } static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; - if (offset == UART_LCR) - d->last_lcr = value; + if (offset == UART_MCR) + d->last_mcr = value; + + writel(value, p->membase + (offset << p->regshift)); - offset <<= p->regshift; - writel(value, p->membase + offset); + /* Make sure LCR write wasn't ignored */ + if (offset == UART_LCR) { + int tries = 1000; + while (tries--) { + unsigned int lcr = p->serial_in(p, UART_LCR); + if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) + return; + dw8250_force_idle(p); + writel(value, p->membase + (UART_LCR << p->regshift)); + } + dev_err(p->dev, "Couldn't set LCR to %d\n", value); + } } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) { - offset <<= p->regshift; + unsigned int value = readl(p->membase + (offset << p->regshift)); - return readl(p->membase + offset); + return dw8250_modify_msr(p, offset, value); } -/* Offset for the DesignWare's UART Status Register. */ -#define UART_USR 0x1f - static int dw8250_handle_irq(struct uart_port *p) { struct dw8250_data *d = p->private_data; @@ -77,9 +221,8 @@ static int dw8250_handle_irq(struct uart_port *p) if (serial8250_handle_irq(p, iir)) { return 1; } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { - /* Clear the USR and write the LCR again. */ - (void)p->serial_in(p, UART_USR); - p->serial_out(p, UART_LCR, d->last_lcr); + /* Clear the USR */ + (void)p->serial_in(p, d->usr_reg); return 1; } @@ -87,61 +230,206 @@ static int dw8250_handle_irq(struct uart_port *p) return 0; } +static void +dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) +{ + if (!state) + pm_runtime_get_sync(port->dev); + + serial8250_do_pm(port, state, old); + + if (state) + pm_runtime_put_sync_suspend(port->dev); +} + +static bool dw8250_dma_filter(struct dma_chan *chan, void *param) +{ + struct dw8250_data *data = param; + + return chan->chan_id == data->dma.tx_chan_id || + chan->chan_id == data->dma.rx_chan_id; +} + +static void dw8250_setup_port(struct uart_8250_port *up) +{ + struct uart_port *p = &up->port; + u32 reg = readl(p->membase + DW_UART_UCV); + + /* + * If the Component Version Register returns zero, we know that + * ADDITIONAL_FEATURES are not enabled. No need to go any further. + */ + if (!reg) + return; + + dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + + reg = readl(p->membase + DW_UART_CPR); + if (!reg) + return; + + /* Select the type based on fifo */ + if (reg & DW_UART_CPR_FIFO_MODE) { + p->type = PORT_16550A; + p->flags |= UPF_FIXED_TYPE; + p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); + up->tx_loadsz = p->fifosize; + up->capabilities = UART_CAP_FIFO; + } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; +} + +static int dw8250_probe_of(struct uart_port *p, + struct dw8250_data *data) +{ + struct device_node *np = p->dev->of_node; + u32 val; + bool has_ucv = true; + + if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { +#ifdef __BIG_ENDIAN + /* + * Low order bits of these 64-bit registers, when + * accessed as a byte, are 7 bytes further down in the + * address space in big endian mode. + */ + p->membase += 7; +#endif + p->serial_out = dw8250_serial_out_rb; + p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->usr_reg = 0x27; + has_ucv = false; + } else if (!of_property_read_u32(np, "reg-io-width", &val)) { + switch (val) { + case 1: + break; + case 4: + p->iotype = UPIO_MEM32; + p->serial_in = dw8250_serial_in32; + p->serial_out = dw8250_serial_out32; + break; + default: + dev_err(p->dev, "unsupported reg-io-width (%u)\n", val); + return -EINVAL; + } + } + if (has_ucv) + dw8250_setup_port(container_of(p, struct uart_8250_port, port)); + + if (!of_property_read_u32(np, "reg-shift", &val)) + p->regshift = val; + + /* clock got configured through clk api, all done */ + if (p->uartclk) + return 0; + + /* try to find out clock frequency from DT as fallback */ + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(p->dev, "clk or clock-frequency not defined\n"); + return -EINVAL; + } + p->uartclk = val; + + return 0; +} + +static int dw8250_probe_acpi(struct uart_8250_port *up, + struct dw8250_data *data) +{ + const struct acpi_device_id *id; + struct uart_port *p = &up->port; + struct dw8250_acpi_desc *acpi_desc; + + dw8250_setup_port(up); + + id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); + if (!id) + return -ENODEV; + + p->iotype = UPIO_MEM32; + p->serial_in = dw8250_serial_in32; + p->serial_out = dw8250_serial_out32; + p->regshift = 2; + + up->dma = &data->dma; + + up->dma->rxconf.src_maxburst = p->fifosize / 4; + up->dma->txconf.dst_maxburst = p->fifosize / 4; + + acpi_desc = (struct dw8250_acpi_desc *)id->driver_data; + if (!acpi_desc) + return 0; + + if (acpi_desc->set_termios) + p->set_termios = acpi_desc->set_termios; + + return 0; +} + static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - struct device_node *np = pdev->dev.of_node; - u32 val; struct dw8250_data *data; + int err; if (!regs || !irq) { dev_err(&pdev->dev, "no registers/irq defined\n"); return -EINVAL; } - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - uart.port.private_data = data; - spin_lock_init(&uart.port.lock); uart.port.mapbase = regs->start; uart.port.irq = irq->start; uart.port.handle_irq = dw8250_handle_irq; + uart.port.pm = dw8250_do_pm; uart.port.type = PORT_8250; - uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | - UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; uart.port.dev = &pdev->dev; + uart.port.membase = devm_ioremap(&pdev->dev, regs->start, + resource_size(regs)); + if (!uart.port.membase) + return -ENOMEM; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->usr_reg = DW_UART_USR; + data->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(data->clk)) { + clk_prepare_enable(data->clk); + uart.port.uartclk = clk_get_rate(data->clk); + } + + data->dma.rx_chan_id = -1; + data->dma.tx_chan_id = -1; + data->dma.rx_param = data; + data->dma.tx_param = data; + data->dma.fn = dw8250_dma_filter; + uart.port.iotype = UPIO_MEM; uart.port.serial_in = dw8250_serial_in; uart.port.serial_out = dw8250_serial_out; - if (!of_property_read_u32(np, "reg-io-width", &val)) { - switch (val) { - case 1: - break; - case 4: - uart.port.iotype = UPIO_MEM32; - uart.port.serial_in = dw8250_serial_in32; - uart.port.serial_out = dw8250_serial_out32; - break; - default: - dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n", - val); - return -EINVAL; - } - } - - if (!of_property_read_u32(np, "reg-shift", &val)) - uart.port.regshift = val; + uart.port.private_data = data; - if (of_property_read_u32(np, "clock-frequency", &val)) { - dev_err(&pdev->dev, "no clock-frequency property set\n"); - return -EINVAL; + if (pdev->dev.of_node) { + err = dw8250_probe_of(&uart.port, data); + if (err) + return err; + } else if (ACPI_HANDLE(&pdev->dev)) { + err = dw8250_probe_acpi(&uart, data); + if (err) + return err; + } else { + return -ENODEV; } - uart.port.uartclk = val; data->line = serial8250_register_8250_port(&uart); if (data->line < 0) @@ -149,6 +437,9 @@ static int dw8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; } @@ -156,50 +447,97 @@ static int dw8250_remove(struct platform_device *pdev) { struct dw8250_data *data = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + serial8250_unregister_port(data->line); + if (!IS_ERR(data->clk)) + clk_disable_unprepare(data->clk); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + return 0; } -#ifdef CONFIG_PM -static int dw8250_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int dw8250_suspend(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_suspend_port(data->line); return 0; } -static int dw8250_resume(struct platform_device *pdev) +static int dw8250_resume(struct device *dev) { - struct dw8250_data *data = platform_get_drvdata(pdev); + struct dw8250_data *data = dev_get_drvdata(dev); serial8250_resume_port(data->line); return 0; } -#else -#define dw8250_suspend NULL -#define dw8250_resume NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM_RUNTIME +static int dw8250_runtime_suspend(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->clk)) + clk_disable_unprepare(data->clk); + + return 0; +} + +static int dw8250_runtime_resume(struct device *dev) +{ + struct dw8250_data *data = dev_get_drvdata(dev); + + if (!IS_ERR(data->clk)) + clk_prepare_enable(data->clk); + + return 0; +} +#endif + +static const struct dev_pm_ops dw8250_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dw8250_suspend, dw8250_resume) + SET_RUNTIME_PM_OPS(dw8250_runtime_suspend, dw8250_runtime_resume, NULL) +}; -static const struct of_device_id dw8250_match[] = { +static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, + { .compatible = "cavium,octeon-3860-uart" }, { /* Sentinel */ } }; -MODULE_DEVICE_TABLE(of, dw8250_match); +MODULE_DEVICE_TABLE(of, dw8250_of_match); + +static struct dw8250_acpi_desc byt_8250_desc = { + .set_termios = byt_set_termios, +}; + +static const struct acpi_device_id dw8250_acpi_match[] = { + { "INT33C4", 0 }, + { "INT33C5", 0 }, + { "INT3434", 0 }, + { "INT3435", 0 }, + { "80860F0A", (kernel_ulong_t)&byt_8250_desc}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); static struct platform_driver dw8250_platform_driver = { .driver = { .name = "dw-apb-uart", .owner = THIS_MODULE, - .of_match_table = dw8250_match, + .pm = &dw8250_pm_ops, + .of_match_table = dw8250_of_match, + .acpi_match_table = ACPI_PTR(dw8250_acpi_match), }, .probe = dw8250_probe, .remove = dw8250_remove, - .suspend = dw8250_suspend, - .resume = dw8250_resume, }; module_platform_driver(dw8250_platform_driver); diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index f53a7db4350..4858b8a99d3 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -35,18 +35,8 @@ #include <linux/serial_8250.h> #include <asm/io.h> #include <asm/serial.h> -#ifdef CONFIG_FIX_EARLYCON_MEM -#include <asm/pgtable.h> -#include <asm/fixmap.h> -#endif -struct early_serial8250_device { - struct uart_port port; - char options[16]; /* e.g., 115200n8 */ - unsigned int baud; -}; - -static struct early_serial8250_device early_device; +static struct earlycon_device *early_device; unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) { @@ -100,7 +90,7 @@ static void __init serial_putc(struct uart_port *port, int c) static void __init early_serial8250_write(struct console *console, const char *s, unsigned int count) { - struct uart_port *port = &early_device.port; + struct uart_port *port = &early_device->port; unsigned int ier; /* Save the IER and disable interrupts */ @@ -129,7 +119,7 @@ static unsigned int __init probe_baud(struct uart_port *port) return (port->uartclk / 16) / quot; } -static void __init init_port(struct early_serial8250_device *device) +static void __init init_port(struct earlycon_device *device) { struct uart_port *port = &device->port; unsigned int divisor; @@ -148,127 +138,45 @@ static void __init init_port(struct early_serial8250_device *device) serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } -static int __init parse_options(struct early_serial8250_device *device, - char *options) +static int __init early_serial8250_setup(struct earlycon_device *device, + const char *options) { - struct uart_port *port = &device->port; - int mmio, mmio32, length; - - if (!options) - return -ENODEV; - - port->uartclk = BASE_BAUD * 16; - - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), - &options, 0); - if (mmio32) - port->regshift = 2; -#ifdef CONFIG_FIX_EARLYCON_MEM - set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, - port->mapbase & PAGE_MASK); - port->membase = - (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); - port->membase += port->mapbase & ~PAGE_MASK; -#else - port->membase = ioremap_nocache(port->mapbase, 64); - if (!port->membase) { - printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", - __func__, - (unsigned long long) port->mapbase); - return -ENOMEM; - } -#endif - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - port->iobase = simple_strtoul(options + 3, &options, 0); - mmio = 0; - } else - return -EINVAL; + if (!(device->port.membase || device->port.iobase)) + return 0; - options = strchr(options, ','); - if (options) { - options++; - device->baud = simple_strtoul(options, NULL, 0); - length = min(strcspn(options, " "), sizeof(device->options)); - strncpy(device->options, options, length); - } else { - device->baud = probe_baud(port); + if (!device->baud) { + device->baud = probe_baud(&device->port); snprintf(device->options, sizeof(device->options), "%u", - device->baud); + device->baud); } - if (mmio || mmio32) - printk(KERN_INFO - "Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", - (unsigned long long)port->mapbase, - device->options); - else - printk(KERN_INFO - "Early serial console at I/O port 0x%lx (options '%s')\n", - port->iobase, - device->options); - - return 0; -} - -static struct console early_serial8250_console __initdata = { - .name = "uart", - .write = early_serial8250_write, - .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1, -}; - -static int __init early_serial8250_setup(char *options) -{ - struct early_serial8250_device *device = &early_device; - int err; - - if (device->port.membase || device->port.iobase) - return 0; - - err = parse_options(device, options); - if (err < 0) - return err; - init_port(device); + + early_device = device; + device->con->write = early_serial8250_write; return 0; } +EARLYCON_DECLARE(uart8250, early_serial8250_setup); +EARLYCON_DECLARE(uart, early_serial8250_setup); int __init setup_early_serial8250_console(char *cmdline) { - char *options; - int err; + char match[] = "uart8250"; - options = strstr(cmdline, "uart8250,"); - if (!options) { - options = strstr(cmdline, "uart,"); - if (!options) - return 0; - } - - options = strchr(cmdline, ',') + 1; - err = early_serial8250_setup(options); - if (err < 0) - return err; - - register_console(&early_serial8250_console); + if (cmdline && cmdline[4] == ',') + match[4] = '\0'; - return 0; + return setup_earlycon(cmdline, match, early_serial8250_setup); } int serial8250_find_port_for_earlycon(void) { - struct early_serial8250_device *device = &early_device; - struct uart_port *port = &device->port; + struct earlycon_device *device = early_device; + struct uart_port *port = device ? &device->port : NULL; int line; int ret; - if (!device->port.membase && !device->port.iobase) + if (!port || (!port->membase && !port->iobase)) return -ENODEV; line = serial8250_find_port(port); @@ -283,5 +191,3 @@ int serial8250_find_port_for_earlycon(void) return ret; } - -early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/tty/serial/8250/8250_em.c b/drivers/tty/serial/8250/8250_em.c index 916cc19fbbd..56c87232b6a 100644 --- a/drivers/tty/serial/8250/8250_em.c +++ b/drivers/tty/serial/8250/8250_em.c @@ -18,7 +18,6 @@ */ #include <linux/device.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/module.h> #include <linux/serial_8250.h> @@ -95,25 +94,23 @@ static int serial8250_em_probe(struct platform_device *pdev) struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct serial8250_em_priv *priv; struct uart_8250_port up; - int ret = -EINVAL; + int ret; if (!regs || !irq) { dev_err(&pdev->dev, "missing registers or irq\n"); - goto err0; + return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&pdev->dev, "unable to allocate private data\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } - priv->sclk = clk_get(&pdev->dev, "sclk"); + priv->sclk = devm_clk_get(&pdev->dev, "sclk"); if (IS_ERR(priv->sclk)) { dev_err(&pdev->dev, "unable to get clock\n"); - ret = PTR_ERR(priv->sclk); - goto err1; + return PTR_ERR(priv->sclk); } memset(&up, 0, sizeof(up)); @@ -124,7 +121,7 @@ static int serial8250_em_probe(struct platform_device *pdev) up.port.dev = &pdev->dev; up.port.private_data = priv; - clk_enable(priv->sclk); + clk_prepare_enable(priv->sclk); up.port.uartclk = clk_get_rate(priv->sclk); up.port.iotype = UPIO_MEM32; @@ -136,20 +133,13 @@ static int serial8250_em_probe(struct platform_device *pdev) ret = serial8250_register_8250_port(&up); if (ret < 0) { dev_err(&pdev->dev, "unable to register 8250 port\n"); - goto err2; + clk_disable_unprepare(priv->sclk); + return ret; } priv->line = ret; platform_set_drvdata(pdev, priv); return 0; - - err2: - clk_disable(priv->sclk); - clk_put(priv->sclk); - err1: - kfree(priv); - err0: - return ret; } static int serial8250_em_remove(struct platform_device *pdev) @@ -157,9 +147,7 @@ static int serial8250_em_remove(struct platform_device *pdev) struct serial8250_em_priv *priv = platform_get_drvdata(pdev); serial8250_unregister_port(priv->line); - clk_disable(priv->sclk); - clk_put(priv->sclk); - kfree(priv); + clk_disable_unprepare(priv->sclk); return 0; } diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c index 097dff9c08a..2e3ea1a70d7 100644 --- a/drivers/tty/serial/8250/8250_gsc.c +++ b/drivers/tty/serial/8250/8250_gsc.c @@ -30,6 +30,11 @@ static int __init serial_init_chip(struct parisc_device *dev) unsigned long address; int err; +#ifdef CONFIG_64BIT + if (!dev->irq && (dev->id.sversion == 0xad)) + dev->irq = iosapic_serial_irq(dev); +#endif + if (!dev->irq) { /* We find some unattached serial ports by walking native * busses. These should be silently ignored. Otherwise, @@ -51,7 +56,8 @@ static int __init serial_init_chip(struct parisc_device *dev) memset(&uart, 0, sizeof(uart)); uart.port.iotype = UPIO_MEM; /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - uart.port.uartclk = 7272727; + uart.port.uartclk = (dev->id.sversion != 0xad) ? + 7272727 : 1843200; uart.port.mapbase = address; uart.port.membase = ioremap_nocache(address, 16); uart.port.irq = dev->irq; @@ -73,6 +79,7 @@ static struct parisc_device_id serial_tbl[] = { { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x000ad }, { 0 } }; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index a27a98e1b06..33137b3ba94 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -9,8 +9,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ +#undef DEBUG #include <linux/module.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/string.h> #include <linux/kernel.h> @@ -27,8 +27,6 @@ #include "8250.h" -#undef SERIAL_DEBUG_PCI - /* * init function returns: * > 0 - number of ports @@ -63,7 +61,7 @@ static int pci_default_setup(struct serial_private*, static void moan_device(const char *str, struct pci_dev *dev) { - printk(KERN_WARNING + dev_err(&dev->dev, "%s: %s\n" "Please send the output of lspci -vv, this\n" "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" @@ -233,7 +231,7 @@ static int pci_inteli960ni_init(struct pci_dev *dev) /* is firmware started? */ pci_read_config_dword(dev, 0x44, (void *)&oldval); if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); + dev_dbg(&dev->dev, "Local i960 firmware missing\n"); return -ENODEV; } return 0; @@ -785,7 +783,8 @@ static int pci_netmos_9900_setup(struct serial_private *priv, { unsigned int bar; - if ((priv->dev->subsystem_device & 0xff00) == 0x3000) { + if ((priv->dev->device != PCI_DEVICE_ID_NETMOS_9865) && + (priv->dev->subsystem_device & 0xff00) == 0x3000) { /* netmos apparently orders BARs by datasheet layout, so serial * ports get BARs 0 and 3 (or 1 and 4 for memmapped) */ @@ -827,7 +826,7 @@ static int pci_netmos_9900_numports(struct pci_dev *dev) if (sub_serports > 0) { return sub_serports; } else { - printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); + dev_err(&dev->dev, "NetMos/Mostech serial driver ignoring port on ambiguous config.\n"); return 0; } } @@ -931,7 +930,7 @@ static int pci_ite887x_init(struct pci_dev *dev) } if (!inta_addr[i]) { - printk(KERN_ERR "ite887x: could not find iobase\n"); + dev_err(&dev->dev, "ite887x: could not find iobase\n"); return -ENODEV; } @@ -1024,9 +1023,9 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev) /* Tornado device */ if (deviceID == 0x07000200) { number_uarts = ioread8(p + 4); - printk(KERN_DEBUG + dev_dbg(&dev->dev, "%d ports detected on Oxford PCI Express device\n", - number_uarts); + number_uarts); } pci_iounmap(dev, p); return number_uarts; @@ -1040,6 +1039,253 @@ static int pci_asix_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +/* Quatech devices have their own extra interface features */ + +struct quatech_feature { + u16 devid; + bool amcc; +}; + +#define QPCR_TEST_FOR1 0x3F +#define QPCR_TEST_GET1 0x00 +#define QPCR_TEST_FOR2 0x40 +#define QPCR_TEST_GET2 0x40 +#define QPCR_TEST_FOR3 0x80 +#define QPCR_TEST_GET3 0x40 +#define QPCR_TEST_FOR4 0xC0 +#define QPCR_TEST_GET4 0x80 + +#define QOPR_CLOCK_X1 0x0000 +#define QOPR_CLOCK_X2 0x0001 +#define QOPR_CLOCK_X4 0x0002 +#define QOPR_CLOCK_X8 0x0003 +#define QOPR_CLOCK_RATE_MASK 0x0003 + + +static struct quatech_feature quatech_cards[] = { + { PCI_DEVICE_ID_QUATECH_QSC100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC100E, 0 }, + { PCI_DEVICE_ID_QUATECH_DSC200, 1 }, + { PCI_DEVICE_ID_QUATECH_DSC200E, 0 }, + { PCI_DEVICE_ID_QUATECH_ESC100D, 1 }, + { PCI_DEVICE_ID_QUATECH_ESC100M, 1 }, + { PCI_DEVICE_ID_QUATECH_QSCP100, 1 }, + { PCI_DEVICE_ID_QUATECH_DSCP100, 1 }, + { PCI_DEVICE_ID_QUATECH_QSCP200, 1 }, + { PCI_DEVICE_ID_QUATECH_DSCP200, 1 }, + { PCI_DEVICE_ID_QUATECH_ESCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_QSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_DSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_SSCLP100, 0 }, + { PCI_DEVICE_ID_QUATECH_QSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_DSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_SSCLP200, 0 }, + { PCI_DEVICE_ID_QUATECH_SPPXP_100, 0 }, + { 0, } +}; + +static int pci_quatech_amcc(u16 devid) +{ + struct quatech_feature *qf = &quatech_cards[0]; + while (qf->devid) { + if (qf->devid == devid) + return qf->amcc; + qf++; + } + pr_err("quatech: unknown port type '0x%04X'.\n", devid); + return 0; +}; + +static int pci_quatech_rqopr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(LCR, base + UART_LCR); + return val; +} + +static void pci_quatech_wqopr(struct uart_8250_port *port, u8 qopr) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(qopr, base + UART_SCR); + outb(LCR, base + UART_LCR); +} + +static int pci_quatech_rqmcr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val, qmcr; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(val | 0x10, base + UART_SCR); + qmcr = inb(base + UART_MCR); + outb(val, base + UART_SCR); + outb(LCR, base + UART_LCR); + + return qmcr; +} + +static void pci_quatech_wqmcr(struct uart_8250_port *port, u8 qmcr) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + outb(val | 0x10, base + UART_SCR); + outb(qmcr, base + UART_MCR); + outb(val, base + UART_SCR); + outb(LCR, base + UART_LCR); +} + +static int pci_quatech_has_qmcr(struct uart_8250_port *port) +{ + unsigned long base = port->port.iobase; + u8 LCR, val; + + LCR = inb(base + UART_LCR); + outb(0xBF, base + UART_LCR); + val = inb(base + UART_SCR); + if (val & 0x20) { + outb(0x80, UART_LCR); + if (!(inb(UART_SCR) & 0x20)) { + outb(LCR, base + UART_LCR); + return 1; + } + } + return 0; +} + +static int pci_quatech_test(struct uart_8250_port *port) +{ + u8 reg; + u8 qopr = pci_quatech_rqopr(port); + pci_quatech_wqopr(port, qopr & QPCR_TEST_FOR1); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET1) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR2); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET2) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR3); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET3) + return -EINVAL; + pci_quatech_wqopr(port, (qopr & QPCR_TEST_FOR1)|QPCR_TEST_FOR4); + reg = pci_quatech_rqopr(port) & 0xC0; + if (reg != QPCR_TEST_GET4) + return -EINVAL; + + pci_quatech_wqopr(port, qopr); + return 0; +} + +static int pci_quatech_clock(struct uart_8250_port *port) +{ + u8 qopr, reg, set; + unsigned long clock; + + if (pci_quatech_test(port) < 0) + return 1843200; + + qopr = pci_quatech_rqopr(port); + + pci_quatech_wqopr(port, qopr & ~QOPR_CLOCK_X8); + reg = pci_quatech_rqopr(port); + if (reg & QOPR_CLOCK_X8) { + clock = 1843200; + goto out; + } + pci_quatech_wqopr(port, qopr | QOPR_CLOCK_X8); + reg = pci_quatech_rqopr(port); + if (!(reg & QOPR_CLOCK_X8)) { + clock = 1843200; + goto out; + } + reg &= QOPR_CLOCK_X8; + if (reg == QOPR_CLOCK_X2) { + clock = 3685400; + set = QOPR_CLOCK_X2; + } else if (reg == QOPR_CLOCK_X4) { + clock = 7372800; + set = QOPR_CLOCK_X4; + } else if (reg == QOPR_CLOCK_X8) { + clock = 14745600; + set = QOPR_CLOCK_X8; + } else { + clock = 1843200; + set = QOPR_CLOCK_X1; + } + qopr &= ~QOPR_CLOCK_RATE_MASK; + qopr |= set; + +out: + pci_quatech_wqopr(port, qopr); + return clock; +} + +static int pci_quatech_rs422(struct uart_8250_port *port) +{ + u8 qmcr; + int rs422 = 0; + + if (!pci_quatech_has_qmcr(port)) + return 0; + qmcr = pci_quatech_rqmcr(port); + pci_quatech_wqmcr(port, 0xFF); + if (pci_quatech_rqmcr(port)) + rs422 = 1; + pci_quatech_wqmcr(port, qmcr); + return rs422; +} + +static int pci_quatech_init(struct pci_dev *dev) +{ + if (pci_quatech_amcc(dev->device)) { + unsigned long base = pci_resource_start(dev, 0); + if (base) { + u32 tmp; + outl(inl(base + 0x38) | 0x00002000, base + 0x38); + tmp = inl(base + 0x3c); + outl(tmp | 0x01000000, base + 0x3c); + outl(tmp &= ~0x01000000, base + 0x3c); + } + } + return 0; +} + +static int pci_quatech_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + /* Needed by pci_quatech calls below */ + port->port.iobase = pci_resource_start(priv->dev, FL_GET_BASE(board->flags)); + /* Set up the clocking */ + port->port.uartclk = pci_quatech_clock(port); + /* For now just warn about RS422 */ + if (pci_quatech_rs422(port)) + pr_warn("quatech: software control of RS422 features not currently supported.\n"); + return pci_default_setup(priv, board, port, idx); +} + +static void pci_quatech_exit(struct pci_dev *dev) +{ +} + static int pci_default_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) @@ -1061,6 +1307,29 @@ static int pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int pci_pericom_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + port->port.uartclk = 14745600; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + static int ce4100_serial_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1077,6 +1346,141 @@ ce4100_serial_setup(struct serial_private *priv, return ret; } +#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a +#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c + +#define BYT_PRV_CLK 0x800 +#define BYT_PRV_CLK_EN (1 << 0) +#define BYT_PRV_CLK_M_VAL_SHIFT 1 +#define BYT_PRV_CLK_N_VAL_SHIFT 16 +#define BYT_PRV_CLK_UPDATE (1 << 31) + +#define BYT_GENERAL_REG 0x808 +#define BYT_GENERAL_DIS_RTS_N_OVERRIDE (1 << 3) + +#define BYT_TX_OVF_INT 0x820 +#define BYT_TX_OVF_INT_MASK (1 << 1) + +static void +byt_set_termios(struct uart_port *p, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + unsigned int m, n; + u32 reg; + + /* + * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the + * dividers must be adjusted. + * + * uartclk = (m / n) * 100 MHz, where m <= n + */ + switch (baud) { + case 500000: + case 1000000: + case 2000000: + case 4000000: + m = 64; + n = 100; + p->uartclk = 64000000; + break; + case 3500000: + m = 56; + n = 100; + p->uartclk = 56000000; + break; + case 1500000: + case 3000000: + m = 48; + n = 100; + p->uartclk = 48000000; + break; + case 2500000: + m = 40; + n = 100; + p->uartclk = 40000000; + break; + default: + m = 2304; + n = 3125; + p->uartclk = 73728000; + } + + /* Reset the clock */ + reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT); + writel(reg, p->membase + BYT_PRV_CLK); + reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE; + writel(reg, p->membase + BYT_PRV_CLK); + + /* + * If auto-handshake mechanism is not enabled, + * disable rts_n override + */ + reg = readl(p->membase + BYT_GENERAL_REG); + reg &= ~BYT_GENERAL_DIS_RTS_N_OVERRIDE; + if (termios->c_cflag & CRTSCTS) + reg |= BYT_GENERAL_DIS_RTS_N_OVERRIDE; + writel(reg, p->membase + BYT_GENERAL_REG); + + serial8250_do_set_termios(p, termios, old); +} + +static bool byt_dma_filter(struct dma_chan *chan, void *param) +{ + return chan->chan_id == *(int *)param; +} + +static int +byt_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct uart_8250_dma *dma; + int ret; + + dma = devm_kzalloc(port->port.dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + switch (priv->dev->device) { + case PCI_DEVICE_ID_INTEL_BYT_UART1: + dma->rx_chan_id = 3; + dma->tx_chan_id = 2; + break; + case PCI_DEVICE_ID_INTEL_BYT_UART2: + dma->rx_chan_id = 5; + dma->tx_chan_id = 4; + break; + default: + return -EINVAL; + } + + dma->rxconf.slave_id = dma->rx_chan_id; + dma->rxconf.src_maxburst = 16; + + dma->txconf.slave_id = dma->tx_chan_id; + dma->txconf.dst_maxburst = 16; + + dma->fn = byt_dma_filter; + dma->rx_param = &dma->rx_chan_id; + dma->tx_param = &dma->tx_chan_id; + + ret = pci_default_setup(priv, board, port, idx); + port->port.iotype = UPIO_MEM; + 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.fifosize = 64; + port->tx_loadsz = 64; + port->dma = dma; + port->capabilities = UART_CAP_FIFO | UART_CAP_AFE; + + /* Disable Tx counter interrupts */ + writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT); + + return ret; +} + static int pci_omegapci_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1097,17 +1501,80 @@ pci_brcm_trumanage_setup(struct serial_private *priv, return ret; } +static int pci_fintek_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + struct pci_dev *pdev = priv->dev; + unsigned long base; + unsigned long iobase; + unsigned long ciobase = 0; + u8 config_base; + + /* + * We are supposed to be able to read these from the PCI config space, + * but the values there don't seem to match what we need to use, so + * just use these hard-coded values for now, as they are correct. + */ + switch (idx) { + case 0: iobase = 0xe000; config_base = 0x40; break; + case 1: iobase = 0xe008; config_base = 0x48; break; + case 2: iobase = 0xe010; config_base = 0x50; break; + case 3: iobase = 0xe018; config_base = 0x58; break; + case 4: iobase = 0xe020; config_base = 0x60; break; + case 5: iobase = 0xe028; config_base = 0x68; break; + case 6: iobase = 0xe030; config_base = 0x70; break; + case 7: iobase = 0xe038; config_base = 0x78; break; + case 8: iobase = 0xe040; config_base = 0x80; break; + case 9: iobase = 0xe048; config_base = 0x88; break; + case 10: iobase = 0xe050; config_base = 0x90; break; + case 11: iobase = 0xe058; config_base = 0x98; break; + default: + /* Unknown number of ports, get out of here */ + return -EINVAL; + } + + if (idx < 4) { + base = pci_resource_start(priv->dev, 3); + ciobase = (int)(base + (0x8 * idx)); + } + + dev_dbg(&pdev->dev, "%s: idx=%d iobase=0x%lx ciobase=0x%lx config_base=0x%2x\n", + __func__, idx, iobase, ciobase, config_base); + + /* Enable UART I/O port */ + pci_write_config_byte(pdev, config_base + 0x00, 0x01); + + /* Select 128-byte FIFO and 8x FIFO threshold */ + pci_write_config_byte(pdev, config_base + 0x01, 0x33); + + /* LSB UART */ + pci_write_config_byte(pdev, config_base + 0x04, (u8)(iobase & 0xff)); + + /* MSB UART */ + pci_write_config_byte(pdev, config_base + 0x05, (u8)((iobase & 0xff00) >> 8)); + + /* irq number, this usually fails, but the spec says to do it anyway. */ + pci_write_config_byte(pdev, config_base + 0x06, pdev->irq); + + port->port.iotype = UPIO_PORT; + port->port.iobase = iobase; + port->port.mapbase = 0; + port->port.membase = NULL; + port->port.regshift = 0; + + return 0; +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { port->port.flags |= UPF_NO_TXEN_TEST; - printk(KERN_DEBUG "serial8250: skipping TxEn test for device " - "[%04x:%04x] subsystem [%04x:%04x]\n", - priv->dev->vendor, - priv->dev->device, - priv->dev->subsystem_vendor, - priv->dev->subsystem_device); + dev_dbg(&priv->dev->dev, + "serial8250: skipping TxEn test for device [%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, priv->dev->device, + priv->dev->subsystem_vendor, priv->dev->subsystem_device); return pci_default_setup(priv, board, port, idx); } @@ -1286,6 +1753,8 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_ADVANTECH 0x13fe #define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 #define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_ADVANTECH_PCI3618 0x3618 +#define PCI_DEVICE_ID_ADVANTECH_PCIf618 0xf618 #define PCI_DEVICE_ID_TITAN_200I 0x8028 #define PCI_DEVICE_ID_TITAN_400I 0x8048 #define PCI_DEVICE_ID_TITAN_800I 0x8088 @@ -1298,6 +1767,7 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_TITAN_800E 0xA014 #define PCI_DEVICE_ID_TITAN_200EI 0xA016 #define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_TITAN_200V3 0xA306 #define PCI_DEVICE_ID_TITAN_400V3 0xA310 #define PCI_DEVICE_ID_TITAN_410V3 0xA312 #define PCI_DEVICE_ID_TITAN_800V3 0xA314 @@ -1307,8 +1777,10 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA 0xc001 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d #define PCI_VENDOR_ID_WCH 0x4348 +#define PCI_DEVICE_ID_WCH_CH352_2S 0x3253 #define PCI_DEVICE_ID_WCH_CH353_4S 0x3453 #define PCI_DEVICE_ID_WCH_CH353_2S1PF 0x5046 +#define PCI_DEVICE_ID_WCH_CH353_1S1P 0x5053 #define PCI_DEVICE_ID_WCH_CH353_2S1P 0x7053 #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 @@ -1317,10 +1789,15 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 #define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a +#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e + +#define PCI_VENDOR_ID_SUNIX 0x1fd4 +#define PCI_DEVICE_ID_SUNIX_1999 0x1999 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 /* * Master list of serial port init/setup/exit quirks. @@ -1335,8 +1812,8 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { * ADDI-DATA GmbH communication cards <info@addi-data.com> */ { - .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, - .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, + .vendor = PCI_VENDOR_ID_AMCC, + .device = PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .setup = addidata_apci7800_setup, @@ -1409,6 +1886,20 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = kt_serial_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_UART1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = byt_serial_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_UART2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = byt_serial_setup, + }, /* * ITE */ @@ -1541,6 +2032,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_ni8430_setup, .exit = pci_ni8430_exit, }, + /* Quatech */ + { + .vendor = PCI_VENDOR_ID_QUATECH, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_quatech_init, + .setup = pci_quatech_setup, + .exit = pci_quatech_exit, + }, /* * Panacom */ @@ -1563,6 +2064,31 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .exit = pci_plx9050_exit, }, /* + * Pericom + */ + { + .vendor = 0x12d8, + .device = 0x7952, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, + { + .vendor = 0x12d8, + .device = 0x7954, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, + { + .vendor = 0x12d8, + .device = 0x7958, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, + + /* * PLX */ { @@ -1592,15 +2118,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { }, { .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, - .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = pci_plx9050_exit, - }, - { - .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_ROMULUS, .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, @@ -1704,6 +2221,23 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_timedia_setup, }, /* + * SUNIX (Timedia) cards + * Do not "probe" for these cards as there is at least one combination + * card that should be handled by parport_pc that doesn't match the + * rule in pci_timedia_probe. + * It is part number is MIO5079A but its subdevice ID is 0x0102. + * There are some boards with part number SER5037AL that report + * subdevice ID 0x0002. + */ + { + .vendor = PCI_VENDOR_ID_SUNIX, + .device = PCI_DEVICE_ID_SUNIX_1999, + .subvendor = PCI_VENDOR_ID_SUNIX, + .subdevice = PCI_ANY_ID, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + /* * Exar cards */ { @@ -1879,6 +2413,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_omegapci_setup, }, + /* WCH CH353 1S1P card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH353_1S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, /* WCH CH353 2S1P card (16550 clone) */ { .vendor = PCI_VENDOR_ID_WCH, @@ -1903,6 +2445,14 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, + /* WCH CH352 2S card (16550 clone) */ + { + .vendor = PCI_VENDOR_ID_WCH, + .device = PCI_DEVICE_ID_WCH_CH352_2S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch353_setup, + }, /* * ASIX devices with FIFO bug */ @@ -1976,6 +2526,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_brcm_trumanage_setup, }, + { + .vendor = 0x1c29, + .device = 0x1104, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + }, + { + .vendor = 0x1c29, + .device = 0x1108, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + }, + { + .vendor = 0x1c29, + .device = 0x1112, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fintek_setup, + }, /* * Default "match everything" terminator entry @@ -2170,9 +2741,13 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_4_3906250, pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, + pbn_byt, pbn_omegapci, pbn_NETMOS9900_2s_115200, pbn_brcm_trumanage, + pbn_fintek_4, + pbn_fintek_8, + pbn_fintek_12, }; /* @@ -2906,6 +3481,17 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .reg_shift = 2, }, + /* + * Intel BayTrail HSUART reference clock is 44.2368 MHz at power-on, + * but is overridden by byt_set_termios. + */ + [pbn_byt] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 2764800, + .uart_offset = 0x80, + .reg_shift = 2, + }, [pbn_omegapci] = { .flags = FL_BASE0, .num_ports = 8, @@ -2923,6 +3509,24 @@ static struct pciserial_board pci_boards[] = { .reg_shift = 2, .base_baud = 115200, }, + [pbn_fintek_4] = { + .num_ports = 4, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, + [pbn_fintek_8] = { + .num_ports = 8, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, + [pbn_fintek_12] = { + .num_ports = 12, + .uart_offset = 8, + .base_baud = 115200, + .first_offset = 0x40, + }, }; static const struct pci_device_id blacklist[] = { @@ -2933,6 +3537,7 @@ static const struct pci_device_id blacklist[] = { /* multi-io cards handled by parport_serial */ { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ + { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ }; /* @@ -3083,14 +3688,15 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) if (quirk->setup(priv, board, &uart, i)) break; -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", - uart.port.iobase, uart.port.irq, uart.port.iotype); -#endif + dev_dbg(&dev->dev, "Setup PCI port: port %lx, irq %d, type %d\n", + uart.port.iobase, uart.port.irq, uart.port.iotype); priv->line[i] = serial8250_register_8250_port(&uart); if (priv->line[i] < 0) { - printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); + dev_err(&dev->dev, + "Couldn't register serial port %lx, irq %d, type %d, error %d\n", + uart.port.iobase, uart.port.irq, + uart.port.iotype, priv->line[i]); break; } } @@ -3183,7 +3789,7 @@ pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) } if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { - printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", + dev_err(&dev->dev, "invalid driver_data: %ld\n", ent->driver_data); return -EINVAL; } @@ -3241,8 +3847,6 @@ static void pciserial_remove_one(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); - pci_set_drvdata(dev, NULL); - pciserial_remove_ports(priv); pci_disable_device(dev); @@ -3276,7 +3880,7 @@ static int pciserial_resume_one(struct pci_dev *dev) err = pci_enable_device(dev); /* FIXME: We cannot simply error out here */ if (err) - printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); + dev_err(&dev->dev, "Unable to re-enable ports, trying to continue.\n"); pciserial_resume_ports(priv); } return 0; @@ -3288,6 +3892,13 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, pbn_b2_8_921600 }, + /* Advantech also use 0x3618 and 0xf618 */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCIf618, + PCI_DEVICE_ID_ADVANTECH_PCI3618, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, @@ -3456,7 +4067,12 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, - pbn_b0_4_115200 }, + pbn_b2_4_115200 }, + /* Unknown card - subdevice 0x1588 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0, + pbn_b2_8_115200 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_SUBVENDOR_ID_KEYSPAN, PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, @@ -3506,18 +4122,70 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, + /* + * Quatech cards. These actually have configurable clocks but for + * now we just use the default. + * + * 100 series are RS232, 200 series RS422, + */ { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_4_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_8_115200 }, { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_SSCLP200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESCLP100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, @@ -3804,6 +4472,9 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_4_921600 }, @@ -3902,6 +4573,19 @@ static struct pci_device_id serial_pci_tbl[] = { pbn_b0_bt_1_921600 }, /* + * SUNIX (TIMEDIA) + */ + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, + pbn_b0_bt_1_921600 }, + + { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999, + PCI_VENDOR_ID_SUNIX, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00, + pbn_b0_bt_1_921600 }, + + /* * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */ { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, @@ -4349,8 +5033,8 @@ static struct pci_device_id serial_pci_tbl[] = { 0, pbn_b0_1_115200 }, - { PCI_VENDOR_ID_ADDIDATA_OLD, - PCI_DEVICE_ID_ADDIDATA_APCI7800, + { PCI_VENDOR_ID_AMCC, + PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800, PCI_ANY_ID, PCI_ANY_ID, 0, @@ -4449,6 +5133,12 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_VENDOR_ID_IBM, 0x0299, 0, 0, pbn_b0_bt_2_115200 }, + /* + * other NetMos 9835 devices are most likely handled by the + * parport_serial driver, check drivers/parport/parport_serial.c + * before adding them here. + */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, 0xA000, 0x1000, 0, 0, pbn_b0_1_115200 }, @@ -4493,6 +5183,15 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_ce4100_1_115200 }, + /* Intel BayTrail */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, + pbn_byt }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, + pbn_byt }, /* * Cronyx Omega PCI @@ -4527,6 +5226,10 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_b0_bt_2_115200 }, + /* * Commtech, Inc. Fastcom adapters */ @@ -4559,6 +5262,11 @@ static struct pci_device_id serial_pci_tbl[] = { 0, 0, pbn_exar_XR17V358 }, + /* Fintek PCI serial cards */ + { PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 }, + { PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 }, + { PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 35d9ab95c5c..682a2fbe5c0 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -12,7 +12,6 @@ * the Free Software Foundation; either version 2 of the License. */ #include <linux/module.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/pnp.h> #include <linux/string.h> diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index c31133a6ea8..349ee598b34 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -12,9 +12,8 @@ config SERIAL_8250 here are those that are setting up dedicated Ethernet WWW/FTP servers, or users that have one of the various bus mice instead of a serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) + port for anything. (Note that the Cyclades multi serial port driver + does not need this driver built in for it to work.) To compile this driver as a module, choose M here: the module will be called 8250. @@ -33,6 +32,23 @@ config SERIAL_8250 Most people will say Y or M here, so that they can use serial mice, modems and similar devices connecting to the standard serial ports. +config SERIAL_8250_DEPRECATED_OPTIONS + bool "Support 8250_core.* kernel options (DEPRECATED)" + depends on SERIAL_8250 + default y + ---help--- + In 3.7 we renamed 8250 to 8250_core by mistake, so now we have to + accept kernel parameters in both forms like 8250_core.nr_uarts=4 and + 8250.nr_uarts=4. We now renamed the module back to 8250, but if + anybody noticed in 3.7 and changed their userspace we still have to + keep the 8250_core.* options around until they revert the changes + they already did. + + If 8250 is built as a module, this adds 8250_core alias instead. + + If you did not notice yet and/or you have userspace from pre-3.7, it + is safe (and recommended) to say N here. + config SERIAL_8250_PNP bool "8250/16550 PNP device support" if EXPERT depends on SERIAL_8250 && PNP @@ -45,6 +61,7 @@ config SERIAL_8250_CONSOLE bool "Console on 8250/16550 and compatible serial port" depends on SERIAL_8250=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON ---help--- If you say Y here, it will be possible to use a serial port as the system console (the system console is the device which receives all @@ -74,16 +91,19 @@ config SERIAL_8250_CONSOLE If unsure, say N. -config FIX_EARLYCON_MEM - bool - depends on X86 - default y - config SERIAL_8250_GSC tristate depends on SERIAL_8250 && GSC default SERIAL_8250 +config SERIAL_8250_DMA + bool "DMA support for 16550 compatible UART controllers" if EXPERT + depends on SERIAL_8250 && DMADEVICES=y + default SERIAL_8250 + help + This builds DMA support that can be used with 8250/16650 + compatible UART controllers that support DMA signaling. + config SERIAL_8250_PCI tristate "8250/16550 PCI device support" if EXPERT depends on SERIAL_8250 && PCI @@ -92,6 +112,8 @@ config SERIAL_8250_PCI This builds standard PCI serial support. You may be able to disable this feature if you only need legacy serial support. Saves about 9K. + Note that serial ports on NetMos 9835 Multi-I/O cards are handled + by the parport_serial driver, enabled with CONFIG_PARPORT_SERIAL. config SERIAL_8250_HP300 tristate @@ -249,15 +271,6 @@ config SERIAL_8250_ACORN system, say Y to this option. The driver can handle 1, 2, or 3 port cards. If unsure, say N. -config SERIAL_8250_RM9K - bool "Support for MIPS RM9xxx integrated serial port" - depends on SERIAL_8250 != n && SERIAL_RM9000 - select SERIAL_8250_SHARE_IRQ - help - Selecting this option will add support for the integrated serial - port hardware found on MIPS RM9122 and similar processors. - If unsure, say N. - config SERIAL_8250_FSL bool depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 @@ -265,7 +278,7 @@ config SERIAL_8250_FSL config SERIAL_8250_DW tristate "Support for Synopsys DesignWare 8250 quirks" - depends on SERIAL_8250 && OF + depends on SERIAL_8250 help Selecting this option will enable handling of the extra features present in the Synopsys DesignWare APB UART. @@ -277,3 +290,11 @@ config SERIAL_8250_EM Selecting this option will add support for the integrated serial port hardware found on the Emma Mobile line of processors. If unsure, say N. + +config SERIAL_8250_RT288X + bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" + depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883) + help + If you have a Ralink RT288x/RT305x SoC based board and want to use the + serial port, say Y to this option. The driver can handle up to 2 serial + ports. If unsure, say N. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 108fe7fe13e..36d68d05430 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -2,9 +2,10 @@ # Makefile for the 8250 serial device drivers. # -obj-$(CONFIG_SERIAL_8250) += 8250_core.o -8250_core-y := 8250.o -8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250) += 8250.o +8250-y := 8250_core.o +8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +8250-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.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 diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index b7d48b34639..4d180c9423e 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -34,7 +34,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> @@ -852,18 +851,6 @@ static struct pcmcia_driver serial_cs_driver = { .suspend = serial_suspend, .resume = serial_resume, }; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); +module_pcmcia_driver(serial_cs_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 59c23d03810..fb57159bad3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -2,9 +2,18 @@ # Serial device configuration # +if TTY + menu "Serial drivers" depends on HAS_IOMEM +config SERIAL_EARLYCON + bool + help + Support for early consoles with the earlycon parameter. This enables + the console before standard serial driver is probed. The console is + enabled when early_param is processed. + source "drivers/tty/serial/8250/Kconfig" comment "Non-8250 serial port support" @@ -51,6 +60,7 @@ config SERIAL_AMBA_PL011_CONSOLE bool "Support for console on AMBA serial port" depends on SERIAL_AMBA_PL011=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON ---help--- Say Y here if you wish to use an AMBA PrimeCell UART as the system console (the system console is the device which receives all kernel @@ -63,6 +73,18 @@ config SERIAL_AMBA_PL011_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_EARLYCON_ARM_SEMIHOST + bool "Early console using ARM semihosting" + depends on ARM64 || ARM + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Support for early debug console using ARM semihosting. This enables + the console before standard serial driver is probed. This is enabled + with "earlycon=smh" on the kernel command line. The console is + enabled when early_param is processed. + config SERIAL_SB1250_DUART tristate "BCM1xxx on-chip DUART serial support" depends on SIBYTE_SB1xxx_SOC=y @@ -95,6 +117,7 @@ config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" depends on ARCH_AT91 || AVR32 select SERIAL_CORE + select SERIAL_MCTRL_GPIO help This enables the driver for the on-chip UARTs of the Atmel AT91 and AT32 processors. @@ -179,9 +202,8 @@ config SERIAL_KS8695_CONSOLE config SERIAL_CLPS711X tristate "CLPS711X serial port support" - depends on ARCH_CLPS711X + depends on ARCH_CLPS711X || COMPILE_TEST select SERIAL_CORE - default y help This enables the driver for the on-chip UARTs of the Cirrus Logic EP711x/EP721x/EP731x processors. @@ -209,14 +231,14 @@ config SERIAL_SAMSUNG config SERIAL_SAMSUNG_UARTS_4 bool depends on PLAT_SAMSUNG - default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442) + default y if !(CPU_S3C2410 || CPU_S3C2412 || CPU_S3C2440 || CPU_S3C2442) help Internal node for the common case of 4 Samsung compatible UARTs config SERIAL_SAMSUNG_UARTS int depends on PLAT_SAMSUNG - default 6 if ARCH_S5P6450 + default 6 if CPU_S5P6450 default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416 default 3 help @@ -249,7 +271,7 @@ config SERIAL_SAMSUNG_CONSOLE config SERIAL_SIRFSOC tristate "SiRF SoC Platform Serial port support" - depends on ARCH_PRIMA2 + depends on ARCH_SIRF select SERIAL_CORE help Support for the on-chip UART on the CSR SiRFprimaII series, @@ -269,6 +291,17 @@ config SERIAL_SIRFSOC_CONSOLE your boot loader about how to pass options to the kernel at boot time.) +config SERIAL_TEGRA + tristate "NVIDIA Tegra20/30 SoC serial controller" + depends on ARCH_TEGRA && TEGRA20_APB_DMA + select SERIAL_CORE + help + Support for the on-chip UARTs on the NVIDIA Tegra series SOCs + providing /dev/ttyTHS0, 1, 2, 3 and 4 (note, some machines may not + provide all of these ports, depending on how the serial port + are enabled). This driver uses the APB DMA to achieve higher baudrate + and better performance. + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI @@ -277,14 +310,14 @@ config SERIAL_MAX3100 MAX3100 chip support config SERIAL_MAX310X - bool "MAX310X support" - depends on SPI + tristate "MAX310X support" + depends on SPI_MASTER select SERIAL_CORE - select REGMAP_SPI if SPI + select REGMAP_SPI if SPI_MASTER default n help This selects support for an advanced UART from Maxim (Dallas). - Supported ICs are MAX3107, MAX3108. + Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. Each IC contains 128 words each of receive and transmit FIFO that can be controlled through I2C or high-speed SPI. @@ -538,7 +571,7 @@ config BFIN_UART3_CTSRTS Enable hardware flow control in the driver. config SERIAL_IMX - bool "IMX serial port support" + tristate "IMX serial port support" depends on ARCH_MXC select SERIAL_CORE select RATIONAL @@ -548,22 +581,21 @@ config SERIAL_IMX config SERIAL_IMX_CONSOLE bool "Console on IMX serial port" - depends on SERIAL_IMX + depends on SERIAL_IMX=y select SERIAL_CORE_CONSOLE help - If you have enabled the serial port on the Motorola IMX + If you have enabled the serial port on the Freescale IMX CPU you can make it the console by answering Y to this option. Even if you say Y here, the currently visible virtual console (/dev/tty0) will still be used as the system console by default, but you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) + "console=ttymxc0". (Try "man bootparam" or see the documentation of + your bootloader about how to pass options to the kernel at boot time.) config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE || ARCH_ZYNQ select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -697,7 +729,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE) + depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS @@ -716,19 +748,19 @@ config SERIAL_SH_SCI_DMA config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" - depends on SOC_PNX8550 || SOC_PNX833X + depends on SOC_PNX833X select SERIAL_CORE help - If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 - and you want to use serial ports, say Y. Otherwise, say N. + If you have a MIPS-based Philips SoC such as PNX8330 and you want + to use serial ports, say Y. Otherwise, say N. config SERIAL_PNX8XXX_CONSOLE bool "Enable PNX8XX0 serial console" depends on SERIAL_PNX8XXX select SERIAL_CORE_CONSOLE help - If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 - and you want to use serial console, say Y. Otherwise, say N. + If you have a MIPS-based Philips SoC such as PNX8330 and you want + to use serial console, say Y. Otherwise, say N. config SERIAL_HS_LPC32XX tristate "LPC32XX high speed serial port support" @@ -1013,7 +1045,7 @@ config SERIAL_SGI_IOC3 config SERIAL_MSM bool "MSM on-chip serial port support" - depends on ARCH_MSM + depends on ARCH_MSM || ARCH_QCOM select SERIAL_CORE config SERIAL_MSM_CONSOLE @@ -1023,7 +1055,7 @@ config SERIAL_MSM_CONSOLE config SERIAL_MSM_HS tristate "MSM UART High Speed: Serial Driver" - depends on ARCH_MSM + depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50 select SERIAL_CORE help If you have a machine based on MSM family of SoCs, you @@ -1134,31 +1166,13 @@ config SERIAL_QE This driver supports the QE serial ports on Freescale embedded PowerPC that contain a QUICC Engine. -config SERIAL_SC26XX - tristate "SC2681/SC2692 serial port support" - depends on SNI_RM - select SERIAL_CORE - help - This is a driver for the onboard serial ports of - older RM400 machines. - -config SERIAL_SC26XX_CONSOLE - bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX=y - select SERIAL_CORE_CONSOLE - help - Support for Console on SC2681/SC2692 serial ports. - config SERIAL_SCCNXP tristate "SCCNXP serial port support" - depends on !SERIAL_SC26XX select SERIAL_CORE - default n help This selects support for an advanced UART from NXP (Philips). Supported ICs are SCC2681, SCC2691, SCC2692, SC28L91, SC28L92, SC28L202, SCC68681 and SCC68692. - Positioned as a replacement for the driver SC26XX. config SERIAL_SCCNXP_CONSOLE bool "Console on SCCNXP serial port" @@ -1167,6 +1181,16 @@ config SERIAL_SCCNXP_CONSOLE help Support for console on SCCNXP serial ports. +config SERIAL_SC16IS7XX + tristate "SC16IS7xx serial support" + depends on I2C + select SERIAL_CORE + select REGMAP_I2C if I2C + help + This selects support for SC16IS7xx serial ports. + Supported ICs are SC16IS740, SC16IS741, SC16IS750, SC16IS752, + SC16IS760 and SC16IS762. + config SERIAL_BFIN_SPORT tristate "Blackfin SPORT emulate UART" depends on BLACKFIN @@ -1233,6 +1257,7 @@ config SERIAL_BFIN_SPORT3_UART_CTSRTS config SERIAL_TIMBERDALE tristate "Support for timberdale UART" select SERIAL_CORE + depends on X86_32 || COMPILE_TEST ---help--- Add support for UART controller on timberdale. @@ -1329,7 +1354,7 @@ config SERIAL_IFX6X60 config SERIAL_PCH_UART tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) select SERIAL_CORE help This driver is for PCH(Platform controller Hub) UART of Intel EG20T @@ -1375,27 +1400,31 @@ config SERIAL_MXS_AUART_CONSOLE Enable a MXS AUART port to be the system console. config SERIAL_XILINX_PS_UART - tristate "Xilinx PS UART support" + tristate "Cadence (Xilinx Zynq) UART support" depends on OF select SERIAL_CORE help - This driver supports the Xilinx PS UART port. + This driver supports the Cadence UART. It is found e.g. in Xilinx + Zynq. config SERIAL_XILINX_PS_UART_CONSOLE - bool "Xilinx PS UART console support" + bool "Cadence UART console support" depends on SERIAL_XILINX_PS_UART=y select SERIAL_CORE_CONSOLE help - Enable a Xilinx PS UART port to be the system console. + Enable a Cadence UART port to be the system console. config SERIAL_AR933X - bool "AR933X serial port support" - depends on SOC_AR933X + tristate "AR933X serial port support" + depends on HAVE_CLK && SOC_AR933X select SERIAL_CORE help If you have an Atheros AR933X SOC based board and want to use the built-in UART of the SoC, say Y to this option. + To compile this driver as a module, choose M here: the + module will be called ar933x_uart. + config SERIAL_AR933X_CONSOLE bool "Console on AR933X serial port" depends on SERIAL_AR933X=y @@ -1412,8 +1441,8 @@ config SERIAL_AR933X_NR_UARTS to support. config SERIAL_EFM32_UART - tristate "EFM32 UART/USART port." - depends on ARCH_EFM32 + tristate "EFM32 UART/USART port" + depends on ARM && (ARCH_EFM32 || COMPILE_TEST) select SERIAL_CORE help This driver support the USART and UART ports on @@ -1424,6 +1453,15 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_TILEGX + tristate "TILE-Gx on-chip serial port support" + depends on TILEGX + select TILE_GXIO_UART + select SERIAL_CORE + ---help--- + This device provides access to the on-chip UARTs on the TILE-Gx + processor. + config SERIAL_ARC tristate "ARC UART driver support" select SERIAL_CORE @@ -1447,4 +1485,76 @@ config SERIAL_ARC_NR_PORTS Set this to the number of serial ports you want the driver to support. +config SERIAL_RP2 + tristate "Comtrol RocketPort EXPRESS/INFINITY support" + depends on PCI + select SERIAL_CORE + help + This driver supports the Comtrol RocketPort EXPRESS and + RocketPort INFINITY families of PCI/PCIe multiport serial adapters. + These adapters use a "RocketPort 2" ASIC that is not compatible + with the original RocketPort driver (CONFIG_ROCKETPORT). + + To compile this driver as a module, choose M here: the + module will be called rp2. + + If you want to compile this driver into the kernel, say Y here. If + you don't have a suitable RocketPort card installed, say N. + +config SERIAL_RP2_NR_UARTS + int "Maximum number of RocketPort EXPRESS/INFINITY ports" + depends on SERIAL_RP2 + default "32" + help + If multiple cards are present, the default limit of 32 ports may + need to be increased. + +config SERIAL_FSL_LPUART + tristate "Freescale lpuart serial port support" + depends on HAS_DMA + select SERIAL_CORE + help + Support for the on-chip lpuart on some Freescale SOCs. + +config SERIAL_FSL_LPUART_CONSOLE + bool "Console on Freescale lpuart serial port" + depends on SERIAL_FSL_LPUART=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the lpuart serial port on the Freescale SoCs, + you can make it the console by answering Y to this option. + +config SERIAL_ST_ASC + tristate "ST ASC serial port support" + select SERIAL_CORE + depends on ARM || COMPILE_TEST + help + This driver is for the on-chip Asychronous Serial Controller on + STMicroelectronics STi SoCs. + ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality. + It support all industry standard baud rates. + + If unsure, say N. + +config SERIAL_ST_ASC_CONSOLE + bool "Support for console on ST ASC" + depends on SERIAL_ST_ASC=y + select SERIAL_CORE_CONSOLE + +config SERIAL_MEN_Z135 + tristate "MEN 16z135 Support" + select SERIAL_CORE + depends on MCB + help + Say yes here to enable support for the MEN 16z135 High Speed UART IP-Core + on a MCB carrier. + + This driver can also be build as a module. If so, the module will be called + men_z135_uart.ko + endmenu + +config SERIAL_MCTRL_GPIO + tristate + +endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index df1b998c436..0080cc362e0 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -5,6 +5,9 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o +obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o +obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o + # These Sparc drivers have to appear before others such as 8250 # which share ttySx minor node space. Otherwise console device # names change and other unplesantries. @@ -47,8 +50,8 @@ obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o obj-$(CONFIG_SERIAL_MPSC) += mpsc.o obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o -obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o +obj-$(CONFIG_SERIAL_SC16IS7XX) += sc16is7xx.o obj-$(CONFIG_SERIAL_JSM) += jsm/ obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o @@ -65,6 +68,8 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o +obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o @@ -80,6 +85,13 @@ obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o +obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o +obj-$(CONFIG_SERIAL_RP2) += rp2.o +obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o +obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o + +# GPIOLIB helpers for modem control lines +obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 872f14ae43d..59b3da9bcc3 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -139,7 +139,9 @@ static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) uart_insert_char(port, 0, 0, ch, flag); } - tty_flip_buffer_push(port->state->port.tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) @@ -408,7 +410,8 @@ static struct uart_driver altera_jtaguart_driver = { static int altera_jtaguart_probe(struct platform_device *pdev) { - struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct altera_jtaguart_platform_uart *platp = + dev_get_platdata(&pdev->dev); struct uart_port *port; struct resource *res_irq, *res_mem; int i = pdev->id; @@ -470,6 +473,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev) #ifdef CONFIG_OF static struct of_device_id altera_jtaguart_match[] = { { .compatible = "ALTR,juart-1.0", }, + { .compatible = "altr,juart-1.0", }, {}, }; MODULE_DEVICE_TABLE(of, altera_jtaguart_match); @@ -493,11 +497,9 @@ static int __init altera_jtaguart_init(void) if (rc) return rc; rc = platform_driver_register(&altera_jtaguart_platform_driver); - if (rc) { + if (rc) uart_unregister_driver(&altera_jtaguart_driver); - return rc; - } - return 0; + return rc; } static void __exit altera_jtaguart_exit(void) diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 684a0808e1c..323376668b7 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -185,6 +185,12 @@ static void altera_uart_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); spin_unlock_irqrestore(&port->lock, flags); + + /* + * FIXME: port->read_status_mask and port->ignore_status_mask + * need to be initialized based on termios settings for + * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT + */ } static void altera_uart_rx_chars(struct altera_uart *pp) @@ -231,7 +237,9 @@ static void altera_uart_rx_chars(struct altera_uart *pp) flag); } - tty_flip_buffer_push(port->state->port.tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void altera_uart_tx_chars(struct altera_uart *pp) @@ -534,7 +542,7 @@ static int altera_uart_get_of_uartclk(struct platform_device *pdev, static int altera_uart_probe(struct platform_device *pdev) { - struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev); struct uart_port *port; struct resource *res_mem; struct resource *res_irq; @@ -604,7 +612,6 @@ static int altera_uart_remove(struct platform_device *pdev) if (port) { uart_remove_one_port(&altera_uart_driver, port); - platform_set_drvdata(pdev, NULL); port->mapbase = 0; } @@ -614,6 +621,7 @@ static int altera_uart_remove(struct platform_device *pdev) #ifdef CONFIG_OF static struct of_device_id altera_uart_match[] = { { .compatible = "ALTR,uart-1.0", }, + { .compatible = "altr,uart-1.0", }, {}, }; MODULE_DEVICE_TABLE(of, altera_uart_match); @@ -637,11 +645,9 @@ static int __init altera_uart_init(void) if (rc) return rc; rc = platform_driver_register(&altera_uart_platform_driver); - if (rc) { + if (rc) uart_unregister_driver(&altera_uart_driver); - return rc; - } - return 0; + return rc; } static void __exit altera_uart_exit(void) diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c index 22317dd1647..971af1e22d0 100644 --- a/drivers/tty/serial/amba-pl010.c +++ b/drivers/tty/serial/amba-pl010.c @@ -116,7 +116,6 @@ static void pl010_enable_ms(struct uart_port *port) static void pl010_rx_chars(struct uart_amba_port *uap) { - struct tty_struct *tty = uap->port.state->port.tty; unsigned int status, ch, flag, rsr, max_count = 256; status = readb(uap->port.membase + UART01x_FR); @@ -165,7 +164,7 @@ static void pl010_rx_chars(struct uart_amba_port *uap) status = readb(uap->port.membase + UART01x_FR); } spin_unlock(&uap->port.lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&uap->port.state->port); spin_lock(&uap->port.lock); } @@ -421,7 +420,7 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, uap->port.read_status_mask = UART01x_RSR_OE; if (termios->c_iflag & INPCK) uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) uap->port.read_status_mask |= UART01x_RSR_BE; /* @@ -722,14 +721,13 @@ static int pl010_probe(struct amba_device *dev, const struct amba_id *id) uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; uap->dev = dev; - uap->data = dev->dev.platform_data; + uap->data = dev_get_platdata(&dev->dev); amba_ports[i] = uap; amba_set_drvdata(dev, uap); ret = uart_add_one_port(&amba_reg, &uap->port); if (ret) { - amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; clk_put(uap->clk); unmap: @@ -746,8 +744,6 @@ static int pl010_remove(struct amba_device *dev) struct uart_amba_port *uap = amba_get_drvdata(dev); int i; - amba_set_drvdata(dev, NULL); - uart_remove_one_port(&amba_reg, &uap->port); for (i = 0; i < ARRAY_SIZE(amba_ports); i++) @@ -760,9 +756,10 @@ static int pl010_remove(struct amba_device *dev) return 0; } -static int pl010_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pl010_suspend(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (uap) uart_suspend_port(&amba_reg, &uap->port); @@ -770,15 +767,18 @@ static int pl010_suspend(struct amba_device *dev, pm_message_t state) return 0; } -static int pl010_resume(struct amba_device *dev) +static int pl010_resume(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (uap) uart_resume_port(&amba_reg, &uap->port); return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume); static struct amba_id pl010_ids[] = { { @@ -793,12 +793,11 @@ MODULE_DEVICE_TABLE(amba, pl010_ids); static struct amba_driver pl010_driver = { .drv = { .name = "uart-pl010", + .pm = &pl010_dev_pm_ops, }, .id_table = pl010_ids, .probe = pl010_probe, .remove = pl010_remove, - .suspend = pl010_suspend, - .resume = pl010_resume, }; static int __init pl010_init(void) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 7fca4022a8b..0e26dcbd5ea 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -29,6 +29,7 @@ * and hooked into this driver. */ + #if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif @@ -72,36 +73,45 @@ /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; - unsigned int fifosize; unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; bool dma_threshold; bool cts_event_workaround; + + unsigned int (*get_fifosize)(struct amba_device *dev); }; +static unsigned int get_fifosize_arm(struct amba_device *dev) +{ + return amba_rev(dev) < 3 ? 16 : 32; +} + static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, - .fifosize = 16, .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, + .get_fifosize = get_fifosize_arm, }; +static unsigned int get_fifosize_st(struct amba_device *dev) +{ + return 64; +} + static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, - .fifosize = 64, .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, + .get_fifosize = get_fifosize_st, }; -static struct uart_amba_port *amba_ports[UART_NR]; - /* Deals with DMA transactions */ struct pl011_sgbuf { @@ -117,6 +127,12 @@ struct pl011_dmarx_data { struct pl011_sgbuf sgbuf_b; dma_cookie_t cookie; bool running; + struct timer_list timer; + unsigned int last_residue; + unsigned long last_jiffies; + bool auto_poll_rate; + unsigned int poll_rate; + unsigned int poll_timeout; }; struct pl011_dmatx_data { @@ -132,10 +148,6 @@ struct pl011_dmatx_data { struct uart_amba_port { struct uart_port port; struct clk *clk; - /* Two optional pin states - default & sleep */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_sleep; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ @@ -223,16 +235,18 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { - sg->buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + dma_addr_t dma_addr; + + sg->buf = dma_alloc_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, &dma_addr, GFP_KERNEL); if (!sg->buf) return -ENOMEM; - sg_init_one(&sg->sg, sg->buf, PL011_DMA_BUFFER_SIZE); + sg_init_table(&sg->sg, 1); + sg_set_page(&sg->sg, phys_to_page(dma_addr), + PL011_DMA_BUFFER_SIZE, offset_in_page(dma_addr)); + sg_dma_address(&sg->sg) = dma_addr; - if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) { - kfree(sg->buf); - return -EINVAL; - } return 0; } @@ -240,15 +254,16 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg, enum dma_data_direction dir) { if (sg->buf) { - dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir); - kfree(sg->buf); + dma_free_coherent(chan->device->dev, + PL011_DMA_BUFFER_SIZE, sg->buf, + sg_dma_address(&sg->sg)); } } -static void pl011_dma_probe_initcall(struct uart_amba_port *uap) +static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap) { /* DMA is the sole user of the platform data right now */ - struct amba_pl011_data *plat = uap->port.dev->platform_data; + struct amba_pl011_data *plat = dev_get_platdata(uap->port.dev); struct dma_slave_config tx_conf = { .dst_addr = uap->port.mapbase + UART01x_DR, .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, @@ -259,20 +274,25 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_chan *chan; dma_cap_mask_t mask; - /* We need platform data */ - if (!plat || !plat->dma_filter) { - dev_info(uap->port.dev, "no DMA platform data\n"); - return; - } - - /* Try to acquire a generic DMA engine slave TX channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + chan = dma_request_slave_channel(dev, "tx"); - chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); if (!chan) { - dev_err(uap->port.dev, "no TX DMA channel!\n"); - return; + /* We need platform data */ + if (!plat || !plat->dma_filter) { + dev_info(uap->port.dev, "no DMA platform data\n"); + return; + } + + /* Try to acquire a generic DMA engine slave TX channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, plat->dma_filter, + plat->dma_tx_param); + if (!chan) { + dev_err(uap->port.dev, "no TX DMA channel!\n"); + return; + } } dmaengine_slave_config(chan, &tx_conf); @@ -282,24 +302,52 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) dma_chan_name(uap->dmatx.chan)); /* Optionally make use of an RX channel as well */ - if (plat->dma_rx_param) { - struct dma_slave_config rx_conf = { - .src_addr = uap->port.mapbase + UART01x_DR, - .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .direction = DMA_DEV_TO_MEM, - .src_maxburst = uap->fifosize >> 1, - .device_fc = false, - }; + chan = dma_request_slave_channel(dev, "rx"); + if (!chan && plat->dma_rx_param) { chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); + if (!chan) { dev_err(uap->port.dev, "no RX DMA channel!\n"); return; } + } + + if (chan) { + struct dma_slave_config rx_conf = { + .src_addr = uap->port.mapbase + UART01x_DR, + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_DEV_TO_MEM, + .src_maxburst = uap->fifosize >> 2, + .device_fc = false, + }; dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; + if (plat && plat->dma_rx_poll_enable) { + /* Set poll rate if specified. */ + if (plat->dma_rx_poll_rate) { + uap->dmarx.auto_poll_rate = false; + uap->dmarx.poll_rate = plat->dma_rx_poll_rate; + } else { + /* + * 100 ms defaults to poll rate if not + * specified. This will be adjusted with + * the baud rate at set_termios. + */ + uap->dmarx.auto_poll_rate = true; + uap->dmarx.poll_rate = 100; + } + /* 3 secs defaults poll_timeout if not specified. */ + if (plat->dma_rx_poll_timeout) + uap->dmarx.poll_timeout = + plat->dma_rx_poll_timeout; + else + uap->dmarx.poll_timeout = 3000; + } else + uap->dmarx.auto_poll_rate = false; + dev_info(uap->port.dev, "DMA channel RX %s\n", dma_chan_name(uap->dmarx.chan)); } @@ -315,6 +363,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap) struct dma_uap { struct list_head node; struct uart_amba_port *uap; + struct device *dev; }; static LIST_HEAD(pl011_dma_uarts); @@ -325,7 +374,7 @@ static int __init pl011_dma_initcall(void) list_for_each_safe(node, tmp, &pl011_dma_uarts) { struct dma_uap *dmau = list_entry(node, struct dma_uap, node); - pl011_dma_probe_initcall(dmau->uap); + pl011_dma_probe_initcall(dmau->dev, dmau->uap); list_del(node); kfree(dmau); } @@ -334,18 +383,19 @@ static int __init pl011_dma_initcall(void) device_initcall(pl011_dma_initcall); -static void pl011_dma_probe(struct uart_amba_port *uap) +static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL); if (dmau) { dmau->uap = uap; + dmau->dev = dev; list_add_tail(&dmau->node, &pl011_dma_uarts); } } #else -static void pl011_dma_probe(struct uart_amba_port *uap) +static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { - pl011_dma_probe_initcall(uap); + pl011_dma_probe_initcall(dev, uap); } #endif @@ -625,6 +675,8 @@ static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) * Locking: called with port lock held and IRQs disabled. */ static void pl011_dma_flush_buffer(struct uart_port *port) +__releases(&uap->port.lock) +__acquires(&uap->port.lock) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -698,28 +750,33 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, u32 pending, bool use_buf_b, bool readfifo) { - struct tty_struct *tty = uap->port.state->port.tty; + struct tty_port *port = &uap->port.state->port; struct pl011_sgbuf *sgbuf = use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; - struct device *dev = uap->dmarx.chan->device->dev; int dma_count = 0; u32 fifotaken = 0; /* only used for vdbg() */ - /* Pick everything from the DMA first */ + struct pl011_dmarx_data *dmarx = &uap->dmarx; + int dmataken = 0; + + if (uap->dmarx.poll_rate) { + /* The data can be taken by polling */ + dmataken = sgbuf->sg.length - dmarx->last_residue; + /* Recalculate the pending size */ + if (pending >= dmataken) + pending -= dmataken; + } + + /* Pick the remain data from the DMA */ if (pending) { - /* Sync in buffer */ - dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); /* * First take all chars in the DMA pipe, then look in the FIFO. * Note that tty_insert_flip_buf() tries to take as many chars * as it can. */ - dma_count = tty_insert_flip_string(uap->port.state->port.tty, - sgbuf->buf, pending); - - /* Return buffer to device */ - dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE); + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + pending); uap->port.icount.rx += dma_count; if (dma_count < pending) @@ -727,6 +784,10 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, "couldn't insert all characters (TTY is full?)\n"); } + /* Reset the last_residue for Rx DMA poll */ + if (uap->dmarx.poll_rate) + dmarx->last_residue = sgbuf->sg.length; + /* * Only continue with trying to read the FIFO if all DMA chars have * been taken first. @@ -754,7 +815,7 @@ static void pl011_dma_rx_chars(struct uart_amba_port *uap, dev_vdbg(uap->port.dev, "Took %d chars from DMA buffer and %d chars from the FIFO\n", dma_count, fifotaken); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(port); spin_lock(&uap->port.lock); } @@ -866,6 +927,59 @@ static inline void pl011_dma_rx_stop(struct uart_amba_port *uap) writew(uap->dmacr, uap->port.membase + UART011_DMACR); } +/* + * Timer handler for Rx DMA polling. + * Every polling, It checks the residue in the dma buffer and transfer + * data to the tty. Also, last_residue is updated for the next polling. + */ +static void pl011_dma_rx_poll(unsigned long args) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)args; + struct tty_port *port = &uap->port.state->port; + struct pl011_dmarx_data *dmarx = &uap->dmarx; + struct dma_chan *rxchan = uap->dmarx.chan; + unsigned long flags = 0; + unsigned int dmataken = 0; + unsigned int size = 0; + struct pl011_sgbuf *sgbuf; + int dma_count; + struct dma_tx_state state; + + sgbuf = dmarx->use_buf_b ? &uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a; + rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state); + if (likely(state.residue < dmarx->last_residue)) { + dmataken = sgbuf->sg.length - dmarx->last_residue; + size = dmarx->last_residue - state.residue; + dma_count = tty_insert_flip_string(port, sgbuf->buf + dmataken, + size); + if (dma_count == size) + dmarx->last_residue = state.residue; + dmarx->last_jiffies = jiffies; + } + tty_flip_buffer_push(port); + + /* + * If no data is received in poll_timeout, the driver will fall back + * to interrupt mode. We will retrigger DMA at the first interrupt. + */ + if (jiffies_to_msecs(jiffies - dmarx->last_jiffies) + > uap->dmarx.poll_timeout) { + + spin_lock_irqsave(&uap->port.lock, flags); + pl011_dma_rx_stop(uap); + uap->im |= UART011_RXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + spin_unlock_irqrestore(&uap->port.lock, flags); + + uap->dmarx.running = false; + dmaengine_terminate_all(rxchan); + del_timer(&uap->dmarx.timer); + } else { + mod_timer(&uap->dmarx.timer, + jiffies + msecs_to_jiffies(uap->dmarx.poll_rate)); + } +} + static void pl011_dma_startup(struct uart_amba_port *uap) { int ret; @@ -928,6 +1042,16 @@ skip_rx: if (pl011_dma_rx_trigger_dma(uap)) dev_dbg(uap->port.dev, "could not trigger initial " "RX DMA job, fall back to interrupt mode\n"); + if (uap->dmarx.poll_rate) { + init_timer(&(uap->dmarx.timer)); + uap->dmarx.timer.function = pl011_dma_rx_poll; + uap->dmarx.timer.data = (unsigned long)uap; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + uap->dmarx.last_jiffies = jiffies; + } } } @@ -963,6 +1087,8 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap) /* Clean up the RX DMA */ pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a, DMA_FROM_DEVICE); pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_b, DMA_FROM_DEVICE); + if (uap->dmarx.poll_rate) + del_timer_sync(&uap->dmarx.timer); uap->using_rx_dma = false; } } @@ -977,10 +1103,9 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) return uap->using_rx_dma && uap->dmarx.running; } - #else /* Blank functions if the DMA engine is not available */ -static inline void pl011_dma_probe(struct uart_amba_port *uap) +static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap) { } @@ -1075,13 +1200,13 @@ static void pl011_enable_ms(struct uart_port *port) } static void pl011_rx_chars(struct uart_amba_port *uap) +__releases(&uap->port.lock) +__acquires(&uap->port.lock) { - struct tty_struct *tty = uap->port.state->port.tty; - pl011_fifo_to_tty(uap); spin_unlock(&uap->port.lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&uap->port.state->port); /* * If we were temporarily out of DMA mode for a while, * attempt to switch back to DMA mode again. @@ -1091,9 +1216,19 @@ static void pl011_rx_chars(struct uart_amba_port *uap) dev_dbg(uap->port.dev, "could not trigger RX DMA job " "fall back to interrupt mode again\n"); uap->im |= UART011_RXIM; - } else - uap->im &= ~UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + writew(uap->im, uap->port.membase + UART011_IMSC); + } else { +#ifdef CONFIG_DMA_ENGINE + /* Start Rx DMA poll */ + if (uap->dmarx.poll_rate) { + uap->dmarx.last_jiffies = jiffies; + uap->dmarx.last_residue = PL011_DMA_BUFFER_SIZE; + mod_timer(&uap->dmarx.timer, + jiffies + + msecs_to_jiffies(uap->dmarx.poll_rate)); + } +#endif + } } spin_lock(&uap->port.lock); } @@ -1167,7 +1302,6 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); - status = readw(uap->port.membase + UART011_MIS); if (status) { do { @@ -1343,12 +1477,7 @@ static int pl011_hwinit(struct uart_port *port) int retval; /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (retval) - dev_err(port->dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(port->dev); /* * Try to enable the clock producer. @@ -1370,10 +1499,10 @@ static int pl011_hwinit(struct uart_port *port) uap->im = readw(uap->port.membase + UART011_IMSC); writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC); - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->init) plat->init(); } @@ -1382,10 +1511,25 @@ static int pl011_hwinit(struct uart_port *port) return retval; } +static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) +{ + writew(lcr_h, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, uap->port.membase + uap->lcrh_tx); + } +} + static int pl011_startup(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; + unsigned int cr, lcr_h, fbrd, ibrd; int retval; retval = pl011_hwinit(port); @@ -1404,32 +1548,36 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); /* - * Provoke TX FIFO interrupt into asserting. + * Provoke TX FIFO interrupt into asserting. Taking care to preserve + * baud rate and data format specified by FBRD, IBRD and LCRH as the + * UART may already be in use as a console. */ + spin_lock_irq(&uap->port.lock); + + fbrd = readw(uap->port.membase + UART011_FBRD); + ibrd = readw(uap->port.membase + UART011_IBRD); + lcr_h = readw(uap->port.membase + uap->lcrh_rx); + cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + uap->lcrh_rx); - if (uap->lcrh_tx != uap->lcrh_rx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(0, uap->port.membase + uap->lcrh_tx); - } + pl011_write_lcr_h(uap, 0); writew(0, uap->port.membase + UART01x_DR); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); + writew(fbrd, uap->port.membase + UART011_FBRD); + writew(ibrd, uap->port.membase + UART011_IBRD); + pl011_write_lcr_h(uap, lcr_h); + /* restore RTS and DTR */ cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR); cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); + spin_unlock_irq(&uap->port.lock); + /* * initialise the old status of the modem signals */ @@ -1474,7 +1622,6 @@ static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int cr; - int retval; /* * disable all interrupts @@ -1499,11 +1646,13 @@ static void pl011_shutdown(struct uart_port *port) * it during startup(). */ uap->autorts = false; + spin_lock_irq(&uap->port.lock); cr = readw(uap->port.membase + UART011_CR); uap->old_cr = cr; cr &= UART011_CR_RTS | UART011_CR_DTR; cr |= UART01x_CR_UARTEN | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); + spin_unlock_irq(&uap->port.lock); /* * disable break condition and fifos @@ -1517,18 +1666,12 @@ static void pl011_shutdown(struct uart_port *port) */ clk_disable_unprepare(uap->clk); /* Optionally let pins go into sleep states */ - if (!IS_ERR(uap->pins_sleep)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep); - if (retval) - dev_err(port->dev, - "could not set pins to sleep state\n"); - } + pinctrl_pm_select_sleep_state(port->dev); - - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->exit) plat->exit(); } @@ -1554,6 +1697,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); +#ifdef CONFIG_DMA_ENGINE + /* + * Adjust RX DMA polling rate with baud rate if not specified. + */ + if (uap->dmarx.auto_poll_rate) + uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); +#endif if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); @@ -1594,7 +1744,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, port->read_status_mask = UART011_DR_OE | 255; if (termios->c_iflag & INPCK) port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= UART011_DR_BE; /* @@ -1666,17 +1816,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * UART011_FBRD & UART011_IBRD. * ----------^----------^----------^----------^----- */ - writew(lcr_h, port->membase + uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(lcr_h, port->membase + uap->lcrh_tx); - } + pl011_write_lcr_h(uap, lcr_h); writew(old_cr, port->membase + UART011_CR); spin_unlock_irqrestore(&port->lock, flags); @@ -1869,21 +2009,16 @@ static int __init pl011_console_setup(struct console *co, char *options) return -ENODEV; /* Allow pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - ret = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (ret) - dev_err(uap->port.dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(uap->port.dev); ret = clk_prepare(uap->clk); if (ret) return ret; - if (uap->port.dev->platform_data) { + if (dev_get_platdata(uap->port.dev)) { struct amba_pl011_data *plat; - plat = uap->port.dev->platform_data; + plat = dev_get_platdata(uap->port.dev); if (plat->init) plat->init(); } @@ -1910,6 +2045,35 @@ static struct console amba_console = { }; #define AMBA_CONSOLE (&amba_console) + +static void pl011_putc(struct uart_port *port, int c) +{ + while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) + ; + writeb(c, port->membase + UART01x_DR); + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) + ; +} + +static void pl011_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, pl011_putc); +} + +static int __init pl011_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = pl011_early_write; + return 0; +} +EARLYCON_DECLARE(pl011, pl011_early_console_setup); +OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); + #else #define AMBA_CONSOLE NULL #endif @@ -1988,21 +2152,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } - uap->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(uap->pinctrl)) { - ret = PTR_ERR(uap->pinctrl); - goto out; - } - uap->pins_default = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(uap->pins_default)) - dev_err(&dev->dev, "could not get default pinstate\n"); - - uap->pins_sleep = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_SLEEP); - if (IS_ERR(uap->pins_sleep)) - dev_dbg(&dev->dev, "could not get sleep pinstate\n"); - uap->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); @@ -2013,7 +2162,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->lcrh_rx = vendor->lcrh_rx; uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->fifosize; + uap->fifosize = vendor->get_fifosize(dev); uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; @@ -2023,7 +2172,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; - pl011_dma_probe(uap); + pl011_dma_probe(&dev->dev, uap); /* Ensure interrupts from this UART are masked and cleared */ writew(0, uap->port.membase + UART011_IMSC); @@ -2034,10 +2183,19 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) amba_ports[i] = uap; amba_set_drvdata(dev, uap); + + if (!amba_reg.state) { + ret = uart_register_driver(&amba_reg); + if (ret < 0) { + pr_err("Failed to register AMBA-PL011 driver\n"); + return ret; + } + } + ret = uart_add_one_port(&amba_reg, &uap->port); if (ret) { - amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; + uart_unregister_driver(&amba_reg); pl011_dma_remove(uap); } out: @@ -2047,24 +2205,27 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) static int pl011_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); + bool busy = false; int i; - amba_set_drvdata(dev, NULL); - uart_remove_one_port(&amba_reg, &uap->port); for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == uap) amba_ports[i] = NULL; + else if (amba_ports[i]) + busy = true; pl011_dma_remove(uap); + if (!busy) + uart_unregister_driver(&amba_reg); return 0; } -#ifdef CONFIG_PM -static int pl011_suspend(struct amba_device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pl011_suspend(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (!uap) return -EINVAL; @@ -2072,9 +2233,9 @@ static int pl011_suspend(struct amba_device *dev, pm_message_t state) return uart_suspend_port(&amba_reg, &uap->port); } -static int pl011_resume(struct amba_device *dev) +static int pl011_resume(struct device *dev) { - struct uart_amba_port *uap = amba_get_drvdata(dev); + struct uart_amba_port *uap = dev_get_drvdata(dev); if (!uap) return -EINVAL; @@ -2083,6 +2244,8 @@ static int pl011_resume(struct amba_device *dev) } #endif +static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); + static struct amba_id pl011_ids[] = { { .id = 0x00041011, @@ -2102,34 +2265,23 @@ MODULE_DEVICE_TABLE(amba, pl011_ids); static struct amba_driver pl011_driver = { .drv = { .name = "uart-pl011", + .pm = &pl011_dev_pm_ops, }, .id_table = pl011_ids, .probe = pl011_probe, .remove = pl011_remove, -#ifdef CONFIG_PM - .suspend = pl011_suspend, - .resume = pl011_resume, -#endif }; static int __init pl011_init(void) { - int ret; printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); - ret = uart_register_driver(&amba_reg); - if (ret == 0) { - ret = amba_driver_register(&pl011_driver); - if (ret) - uart_unregister_driver(&amba_reg); - } - return ret; + return amba_driver_register(&pl011_driver); } static void __exit pl011_exit(void) { amba_driver_unregister(&pl011_driver); - uart_unregister_driver(&amba_reg); } /* diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 59ae2b53e76..de11ab8ffd9 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -78,7 +78,6 @@ static void apbuart_enable_ms(struct uart_port *port) static void apbuart_rx_chars(struct uart_port *port) { - struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, rsr, flag; unsigned int max_chars = port->fifosize; @@ -126,7 +125,9 @@ static void apbuart_rx_chars(struct uart_port *port) status = UART_GET_STATUS(port); } - tty_flip_buffer_push(tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } static void apbuart_tx_chars(struct uart_port *port) diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 505c490c0b4..acd03af7cd5 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -17,6 +17,8 @@ #include <linux/sysrq.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> @@ -24,11 +26,11 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/clk.h> #include <asm/div64.h> #include <asm/mach-ath79/ar933x_uart.h> -#include <asm/mach-ath79/ar933x_uart_platform.h> #define DRIVER_NAME "ar933x-uart" @@ -47,8 +49,14 @@ struct ar933x_uart_port { unsigned int ier; /* shadow Interrupt Enable Register */ unsigned int min_baud; unsigned int max_baud; + struct clk *clk; }; +static inline bool ar933x_uart_console_enabled(void) +{ + return config_enabled(CONFIG_SERIAL_AR933X_CONSOLE); +} + static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up, int offset) { @@ -297,10 +305,9 @@ static void ar933x_uart_set_termios(struct uart_port *port, static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) { - struct tty_struct *tty; + struct tty_port *port = &up->port.state->port; int max_count = 256; - tty = tty_port_tty_get(&up->port.state->port); do { unsigned int rdata; unsigned char ch; @@ -313,11 +320,6 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) ar933x_uart_write(up, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); - if (!tty) { - /* discard the data if no tty available */ - continue; - } - up->port.icount.rx++; ch = rdata & AR933X_UART_DATA_TX_RX_MASK; @@ -325,13 +327,12 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) continue; if ((up->port.ignore_status_mask & AR933X_DUMMY_STATUS_RD) == 0) - tty_insert_flip_char(tty, ch, TTY_NORMAL); + tty_insert_flip_char(port, ch, TTY_NORMAL); } while (max_count-- > 0); - if (tty) { - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } + spin_unlock(&up->port.lock); + tty_flip_buffer_push(port); + spin_lock(&up->port.lock); } static void ar933x_uart_tx_chars(struct ar933x_uart_port *up) @@ -506,8 +507,6 @@ static struct uart_ops ar933x_uart_ops = { .verify_port = ar933x_uart_verify_port, }; -#ifdef CONFIG_SERIAL_AR933X_CONSOLE - static struct ar933x_uart_port * ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS]; @@ -606,80 +605,88 @@ static struct console ar933x_uart_console = { static void ar933x_uart_add_console_port(struct ar933x_uart_port *up) { + if (!ar933x_uart_console_enabled()) + return; + ar933x_console_ports[up->port.line] = up; } -#define AR933X_SERIAL_CONSOLE (&ar933x_uart_console) - -#else - -static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {} - -#define AR933X_SERIAL_CONSOLE NULL - -#endif /* CONFIG_SERIAL_AR933X_CONSOLE */ - static struct uart_driver ar933x_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttyATH", .nr = CONFIG_SERIAL_AR933X_NR_UARTS, - .cons = AR933X_SERIAL_CONSOLE, + .cons = NULL, /* filled in runtime */ }; static int ar933x_uart_probe(struct platform_device *pdev) { - struct ar933x_uart_platform_data *pdata; struct ar933x_uart_port *up; struct uart_port *port; struct resource *mem_res; struct resource *irq_res; + struct device_node *np; unsigned int baud; int id; int ret; - pdata = pdev->dev.platform_data; - if (!pdata) - return -EINVAL; - - id = pdev->id; - if (id == -1) - id = 0; + np = pdev->dev.of_node; + if (config_enabled(CONFIG_OF) && np) { + id = of_alias_get_id(np, "serial"); + if (id < 0) { + dev_err(&pdev->dev, "unable to get alias id, err=%d\n", + id); + return id; + } + } else { + id = pdev->id; + if (id == -1) + id = 0; + } if (id > CONFIG_SERIAL_AR933X_NR_UARTS) return -EINVAL; - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "no MEM resource\n"); - return -EINVAL; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { dev_err(&pdev->dev, "no IRQ resource\n"); return -EINVAL; } - up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL); + up = devm_kzalloc(&pdev->dev, sizeof(struct ar933x_uart_port), + GFP_KERNEL); if (!up) return -ENOMEM; + up->clk = devm_clk_get(&pdev->dev, "uart"); + if (IS_ERR(up->clk)) { + dev_err(&pdev->dev, "unable to get UART clock\n"); + return PTR_ERR(up->clk); + } + port = &up->port; - port->mapbase = mem_res->start; - port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE); - if (!port->membase) { - ret = -ENOMEM; - goto err_free_up; + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + port->membase = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + ret = clk_prepare_enable(up->clk); + if (ret) + return ret; + + port->uartclk = clk_get_rate(up->clk); + if (!port->uartclk) { + ret = -EINVAL; + goto err_disable_clk; } + port->mapbase = mem_res->start; port->line = id; port->irq = irq_res->start; port->dev = &pdev->dev; port->type = PORT_AR933X; port->iotype = UPIO_MEM32; - port->uartclk = pdata->uartclk; port->regshift = 2; port->fifosize = AR933X_UART_FIFO_SIZE; @@ -695,15 +702,13 @@ static int ar933x_uart_probe(struct platform_device *pdev) ret = uart_add_one_port(&ar933x_uart_driver, &up->port); if (ret) - goto err_unmap; + goto err_disable_clk; platform_set_drvdata(pdev, up); return 0; -err_unmap: - iounmap(up->port.membase); -err_free_up: - kfree(up); +err_disable_clk: + clk_disable_unprepare(up->clk); return ret; } @@ -712,23 +717,30 @@ static int ar933x_uart_remove(struct platform_device *pdev) struct ar933x_uart_port *up; up = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); if (up) { uart_remove_one_port(&ar933x_uart_driver, &up->port); - iounmap(up->port.membase); - kfree(up); + clk_disable_unprepare(up->clk); } return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ar933x_uart_of_ids[] = { + { .compatible = "qca,ar9330-uart" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ar933x_uart_of_ids); +#endif + static struct platform_driver ar933x_uart_platform_driver = { .probe = ar933x_uart_probe, .remove = ar933x_uart_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ar933x_uart_of_ids), }, }; @@ -736,7 +748,9 @@ static int __init ar933x_uart_init(void) { int ret; - ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS; + if (ar933x_uart_console_enabled()) + ar933x_uart_driver.cons = &ar933x_uart_console; + ret = uart_register_driver(&ar933x_uart_driver); if (ret) goto err_out; diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index 3e0b3fac6a0..008c223eaf2 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -37,6 +37,8 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_platform.h> /************************************* * ARC UART Hardware Specs @@ -160,7 +162,7 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) /* * Driver internal routine, used by both tty(serial core) as well as tx-isr * -Called under spinlock in either cases - * -also tty->stopped / tty->hw_stopped has already been checked + * -also tty->stopped has already been checked * = by uart_start( ) before calling us * = tx_ist checks that too before calling */ @@ -175,7 +177,7 @@ static void arc_serial_tx_chars(struct arc_uart_port *uart) uart->port.icount.tx++; uart->port.x_char = 0; sent = 1; - } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */ + } else if (!uart_circ_empty(xmit)) { ch = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; @@ -207,13 +209,9 @@ static void arc_serial_start_tx(struct uart_port *port) arc_serial_tx_chars(uart); } -static void arc_serial_rx_chars(struct arc_uart_port *uart) +static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status) { - struct tty_struct *tty = tty_port_tty_get(&uart->port.state->port); - unsigned int status, ch, flg = 0; - - if (!tty) - return; + unsigned int ch, flg = 0; /* * UART has 4 deep RX-FIFO. Driver's recongnition of this fact @@ -224,11 +222,11 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart) * before RX-EMPTY=0, implies some sort of buffering going on in the * controller, which is indeed the Rx-FIFO. */ - while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) { - - ch = UART_GET_DATA(uart); - uart->port.icount.rx++; - + do { + /* + * This could be an Rx Intr for err (no data), + * so check err and clear that Intr first + */ if (unlikely(status & (RXOERR | RXFERR))) { if (status & RXOERR) { uart->port.icount.overrun++; @@ -244,16 +242,19 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart) } else flg = TTY_NORMAL; - if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) - goto done; + if (status & RXEMPTY) + continue; - uart_insert_char(&uart->port, status, RXOERR, ch, flg); + ch = UART_GET_DATA(uart); + uart->port.icount.rx++; -done: - tty_flip_buffer_push(tty); - } + if (!(uart_handle_sysrq_char(&uart->port, ch))) + uart_insert_char(&uart->port, status, RXOERR, ch, flg); - tty_kref_put(tty); + spin_unlock(&uart->port.lock); + tty_flip_buffer_push(&uart->port.state->port); + spin_lock(&uart->port.lock); + } while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)); } /* @@ -296,11 +297,11 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) * notifications from the UART Controller. * To demultiplex between the two, we check the relevant bits */ - if ((status & RXIENB) && !(status & RXEMPTY)) { + if (status & RXIENB) { /* already in ISR, no need of xx_irqsave */ spin_lock(&uart->port.lock); - arc_serial_rx_chars(uart); + arc_serial_rx_chars(uart, status); spin_unlock(&uart->port.lock); } @@ -526,18 +527,37 @@ static struct uart_ops arc_serial_pops = { }; static int -arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) +arc_uart_init_one(struct platform_device *pdev, int dev_id) { struct resource *res, *res2; unsigned long *plat_data; + struct arc_uart_port *uart = &arc_uart_ports[dev_id]; - if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_ARC_NR_PORTS) { - dev_err(&pdev->dev, "Wrong uart platform device id.\n"); - return -ENOENT; - } + plat_data = dev_get_platdata(&pdev->dev); + if (!plat_data) + return -ENODEV; - plat_data = ((unsigned long *)(pdev->dev.platform_data)); - uart->baud = plat_data[0]; + uart->is_emulated = !!plat_data[0]; /* workaround ISS bug */ + + if (is_early_platform_device(pdev)) { + uart->port.uartclk = plat_data[1]; + uart->baud = plat_data[2]; + } else { + struct device_node *np = pdev->dev.of_node; + u32 val; + + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(&pdev->dev, "clock-frequency property NOTset\n"); + return -EINVAL; + } + uart->port.uartclk = val; + + if (of_property_read_u32(np, "current-speed", &val)) { + dev_err(&pdev->dev, "current-speed property NOT set\n"); + return -EINVAL; + } + uart->baud = val; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -557,10 +577,9 @@ arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) uart->port.dev = &pdev->dev; uart->port.iotype = UPIO_MEM; uart->port.flags = UPF_BOOT_AUTOCONF; - uart->port.line = pdev->id; + uart->port.line = dev_id; uart->port.ops = &arc_serial_pops; - uart->port.uartclk = plat_data[1]; uart->port.fifosize = ARC_UART_TX_FIFO_SIZE; /* @@ -569,9 +588,6 @@ arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) */ uart->port.ignore_status_mask = 0; - /* Real Hardware vs. emulated to work around a bug */ - uart->is_emulated = !!plat_data[2]; - return 0; } @@ -648,45 +664,50 @@ static __init void early_serial_write(struct console *con, const char *s, } } -static struct __initdata console arc_early_serial_console = { +static struct console arc_early_serial_console __initdata = { .name = "early_ARCuart", .write = early_serial_write, .flags = CON_PRINTBUFFER | CON_BOOT, .index = -1 }; -static int arc_serial_probe_earlyprintk(struct platform_device *pdev) +static int __init arc_serial_probe_earlyprintk(struct platform_device *pdev) { - arc_early_serial_console.index = pdev->id; + int dev_id = pdev->id < 0 ? 0 : pdev->id; + int rc; - arc_uart_init_one(pdev, &arc_uart_ports[pdev->id]); + arc_early_serial_console.index = dev_id; + + rc = arc_uart_init_one(pdev, dev_id); + if (rc) + panic("early console init failed\n"); arc_serial_console_setup(&arc_early_serial_console, NULL); register_console(&arc_early_serial_console); return 0; } -#else -static int arc_serial_probe_earlyprintk(struct platform_device *pdev) -{ - return -ENODEV; -} #endif /* CONFIG_SERIAL_ARC_CONSOLE */ static int arc_serial_probe(struct platform_device *pdev) { - struct arc_uart_port *uart; - int rc; + int rc, dev_id; + struct device_node *np = pdev->dev.of_node; + + /* no device tree device */ + if (!np) + return -ENODEV; - if (is_early_platform_device(pdev)) - return arc_serial_probe_earlyprintk(pdev); + dev_id = of_alias_get_id(np, "serial"); + if (dev_id < 0) + dev_id = 0; - uart = &arc_uart_ports[pdev->id]; - rc = arc_uart_init_one(pdev, uart); + rc = arc_uart_init_one(pdev, dev_id); if (rc) return rc; - return uart_add_one_port(&arc_uart_driver, &uart->port); + rc = uart_add_one_port(&arc_uart_driver, &arc_uart_ports[dev_id].port); + return rc; } static int arc_serial_remove(struct platform_device *pdev) @@ -695,16 +716,32 @@ static int arc_serial_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id arc_uart_dt_ids[] = { + { .compatible = "snps,arc-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, arc_uart_dt_ids); + static struct platform_driver arc_platform_driver = { .probe = arc_serial_probe, .remove = arc_serial_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = arc_uart_dt_ids, }, }; #ifdef CONFIG_SERIAL_ARC_CONSOLE + +static struct platform_driver early_arc_platform_driver __initdata = { + .probe = arc_serial_probe_earlyprintk, + .remove = arc_serial_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; /* * Register an early platform driver of "earlyprintk" class. * ARCH platform code installs the driver and probes the early devices @@ -712,7 +749,7 @@ static struct platform_driver arc_platform_driver = { * or it could be done independently, for all "earlyprintk" class drivers. * [see arch/arc/plat-arcfpga/platform.c] */ -early_platform_init("earlyprintk", &arc_platform_driver); +early_platform_init("earlyprintk", &early_arc_platform_driver); #endif /* CONFIG_SERIAL_ARC_CONSOLE */ @@ -741,6 +778,6 @@ module_init(arc_serial_init); module_exit(arc_serial_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Vineet Gupta"); MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 922e85aeb63..c4f75031410 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -35,21 +35,21 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> #include <linux/uaccess.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_data/atmel.h> +#include <linux/timer.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/err.h> +#include <linux/irq.h> #include <asm/io.h> #include <asm/ioctls.h> -#ifdef CONFIG_ARM -#include <mach/cpu.h> -#include <asm/gpio.h> -#endif - #define PDC_BUFFER_SIZE 512 /* Revisit: We should calculate this based on the actual port settings */ #define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ @@ -60,6 +60,8 @@ #include <linux/serial_core.h> +#include "serial_mctrl_gpio.h" + static void atmel_start_rx(struct uart_port *port); static void atmel_stop_rx(struct uart_port *port); @@ -98,6 +100,8 @@ static void atmel_stop_rx(struct uart_port *port); #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) +#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME) +#define UART_GET_IP_VERSION(port) __raw_readl((port)->membase + ATMEL_US_VERSION) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -113,9 +117,6 @@ static void atmel_stop_rx(struct uart_port *port); #define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) #define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR) -static int (*atmel_open_hook)(struct uart_port *); -static void (*atmel_close_hook)(struct uart_port *); - struct atmel_dma_buffer { unsigned char *buf; dma_addr_t dma_addr; @@ -140,13 +141,25 @@ struct atmel_uart_port { u32 backup_imr; /* IMR saved during suspend */ int break_active; /* break being received */ - short use_dma_rx; /* enable PDC receiver */ + bool use_dma_rx; /* enable DMA receiver */ + bool use_pdc_rx; /* enable PDC receiver */ short pdc_rx_idx; /* current PDC RX buffer */ struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ - short use_dma_tx; /* enable PDC transmitter */ + bool use_dma_tx; /* enable DMA transmitter */ + bool use_pdc_tx; /* enable PDC transmitter */ struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ + spinlock_t lock_tx; /* port lock */ + spinlock_t lock_rx; /* port lock */ + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx; + struct scatterlist sg_tx; + struct scatterlist sg_rx; struct tasklet_struct tasklet; unsigned int irq_status; unsigned int irq_status_prev; @@ -154,11 +167,22 @@ struct atmel_uart_port { struct circ_buf rx_ring; struct serial_rs485 rs485; /* rs485 settings */ + struct mctrl_gpios *gpios; + int gpio_irq[UART_GPIO_MAX]; unsigned int tx_done_mask; + bool ms_irq_enabled; + bool is_usart; /* usart or uart */ + struct timer_list uart_timer; /* uart timer */ + int (*prepare_rx)(struct uart_port *port); + int (*prepare_tx)(struct uart_port *port); + void (*schedule_rx)(struct uart_port *port); + void (*schedule_tx)(struct uart_port *port); + void (*release_rx)(struct uart_port *port); + void (*release_tx)(struct uart_port *port); }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; -static unsigned long atmel_ports_in_use; +static DECLARE_BITMAP(atmel_ports_in_use, ATMEL_MAX_UART); #ifdef SUPPORT_SYSRQ static struct console atmel_console; @@ -181,31 +205,89 @@ to_atmel_uart_port(struct uart_port *uart) } #ifdef CONFIG_SERIAL_ATMEL_PDC -static bool atmel_use_dma_rx(struct uart_port *port) +static bool atmel_use_pdc_rx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - return atmel_port->use_dma_rx; + return atmel_port->use_pdc_rx; } -static bool atmel_use_dma_tx(struct uart_port *port) +static bool atmel_use_pdc_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - return atmel_port->use_dma_tx; + return atmel_port->use_pdc_tx; } #else -static bool atmel_use_dma_rx(struct uart_port *port) +static bool atmel_use_pdc_rx(struct uart_port *port) { return false; } -static bool atmel_use_dma_tx(struct uart_port *port) +static bool atmel_use_pdc_tx(struct uart_port *port) { return false; } #endif +static bool atmel_use_dma_tx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_tx; +} + +static bool atmel_use_dma_rx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_rx; +} + +static unsigned int atmel_get_lines_status(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, ret = 0; + + status = UART_GET_CSR(port); + + mctrl_gpio_get(atmel_port->gpios, &ret); + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_CTS))) { + if (ret & TIOCM_CTS) + status &= ~ATMEL_US_CTS; + else + status |= ATMEL_US_CTS; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_DSR))) { + if (ret & TIOCM_DSR) + status &= ~ATMEL_US_DSR; + else + status |= ATMEL_US_DSR; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_RI))) { + if (ret & TIOCM_RI) + status &= ~ATMEL_US_RI; + else + status |= ATMEL_US_RI; + } + + if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, + UART_GPIO_DCD))) { + if (ret & TIOCM_CD) + status &= ~ATMEL_US_DCD; + else + status |= ATMEL_US_DCD; + } + + return status; +} + /* Enable or disable the rs485 support */ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) { @@ -233,7 +315,7 @@ void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) mode |= ATMEL_US_USMODE_RS485; } else { dev_dbg(port->dev, "Setting UART to RS232\n"); - if (atmel_use_dma_tx(port)) + if (atmel_use_pdc_tx(port)) atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; else @@ -265,21 +347,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) unsigned int mode; struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); -#ifdef CONFIG_ARCH_AT91RM9200 - if (cpu_is_at91rm9200()) { - /* - * AT91RM9200 Errata #39: RTS0 is not internally connected - * to PA21. We need to drive the pin manually. - */ - if (port->mapbase == AT91RM9200_BASE_US0) { - if (mctrl & TIOCM_RTS) - at91_set_gpio_value(AT91_PIN_PA21, 0); - else - at91_set_gpio_value(AT91_PIN_PA21, 1); - } - } -#endif - if (mctrl & TIOCM_RTS) control |= ATMEL_US_RTSEN; else @@ -292,6 +359,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) UART_PUT_CR(port, control); + mctrl_gpio_set(atmel_port->gpios, mctrl); + /* Local loopback mode? */ mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; if (mctrl & TIOCM_LOOP) @@ -319,7 +388,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) */ static u_int atmel_get_mctrl(struct uart_port *port) { - unsigned int status, ret = 0; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int ret = 0, status; status = UART_GET_CSR(port); @@ -335,7 +405,7 @@ static u_int atmel_get_mctrl(struct uart_port *port) if (!(status & ATMEL_US_RI)) ret |= TIOCM_RI; - return ret; + return mctrl_gpio_get(atmel_port->gpios, &ret); } /* @@ -345,7 +415,7 @@ static void atmel_stop_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); } @@ -364,7 +434,7 @@ static void atmel_start_tx(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ @@ -390,7 +460,7 @@ static void atmel_start_rx(struct uart_port *port) UART_PUT_CR(port, ATMEL_US_RXEN); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* enable PDC controller */ UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | port->read_status_mask); @@ -407,7 +477,7 @@ static void atmel_stop_rx(struct uart_port *port) { UART_PUT_CR(port, ATMEL_US_RXDIS); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | @@ -422,8 +492,38 @@ static void atmel_stop_rx(struct uart_port *port) */ static void atmel_enable_ms(struct uart_port *port) { - UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC - | ATMEL_US_DCDIC | ATMEL_US_CTSIC); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + uint32_t ier = 0; + + /* + * Interrupt should not be enabled twice + */ + if (atmel_port->ms_irq_enabled) + return; + + atmel_port->ms_irq_enabled = true; + + if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]); + else + ier |= ATMEL_US_CTSIC; + + if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); + else + ier |= ATMEL_US_DSRIC; + + if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); + else + ier |= ATMEL_US_RIIC; + + if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) + enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); + else + ier |= ATMEL_US_DCDIC; + + UART_PUT_IER(port, ier); } /* @@ -564,6 +664,369 @@ static void atmel_tx_chars(struct uart_port *port) UART_PUT_IER(port, atmel_port->tx_done_mask); } +static void atmel_complete_tx_dma(void *arg) +{ + struct atmel_uart_port *atmel_port = arg; + struct uart_port *port = &atmel_port->uart; + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (chan) + dmaengine_terminate_all(chan); + xmit->tail += sg_dma_len(&atmel_port->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&atmel_port->sg_tx); + + spin_lock_irq(&atmel_port->lock_tx); + async_tx_ack(atmel_port->desc_tx); + atmel_port->cookie_tx = -EINVAL; + atmel_port->desc_tx = NULL; + spin_unlock_irq(&atmel_port->lock_tx); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* Do we really need this? */ + if (!uart_circ_empty(xmit)) + tasklet_schedule(&atmel_port->tasklet); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void atmel_release_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_chan *chan = atmel_port->chan_tx; + + if (chan) { + dmaengine_terminate_all(chan); + dma_release_channel(chan); + dma_unmap_sg(port->dev, &atmel_port->sg_tx, 1, + DMA_MEM_TO_DEV); + } + + atmel_port->desc_tx = NULL; + atmel_port->chan_tx = NULL; + atmel_port->cookie_tx = -EINVAL; +} + +/* + * Called from tasklet with TXRDY interrupt is disabled. + */ +static void atmel_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg = &atmel_port->sg_tx; + + /* Make sure we have an idle channel */ + if (atmel_port->desc_tx != NULL) + return; + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + /* + * DMA is idle now. + * Port xmit buffer is already mapped, + * and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, + * we have to transmit till the end, and then the rest. + * Take the port lock to get a + * consistent xmit buffer state. + */ + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & + ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE); + BUG_ON(!sg_dma_len(sg)); + + desc = dmaengine_prep_slave_sg(chan, + sg, + 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!desc) { + dev_err(port->dev, "Failed to send via dma!\n"); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV); + + atmel_port->desc_tx = desc; + desc->callback = atmel_complete_tx_dma; + desc->callback_param = atmel_port; + atmel_port->cookie_tx = dmaengine_submit(desc); + + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static int atmel_prepare_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + dma_cap_mask_t mask; + struct dma_slave_config config; + int ret, nent; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx"); + if (atmel_port->chan_tx == NULL) + goto chan_err; + dev_info(port->dev, "using %s for tx DMA transfers\n", + dma_chan_name(atmel_port->chan_tx)); + + spin_lock_init(&atmel_port->lock_tx); + sg_init_table(&atmel_port->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&atmel_port->sg_tx, + virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, + (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, + &atmel_port->sg_tx, + 1, + DMA_MEM_TO_DEV); + + if (!nent) { + dev_dbg(port->dev, "need to release resource of dma\n"); + goto chan_err; + } else { + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&atmel_port->sg_tx), + port->state->xmit.buf, + sg_dma_address(&atmel_port->sg_tx)); + } + + /* Configure the slave DMA */ + memset(&config, 0, sizeof(config)); + config.direction = DMA_MEM_TO_DEV; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr = port->mapbase + ATMEL_US_THR; + + ret = dmaengine_device_control(atmel_port->chan_tx, + DMA_SLAVE_CONFIG, + (unsigned long)&config); + if (ret) { + dev_err(port->dev, "DMA tx slave configuration failed\n"); + goto chan_err; + } + + return 0; + +chan_err: + dev_err(port->dev, "TX channel not available, switch to pio\n"); + atmel_port->use_dma_tx = 0; + if (atmel_port->chan_tx) + atmel_release_tx_dma(port); + return -EINVAL; +} + +static void atmel_flip_buffer_rx_dma(struct uart_port *port, + char *buf, size_t count) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; + + dma_sync_sg_for_cpu(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + tty_insert_flip_string(tport, buf, count); + + dma_sync_sg_for_device(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); +} + +static void atmel_complete_rx_dma(void *arg) +{ + struct uart_port *port = arg; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + tasklet_schedule(&atmel_port->tasklet); +} + +static void atmel_release_rx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_chan *chan = atmel_port->chan_rx; + + if (chan) { + dmaengine_terminate_all(chan); + dma_release_channel(chan); + dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1, + DMA_DEV_TO_MEM); + } + + atmel_port->desc_rx = NULL; + atmel_port->chan_rx = NULL; + atmel_port->cookie_rx = -EINVAL; +} + +static void atmel_rx_from_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + struct dma_chan *chan = atmel_port->chan_rx; + struct dma_tx_state state; + enum dma_status dmastat; + size_t pending, count; + + + /* Reset the UART timeout early so that we don't miss one */ + UART_PUT_CR(port, ATMEL_US_STTTO); + dmastat = dmaengine_tx_status(chan, + atmel_port->cookie_rx, + &state); + /* Restart a new tasklet if DMA status is error */ + if (dmastat == DMA_ERROR) { + dev_dbg(port->dev, "Get residue error, restart tasklet\n"); + UART_PUT_IER(port, ATMEL_US_TIMEOUT); + tasklet_schedule(&atmel_port->tasklet); + return; + } + /* current transfer size should no larger than dma buffer */ + pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; + BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); + + /* + * This will take the chars we have so far, + * ring->head will record the transfer size, only new bytes come + * will insert into the framework. + */ + if (pending > ring->head) { + count = pending - ring->head; + + atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); + + ring->head += count; + if (ring->head == sg_dma_len(&atmel_port->sg_rx)) + ring->head = 0; + + port->icount.rx += count; + } + + UART_PUT_IER(port, ATMEL_US_TIMEOUT); +} + +static int atmel_prepare_rx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct dma_async_tx_descriptor *desc; + dma_cap_mask_t mask; + struct dma_slave_config config; + struct circ_buf *ring; + int ret, nent; + + ring = &atmel_port->rx_ring; + + dma_cap_zero(mask); + dma_cap_set(DMA_CYCLIC, mask); + + atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx"); + if (atmel_port->chan_rx == NULL) + goto chan_err; + dev_info(port->dev, "using %s for rx DMA transfers\n", + dma_chan_name(atmel_port->chan_rx)); + + spin_lock_init(&atmel_port->lock_rx); + sg_init_table(&atmel_port->sg_rx, 1); + /* UART circular rx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&atmel_port->sg_rx, + virt_to_page(ring->buf), + ATMEL_SERIAL_RINGSIZE, + (int)ring->buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, + &atmel_port->sg_rx, + 1, + DMA_DEV_TO_MEM); + + if (!nent) { + dev_dbg(port->dev, "need to release resource of dma\n"); + goto chan_err; + } else { + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&atmel_port->sg_rx), + ring->buf, + sg_dma_address(&atmel_port->sg_rx)); + } + + /* Configure the slave DMA */ + memset(&config, 0, sizeof(config)); + config.direction = DMA_DEV_TO_MEM; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = port->mapbase + ATMEL_US_RHR; + + ret = dmaengine_device_control(atmel_port->chan_rx, + DMA_SLAVE_CONFIG, + (unsigned long)&config); + if (ret) { + dev_err(port->dev, "DMA rx slave configuration failed\n"); + goto chan_err; + } + /* + * Prepare a cyclic dma transfer, assign 2 descriptors, + * each one is half ring buffer size + */ + desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx, + sg_dma_address(&atmel_port->sg_rx), + sg_dma_len(&atmel_port->sg_rx), + sg_dma_len(&atmel_port->sg_rx)/2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + desc->callback = atmel_complete_rx_dma; + desc->callback_param = port; + atmel_port->desc_rx = desc; + atmel_port->cookie_rx = dmaengine_submit(desc); + + return 0; + +chan_err: + dev_err(port->dev, "RX channel not available, switch to pio\n"); + atmel_port->use_dma_rx = 0; + if (atmel_port->chan_rx) + atmel_release_rx_dma(port); + return -EINVAL; +} + +static void atmel_uart_timer_callback(unsigned long data) +{ + struct uart_port *port = (void *)data; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + tasklet_schedule(&atmel_port->tasklet); + mod_timer(&atmel_port->uart_timer, jiffies + uart_poll_timeout(port)); +} + /* * receive interrupt handler. */ @@ -572,7 +1035,7 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_rx(port)) { + if (atmel_use_pdc_rx(port)) { /* * PDC receive. Just schedule the tasklet and let it * figure out the details. @@ -591,6 +1054,13 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending) atmel_pdc_rxerr(port, pending); } + if (atmel_use_dma_rx(port)) { + if (pending & ATMEL_US_TIMEOUT) { + UART_PUT_IDR(port, ATMEL_US_TIMEOUT); + tasklet_schedule(&atmel_port->tasklet); + } + } + /* Interrupt receive */ if (pending & ATMEL_US_RXRDY) atmel_rx_chars(port); @@ -642,11 +1112,31 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, static irqreturn_t atmel_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, pending, pass_counter = 0; + bool gpio_handled = false; do { - status = UART_GET_CSR(port); + status = atmel_get_lines_status(port); pending = status & UART_GET_IMR(port); + if (!gpio_handled) { + /* + * Dealing with GPIO interrupt + */ + if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) + pending |= ATMEL_US_CTSIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) + pending |= ATMEL_US_DSRIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) + pending |= ATMEL_US_RIIC; + + if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) + pending |= ATMEL_US_DCDIC; + + gpio_handled = true; + } if (!pending) break; @@ -658,10 +1148,21 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) return pass_counter ? IRQ_HANDLED : IRQ_NONE; } +static void atmel_release_tx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); +} + /* * Called from tasklet with ENDTX and TXBUFE interrupts disabled. */ -static void atmel_tx_dma(struct uart_port *port) +static void atmel_tx_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct circ_buf *xmit = &port->state->xmit; @@ -710,6 +1211,23 @@ static void atmel_tx_dma(struct uart_port *port) uart_write_wakeup(port); } +static int atmel_prepare_tx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + struct circ_buf *xmit = &port->state->xmit; + + pdc->buf = xmit->buf; + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + pdc->dma_size = UART_XMIT_SIZE; + pdc->ofs = 0; + + return 0; +} + static void atmel_rx_from_ring(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); @@ -774,14 +1292,30 @@ static void atmel_rx_from_ring(struct uart_port *port) * uart_start(), which takes the lock. */ spin_unlock(&port->lock); - tty_flip_buffer_push(port->state->port.tty); + tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); } -static void atmel_rx_from_dma(struct uart_port *port) +static void atmel_release_rx_pdc(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->state->port.tty; + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_FROM_DEVICE); + kfree(pdc->buf); + } +} + +static void atmel_rx_from_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_port *tport = &port->state->port; struct atmel_dma_buffer *pdc; int rx_idx = atmel_port->pdc_rx_idx; unsigned int head; @@ -820,7 +1354,8 @@ static void atmel_rx_from_dma(struct uart_port *port) */ count = head - tail; - tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); + tty_insert_flip_string(tport, pdc->buf + pdc->ofs, + count); dma_sync_single_for_device(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE); @@ -848,12 +1383,51 @@ static void atmel_rx_from_dma(struct uart_port *port) * uart_start(), which takes the lock. */ spin_unlock(&port->lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); spin_lock(&port->lock); UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); } +static int atmel_prepare_rx_pdc(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); + if (pdc->buf == NULL) { + if (i != 0) { + dma_unmap_single(port->dev, + atmel_port->pdc_rx[0].dma_addr, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + kfree(atmel_port->pdc_rx[0].buf); + } + atmel_port->use_pdc_rx = 0; + return -ENOMEM; + } + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + pdc->dma_size = PDC_BUFFER_SIZE; + pdc->ofs = 0; + } + + atmel_port->pdc_rx_idx = 0; + + UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); + UART_PUT_RCR(port, PDC_BUFFER_SIZE); + + UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); + UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + + return 0; +} + /* * tasklet handling tty stuff outside the interrupt handler. */ @@ -867,10 +1441,7 @@ static void atmel_tasklet_func(unsigned long data) /* The interrupt handler does not take the lock */ spin_lock(&port->lock); - if (atmel_use_dma_tx(port)) - atmel_tx_dma(port); - else - atmel_tx_chars(port); + atmel_port->schedule_tx(port); status = atmel_port->irq_status; status_change = status ^ atmel_port->irq_status_prev; @@ -892,19 +1463,204 @@ static void atmel_tasklet_func(unsigned long data) atmel_port->irq_status_prev = status; } - if (atmel_use_dma_rx(port)) - atmel_rx_from_dma(port); - else - atmel_rx_from_ring(port); + atmel_port->schedule_rx(port); spin_unlock(&port->lock); } +static int atmel_init_property(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); + + if (np) { + /* DMA/PDC usage specification */ + if (of_get_property(np, "atmel,use-dma-rx", NULL)) { + if (of_get_property(np, "dmas", NULL)) { + atmel_port->use_dma_rx = true; + atmel_port->use_pdc_rx = false; + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = true; + } + } else { + atmel_port->use_dma_rx = false; + atmel_port->use_pdc_rx = false; + } + + if (of_get_property(np, "atmel,use-dma-tx", NULL)) { + if (of_get_property(np, "dmas", NULL)) { + atmel_port->use_dma_tx = true; + atmel_port->use_pdc_tx = false; + } else { + atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = true; + } + } else { + atmel_port->use_dma_tx = false; + atmel_port->use_pdc_tx = false; + } + + } else { + atmel_port->use_pdc_rx = pdata->use_dma_rx; + atmel_port->use_pdc_tx = pdata->use_dma_tx; + atmel_port->use_dma_rx = false; + atmel_port->use_dma_tx = false; + } + + return 0; +} + +static void atmel_init_rs485(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); + + if (np) { + u32 rs485_delay[2]; + /* rs485 properties */ + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + struct serial_rs485 *rs485conf = &atmel_port->rs485; + + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + rs485conf->flags = 0; + + if (of_get_property(np, "rs485-rx-during-tx", NULL)) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_get_property(np, "linux,rs485-enabled-at-boot-time", + NULL)) + rs485conf->flags |= SER_RS485_ENABLED; + } + } else { + atmel_port->rs485 = pdata->rs485; + } + +} + +static void atmel_set_ops(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_rx(port)) { + atmel_port->prepare_rx = &atmel_prepare_rx_dma; + atmel_port->schedule_rx = &atmel_rx_from_dma; + atmel_port->release_rx = &atmel_release_rx_dma; + } else if (atmel_use_pdc_rx(port)) { + atmel_port->prepare_rx = &atmel_prepare_rx_pdc; + atmel_port->schedule_rx = &atmel_rx_from_pdc; + atmel_port->release_rx = &atmel_release_rx_pdc; + } else { + atmel_port->prepare_rx = NULL; + atmel_port->schedule_rx = &atmel_rx_from_ring; + atmel_port->release_rx = NULL; + } + + if (atmel_use_dma_tx(port)) { + atmel_port->prepare_tx = &atmel_prepare_tx_dma; + atmel_port->schedule_tx = &atmel_tx_dma; + atmel_port->release_tx = &atmel_release_tx_dma; + } else if (atmel_use_pdc_tx(port)) { + atmel_port->prepare_tx = &atmel_prepare_tx_pdc; + atmel_port->schedule_tx = &atmel_tx_pdc; + atmel_port->release_tx = &atmel_release_tx_pdc; + } else { + atmel_port->prepare_tx = NULL; + atmel_port->schedule_tx = &atmel_tx_chars; + atmel_port->release_tx = NULL; + } +} + +/* + * Get ip name usart or uart + */ +static void atmel_get_ip_name(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int name = UART_GET_IP_NAME(port); + u32 version; + int usart, uart; + /* usart and uart ascii */ + usart = 0x55534152; + uart = 0x44424755; + + atmel_port->is_usart = false; + + if (name == usart) { + dev_dbg(port->dev, "This is usart\n"); + atmel_port->is_usart = true; + } else if (name == uart) { + dev_dbg(port->dev, "This is uart\n"); + atmel_port->is_usart = false; + } else { + /* fallback for older SoCs: use version field */ + version = UART_GET_IP_VERSION(port); + switch (version) { + case 0x302: + case 0x10213: + dev_dbg(port->dev, "This version is usart\n"); + atmel_port->is_usart = true; + break; + case 0x203: + case 0x10202: + dev_dbg(port->dev, "This version is uart\n"); + atmel_port->is_usart = false; + break; + default: + dev_err(port->dev, "Not supported ip name nor version, set to uart\n"); + } + } +} + +static void atmel_free_gpio_irq(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + enum mctrl_gpio_idx i; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (atmel_port->gpio_irq[i] >= 0) + free_irq(atmel_port->gpio_irq[i], port); +} + +static int atmel_request_gpio_irq(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int *irq = atmel_port->gpio_irq; + enum mctrl_gpio_idx i; + int err = 0; + + for (i = 0; (i < UART_GPIO_MAX) && !err; i++) { + if (irq[i] < 0) + continue; + + irq_set_status_flags(irq[i], IRQ_NOAUTOEN); + err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH, + "atmel_serial", port); + if (err) + dev_err(port->dev, "atmel_startup - Can't get %d irq\n", + irq[i]); + } + + /* + * If something went wrong, rollback. + */ + while (err && (--i >= 0)) + if (irq[i] >= 0) + free_irq(irq[i], port); + + return err; +} + /* * Perform initialization and enable port for reception */ static int atmel_startup(struct uart_port *port) { + struct platform_device *pdev = to_platform_device(port->dev); struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); struct tty_struct *tty = port->state->port.tty; int retval; @@ -915,6 +1671,7 @@ static int atmel_startup(struct uart_port *port) * handle an unexpected interrupt */ UART_PUT_IDR(port, -1); + atmel_port->ms_irq_enabled = false; /* * Allocate the IRQ @@ -922,74 +1679,36 @@ static int atmel_startup(struct uart_port *port) retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, tty ? tty->name : "atmel_serial", port); if (retval) { - printk("atmel_serial: atmel_startup - Can't get irq\n"); + dev_err(port->dev, "atmel_startup - Can't get irq\n"); return retval; } /* - * Initialize DMA (if necessary) + * Get the GPIO lines IRQ */ - if (atmel_use_dma_rx(port)) { - int i; - - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; - - pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); - if (pdc->buf == NULL) { - if (i != 0) { - dma_unmap_single(port->dev, - atmel_port->pdc_rx[0].dma_addr, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - kfree(atmel_port->pdc_rx[0].buf); - } - free_irq(port->irq, port); - return -ENOMEM; - } - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - pdc->dma_size = PDC_BUFFER_SIZE; - pdc->ofs = 0; - } + retval = atmel_request_gpio_irq(port); + if (retval) + goto free_irq; - atmel_port->pdc_rx_idx = 0; - - UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); - UART_PUT_RCR(port, PDC_BUFFER_SIZE); - - UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); - UART_PUT_RNCR(port, PDC_BUFFER_SIZE); - } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - struct circ_buf *xmit = &port->state->xmit; + /* + * Initialize DMA (if necessary) + */ + atmel_init_property(atmel_port, pdev); - pdc->buf = xmit->buf; - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - pdc->dma_size = UART_XMIT_SIZE; - pdc->ofs = 0; + if (atmel_port->prepare_rx) { + retval = atmel_port->prepare_rx(port); + if (retval < 0) + atmel_set_ops(port); } - /* - * If there is a specific "open" function (to register - * control line interrupts) - */ - if (atmel_open_hook) { - retval = atmel_open_hook(port); - if (retval) { - free_irq(port->irq, port); - return retval; - } + if (atmel_port->prepare_tx) { + retval = atmel_port->prepare_tx(port); + if (retval < 0) + atmel_set_ops(port); } /* Save current CSR for comparison in atmel_tasklet_func() */ - atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status_prev = atmel_get_lines_status(port); atmel_port->irq_status = atmel_port->irq_status_prev; /* @@ -999,20 +1718,47 @@ static int atmel_startup(struct uart_port *port) /* enable xmit & rcvr */ UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); - if (atmel_use_dma_rx(port)) { - /* set UART timeout */ - UART_PUT_RTOR(port, PDC_RX_TIMEOUT); - UART_PUT_CR(port, ATMEL_US_STTTO); + setup_timer(&atmel_port->uart_timer, + atmel_uart_timer_callback, + (unsigned long)port); - UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + if (atmel_use_pdc_rx(port)) { + /* set UART timeout */ + if (!atmel_port->is_usart) { + mod_timer(&atmel_port->uart_timer, + jiffies + uart_poll_timeout(port)); + /* set USART timeout */ + } else { + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + } /* enable PDC controller */ UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else if (atmel_use_dma_rx(port)) { + /* set UART timeout */ + if (!atmel_port->is_usart) { + mod_timer(&atmel_port->uart_timer, + jiffies + uart_poll_timeout(port)); + /* set USART timeout */ + } else { + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_TIMEOUT); + } } else { /* enable receive only */ UART_PUT_IER(port, ATMEL_US_RXRDY); } return 0; + +free_irq: + free_irq(port->irq, port); + + return retval; } /* @@ -1021,54 +1767,51 @@ static int atmel_startup(struct uart_port *port) static void atmel_shutdown(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + /* - * Ensure everything is stopped. + * Prevent any tasklets being scheduled during + * cleanup */ - atmel_stop_rx(port); - atmel_stop_tx(port); + del_timer_sync(&atmel_port->uart_timer); /* - * Shut-down the DMA. + * Clear out any scheduled tasklets before + * we destroy the buffers */ - if (atmel_use_dma_rx(port)) { - int i; + tasklet_kill(&atmel_port->tasklet); - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + /* + * Ensure everything is stopped and + * disable all interrupts, port and break condition. + */ + atmel_stop_rx(port); + atmel_stop_tx(port); - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_FROM_DEVICE); - kfree(pdc->buf); - } - } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, -1); - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_TO_DEVICE); - } /* - * Disable all interrupts, port and break condition. + * Shut-down the DMA. */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - UART_PUT_IDR(port, -1); + if (atmel_port->release_rx) + atmel_port->release_rx(port); + if (atmel_port->release_tx) + atmel_port->release_tx(port); /* - * Free the interrupt + * Reset ring buffer pointers */ - free_irq(port->irq, port); + atmel_port->rx_ring.head = 0; + atmel_port->rx_ring.tail = 0; /* - * If there is a specific "close" function (to unregister - * control line interrupts) + * Free the interrupts */ - if (atmel_close_hook) - atmel_close_hook(port); + free_irq(port->irq, port); + atmel_free_gpio_irq(port); + + atmel_port->ms_irq_enabled = false; } /* @@ -1079,7 +1822,7 @@ static void atmel_flush_buffer(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { + if (atmel_use_pdc_tx(port)) { UART_PUT_TCR(port, 0); atmel_port->pdc_tx.ofs = 0; } @@ -1099,7 +1842,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Enable the peripheral clock for this serial port. * This is called on uart_open() or a resume event. */ - clk_enable(atmel_port->clk); + clk_prepare_enable(atmel_port->clk); /* re-enable interrupts if we disabled some on suspend */ UART_PUT_IER(port, atmel_port->backup_imr); @@ -1113,10 +1856,10 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state, * Disable the peripheral clock for this serial port. * This is called on uart_close() or a suspend event. */ - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); break; default: - printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); + dev_err(port->dev, "atmel_serial: unknown pm %d\n", state); } } @@ -1189,10 +1932,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, port->read_status_mask = ATMEL_US_OVRE; if (termios->c_iflag & INPCK) port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= ATMEL_US_RXBRK; - if (atmel_use_dma_rx(port)) + if (atmel_use_pdc_rx(port)) /* need to enable error interrupts */ UART_PUT_IER(port, port->read_status_mask); @@ -1231,13 +1974,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, mode &= ~ATMEL_US_USMODE; if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); if ((atmel_port->rs485.delay_rts_after_send) > 0) UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_after_send); mode |= ATMEL_US_USMODE_RS485; - } else { - dev_dbg(port->dev, "Setting UART to RS232\n"); } /* set the parity, stop bits and data size */ @@ -1422,54 +2162,20 @@ static struct uart_ops atmel_pops = { #endif }; -static void atmel_of_init_port(struct atmel_uart_port *atmel_port, - struct device_node *np) -{ - u32 rs485_delay[2]; - - /* DMA/PDC usage specification */ - if (of_get_property(np, "atmel,use-dma-rx", NULL)) - atmel_port->use_dma_rx = 1; - else - atmel_port->use_dma_rx = 0; - if (of_get_property(np, "atmel,use-dma-tx", NULL)) - atmel_port->use_dma_tx = 1; - else - atmel_port->use_dma_tx = 0; - - /* rs485 properties */ - if (of_property_read_u32_array(np, "rs485-rts-delay", - rs485_delay, 2) == 0) { - struct serial_rs485 *rs485conf = &atmel_port->rs485; - - rs485conf->delay_rts_before_send = rs485_delay[0]; - rs485conf->delay_rts_after_send = rs485_delay[1]; - rs485conf->flags = 0; - - if (of_get_property(np, "rs485-rx-during-tx", NULL)) - rs485conf->flags |= SER_RS485_RX_DURING_TX; - - if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL)) - rs485conf->flags |= SER_RS485_ENABLED; - } -} - /* * Configure the port from the platform device resource info. */ -static void atmel_init_port(struct atmel_uart_port *atmel_port, +static int atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { + int ret; struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *pdata = pdev->dev.platform_data; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); - if (pdev->dev.of_node) { - atmel_of_init_port(atmel_port, pdev->dev.of_node); - } else { - atmel_port->use_dma_rx = pdata->use_dma_rx; - atmel_port->use_dma_tx = pdata->use_dma_tx; - atmel_port->rs485 = pdata->rs485; - } + if (!atmel_init_property(atmel_port, pdev)) + atmel_set_ops(port); + + atmel_init_rs485(atmel_port, pdev); port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; @@ -1495,21 +2201,33 @@ static void atmel_init_port(struct atmel_uart_port *atmel_port, /* for console, the clock could already be configured */ if (!atmel_port->clk) { atmel_port->clk = clk_get(&pdev->dev, "usart"); - clk_enable(atmel_port->clk); + if (IS_ERR(atmel_port->clk)) { + ret = PTR_ERR(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } + ret = clk_prepare_enable(atmel_port->clk); + if (ret) { + clk_put(atmel_port->clk); + atmel_port->clk = NULL; + return ret; + } port->uartclk = clk_get_rate(atmel_port->clk); - clk_disable(atmel_port->clk); + clk_disable_unprepare(atmel_port->clk); /* only enable clock when USART is in use */ } /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ if (atmel_port->rs485.flags & SER_RS485_ENABLED) atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - else if (atmel_use_dma_tx(port)) { + else if (atmel_use_pdc_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; } else { atmel_port->tx_done_mask = ATMEL_US_TXRDY; } + + return 0; } struct platform_device *atmel_default_console_device; /* the serial console device */ @@ -1600,6 +2318,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud, static int __init atmel_console_setup(struct console *co, char *options) { + int ret; struct uart_port *port = &atmel_ports[co->index].uart; int baud = 115200; int bits = 8; @@ -1611,7 +2330,9 @@ static int __init atmel_console_setup(struct console *co, char *options) return -ENODEV; } - clk_enable(atmel_ports[co->index].clk); + ret = clk_prepare_enable(atmel_ports[co->index].clk); + if (ret) + return ret; UART_PUT_IDR(port, -1); UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); @@ -1644,9 +2365,10 @@ static struct console atmel_console = { */ static int __init atmel_console_init(void) { + int ret; if (atmel_default_console_device) { struct atmel_uart_data *pdata = - atmel_default_console_device->dev.platform_data; + dev_get_platdata(&atmel_default_console_device->dev); int id = pdata->num; struct atmel_uart_port *port = &atmel_ports[id]; @@ -1654,7 +2376,9 @@ static int __init atmel_console_init(void) port->uart.line = id; add_preferred_console(ATMEL_DEVICENAME, id, NULL); - atmel_init_port(port, atmel_default_console_device); + ret = atmel_init_port(port, atmel_default_console_device); + if (ret) + return ret; register_console(&atmel_console); } @@ -1748,14 +2472,33 @@ static int atmel_serial_resume(struct platform_device *pdev) #define atmel_serial_resume NULL #endif +static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) +{ + enum mctrl_gpio_idx i; + struct gpio_desc *gpiod; + + p->gpios = mctrl_gpio_init(dev, 0); + if (IS_ERR_OR_NULL(p->gpios)) + return -1; + + for (i = 0; i < UART_GPIO_MAX; i++) { + gpiod = mctrl_gpio_to_gpiod(p->gpios, i); + if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN)) + p->gpio_irq[i] = gpiod_to_irq(gpiod); + else + p->gpio_irq[i] = -EINVAL; + } + + return 0; +} + static int atmel_serial_probe(struct platform_device *pdev) { struct atmel_uart_port *port; struct device_node *np = pdev->dev.of_node; - struct atmel_uart_data *pdata = pdev->dev.platform_data; + struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); void *data; int ret = -ENODEV; - struct pinctrl *pinctrl; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); @@ -1768,15 +2511,14 @@ static int atmel_serial_probe(struct platform_device *pdev) if (ret < 0) /* port id not found in platform data nor device-tree aliases: * auto-enumerate it */ - ret = find_first_zero_bit(&atmel_ports_in_use, - sizeof(atmel_ports_in_use)); + ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART); - if (ret > ATMEL_MAX_UART) { + if (ret >= ATMEL_MAX_UART) { ret = -ENODEV; goto err; } - if (test_and_set_bit(ret, &atmel_ports_in_use)) { + if (test_and_set_bit(ret, atmel_ports_in_use)) { /* port already in use */ ret = -EBUSY; goto err; @@ -1786,15 +2528,16 @@ static int atmel_serial_probe(struct platform_device *pdev) port->backup_imr = 0; port->uart.line = ret; - atmel_init_port(port, pdev); + ret = atmel_init_gpios(port, &pdev->dev); + if (ret < 0) + dev_err(&pdev->dev, "%s", + "Failed to initialize GPIOs. The serial port may not work as expected"); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); + ret = atmel_init_port(port, pdev); + if (ret) goto err; - } - if (!atmel_use_dma_rx(&port->uart)) { + if (!atmel_use_pdc_rx(&port->uart)) { ret = -ENOMEM; data = kmalloc(sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); @@ -1812,9 +2555,9 @@ static int atmel_serial_probe(struct platform_device *pdev) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* * The serial core enabled the clock for us, so undo - * the clk_enable() in atmel_console_setup() + * the clk_prepare_enable() in atmel_console_setup() */ - clk_disable(port->clk); + clk_disable_unprepare(port->clk); } #endif @@ -1826,6 +2569,11 @@ static int atmel_serial_probe(struct platform_device *pdev) UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } + /* + * Get port name of usart or uart + */ + atmel_get_ip_name(&port->uart); + return 0; err_add_port: @@ -1846,17 +2594,17 @@ static int atmel_serial_remove(struct platform_device *pdev) struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); int ret = 0; + tasklet_kill(&atmel_port->tasklet); + device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); ret = uart_remove_one_port(&atmel_uart, port); - tasklet_kill(&atmel_port->tasklet); kfree(atmel_port->rx_ring.buf); /* "port" is allocated statically, so we shouldn't free it */ - clear_bit(port->line, &atmel_ports_in_use); + clear_bit(port->line, atmel_ports_in_use); clk_put(atmel_port->clk); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index c76a226080f..231519022b7 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -29,11 +29,9 @@ #include <linux/sysrq.h> #include <linux/serial.h> #include <linux/serial_core.h> - -#include <bcm63xx_clk.h> -#include <bcm63xx_irq.h> -#include <bcm63xx_regs.h> -#include <bcm63xx_io.h> +#include <linux/serial_bcm63xx.h> +#include <linux/io.h> +#include <linux/of.h> #define BCM63XX_NR_UARTS 2 @@ -82,13 +80,13 @@ static struct uart_port ports[BCM63XX_NR_UARTS]; static inline unsigned int bcm_uart_readl(struct uart_port *port, unsigned int offset) { - return bcm_readl(port->membase + offset); + return __raw_readl(port->membase + offset); } static inline void bcm_uart_writel(struct uart_port *port, unsigned int value, unsigned int offset) { - bcm_writel(value, port->membase + offset); + __raw_writel(value, port->membase + offset); } /* @@ -235,14 +233,13 @@ static const char *bcm_uart_type(struct uart_port *port) */ static void bcm_uart_do_rx(struct uart_port *port) { - struct tty_struct *tty; + struct tty_port *tty_port = &port->state->port; unsigned int max_count; /* limit number of char read in interrupt, should not be * higher than fifo size anyway since we're much faster than * serial port */ max_count = 32; - tty = port->state->port.tty; do { unsigned int iestat, c, cstat; char flag; @@ -261,7 +258,7 @@ static void bcm_uart_do_rx(struct uart_port *port) bcm_uart_writel(port, val, UART_CTL_REG); port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tty_port, 0, TTY_OVERRUN); } if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) @@ -300,11 +297,13 @@ static void bcm_uart_do_rx(struct uart_port *port) if ((cstat & port->ignore_status_mask) == 0) - tty_insert_flip_char(tty, c, flag); + tty_insert_flip_char(tty_port, c, flag); } while (--max_count); - tty_flip_buffer_push(tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(tty_port); + spin_lock(&port->lock); } /* @@ -568,7 +567,7 @@ static void bcm_uart_set_termios(struct uart_port *port, port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; port->read_status_mask |= UART_FIFO_PARERR_MASK; } - if (new->c_iflag & (BRKINT)) + if (new->c_iflag & (IGNBRK | BRKINT)) port->read_status_mask |= UART_FIFO_BRKDET_MASK; port->ignore_status_mask = 0; @@ -591,7 +590,7 @@ static int bcm_uart_request_port(struct uart_port *port) { unsigned int size; - size = RSET_UART_SIZE; + size = UART_REG_SIZE; if (!request_mem_region(port->mapbase, size, "bcm63xx")) { dev_err(port->dev, "Memory region busy\n"); return -EBUSY; @@ -611,7 +610,7 @@ static int bcm_uart_request_port(struct uart_port *port) */ static void bcm_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, RSET_UART_SIZE); + release_mem_region(port->mapbase, UART_REG_SIZE); iounmap(port->membase); } @@ -808,6 +807,9 @@ static int bcm_uart_probe(struct platform_device *pdev) struct clk *clk; int ret; + if (pdev->dev.of_node) + pdev->id = of_alias_get_id(pdev->dev.of_node, "uart"); + if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) return -EINVAL; @@ -854,12 +856,17 @@ static int bcm_uart_remove(struct platform_device *pdev) port = platform_get_drvdata(pdev); uart_remove_one_port(&bcm_uart_driver, port); - platform_set_drvdata(pdev, NULL); /* mark port as free */ ports[pdev->id].membase = 0; return 0; } +static const struct of_device_id bcm63xx_of_match[] = { + { .compatible = "brcm,bcm6345-uart" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm63xx_of_match); + /* * platform driver stuff */ @@ -869,6 +876,7 @@ static struct platform_driver bcm_uart_platform_driver = { .driver = { .owner = THIS_MODULE, .name = "bcm63xx_uart", + .of_match_table = bcm63xx_of_match, }, }; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index f5d117379b6..4f229703328 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -149,7 +149,7 @@ static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate) static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) { struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; + struct tty_port *port = &up->port.state->port; unsigned int ch; spin_lock(&up->port.lock); @@ -159,12 +159,14 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) up->port.icount.rx++; if (!uart_handle_sysrq_char(&up->port, ch)) - tty_insert_flip_char(tty, ch, TTY_NORMAL); + tty_insert_flip_char(port, ch, TTY_NORMAL); } - tty_flip_buffer_push(tty); spin_unlock(&up->port.lock); + /* XXX this won't deadlock with lowlat? */ + tty_flip_buffer_push(port); + return IRQ_HANDLED; } @@ -182,7 +184,6 @@ static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) { struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; unsigned int stat = SPORT_GET_STAT(up); spin_lock(&up->port.lock); @@ -190,7 +191,7 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) /* Overflow in RX FIFO */ if (stat & ROVF) { up->port.icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(&up->port.state->port, 0, TTY_OVERRUN); SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */ } /* These should not happen */ @@ -205,6 +206,8 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) SSYNC(); spin_unlock(&up->port.lock); + /* XXX we don't push the overrun bit to TTY? */ + return IRQ_HANDLED; } @@ -763,8 +766,8 @@ static int sport_uart_probe(struct platform_device *pdev) return -ENOMEM; } - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRV_NAME); + ret = peripheral_request_list(dev_get_platdata(&pdev->dev), + DRV_NAME); if (ret) { dev_err(&pdev->dev, "Fail to request SPORT peripherals\n"); @@ -840,8 +843,7 @@ static int sport_uart_probe(struct platform_device *pdev) out_error_unmap: iounmap(sport->port.membase); out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + peripheral_free_list(dev_get_platdata(&pdev->dev)); out_error_free_mem: kfree(sport); bfin_sport_uart_ports[pdev->id] = NULL; @@ -860,8 +862,7 @@ static int sport_uart_remove(struct platform_device *pdev) if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); iounmap(sport->port.membase); - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + peripheral_free_list(dev_get_platdata(&pdev->dev)); kfree(sport); bfin_sport_uart_ports[pdev->id] = NULL; } @@ -881,7 +882,7 @@ static struct platform_driver sport_uart_driver = { }; #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE -static __initdata struct early_platform_driver early_sport_uart_driver = { +static struct early_platform_driver early_sport_uart_driver __initdata = { .class_str = CLASS_BFIN_SPORT_CONSOLE, .pdrv = &sport_uart_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index 2e2b2c1cb72..ac86a20992e 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -41,10 +41,6 @@ # undef CONFIG_EARLY_PRINTK #endif -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - /* UART name and device definitions */ #define BFIN_SERIAL_DEV_NAME "ttyBF" #define BFIN_SERIAL_MAJOR 204 @@ -223,7 +219,6 @@ static void bfin_serial_enable_ms(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_PIO static void bfin_serial_rx_chars(struct bfin_serial_port *uart) { - struct tty_struct *tty = NULL; unsigned int status, ch, flg; static struct timeval anomaly_start = { .tv_sec = 0 }; @@ -242,11 +237,9 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) return; } - if (!uart->port.state || !uart->port.state->port.tty) + if (!uart->port.state) return; #endif - tty = uart->port.state->port.tty; - if (ANOMALY_05000363) { /* The BF533 (and BF561) family of processors have a nice anomaly * where they continuously generate characters for a "single" break. @@ -325,7 +318,7 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) uart_insert_char(&uart->port, status, OE, ch, flg); ignore_char: - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&uart->port.state->port); } static void bfin_serial_tx_chars(struct bfin_serial_port *uart) @@ -426,7 +419,6 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) { - struct tty_struct *tty = uart->port.state->port.tty; int i, flg, status; status = UART_GET_LSR(uart); @@ -471,7 +463,7 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) } dma_ignore_char: - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&uart->port.state->port); } void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) @@ -688,7 +680,7 @@ static int bfin_serial_startup(struct uart_port *port) default: uart_dma_ch_rx = uart_dma_ch_tx = 0; break; - }; + } if (uart_dma_ch_rx && request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { @@ -734,7 +726,7 @@ static int bfin_serial_startup(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0) { if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int, - IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { + 0, "BFIN_UART_MODEM_STATUS", uart)) { uart->cts_pin = -1; dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n"); } @@ -773,7 +765,7 @@ static void bfin_serial_shutdown(struct uart_port *port) break; default: break; - }; + } #endif free_irq(uart->rx_irq, uart); free_irq(uart->tx_irq, uart); @@ -841,7 +833,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, port->read_status_mask = OE; if (termios->c_iflag & INPCK) port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= BI; /* @@ -1015,24 +1007,6 @@ static int bfin_serial_poll_get_char(struct uart_port *port) } #endif -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) -static void bfin_kgdboc_port_shutdown(struct uart_port *port) -{ - if (kgdboc_break_enabled) { - kgdboc_break_enabled = 0; - bfin_serial_shutdown(port); - } -} - -static int bfin_kgdboc_port_startup(struct uart_port *port) -{ - kgdboc_port_line = port->line; - kgdboc_break_enabled = !bfin_serial_startup(port); - return 0; -} -#endif - static struct uart_ops bfin_serial_pops = { .tx_empty = bfin_serial_tx_empty, .set_mctrl = bfin_serial_set_mctrl, @@ -1051,11 +1025,6 @@ static struct uart_ops bfin_serial_pops = { .request_port = bfin_serial_request_port, .config_port = bfin_serial_config_port, .verify_port = bfin_serial_verify_port, -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - .kgdboc_port_startup = bfin_kgdboc_port_startup, - .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, -#endif #ifdef CONFIG_CONSOLE_POLL .poll_put_char = bfin_serial_poll_put_char, .poll_get_char = bfin_serial_poll_get_char, @@ -1207,7 +1176,7 @@ bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int c * don't let the common infrastructure play with things. (see calls to setup * & earlysetup in ./kernel/printk.c:register_console() */ -static struct __initdata console bfin_early_serial_console = { +static struct console bfin_early_serial_console __initdata = { .name = "early_BFuart", .write = bfin_earlyprintk_console_write, .device = uart_console_device, @@ -1271,7 +1240,8 @@ static int bfin_serial_probe(struct platform_device *pdev) */ #endif ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + dev_get_platdata(&pdev->dev), + DRIVER_NAME); if (ret) { dev_err(&pdev->dev, "fail to request bfin serial peripherals\n"); @@ -1388,8 +1358,7 @@ static int bfin_serial_probe(struct platform_device *pdev) out_error_unmap: iounmap(uart->port.membase); out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + peripheral_free_list(dev_get_platdata(&pdev->dev)); out_error_free_mem: kfree(uart); bfin_serial_ports[pdev->id] = NULL; @@ -1407,8 +1376,7 @@ static int bfin_serial_remove(struct platform_device *pdev) if (uart) { uart_remove_one_port(&bfin_serial_reg, &uart->port); iounmap(uart->port.membase); - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + peripheral_free_list(dev_get_platdata(&pdev->dev)); kfree(uart); bfin_serial_ports[pdev->id] = NULL; } @@ -1428,7 +1396,7 @@ static struct platform_driver bfin_serial_driver = { }; #if defined(CONFIG_SERIAL_BFIN_CONSOLE) -static __initdata struct early_platform_driver early_bfin_serial_driver = { +static struct early_platform_driver early_bfin_serial_driver __initdata = { .class_str = CLASS_BFIN_CONSOLE, .pdrv = &bfin_serial_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, @@ -1462,8 +1430,8 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) return -ENOENT; } - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + ret = peripheral_request_list(dev_get_platdata(&pdev->dev), + DRIVER_NAME); if (ret) { dev_err(&pdev->dev, "fail to request bfin serial peripherals\n"); @@ -1493,8 +1461,7 @@ static int bfin_earlyprintk_probe(struct platform_device *pdev) return 0; out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); + peripheral_free_list(dev_get_platdata(&pdev->dev)); return ret; } @@ -1507,7 +1474,7 @@ static struct platform_driver bfin_earlyprintk_driver = { }, }; -static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { +static struct early_platform_driver early_bfin_earlyprintk_driver __initdata = { .class_str = CLASS_BFIN_EARLYPRINTK, .pdrv = &bfin_earlyprintk_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index 3fd2526d121..14aaea0d413 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -21,44 +21,66 @@ #include <linux/console.h> #include <linux/serial_core.h> #include <linux/serial.h> -#include <linux/io.h> #include <linux/clk.h> +#include <linux/io.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/ioport.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/regmap.h> -#include <mach/hardware.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/clps711x.h> -#define UART_CLPS711X_NAME "uart-clps711x" +#define UART_CLPS711X_DEVNAME "ttyCL" #define UART_CLPS711X_NR 2 #define UART_CLPS711X_MAJOR 204 #define UART_CLPS711X_MINOR 40 -#define UBRLCR(port) ((port)->line ? UBRLCR2 : UBRLCR1) -#define UARTDR(port) ((port)->line ? UARTDR2 : UARTDR1) -#define SYSFLG(port) ((port)->line ? SYSFLG2 : SYSFLG1) -#define SYSCON(port) ((port)->line ? SYSCON2 : SYSCON1) -#define TX_IRQ(port) ((port)->line ? IRQ_UTXINT2 : IRQ_UTXINT1) -#define RX_IRQ(port) ((port)->line ? IRQ_URXINT2 : IRQ_URXINT1) +#define UARTDR_OFFSET (0x00) +#define UBRLCR_OFFSET (0x40) + +#define UARTDR_FRMERR (1 << 8) +#define UARTDR_PARERR (1 << 9) +#define UARTDR_OVERR (1 << 10) + +#define UBRLCR_BAUD_MASK ((1 << 12) - 1) +#define UBRLCR_BREAK (1 << 12) +#define UBRLCR_PRTEN (1 << 13) +#define UBRLCR_EVENPRT (1 << 14) +#define UBRLCR_XSTOP (1 << 15) +#define UBRLCR_FIFOEN (1 << 16) +#define UBRLCR_WRDLEN5 (0 << 17) +#define UBRLCR_WRDLEN6 (1 << 17) +#define UBRLCR_WRDLEN7 (2 << 17) +#define UBRLCR_WRDLEN8 (3 << 17) +#define UBRLCR_WRDLEN_MASK (3 << 17) struct clps711x_port { - struct uart_driver uart; - struct clk *uart_clk; - struct uart_port port[UART_CLPS711X_NR]; - int tx_enabled[UART_CLPS711X_NR]; -#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE - struct console console; -#endif + struct uart_port port; + unsigned int tx_enabled; + int rx_irq; + struct regmap *syscon; + bool use_ms; +}; + +static struct uart_driver clps711x_uart = { + .owner = THIS_MODULE, + .driver_name = UART_CLPS711X_DEVNAME, + .dev_name = UART_CLPS711X_DEVNAME, + .major = UART_CLPS711X_MAJOR, + .minor = UART_CLPS711X_MINOR, + .nr = UART_CLPS711X_NR, }; static void uart_clps711x_stop_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - if (s->tx_enabled[port->line]) { - disable_irq(TX_IRQ(port)); - s->tx_enabled[port->line] = 0; + if (s->tx_enabled) { + disable_irq(port->irq); + s->tx_enabled = 0; } } @@ -66,37 +88,27 @@ static void uart_clps711x_start_tx(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - if (!s->tx_enabled[port->line]) { - enable_irq(TX_IRQ(port)); - s->tx_enabled[port->line] = 1; + if (!s->tx_enabled) { + s->tx_enabled = 1; + enable_irq(port->irq); } } -static void uart_clps711x_stop_rx(struct uart_port *port) -{ - disable_irq(RX_IRQ(port)); -} - -static void uart_clps711x_enable_ms(struct uart_port *port) -{ - /* Do nothing */ -} - static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - unsigned int status, ch, flg; - - if (!tty) - return IRQ_HANDLED; + struct clps711x_port *s = dev_get_drvdata(port->dev); + unsigned int status, flg; + u16 ch; for (;;) { - status = clps_readl(SYSFLG(port)); - if (status & SYSFLG_URXFE) + u32 sysflg = 0; + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG_URXFE) break; - ch = clps_readw(UARTDR(port)); + ch = readw(port->membase + UARTDR_OFFSET); status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR); ch &= 0xff; @@ -130,9 +142,7 @@ static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id) uart_insert_char(port, status, UARTDR_OVERR, ch, flg); } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); return IRQ_HANDLED; } @@ -144,23 +154,29 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { - clps_writew(port->x_char, UARTDR(port)); + writew(port->x_char, port->membase + UARTDR_OFFSET); port->icount.tx++; port->x_char = 0; return IRQ_HANDLED; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - disable_irq_nosync(TX_IRQ(port)); - s->tx_enabled[port->line] = 0; + if (s->tx_enabled) { + disable_irq_nosync(port->irq); + s->tx_enabled = 0; + } return IRQ_HANDLED; } while (!uart_circ_empty(xmit)) { - clps_writew(xmit->buf[xmit->tail], UARTDR(port)); + u32 sysflg = 0; + + writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; - if (clps_readl(SYSFLG(port) & SYSFLG_UTXFF)) + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG_UTXFF) break; } @@ -172,20 +188,28 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id) static unsigned int uart_clps711x_tx_empty(struct uart_port *port) { - return (clps_readl(SYSFLG(port) & SYSFLG_UBUSY)) ? 0 : TIOCSER_TEMT; + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg = 0; + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + + return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT; } static unsigned int uart_clps711x_get_mctrl(struct uart_port *port) { - unsigned int status, result = 0; + struct clps711x_port *s = dev_get_drvdata(port->dev); + unsigned int result = 0; - if (port->line == 0) { - status = clps_readl(SYSFLG1); - if (status & SYSFLG1_DCD) + if (s->use_ms) { + u32 sysflg = 0; + + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + if (sysflg & SYSFLG1_DCD) result |= TIOCM_CAR; - if (status & SYSFLG1_DSR) + if (sysflg & SYSFLG1_DSR) result |= TIOCM_DSR; - if (status & SYSFLG1_CTS) + if (sysflg & SYSFLG1_CTS) result |= TIOCM_CTS; } else result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; @@ -200,65 +224,53 @@ static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl) static void uart_clps711x_break_ctl(struct uart_port *port, int break_state) { - unsigned long flags; unsigned int ubrlcr; - spin_lock_irqsave(&port->lock, flags); - - ubrlcr = clps_readl(UBRLCR(port)); + ubrlcr = readl(port->membase + UBRLCR_OFFSET); if (break_state) ubrlcr |= UBRLCR_BREAK; else ubrlcr &= ~UBRLCR_BREAK; - clps_writel(ubrlcr, UBRLCR(port)); + writel(ubrlcr, port->membase + UBRLCR_OFFSET); +} - spin_unlock_irqrestore(&port->lock, flags); +static void uart_clps711x_set_ldisc(struct uart_port *port, int ld) +{ + if (!port->line) { + struct clps711x_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN, + (ld == N_IRDA) ? SYSCON1_SIREN : 0); + } } static int uart_clps711x_startup(struct uart_port *port) { struct clps711x_port *s = dev_get_drvdata(port->dev); - int ret; - - s->tx_enabled[port->line] = 1; - /* Allocate the IRQs */ - ret = devm_request_irq(port->dev, TX_IRQ(port), uart_clps711x_int_tx, - 0, UART_CLPS711X_NAME " TX", port); - if (ret) - return ret; - - ret = devm_request_irq(port->dev, RX_IRQ(port), uart_clps711x_int_rx, - 0, UART_CLPS711X_NAME " RX", port); - if (ret) { - devm_free_irq(port->dev, TX_IRQ(port), port); - return ret; - } /* Disable break */ - clps_writel(clps_readl(UBRLCR(port)) & ~UBRLCR_BREAK, UBRLCR(port)); + writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK, + port->membase + UBRLCR_OFFSET); /* Enable the port */ - clps_writel(clps_readl(SYSCON(port)) | SYSCON_UARTEN, SYSCON(port)); - - return 0; + return regmap_update_bits(s->syscon, SYSCON_OFFSET, + SYSCON_UARTEN, SYSCON_UARTEN); } static void uart_clps711x_shutdown(struct uart_port *port) { - /* Free the interrupts */ - devm_free_irq(port->dev, TX_IRQ(port), port); - devm_free_irq(port->dev, RX_IRQ(port), port); + struct clps711x_port *s = dev_get_drvdata(port->dev); /* Disable the port */ - clps_writel(clps_readl(SYSCON(port)) & ~SYSCON_UARTEN, SYSCON(port)); + regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0); } static void uart_clps711x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - unsigned int ubrlcr, baud, quot; - unsigned long flags; + u32 ubrlcr; + unsigned int baud, quot; /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -297,8 +309,6 @@ static void uart_clps711x_set_termios(struct uart_port *port, /* Enable FIFO */ ubrlcr |= UBRLCR_FIFOEN; - spin_lock_irqsave(&port->lock, flags); - /* Set read status mask */ port->read_status_mask = UARTDR_OVERR; if (termios->c_iflag & INPCK) @@ -312,9 +322,7 @@ static void uart_clps711x_set_termios(struct uart_port *port, uart_update_timeout(port, termios->c_cflag, baud); - clps_writel(ubrlcr | (quot - 1), UBRLCR(port)); - - spin_unlock_irqrestore(&port->lock, flags); + writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET); } static const char *uart_clps711x_type(struct uart_port *port) @@ -328,14 +336,12 @@ static void uart_clps711x_config_port(struct uart_port *port, int flags) port->type = PORT_CLPS711X; } -static void uart_clps711x_release_port(struct uart_port *port) +static void uart_clps711x_nop_void(struct uart_port *port) { - /* Do nothing */ } -static int uart_clps711x_request_port(struct uart_port *port) +static int uart_clps711x_nop_int(struct uart_port *port) { - /* Do nothing */ return 0; } @@ -345,148 +351,190 @@ static const struct uart_ops uart_clps711x_ops = { .get_mctrl = uart_clps711x_get_mctrl, .stop_tx = uart_clps711x_stop_tx, .start_tx = uart_clps711x_start_tx, - .stop_rx = uart_clps711x_stop_rx, - .enable_ms = uart_clps711x_enable_ms, + .stop_rx = uart_clps711x_nop_void, + .enable_ms = uart_clps711x_nop_void, .break_ctl = uart_clps711x_break_ctl, + .set_ldisc = uart_clps711x_set_ldisc, .startup = uart_clps711x_startup, .shutdown = uart_clps711x_shutdown, .set_termios = uart_clps711x_set_termios, .type = uart_clps711x_type, .config_port = uart_clps711x_config_port, - .release_port = uart_clps711x_release_port, - .request_port = uart_clps711x_request_port, + .release_port = uart_clps711x_nop_void, + .request_port = uart_clps711x_nop_int, }; #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE static void uart_clps711x_console_putchar(struct uart_port *port, int ch) { - while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) - barrier(); + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg = 0; + + /* Wait for FIFO is not full */ + do { + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + } while (sysflg & SYSFLG_UTXFF); - clps_writew(ch, UARTDR(port)); + writew(ch, port->membase + UARTDR_OFFSET); } static void uart_clps711x_console_write(struct console *co, const char *c, unsigned n) { - struct clps711x_port *s = (struct clps711x_port *)co->data; - struct uart_port *port = &s->port[co->index]; - u32 syscon; - - /* Ensure that the port is enabled */ - syscon = clps_readl(SYSCON(port)); - clps_writel(syscon | SYSCON_UARTEN, SYSCON(port)); + struct uart_port *port = clps711x_uart.state[co->index].uart_port; + struct clps711x_port *s = dev_get_drvdata(port->dev); + u32 sysflg = 0; uart_console_write(port, c, n, uart_clps711x_console_putchar); /* Wait for transmitter to become empty */ - while (clps_readl(SYSFLG(port)) & SYSFLG_UBUSY) - barrier(); - - /* Restore the uart state */ - clps_writel(syscon, SYSCON(port)); + do { + regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg); + } while (sysflg & SYSFLG_UBUSY); } -static void uart_clps711x_console_get_options(struct uart_port *port, - int *baud, int *parity, - int *bits) +static int uart_clps711x_console_setup(struct console *co, char *options) { - if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { - unsigned int ubrlcr, quot; + int baud = 38400, bits = 8, parity = 'n', flow = 'n'; + int ret, index = co->index; + struct clps711x_port *s; + struct uart_port *port; + unsigned int quot; + u32 ubrlcr; - ubrlcr = clps_readl(UBRLCR(port)); + if (index < 0 || index >= UART_CLPS711X_NR) + return -EINVAL; - *parity = 'n'; - if (ubrlcr & UBRLCR_PRTEN) { - if (ubrlcr & UBRLCR_EVENPRT) - *parity = 'e'; - else - *parity = 'o'; - } + port = clps711x_uart.state[index].uart_port; + if (!port) + return -ENODEV; - if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) - *bits = 7; - else - *bits = 8; + s = dev_get_drvdata(port->dev); - quot = ubrlcr & UBRLCR_BAUD_MASK; - *baud = port->uartclk / (16 * (quot + 1)); - } -} + if (!options) { + u32 syscon = 0; -static int uart_clps711x_console_setup(struct console *co, char *options) -{ - int baud = 38400, bits = 8, parity = 'n', flow = 'n'; - struct clps711x_port *s = (struct clps711x_port *)co->data; - struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0]; + regmap_read(s->syscon, SYSCON_OFFSET, &syscon); + if (syscon & SYSCON_UARTEN) { + ubrlcr = readl(port->membase + UBRLCR_OFFSET); + + if (ubrlcr & UBRLCR_PRTEN) { + if (ubrlcr & UBRLCR_EVENPRT) + parity = 'e'; + else + parity = 'o'; + } + + if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) + bits = 7; - if (options) + quot = ubrlcr & UBRLCR_BAUD_MASK; + baud = port->uartclk / (16 * (quot + 1)); + } + } else uart_parse_options(options, &baud, &parity, &bits, &flow); - else - uart_clps711x_console_get_options(port, &baud, &parity, &bits); - return uart_set_options(port, co, baud, parity, bits, flow); + ret = uart_set_options(port, co, baud, parity, bits, flow); + if (ret) + return ret; + + return regmap_update_bits(s->syscon, SYSCON_OFFSET, + SYSCON_UARTEN, SYSCON_UARTEN); } + +static struct console clps711x_console = { + .name = UART_CLPS711X_DEVNAME, + .device = uart_console_device, + .write = uart_clps711x_console_write, + .setup = uart_clps711x_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; #endif static int uart_clps711x_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; + int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id; struct clps711x_port *s; - int ret, i; + struct resource *res; + struct clk *uart_clk; + + if (index < 0 || index >= UART_CLPS711X_NR) + return -EINVAL; - s = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_port), GFP_KERNEL); - if (!s) { - dev_err(&pdev->dev, "Error allocating port structure\n"); + s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL); + if (!s) return -ENOMEM; + + uart_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(uart_clk)) + return PTR_ERR(uart_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + s->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(s->port.membase)) + return PTR_ERR(s->port.membase); + + s->port.irq = platform_get_irq(pdev, 0); + if (IS_ERR_VALUE(s->port.irq)) + return s->port.irq; + + s->rx_irq = platform_get_irq(pdev, 1); + if (IS_ERR_VALUE(s->rx_irq)) + return s->rx_irq; + + if (!np) { + char syscon_name[9]; + + sprintf(syscon_name, "syscon.%i", index + 1); + s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name); + if (IS_ERR(s->syscon)) + return PTR_ERR(s->syscon); + + s->use_ms = !index; + } else { + s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); + if (IS_ERR(s->syscon)) + return PTR_ERR(s->syscon); + + if (!index) + s->use_ms = of_property_read_bool(np, "uart-use-ms"); } + + s->port.line = index; + s->port.dev = &pdev->dev; + s->port.iotype = UPIO_MEM32; + s->port.mapbase = res->start; + s->port.type = PORT_CLPS711X; + s->port.fifosize = 16; + s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; + s->port.uartclk = clk_get_rate(uart_clk); + s->port.ops = &uart_clps711x_ops; + platform_set_drvdata(pdev, s); - s->uart_clk = devm_clk_get(&pdev->dev, "uart"); - if (IS_ERR(s->uart_clk)) { - dev_err(&pdev->dev, "Can't get UART clocks\n"); - ret = PTR_ERR(s->uart_clk); - goto err_out; - } + ret = uart_add_one_port(&clps711x_uart, &s->port); + if (ret) + return ret; - s->uart.owner = THIS_MODULE; - s->uart.dev_name = "ttyCL"; - s->uart.major = UART_CLPS711X_MAJOR; - s->uart.minor = UART_CLPS711X_MINOR; - s->uart.nr = UART_CLPS711X_NR; -#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE - s->uart.cons = &s->console; - s->uart.cons->device = uart_console_device; - s->uart.cons->write = uart_clps711x_console_write; - s->uart.cons->setup = uart_clps711x_console_setup; - s->uart.cons->flags = CON_PRINTBUFFER; - s->uart.cons->index = -1; - s->uart.cons->data = s; - strcpy(s->uart.cons->name, "ttyCL"); -#endif - ret = uart_register_driver(&s->uart); - if (ret) { - dev_err(&pdev->dev, "Registering UART driver failed\n"); - devm_clk_put(&pdev->dev, s->uart_clk); - goto err_out; - } + /* Disable port */ + if (!uart_console(&s->port)) + regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0); - for (i = 0; i < UART_CLPS711X_NR; i++) { - s->port[i].line = i; - s->port[i].dev = &pdev->dev; - s->port[i].irq = TX_IRQ(&s->port[i]); - s->port[i].iobase = SYSCON(&s->port[i]); - s->port[i].type = PORT_CLPS711X; - s->port[i].fifosize = 16; - s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port[i].uartclk = clk_get_rate(s->uart_clk); - s->port[i].ops = &uart_clps711x_ops; - WARN_ON(uart_add_one_port(&s->uart, &s->port[i])); - } + s->tx_enabled = 1; - return 0; + ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0, + dev_name(&pdev->dev), &s->port); + if (ret) { + uart_remove_one_port(&clps711x_uart, &s->port); + return ret; + } -err_out: - platform_set_drvdata(pdev, NULL); + ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0, + dev_name(&pdev->dev), &s->port); + if (ret) + uart_remove_one_port(&clps711x_uart, &s->port); return ret; } @@ -494,41 +542,47 @@ err_out: static int uart_clps711x_remove(struct platform_device *pdev) { struct clps711x_port *s = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < UART_CLPS711X_NR; i++) - uart_remove_one_port(&s->uart, &s->port[i]); - - devm_clk_put(&pdev->dev, s->uart_clk); - uart_unregister_driver(&s->uart); - platform_set_drvdata(pdev, NULL); - return 0; + return uart_remove_one_port(&clps711x_uart, &s->port); } -static struct platform_driver clps711x_uart_driver = { +static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = { + { .compatible = "cirrus,clps711x-uart", }, + { } +}; +MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids); + +static struct platform_driver clps711x_uart_platform = { .driver = { - .name = UART_CLPS711X_NAME, - .owner = THIS_MODULE, + .name = "clps711x-uart", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(clps711x_uart_dt_ids), }, .probe = uart_clps711x_probe, .remove = uart_clps711x_remove, }; -module_platform_driver(clps711x_uart_driver); - -static struct platform_device clps711x_uart_device = { - .name = UART_CLPS711X_NAME, -}; static int __init uart_clps711x_init(void) { - return platform_device_register(&clps711x_uart_device); + int ret; + +#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE + clps711x_uart.cons = &clps711x_console; + clps711x_console.data = &clps711x_uart; +#endif + + ret = uart_register_driver(&clps711x_uart); + if (ret) + return ret; + + return platform_driver_register(&clps711x_uart_platform); } module_init(uart_clps711x_init); static void __exit uart_clps711x_exit(void) { - platform_device_unregister(&clps711x_uart_device); + platform_driver_unregister(&clps711x_uart_platform); + uart_unregister_driver(&clps711x_uart); } module_exit(uart_clps711x_exit); diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index ad0caf17680..aa60e6d13ec 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -41,6 +41,8 @@ #include <linux/bootmem.h> #include <linux/dma-mapping.h> #include <linux/fs_uart_pd.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/gpio.h> #include <linux/of_gpio.h> @@ -245,7 +247,7 @@ static void cpm_uart_int_rx(struct uart_port *port) int i; unsigned char ch; u8 *cp; - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; cbd_t __iomem *bdp; u16 status; @@ -276,7 +278,7 @@ static void cpm_uart_int_rx(struct uart_port *port) /* If we have not enough room in tty flip buffer, then we try * later, which will be the next rx-interrupt or a timeout */ - if(tty_buffer_request_room(tty, i) < i) { + if (tty_buffer_request_room(tport, i) < i) { printk(KERN_WARNING "No room in flip buffer\n"); return; } @@ -302,7 +304,7 @@ static void cpm_uart_int_rx(struct uart_port *port) } #endif error_return: - tty_insert_flip_char(tty, ch, flg); + tty_insert_flip_char(tport, ch, flg); } /* End while (i--) */ @@ -322,7 +324,7 @@ static void cpm_uart_int_rx(struct uart_port *port) pinfo->rx_cur = bdp; /* activate BH processing */ - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); return; @@ -507,7 +509,7 @@ static void cpm_uart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); if (baud < HW_BUF_SPD_THRESHOLD || - (pinfo->port.state && pinfo->port.state->port.tty->low_latency)) + (pinfo->port.state && pinfo->port.state->port.low_latency)) pinfo->rx_fifosize = 1; else pinfo->rx_fifosize = RX_BUF_SIZE; @@ -969,7 +971,7 @@ static void cpm_uart_config_port(struct uart_port *port, int flags) * Note that this is called with interrupts already disabled */ static void cpm_uart_early_write(struct uart_cpm_port *pinfo, - const char *string, u_int count) + const char *string, u_int count, bool handle_linefeed) { unsigned int i; cbd_t __iomem *bdp, *bdbase; @@ -1011,7 +1013,7 @@ static void cpm_uart_early_write(struct uart_cpm_port *pinfo, bdp++; /* if a LF, also do CR... */ - if (*string == 10) { + if (handle_linefeed && *string == 10) { while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) ; @@ -1109,7 +1111,7 @@ static void cpm_put_poll_char(struct uart_port *port, static char ch[2]; ch[0] = (char)c; - cpm_uart_early_write(pinfo, ch, 1); + cpm_uart_early_write(pinfo, ch, 1, false); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1207,14 +1209,38 @@ static int cpm_uart_init_port(struct device_node *np, pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize; spin_lock_init(&pinfo->port.lock); - pinfo->port.irq = of_irq_to_resource(np, 0, NULL); + pinfo->port.irq = irq_of_parse_and_map(np, 0); if (pinfo->port.irq == NO_IRQ) { ret = -EINVAL; goto out_pram; } - for (i = 0; i < NUM_GPIOS; i++) - pinfo->gpios[i] = of_get_gpio(np, i); + for (i = 0; i < NUM_GPIOS; i++) { + int gpio; + + pinfo->gpios[i] = -1; + + gpio = of_get_gpio(np, i); + + if (gpio_is_valid(gpio)) { + ret = gpio_request(gpio, "cpm_uart"); + if (ret) { + pr_err("can't request gpio #%d: %d\n", i, ret); + continue; + } + if (i == GPIO_RTS || i == GPIO_DTR) + ret = gpio_direction_output(gpio, 0); + else + ret = gpio_direction_input(gpio); + if (ret) { + pr_err("can't set direction for gpio #%d: %d\n", + i, ret); + gpio_free(gpio); + continue; + } + pinfo->gpios[i] = gpio; + } + } #ifdef CONFIG_PPC_EARLY_DEBUG_CPM udbg_putc = NULL; @@ -1249,7 +1275,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, spin_lock_irqsave(&pinfo->port.lock, flags); } - cpm_uart_early_write(pinfo, s, count); + cpm_uart_early_write(pinfo, s, count, true); if (unlikely(nolock)) { local_irq_restore(flags); @@ -1384,7 +1410,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) if (index >= UART_NR) return -ENODEV; - dev_set_drvdata(&ofdev->dev, pinfo); + platform_set_drvdata(ofdev, pinfo); /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; @@ -1398,7 +1424,7 @@ static int cpm_uart_probe(struct platform_device *ofdev) static int cpm_uart_remove(struct platform_device *ofdev) { - struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); + struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev); return uart_remove_one_port(&cpm_reg, &pinfo->port); } diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c index 18f79575894..6d3b22e9324 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c @@ -29,7 +29,6 @@ #include <linux/tty.h> #include <linux/gfp.h> #include <linux/ioport.h> -#include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> #include <linux/sysrq.h> @@ -45,6 +44,7 @@ #include <linux/kernel.h> #include <linux/of.h> +#include <linux/of_address.h> #include "cpm_uart.h" diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c index a4927e66e74..f46d2ca8720 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c @@ -29,7 +29,6 @@ #include <linux/tty.h> #include <linux/ioport.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> #include <linux/sysrq.h> diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 35ee6a2c687..d567ac5d3af 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -169,7 +169,6 @@ static int get_lsr_info(struct e100_serial *info, unsigned int *value); #define DEF_BAUD 115200 /* 115.2 kbit/s */ -#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ #define DEF_TX 0x80 /* or SERIAL_CTRL_B */ @@ -246,7 +245,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH7_FIRST, .icmdadr = R_DMA_CH7_CMD, .idescradr = R_DMA_CH7_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 2, @@ -288,7 +286,6 @@ static struct e100_serial rs_table[] = { #endif }, /* ttyS0 */ -#ifndef CONFIG_SVINTO_SIM { .baud = DEF_BAUD, .ioport = (unsigned char *)R_SERIAL1_CTRL, .irq = 1U << 16, /* uses DMA 8 and 9 */ @@ -300,7 +297,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH9_FIRST, .icmdadr = R_DMA_CH9_CMD, .idescradr = R_DMA_CH9_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 3, @@ -356,7 +352,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH3_FIRST, .icmdadr = R_DMA_CH3_CMD, .idescradr = R_DMA_CH3_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 0, @@ -410,7 +405,6 @@ static struct e100_serial rs_table[] = { .ifirstadr = R_DMA_CH5_FIRST, .icmdadr = R_DMA_CH5_CMD, .idescradr = R_DMA_CH5_DESCR, - .flags = STD_FLAGS, .rx_ctrl = DEF_RX, .tx_ctrl = DEF_TX, .iseteop = 1, @@ -452,7 +446,6 @@ static struct e100_serial rs_table[] = { .dma_in_enabled = 0 #endif } /* ttyS3 */ -#endif }; @@ -1040,7 +1033,6 @@ cflag_to_etrax_baud(unsigned int cflag) static inline void e100_dtr(struct e100_serial *info, int set) { -#ifndef CONFIG_SVINTO_SIM unsigned char mask = e100_modem_pins[info->line].dtr_mask; #ifdef SERIAL_DEBUG_IO @@ -1065,7 +1057,6 @@ e100_dtr(struct e100_serial *info, int set) info->line, *e100_modem_pins[info->line].dtr_shadow, E100_DTR_GET(info)); #endif -#endif } /* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive @@ -1074,7 +1065,6 @@ e100_dtr(struct e100_serial *info, int set) static inline void e100_rts(struct e100_serial *info, int set) { -#ifndef CONFIG_SVINTO_SIM unsigned long flags; local_irq_save(flags); info->rx_ctrl &= ~E100_RTS_MASK; @@ -1084,7 +1074,6 @@ e100_rts(struct e100_serial *info, int set) #ifdef SERIAL_DEBUG_IO printk("ser%i rts %i\n", info->line, set); #endif -#endif } @@ -1092,7 +1081,6 @@ e100_rts(struct e100_serial *info, int set) static inline void e100_ri_out(struct e100_serial *info, int set) { -#ifndef CONFIG_SVINTO_SIM /* RI is active low */ { unsigned char mask = e100_modem_pins[info->line].ri_mask; @@ -1104,12 +1092,10 @@ e100_ri_out(struct e100_serial *info, int set) *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow; local_irq_restore(flags); } -#endif } static inline void e100_cd_out(struct e100_serial *info, int set) { -#ifndef CONFIG_SVINTO_SIM /* CD is active low */ { unsigned char mask = e100_modem_pins[info->line].cd_mask; @@ -1121,27 +1107,22 @@ e100_cd_out(struct e100_serial *info, int set) *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow; local_irq_restore(flags); } -#endif } static inline void e100_disable_rx(struct e100_serial *info) { -#ifndef CONFIG_SVINTO_SIM /* disable the receiver */ info->ioport[REG_REC_CTRL] = (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); -#endif } static inline void e100_enable_rx(struct e100_serial *info) { -#ifndef CONFIG_SVINTO_SIM /* enable the receiver */ info->ioport[REG_REC_CTRL] = (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); -#endif } /* the rx DMA uses both the dma_descr and the dma_eop interrupts */ @@ -1559,24 +1540,6 @@ transmit_chars_dma(struct e100_serial *info) unsigned int c, sentl; struct etrax_dma_descr *descr; -#ifdef CONFIG_SVINTO_SIM - /* This will output too little if tail is not 0 always since - * we don't reloop to send the other part. Anyway this SHOULD be a - * no-op - transmit_chars_dma would never really be called during sim - * since rs_write does not write into the xmit buffer then. - */ - if (info->xmit.tail) - printk("Error in serial.c:transmit_chars-dma(), tail!=0\n"); - if (info->xmit.head != info->xmit.tail) { - SIMCOUT(info->xmit.buf + info->xmit.tail, - CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE)); - info->xmit.head = info->xmit.tail; /* move back head */ - info->tr_running = 0; - } - return; -#endif /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ *info->oclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | @@ -1760,8 +1723,7 @@ add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char fl info->icount.rx++; } else { - struct tty_struct *tty = info->port.tty; - tty_insert_flip_char(tty, data, flag); + tty_insert_flip_char(&info->port, data, flag); info->icount.rx++; } @@ -1848,13 +1810,6 @@ static void receive_chars_dma(struct e100_serial *info) struct tty_struct *tty; unsigned char rstat; -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - return; -#endif - /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ *info->iclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | @@ -1940,12 +1895,6 @@ static int start_recv_dma(struct e100_serial *info) static void start_receive(struct e100_serial *info) { -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - return; -#endif if (info->uses_dma_in) { /* reset the input dma channel to be sure it works */ @@ -1978,17 +1927,6 @@ tr_interrupt(int irq, void *dev_id) int i; int handled = 0; -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - { - const char *s = "What? tr_interrupt in simulator??\n"; - SIMCOUT(s,strlen(s)); - } - return IRQ_HANDLED; -#endif - /* find out the line that caused this irq and get it from rs_table */ ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ @@ -2027,17 +1965,6 @@ rec_interrupt(int irq, void *dev_id) int i; int handled = 0; -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - { - const char *s = "What? rec_interrupt in simulator??\n"; - SIMCOUT(s,strlen(s)); - } - return IRQ_HANDLED; -#endif - /* find out the line that caused this irq and get it from rs_table */ ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ @@ -2105,22 +2032,15 @@ static int force_eop_if_needed(struct e100_serial *info) static void flush_to_flip_buffer(struct e100_serial *info) { - struct tty_struct *tty; struct etrax_recv_buffer *buffer; unsigned long flags; local_irq_save(flags); - tty = info->port.tty; - - if (!tty) { - local_irq_restore(flags); - return; - } while ((buffer = info->first_recv_buffer) != NULL) { unsigned int count = buffer->length; - tty_insert_flip_string(tty, buffer->buffer, count); + tty_insert_flip_string(&info->port, buffer->buffer, count); info->recv_cnt -= count; if (count == buffer->length) { @@ -2139,7 +2059,7 @@ static void flush_to_flip_buffer(struct e100_serial *info) local_irq_restore(flags); /* This includes a check for low-latency */ - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&info->port); } static void check_flush_timeout(struct e100_serial *info) @@ -2166,7 +2086,7 @@ static void flush_timeout_function(unsigned long data) fast_timers[info->line].function = NULL; serial_fast_timer_expired++; - TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line)); + TIMERD(DEBUG_LOG(info->line, "flush_timeout %i ", info->line)); TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired)); check_flush_timeout(info); } @@ -2186,10 +2106,6 @@ timed_flush_handler(unsigned long ptr) struct e100_serial *info; int i; -#ifdef CONFIG_SVINTO_SIM - return; -#endif - for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; if (info->uses_dma_in) @@ -2271,16 +2187,9 @@ TODO: The break will be delayed until an F or V character is received. */ -static -struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) +static void handle_ser_rx_interrupt_no_dma(struct e100_serial *info) { unsigned long data_read; - struct tty_struct *tty = info->port.tty; - - if (!tty) { - printk("!NO TTY!\n"); - return info; - } /* Read data and status at the same time */ data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]); @@ -2338,8 +2247,7 @@ more_data: data_in, data_read); char flag = TTY_NORMAL; if (info->errorcode == ERRCODE_INSERT_BREAK) { - struct tty_struct *tty = info->port.tty; - tty_insert_flip_char(tty, 0, flag); + tty_insert_flip_char(&info->port, 0, flag); info->icount.rx++; } @@ -2353,7 +2261,7 @@ more_data: info->icount.frame++; flag = TTY_FRAME; } - tty_insert_flip_char(tty, data, flag); + tty_insert_flip_char(&info->port, data, flag); info->errorcode = 0; } info->break_detected_cnt = 0; @@ -2369,7 +2277,7 @@ more_data: log_int(rdpc(), 0, 0); } ); - tty_insert_flip_char(tty, + tty_insert_flip_char(&info->port, IO_EXTRACT(R_SERIAL0_READ, data_in, data_read), TTY_NORMAL); } else { @@ -2384,11 +2292,10 @@ more_data: goto more_data; } - tty_flip_buffer_push(info->port.tty); - return info; + tty_flip_buffer_push(&info->port); } -static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) +static void handle_ser_rx_interrupt(struct e100_serial *info) { unsigned char rstat; @@ -2397,7 +2304,8 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) #endif /* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ if (!info->uses_dma_in) { - return handle_ser_rx_interrupt_no_dma(info); + handle_ser_rx_interrupt_no_dma(info); + return; } /* DMA is used */ rstat = info->ioport[REG_STATUS]; @@ -2504,7 +2412,6 @@ static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int"); - return info; } /* handle_ser_rx_interrupt */ static void handle_ser_tx_interrupt(struct e100_serial *info) @@ -2549,8 +2456,7 @@ static void handle_ser_tx_interrupt(struct e100_serial *info) } /* Normal char-by-char interrupt */ if (info->xmit.head == info->xmit.tail - || info->port.tty->stopped - || info->port.tty->hw_stopped) { + || info->port.tty->stopped) { DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->port.tty->stopped)); e100_disable_serial_tx_ready_irq(info); @@ -2737,7 +2643,7 @@ startup(struct e100_serial * info) /* if it was already initialized, skip this */ - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { local_irq_restore(flags); free_page(xmit_page); return 0; @@ -2752,25 +2658,6 @@ startup(struct e100_serial * info) printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf); #endif -#ifdef CONFIG_SVINTO_SIM - /* Bits and pieces collected from below. Better to have them - in one ifdef:ed clause than to mix in a lot of ifdefs, - right? */ - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->xmit.head = info->xmit.tail = 0; - info->first_recv_buffer = info->last_recv_buffer = NULL; - info->recv_cnt = info->max_recv_cnt = 0; - - for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) - info->rec_descr[i].buf = NULL; - - /* No real action in the simulator, but may set info important - to ioctl. */ - change_speed(info); -#else - /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) @@ -2860,9 +2747,7 @@ startup(struct e100_serial * info) e100_rts(info, 1); e100_dtr(info, 1); -#endif /* CONFIG_SVINTO_SIM */ - - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; local_irq_restore(flags); return 0; @@ -2880,7 +2765,6 @@ shutdown(struct e100_serial * info) struct etrax_recv_buffer *buffer; int i; -#ifndef CONFIG_SVINTO_SIM /* shut down the transmitter and receiver */ DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line)); e100_disable_rx(info); @@ -2905,9 +2789,7 @@ shutdown(struct e100_serial * info) info->tr_running = 0; } -#endif /* CONFIG_SVINTO_SIM */ - - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef SERIAL_DEBUG_OPEN @@ -2938,7 +2820,7 @@ shutdown(struct e100_serial * info) if (info->port.tty) set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; local_irq_restore(flags); } @@ -2963,7 +2845,7 @@ change_speed(struct e100_serial *info) /* possibly, the tx/rx should be disabled first to do this safely */ /* change baud-rate and write it to the hardware */ - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { /* Special baudrate */ u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ unsigned long alt_source = @@ -3018,17 +2900,12 @@ change_speed(struct e100_serial *info) IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); r_alt_ser_baudrate_shadow &= ~mask; r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); -#ifndef CONFIG_SVINTO_SIM *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; -#endif /* CONFIG_SVINTO_SIM */ info->baud = cflag_to_baud(cflag); -#ifndef CONFIG_SVINTO_SIM info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag); -#endif /* CONFIG_SVINTO_SIM */ } -#ifndef CONFIG_SVINTO_SIM /* start with default settings and then fill in changes */ local_irq_save(flags); /* 8 bit, no/even parity */ @@ -3096,7 +2973,6 @@ change_speed(struct e100_serial *info) *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; local_irq_restore(flags); -#endif /* !CONFIG_SVINTO_SIM */ update_char_time(info); @@ -3113,7 +2989,6 @@ rs_flush_chars(struct tty_struct *tty) if (info->tr_running || info->xmit.head == info->xmit.tail || tty->stopped || - tty->hw_stopped || !info->xmit.buf) return; @@ -3137,7 +3012,7 @@ static int rs_raw_write(struct tty_struct *tty, /* first some sanity checks */ - if (!tty || !info->xmit.buf) + if (!info->xmit.buf) return 0; #ifdef SERIAL_DEBUG_DATA @@ -3146,11 +3021,6 @@ static int rs_raw_write(struct tty_struct *tty, count, info->ioport[REG_STATUS]); #endif -#ifdef CONFIG_SVINTO_SIM - /* Really simple. The output is here and now. */ - SIMCOUT(buf, count); - return count; -#endif local_save_flags(flags); DFLOW(DEBUG_LOG(info->line, "write count %i ", count)); DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty))); @@ -3191,7 +3061,6 @@ static int rs_raw_write(struct tty_struct *tty, if (info->xmit.head != info->xmit.tail && !tty->stopped && - !tty->hw_stopped && !info->tr_running) { start_transmit(info); } @@ -3415,10 +3284,10 @@ get_serial_info(struct e100_serial * info, tmp.line = info->line; tmp.port = (int)info->ioport; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.custom_divisor = info->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -3440,16 +3309,16 @@ set_serial_info(struct e100_serial *info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || + (new_serial.close_delay != info->port.close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) + (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); goto check_and_exit; } - if (info->count > 1) + if (info->port.count > 1) return -EBUSY; /* @@ -3458,16 +3327,16 @@ set_serial_info(struct e100_serial *info, */ info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ASYNC_FLAGS) | + info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { change_speed(info); } else retval = startup(info); @@ -3488,7 +3357,6 @@ static int get_lsr_info(struct e100_serial * info, unsigned int *value) { unsigned int result = TIOCSER_TEMT; -#ifndef CONFIG_SVINTO_SIM unsigned long curr_time = jiffies; unsigned long curr_time_usec = GET_JIFFIES_USEC(); unsigned long elapsed_usec = @@ -3499,7 +3367,6 @@ get_lsr_info(struct e100_serial * info, unsigned int *value) elapsed_usec < 2*info->char_time_usec) { result = 0; } -#endif if (copy_to_user(value, &result, sizeof(int))) return -EFAULT; @@ -3748,10 +3615,8 @@ rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios.c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; + !(tty->termios.c_cflag & CRTSCTS)) rs_start(tty); - } } @@ -3787,7 +3652,7 @@ rs_close(struct tty_struct *tty, struct file * filp) printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, info->line, info->count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -3797,32 +3662,32 @@ rs_close(struct tty_struct *tty, struct file * filp) */ printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; + "info->count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { local_irq_restore(flags); return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ - if (info->flags & ASYNC_NORMAL_ACTIVE) + if (info->port.flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the serial receiver and the DMA receive interrupt. @@ -3831,11 +3696,10 @@ rs_close(struct tty_struct *tty, struct file * filp) e100_disable_serial_data_irq(info); #endif -#ifndef CONFIG_SVINTO_SIM e100_disable_rx(info); e100_disable_rx_irq(info); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -3843,7 +3707,6 @@ rs_close(struct tty_struct *tty, struct file * filp) */ rs_wait_until_sent(tty, HZ); } -#endif shutdown(info); rs_flush_buffer(tty); @@ -3851,13 +3714,13 @@ rs_close(struct tty_struct *tty, struct file * filp) tty->closing = 0; info->event = 0; info->port.tty = NULL; - if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); - wake_up_interruptible(&info->open_wait); + if (info->port.blocked_open) { + if (info->port.close_delay) + schedule_timeout_interruptible(info->port.close_delay); + wake_up_interruptible(&info->port.open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); local_irq_restore(flags); /* port closed */ @@ -3950,10 +3813,10 @@ rs_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } /* @@ -3975,11 +3838,11 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -3994,7 +3857,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -4005,23 +3868,23 @@ block_til_ready(struct tty_struct *tty, struct file * filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count++; - info->count--; + info->port.count--; } local_irq_restore(flags); - info->blocked_open++; + info->port.blocked_open++; while (1) { local_irq_save(flags); /* assert RTS and DTR */ @@ -4030,9 +3893,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -4041,7 +3904,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->flags & ASYNC_CLOSING) && do_clocal) + if (!(info->port.flags & ASYNC_CLOSING) && do_clocal) /* && (do_clocal || DCD_IS_ASSERTED) */ break; if (signal_pending(current)) { @@ -4050,24 +3913,24 @@ block_til_ready(struct tty_struct *tty, struct file * filp, } #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif tty_unlock(tty); schedule(); tty_lock(tty); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (extra_count) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; #ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -4101,24 +3964,24 @@ rs_open(struct tty_struct *tty, struct file * filp) #ifdef SERIAL_DEBUG_OPEN printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; tty->driver_data = info; info->port.tty = tty; - tty->low_latency = !!(info->flags & ASYNC_LOW_LATENCY); + info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY); /* * If the port is in the middle of closing, bail out now */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(tty, info->close_wait, - !(info->flags & ASYNC_CLOSING)); + (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? + return ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); #else return -EAGAIN; @@ -4128,7 +3991,7 @@ rs_open(struct tty_struct *tty, struct file * filp) /* * If DMA is enabled try to allocate the irq's. */ - if (info->count == 1) { + if (info->port.count == 1) { allocated_resources = 1; if (info->dma_in_enabled) { if (request_irq(info->dma_in_irq_nbr, @@ -4201,7 +4064,7 @@ rs_open(struct tty_struct *tty, struct file * filp) if (allocated_resources) deinit_port(info); - /* FIXME Decrease count info->count here too? */ + /* FIXME Decrease count info->port.count here too? */ return retval; } @@ -4218,7 +4081,7 @@ rs_open(struct tty_struct *tty, struct file * filp) return retval; } - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if ((info->port.count == 1) && (info->port.flags & ASYNC_SPLIT_TERMIOS)) { tty->termios = info->normal_termios; change_speed(info); } @@ -4271,9 +4134,6 @@ static void seq_line_info(struct seq_file *m, struct e100_serial *info) if (info->port.tty->stopped) seq_printf(m, " stopped:%i", (int)info->port.tty->stopped); - if (info->port.tty->hw_stopped) - seq_printf(m, " hw_stopped:%i", - (int)info->port.tty->hw_stopped); } { @@ -4470,16 +4330,9 @@ static int __init rs_init(void) info->forced_eop = 0; info->baud_base = DEF_BAUD_BASE; info->custom_divisor = 0; - info->flags = 0; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; info->x_char = 0; info->event = 0; - info->count = 0; - info->blocked_open = 0; info->normal_termios = driver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); info->xmit.buf = NULL; info->xmit.tail = info->xmit.head = 0; info->first_recv_buffer = info->last_recv_buffer = NULL; @@ -4516,7 +4369,6 @@ static int __init rs_init(void) fast_timer_init(); #endif -#ifndef CONFIG_SVINTO_SIM #ifndef CONFIG_ETRAX_KGDB /* Not needed in simulator. May only complicate stuff. */ /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ @@ -4526,7 +4378,6 @@ static int __init rs_init(void) panic("%s: Failed to request irq8", __func__); #endif -#endif /* CONFIG_SVINTO_SIM */ return 0; } diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h index ea0beb46a10..7599014ae03 100644 --- a/drivers/tty/serial/crisv10.h +++ b/drivers/tty/serial/crisv10.h @@ -53,8 +53,6 @@ struct e100_serial { volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ - int flags; /* defined in tty.h */ - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ u8 iseteop; /* bit number for R_SET_EOP for the input dma */ @@ -88,19 +86,10 @@ struct e100_serial { volatile int tr_running; /* 1 if output is running */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; unsigned long event; - unsigned long last_active; int line; int type; /* PORT_ETRAX */ - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ struct circ_buf xmit; struct etrax_recv_buffer *first_recv_buffer; struct etrax_recv_buffer *last_recv_buffer; @@ -110,9 +99,6 @@ struct e100_serial { struct work_struct work; struct async_icount icount; /* error-statistics etc.*/ struct ktermios normal_termios; - struct ktermios callout_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; unsigned long char_time_usec; /* The time for 1 char, in usecs */ unsigned long flush_time_usec; /* How often we should flush */ diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index 6491b8644a7..cdbbc788230 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -187,7 +187,6 @@ static inline void dz_receive_chars(struct dz_mux *mux) { struct uart_port *uport; struct dz_port *dport = &mux->dport[0]; - struct tty_struct *tty = NULL; struct uart_icount *icount; int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; unsigned char ch, flag; @@ -197,7 +196,6 @@ static inline void dz_receive_chars(struct dz_mux *mux) while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { dport = &mux->dport[LINE(status)]; uport = &dport->port; - tty = uport->state->port.tty; /* point to the proper dev */ ch = UCHAR(status); /* grab the char */ flag = TTY_NORMAL; @@ -249,7 +247,7 @@ static inline void dz_receive_chars(struct dz_mux *mux) } for (i = 0; i < DZ_NB_PORT; i++) if (lines_rx[i]) - tty_flip_buffer_push(mux->dport[i].port.state->port.tty); + tty_flip_buffer_push(&mux->dport[i].port.state->port); } /* @@ -627,7 +625,7 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, dport->port.read_status_mask = DZ_OERR; if (termios->c_iflag & INPCK) dport->port.read_status_mask |= DZ_FERR | DZ_PERR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) dport->port.read_status_mask |= DZ_BREAK; /* characters to ignore */ diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-arm-semihost.c new file mode 100644 index 00000000000..383db10fbb4 --- /dev/null +++ b/drivers/tty/serial/earlycon-arm-semihost.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier <marc.zyngier@arm.com> + * + * Adapted for ARM and earlycon: + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_core.h> + +#ifdef CONFIG_THUMB2_KERNEL +#define SEMIHOST_SWI "0xab" +#else +#define SEMIHOST_SWI "0x123456" +#endif + +/* + * Semihosting-based debug console + */ +static void smh_putc(struct uart_port *port, int c) +{ +#ifdef CONFIG_ARM64 + asm volatile("mov x1, %0\n" + "mov x0, #3\n" + "hlt 0xf000\n" + : : "r" (&c) : "x0", "x1", "memory"); +#else + asm volatile("mov r1, %0\n" + "mov r0, #3\n" + "svc " SEMIHOST_SWI "\n" + : : "r" (&c) : "r0", "r1", "memory"); +#endif +} + +static void smh_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + uart_console_write(&dev->port, s, n, smh_putc); +} + +int __init early_smh_setup(struct earlycon_device *device, const char *opt) +{ + device->con->write = smh_write; + return 0; +} +EARLYCON_DECLARE(smh, early_smh_setup); diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c new file mode 100644 index 00000000000..a514ee6f540 --- /dev/null +++ b/drivers/tty/serial/earlycon.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring <robh@kernel.org> + * + * Based on 8250 earlycon: + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas <bjorn.helgaas@hp.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. + */ +#include <linux/console.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/serial_core.h> +#include <linux/sizes.h> +#include <linux/mod_devicetable.h> + +#ifdef CONFIG_FIX_EARLYCON_MEM +#include <asm/fixmap.h> +#endif + +#include <asm/serial.h> + +static struct console early_con = { + .name = "uart", /* 8250 console switch requires this name */ + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static struct earlycon_device early_console_dev = { + .con = &early_con, +}; + +static const struct of_device_id __earlycon_of_table_sentinel + __used __section(__earlycon_of_table_end); + +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) +{ + void __iomem *base; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + base += paddr & ~PAGE_MASK; +#else + base = ioremap(paddr, size); +#endif + if (!base) + pr_err("%s: Couldn't map 0x%llx\n", __func__, + (unsigned long long)paddr); + + return base; +} + +static int __init parse_options(struct earlycon_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length; + unsigned long addr; + + if (!options) + return -ENODEV; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + options += mmio ? 5 : 7; + addr = simple_strtoul(options, NULL, 0); + port->mapbase = addr; + if (mmio32) + port->regshift = 2; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + options += 3; + addr = simple_strtoul(options, NULL, 0); + port->iobase = addr; + mmio = 0; + } else if (!strncmp(options, "0x", 2)) { + port->iotype = UPIO_MEM; + addr = simple_strtoul(options, NULL, 0); + port->mapbase = addr; + } else { + return -EINVAL; + } + + port->uartclk = BASE_BAUD * 16; + + options = strchr(options, ','); + if (options) { + options++; + device->baud = simple_strtoul(options, NULL, 0); + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); + strlcpy(device->options, options, length); + } + + if (mmio || mmio32) + pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + pr_info("Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + + return 0; +} + +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + size_t len; + struct uart_port *port = &early_console_dev.port; + + if (!buf || !match || !setup) + return 0; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + if (buf[len] && (buf[len] != ',')) + return 0; + + buf += len + 1; + + err = parse_options(&early_console_dev, buf); + /* On parsing error, pass the options buf to the setup function */ + if (!err) + buf = NULL; + + if (port->mapbase) + port->membase = earlycon_map(port->mapbase, 64); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, buf); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + register_console(early_console_dev.con); + return 0; +} + +int __init of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + struct uart_port *port = &early_console_dev.port; + + port->iotype = UPIO_MEM; + port->mapbase = addr; + port->uartclk = BASE_BAUD * 16; + port->membase = earlycon_map(addr, SZ_4K); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, NULL); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + + register_console(early_console_dev.con); + return 0; +} diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index a8cbb267052..3b0ee9afd76 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -81,6 +81,7 @@ struct efm32_uart_port { struct uart_port port; unsigned int txirq; struct clk *clk; + struct efm32_uart_pdata pdata; }; #define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port) #define efm_debug(efm_port, format, arg...) \ @@ -194,8 +195,7 @@ static void efm32_uart_break_ctl(struct uart_port *port, int ctl) /* not possible without fiddling with gpios */ } -static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port, - struct tty_struct *tty) +static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port) { struct uart_port *port = &efm_port->port; @@ -237,8 +237,8 @@ static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port, rxdata & UARTn_RXDATAX_RXDATA__MASK)) continue; - if (tty && (rxdata & port->ignore_status_mask) == 0) - tty_insert_flip_char(tty, + if ((rxdata & port->ignore_status_mask) == 0) + tty_insert_flip_char(&port->state->port, rxdata & UARTn_RXDATAX_RXDATA__MASK, flag); } } @@ -249,15 +249,13 @@ static irqreturn_t efm32_uart_rxirq(int irq, void *data) u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF); int handled = IRQ_NONE; struct uart_port *port = &efm_port->port; - struct tty_struct *tty; + struct tty_port *tport = &port->state->port; spin_lock(&port->lock); - tty = tty_kref_get(port->state->port.tty); - if (irqflag & UARTn_IF_RXDATAV) { efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC); - efm32_uart_rx_chars(efm_port, tty); + efm32_uart_rx_chars(efm_port); handled = IRQ_HANDLED; } @@ -265,19 +263,15 @@ static irqreturn_t efm32_uart_rxirq(int irq, void *data) if (irqflag & UARTn_IF_RXOF) { efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC); port->icount.overrun++; - if (tty) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); handled = IRQ_HANDLED; } - if (tty) { - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + return handled; } @@ -300,13 +294,8 @@ static irqreturn_t efm32_uart_txirq(int irq, void *data) static int efm32_uart_startup(struct uart_port *port) { struct efm32_uart_port *efm_port = to_efm_port(port); - u32 location = 0; - struct efm32_uart_pdata *pdata = dev_get_platdata(port->dev); int ret; - if (pdata) - location = UARTn_ROUTE_LOCATION(pdata->location); - ret = clk_enable(efm_port->clk); if (ret) { efm_debug(efm_port, "failed to enable clk\n"); @@ -315,7 +304,9 @@ static int efm32_uart_startup(struct uart_port *port) port->uartclk = clk_get_rate(efm_port->clk); /* Enable pins at configured location */ - efm32_uart_write32(efm_port, location | UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, + efm32_uart_write32(efm_port, + UARTn_ROUTE_LOCATION(efm_port->pdata.location) | + UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN, UARTn_ROUTE); ret = request_irq(port->irq, efm32_uart_rxirq, 0, @@ -416,7 +407,7 @@ static void efm32_uart_set_termios(struct uart_port *port, if (new->c_iflag & INPCK) port->read_status_mask |= UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR; - if (new->c_iflag & (BRKINT | PARMRK)) + if (new->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= SW_UARTn_RXDATAX_BERR; port->ignore_status_mask = 0; @@ -674,11 +665,27 @@ static int efm32_uart_probe_dt(struct platform_device *pdev, struct efm32_uart_port *efm_port) { struct device_node *np = pdev->dev.of_node; + u32 location; int ret; if (!np) return 1; + ret = of_property_read_u32(np, "efm32,location", &location); + if (ret) + /* fall back to old and (wrongly) generic property "location" */ + ret = of_property_read_u32(np, "location", &location); + if (!ret) { + if (location > 5) { + dev_err(&pdev->dev, "invalid location\n"); + return -EINVAL; + } + efm_debug(efm_port, "using location %u\n", location); + efm_port->pdata.location = location; + } else { + efm_debug(efm_port, "fall back to location 0\n"); + } + ret = of_alias_get_id(np, "serial"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); @@ -694,6 +701,7 @@ static int efm32_uart_probe(struct platform_device *pdev) { struct efm32_uart_port *efm_port; struct resource *res; + unsigned int line; int ret; efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL); @@ -738,20 +746,29 @@ static int efm32_uart_probe(struct platform_device *pdev) efm_port->port.flags = UPF_BOOT_AUTOCONF; ret = efm32_uart_probe_dt(pdev, efm_port); - if (ret > 0) + if (ret > 0) { /* not created by device tree */ + const struct efm32_uart_pdata *pdata = dev_get_platdata(&pdev->dev); + efm_port->port.line = pdev->id; - if (efm_port->port.line >= 0 && - efm_port->port.line < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[efm_port->port.line] = efm_port; + if (pdata) + efm_port->pdata = *pdata; + } else if (ret < 0) + goto err_probe_dt; + + line = efm_port->port.line; + + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = efm_port; ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port); if (ret) { dev_dbg(&pdev->dev, "failed to add port: %d\n", ret); - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[pdev->id] = NULL; + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = NULL; +err_probe_dt: err_get_rxirq: err_too_small: err_get_base: @@ -767,21 +784,23 @@ err_get_base: static int efm32_uart_remove(struct platform_device *pdev) { struct efm32_uart_port *efm_port = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); + unsigned int line = efm_port->port.line; uart_remove_one_port(&efm32_uart_reg, &efm_port->port); - if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports)) - efm32_uart_ports[pdev->id] = NULL; + if (line >= 0 && line < ARRAY_SIZE(efm32_uart_ports)) + efm32_uart_ports[line] = NULL; kfree(efm_port); return 0; } -static struct of_device_id efm32_uart_dt_ids[] = { +static const struct of_device_id efm32_uart_dt_ids[] = { { + .compatible = "energymicro,efm32-uart", + }, { + /* doesn't follow the "vendor,device" scheme, don't use */ .compatible = "efm32,uart", }, { /* sentinel */ @@ -823,6 +842,7 @@ static void __exit efm32_uart_exit(void) platform_driver_unregister(&efm32_uart_driver); uart_unregister_driver(&efm32_uart_reg); } +module_exit(efm32_uart_exit); MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>"); MODULE_DESCRIPTION("EFM32 UART/USART driver"); diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c new file mode 100644 index 00000000000..49385c86cfb --- /dev/null +++ b/drivers/tty/serial/fsl_lpuart.c @@ -0,0 +1,1285 @@ +/* + * Freescale lpuart serial port driver + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#if defined(CONFIG_SERIAL_FSL_LPUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/dmapool.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_dma.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/tty_flip.h> + +/* All registers are 8-bit width */ +#define UARTBDH 0x00 +#define UARTBDL 0x01 +#define UARTCR1 0x02 +#define UARTCR2 0x03 +#define UARTSR1 0x04 +#define UARTCR3 0x06 +#define UARTDR 0x07 +#define UARTCR4 0x0a +#define UARTCR5 0x0b +#define UARTMODEM 0x0d +#define UARTPFIFO 0x10 +#define UARTCFIFO 0x11 +#define UARTSFIFO 0x12 +#define UARTTWFIFO 0x13 +#define UARTTCFIFO 0x14 +#define UARTRWFIFO 0x15 + +#define UARTBDH_LBKDIE 0x80 +#define UARTBDH_RXEDGIE 0x40 +#define UARTBDH_SBR_MASK 0x1f + +#define UARTCR1_LOOPS 0x80 +#define UARTCR1_RSRC 0x20 +#define UARTCR1_M 0x10 +#define UARTCR1_WAKE 0x08 +#define UARTCR1_ILT 0x04 +#define UARTCR1_PE 0x02 +#define UARTCR1_PT 0x01 + +#define UARTCR2_TIE 0x80 +#define UARTCR2_TCIE 0x40 +#define UARTCR2_RIE 0x20 +#define UARTCR2_ILIE 0x10 +#define UARTCR2_TE 0x08 +#define UARTCR2_RE 0x04 +#define UARTCR2_RWU 0x02 +#define UARTCR2_SBK 0x01 + +#define UARTSR1_TDRE 0x80 +#define UARTSR1_TC 0x40 +#define UARTSR1_RDRF 0x20 +#define UARTSR1_IDLE 0x10 +#define UARTSR1_OR 0x08 +#define UARTSR1_NF 0x04 +#define UARTSR1_FE 0x02 +#define UARTSR1_PE 0x01 + +#define UARTCR3_R8 0x80 +#define UARTCR3_T8 0x40 +#define UARTCR3_TXDIR 0x20 +#define UARTCR3_TXINV 0x10 +#define UARTCR3_ORIE 0x08 +#define UARTCR3_NEIE 0x04 +#define UARTCR3_FEIE 0x02 +#define UARTCR3_PEIE 0x01 + +#define UARTCR4_MAEN1 0x80 +#define UARTCR4_MAEN2 0x40 +#define UARTCR4_M10 0x20 +#define UARTCR4_BRFA_MASK 0x1f +#define UARTCR4_BRFA_OFF 0 + +#define UARTCR5_TDMAS 0x80 +#define UARTCR5_RDMAS 0x20 + +#define UARTMODEM_RXRTSE 0x08 +#define UARTMODEM_TXRTSPOL 0x04 +#define UARTMODEM_TXRTSE 0x02 +#define UARTMODEM_TXCTSE 0x01 + +#define UARTPFIFO_TXFE 0x80 +#define UARTPFIFO_FIFOSIZE_MASK 0x7 +#define UARTPFIFO_TXSIZE_OFF 4 +#define UARTPFIFO_RXFE 0x08 +#define UARTPFIFO_RXSIZE_OFF 0 + +#define UARTCFIFO_TXFLUSH 0x80 +#define UARTCFIFO_RXFLUSH 0x40 +#define UARTCFIFO_RXOFE 0x04 +#define UARTCFIFO_TXOFE 0x02 +#define UARTCFIFO_RXUFE 0x01 + +#define UARTSFIFO_TXEMPT 0x80 +#define UARTSFIFO_RXEMPT 0x40 +#define UARTSFIFO_RXOF 0x04 +#define UARTSFIFO_TXOF 0x02 +#define UARTSFIFO_RXUF 0x01 + +#define DMA_MAXBURST 16 +#define DMA_MAXBURST_MASK (DMA_MAXBURST - 1) +#define FSL_UART_RX_DMA_BUFFER_SIZE 64 + +#define DRIVER_NAME "fsl-lpuart" +#define DEV_NAME "ttyLP" +#define UART_NR 6 + +struct lpuart_port { + struct uart_port port; + struct clk *clk; + unsigned int txfifo_size; + unsigned int rxfifo_size; + + bool lpuart_dma_use; + struct dma_chan *dma_tx_chan; + struct dma_chan *dma_rx_chan; + struct dma_async_tx_descriptor *dma_tx_desc; + struct dma_async_tx_descriptor *dma_rx_desc; + dma_addr_t dma_tx_buf_bus; + dma_addr_t dma_rx_buf_bus; + dma_cookie_t dma_tx_cookie; + dma_cookie_t dma_rx_cookie; + unsigned char *dma_tx_buf_virt; + unsigned char *dma_rx_buf_virt; + unsigned int dma_tx_bytes; + unsigned int dma_rx_bytes; + int dma_tx_in_progress; + int dma_rx_in_progress; + unsigned int dma_rx_timeout; + struct timer_list lpuart_timer; +}; + +static struct of_device_id lpuart_dt_ids[] = { + { + .compatible = "fsl,vf610-lpuart", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lpuart_dt_ids); + +/* Forward declare this for the dma callbacks*/ +static void lpuart_dma_tx_complete(void *arg); +static void lpuart_dma_rx_complete(void *arg); + +static void lpuart_stop_tx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TIE | UARTCR2_TCIE); + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_stop_rx(struct uart_port *port) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp & ~UARTCR2_RE, port->membase + UARTCR2); +} + +static void lpuart_enable_ms(struct uart_port *port) +{ +} + +static void lpuart_copy_rx_to_tty(struct lpuart_port *sport, + struct tty_port *tty, int count) +{ + int copied; + + sport->port.icount.rx += count; + + if (!tty) { + dev_err(sport->port.dev, "No tty port\n"); + return; + } + + dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + copied = tty_insert_flip_string(tty, + ((unsigned char *)(sport->dma_rx_buf_virt)), count); + + if (copied != count) { + WARN_ON(1); + dev_err(sport->port.dev, "RxData copy to tty layer failed\n"); + } + + dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); +} + +static void lpuart_pio_tx(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!uart_circ_empty(xmit) && + readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) { + writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, + sport->port.membase + UARTCR5); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + dma_addr_t tx_bus_addr; + + dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus, + UART_XMIT_SIZE, DMA_TO_DEVICE); + sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK); + tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail; + sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan, + tx_bus_addr, sport->dma_tx_bytes, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + + if (!sport->dma_tx_desc) { + dev_err(sport->port.dev, "Not able to get desc for tx\n"); + return -EIO; + } + + sport->dma_tx_desc->callback = lpuart_dma_tx_complete; + sport->dma_tx_desc->callback_param = sport; + sport->dma_tx_in_progress = 1; + sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc); + dma_async_issue_pending(sport->dma_tx_chan); + + return 0; +} + +static void lpuart_prepare_tx(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long count = CIRC_CNT_TO_END(xmit->head, + xmit->tail, UART_XMIT_SIZE); + + if (!count) + return; + + if (count < DMA_MAXBURST) + writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS, + sport->port.membase + UARTCR5); + else { + writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS, + sport->port.membase + UARTCR5); + lpuart_dma_tx(sport, count); + } +} + +static void lpuart_dma_tx_complete(void *arg) +{ + struct lpuart_port *sport = arg; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + async_tx_ack(sport->dma_tx_desc); + + spin_lock_irqsave(&sport->port.lock, flags); + + xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1); + sport->dma_tx_in_progress = 0; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + lpuart_prepare_tx(sport); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int lpuart_dma_rx(struct lpuart_port *sport) +{ + dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); + sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan, + sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + + if (!sport->dma_rx_desc) { + dev_err(sport->port.dev, "Not able to get desc for rx\n"); + return -EIO; + } + + sport->dma_rx_desc->callback = lpuart_dma_rx_complete; + sport->dma_rx_desc->callback_param = sport; + sport->dma_rx_in_progress = 1; + sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc); + dma_async_issue_pending(sport->dma_rx_chan); + + return 0; +} + +static void lpuart_dma_rx_complete(void *arg) +{ + struct lpuart_port *sport = arg; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + + async_tx_ack(sport->dma_rx_desc); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->dma_rx_in_progress = 0; + lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE); + tty_flip_buffer_push(port); + lpuart_dma_rx(sport); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static void lpuart_timer_func(unsigned long data) +{ + struct lpuart_port *sport = (struct lpuart_port *)data; + struct tty_port *port = &sport->port.state->port; + struct dma_tx_state state; + unsigned long flags; + unsigned char temp; + int count; + + del_timer(&sport->lpuart_timer); + dmaengine_pause(sport->dma_rx_chan); + dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state); + dmaengine_terminate_all(sport->dma_rx_chan); + count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue; + async_tx_ack(sport->dma_rx_desc); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->dma_rx_in_progress = 0; + lpuart_copy_rx_to_tty(sport, port, count); + tty_flip_buffer_push(port); + temp = readb(sport->port.membase + UARTCR5); + writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static inline void lpuart_prepare_rx(struct lpuart_port *sport) +{ + unsigned long flags; + unsigned char temp; + + spin_lock_irqsave(&sport->port.lock, flags); + + init_timer(&sport->lpuart_timer); + sport->lpuart_timer.function = lpuart_timer_func; + sport->lpuart_timer.data = (unsigned long)sport; + sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; + add_timer(&sport->lpuart_timer); + + lpuart_dma_rx(sport); + temp = readb(sport->port.membase + UARTCR5); + writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static inline void lpuart_transmit_buffer(struct lpuart_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && + (readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) { + writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + lpuart_stop_tx(&sport->port); +} + +static void lpuart_start_tx(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned char temp; + + temp = readb(port->membase + UARTCR2); + writeb(temp | UARTCR2_TIE, port->membase + UARTCR2); + + if (sport->lpuart_dma_use) { + if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress) + lpuart_prepare_tx(sport); + } else { + if (readb(port->membase + UARTSR1) & UARTSR1_TDRE) + lpuart_transmit_buffer(sport); + } +} + +static irqreturn_t lpuart_txint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + if (sport->port.x_char) { + writeb(sport->port.x_char, sport->port.membase + UARTDR); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + lpuart_stop_tx(&sport->port); + goto out; + } + + lpuart_transmit_buffer(sport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t lpuart_rxint(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned char rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + /* + * to clear the FE, OR, NF, FE, PE flags, + * read SR1 then read DR + */ + sr = readb(sport->port.membase + UARTSR1); + rx = readb(sport->port.membase + UARTDR); + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (sr & (UARTSR1_PE | UARTSR1_OR | UARTSR1_FE)) { + if (sr & UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & UARTSR1_FE) + sport->port.icount.frame++; + + if (sr & UARTSR1_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & UARTSR1_PE) + flg = TTY_PARITY; + else if (sr & UARTSR1_FE) + flg = TTY_FRAME; + + if (sr & UARTSR1_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + +static irqreturn_t lpuart_int(int irq, void *dev_id) +{ + struct lpuart_port *sport = dev_id; + unsigned char sts; + + sts = readb(sport->port.membase + UARTSR1); + + if (sts & UARTSR1_RDRF) { + if (sport->lpuart_dma_use) + lpuart_prepare_rx(sport); + else + lpuart_rxint(irq, dev_id); + } + if (sts & UARTSR1_TDRE && + !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) { + if (sport->lpuart_dma_use) + lpuart_pio_tx(sport); + else + lpuart_txint(irq, dev_id); + } + + return IRQ_HANDLED; +} + +/* return TIOCSER_TEMT when transmitter is not busy */ +static unsigned int lpuart_tx_empty(struct uart_port *port) +{ + return (readb(port->membase + UARTSR1) & UARTSR1_TC) ? + TIOCSER_TEMT : 0; +} + +static unsigned int lpuart_get_mctrl(struct uart_port *port) +{ + unsigned int temp = 0; + unsigned char reg; + + reg = readb(port->membase + UARTMODEM); + if (reg & UARTMODEM_TXCTSE) + temp |= TIOCM_CTS; + + if (reg & UARTMODEM_RXRTSE) + temp |= TIOCM_RTS; + + return temp; +} + +static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned char temp; + + temp = readb(port->membase + UARTMODEM) & + ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= UARTMODEM_RXRTSE; + + if (mctrl & TIOCM_CTS) + temp |= UARTMODEM_TXCTSE; + + writeb(temp, port->membase + UARTMODEM); +} + +static void lpuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned char temp; + + temp = readb(port->membase + UARTCR2) & ~UARTCR2_SBK; + + if (break_state != 0) + temp |= UARTCR2_SBK; + + writeb(temp, port->membase + UARTCR2); +} + +static void lpuart_setup_watermark(struct lpuart_port *sport) +{ + unsigned char val, cr2; + unsigned char cr2_saved; + + cr2 = readb(sport->port.membase + UARTCR2); + cr2_saved = cr2; + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_TE | + UARTCR2_RIE | UARTCR2_RE); + writeb(cr2, sport->port.membase + UARTCR2); + + /* determine FIFO size and enable FIFO mode */ + val = readb(sport->port.membase + UARTPFIFO); + + sport->txfifo_size = 0x1 << (((val >> UARTPFIFO_TXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + sport->rxfifo_size = 0x1 << (((val >> UARTPFIFO_RXSIZE_OFF) & + UARTPFIFO_FIFOSIZE_MASK) + 1); + + writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE, + sport->port.membase + UARTPFIFO); + + /* flush Tx and Rx FIFO */ + writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, + sport->port.membase + UARTCFIFO); + + writeb(0, sport->port.membase + UARTTWFIFO); + writeb(1, sport->port.membase + UARTRWFIFO); + + /* Restore cr2 */ + writeb(cr2_saved, sport->port.membase + UARTCR2); +} + +static int lpuart_dma_tx_request(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_chan *tx_chan; + struct dma_slave_config dma_tx_sconfig; + dma_addr_t dma_bus; + unsigned char *dma_buf; + int ret; + + tx_chan = dma_request_slave_channel(sport->port.dev, "tx"); + + if (!tx_chan) { + dev_err(sport->port.dev, "Dma tx channel request failed!\n"); + return -ENODEV; + } + + dma_bus = dma_map_single(tx_chan->device->dev, + sport->port.state->xmit.buf, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + if (dma_mapping_error(tx_chan->device->dev, dma_bus)) { + dev_err(sport->port.dev, "dma_map_single tx failed\n"); + dma_release_channel(tx_chan); + return -ENOMEM; + } + + dma_buf = sport->port.state->xmit.buf; + dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR; + dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_tx_sconfig.dst_maxburst = DMA_MAXBURST; + dma_tx_sconfig.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig); + + if (ret < 0) { + dev_err(sport->port.dev, + "Dma slave config failed, err = %d\n", ret); + dma_release_channel(tx_chan); + return ret; + } + + sport->dma_tx_chan = tx_chan; + sport->dma_tx_buf_virt = dma_buf; + sport->dma_tx_buf_bus = dma_bus; + sport->dma_tx_in_progress = 0; + + return 0; +} + +static int lpuart_dma_rx_request(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_chan *rx_chan; + struct dma_slave_config dma_rx_sconfig; + dma_addr_t dma_bus; + unsigned char *dma_buf; + int ret; + + rx_chan = dma_request_slave_channel(sport->port.dev, "rx"); + + if (!rx_chan) { + dev_err(sport->port.dev, "Dma rx channel request failed!\n"); + return -ENODEV; + } + + dma_buf = devm_kzalloc(sport->port.dev, + FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL); + + if (!dma_buf) { + dev_err(sport->port.dev, "Dma rx alloc failed\n"); + dma_release_channel(rx_chan); + return -ENOMEM; + } + + dma_bus = dma_map_single(rx_chan->device->dev, dma_buf, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + + if (dma_mapping_error(rx_chan->device->dev, dma_bus)) { + dev_err(sport->port.dev, "dma_map_single rx failed\n"); + dma_release_channel(rx_chan); + return -ENOMEM; + } + + dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR; + dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_rx_sconfig.src_maxburst = 1; + dma_rx_sconfig.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig); + + if (ret < 0) { + dev_err(sport->port.dev, + "Dma slave config failed, err = %d\n", ret); + dma_release_channel(rx_chan); + return ret; + } + + sport->dma_rx_chan = rx_chan; + sport->dma_rx_buf_virt = dma_buf; + sport->dma_rx_buf_bus = dma_bus; + sport->dma_rx_in_progress = 0; + + sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) * + FSL_UART_RX_DMA_BUFFER_SIZE * 3 / + sport->rxfifo_size / 2; + + if (sport->dma_rx_timeout < msecs_to_jiffies(20)) + sport->dma_rx_timeout = msecs_to_jiffies(20); + + return 0; +} + +static void lpuart_dma_tx_free(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_chan *dma_chan; + + dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_chan = sport->dma_tx_chan; + sport->dma_tx_chan = NULL; + sport->dma_tx_buf_bus = 0; + sport->dma_tx_buf_virt = NULL; + dma_release_channel(dma_chan); +} + +static void lpuart_dma_rx_free(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + struct dma_chan *dma_chan; + + dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus, + FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + + dma_chan = sport->dma_rx_chan; + sport->dma_rx_chan = NULL; + sport->dma_rx_buf_bus = 0; + sport->dma_rx_buf_virt = NULL; + dma_release_channel(dma_chan); +} + +static int lpuart_startup(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + int ret; + unsigned long flags; + unsigned char temp; + + /*whether use dma support by dma request results*/ + if (lpuart_dma_tx_request(port) || lpuart_dma_rx_request(port)) { + sport->lpuart_dma_use = false; + } else { + sport->lpuart_dma_use = true; + temp = readb(port->membase + UARTCR5); + writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5); + } + + ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0, + DRIVER_NAME, sport); + if (ret) + return ret; + + spin_lock_irqsave(&sport->port.lock, flags); + + lpuart_setup_watermark(sport); + + temp = readb(sport->port.membase + UARTCR2); + temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE); + writeb(temp, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return 0; +} + +static void lpuart_shutdown(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned char temp; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable Rx/Tx and interrupts */ + temp = readb(port->membase + UARTCR2); + temp &= ~(UARTCR2_TE | UARTCR2_RE | + UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(temp, port->membase + UARTCR2); + + spin_unlock_irqrestore(&port->lock, flags); + + devm_free_irq(port->dev, port->irq, sport); + + if (sport->lpuart_dma_use) { + lpuart_dma_tx_free(port); + lpuart_dma_rx_free(port); + } +} + +static void +lpuart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct lpuart_port *sport = container_of(port, struct lpuart_port, port); + unsigned long flags; + unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr, brfa; + + cr1 = old_cr1 = readb(sport->port.membase + UARTCR1); + old_cr2 = readb(sport->port.membase + UARTCR2); + cr4 = readb(sport->port.membase + UARTCR4); + bdh = readb(sport->port.membase + UARTBDH); + modem = readb(sport->port.membase + UARTMODEM); + /* + * only support CS8 and CS7, and for CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + cr1 = old_cr1 & ~UARTCR1_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + cr1 |= UARTCR1_M; + } + + if (termios->c_cflag & CRTSCTS) { + modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must be enabled when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + cr1 &= ~UARTCR1_PE; + cr1 |= UARTCR1_M; + } else { + cr1 |= UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + cr1 |= UARTCR1_M; + if (termios->c_cflag & PARODD) + cr1 |= UARTCR1_PT; + else + cr1 &= ~UARTCR1_PT; + } + } + + /* ask the core to calculate the divisor */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE); + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + sport->port.read_status_mask |= UARTSR1_FE; + + /* characters to ignore */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= UARTSR1_FE; + /* + * if we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= UARTSR1_OR; + } + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + /* disable transmit and receive */ + writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE), + sport->port.membase + UARTCR2); + + sbr = sport->port.uartclk / (16 * baud); + brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2) / baud; + bdh &= ~UARTBDH_SBR_MASK; + bdh |= (sbr >> 8) & 0x1F; + cr4 &= ~UARTCR4_BRFA_MASK; + brfa &= UARTCR4_BRFA_MASK; + writeb(cr4 | brfa, sport->port.membase + UARTCR4); + writeb(bdh, sport->port.membase + UARTBDH); + writeb(sbr & 0xFF, sport->port.membase + UARTBDL); + writeb(cr1, sport->port.membase + UARTCR1); + writeb(modem, sport->port.membase + UARTMODEM); + + /* restore control register */ + writeb(old_cr2, sport->port.membase + UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *lpuart_type(struct uart_port *port) +{ + return "FSL_LPUART"; +} + +static void lpuart_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int lpuart_request_port(struct uart_port *port) +{ + return 0; +} + +/* configure/autoconfigure the port */ +static void lpuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_LPUART; +} + +static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_LPUART) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops lpuart_pops = { + .tx_empty = lpuart_tx_empty, + .set_mctrl = lpuart_set_mctrl, + .get_mctrl = lpuart_get_mctrl, + .stop_tx = lpuart_stop_tx, + .start_tx = lpuart_start_tx, + .stop_rx = lpuart_stop_rx, + .enable_ms = lpuart_enable_ms, + .break_ctl = lpuart_break_ctl, + .startup = lpuart_startup, + .shutdown = lpuart_shutdown, + .set_termios = lpuart_set_termios, + .type = lpuart_type, + .request_port = lpuart_request_port, + .release_port = lpuart_release_port, + .config_port = lpuart_config_port, + .verify_port = lpuart_verify_port, +}; + +static struct lpuart_port *lpuart_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE +static void lpuart_console_putchar(struct uart_port *port, int ch) +{ + while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) + barrier(); + + writeb(ch, port->membase + UARTDR); +} + +static void +lpuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct lpuart_port *sport = lpuart_ports[co->index]; + unsigned char old_cr2, cr2; + + /* first save CR2 and then disable interrupts */ + cr2 = old_cr2 = readb(sport->port.membase + UARTCR2); + cr2 |= (UARTCR2_TE | UARTCR2_RE); + cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE); + writeb(cr2, sport->port.membase + UARTCR2); + + uart_console_write(&sport->port, s, count, lpuart_console_putchar); + + /* wait for transmitter finish complete and restore CR2 */ + while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC)) + barrier(); + + writeb(old_cr2, sport->port.membase + UARTCR2); +} + +/* + * if the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +lpuart_console_get_options(struct lpuart_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned char cr, bdh, bdl, brfa; + unsigned int sbr, uartclk, baud_raw; + + cr = readb(sport->port.membase + UARTCR2); + cr &= UARTCR2_TE | UARTCR2_RE; + if (!cr) + return; + + /* ok, the port was enabled */ + + cr = readb(sport->port.membase + UARTCR1); + + *parity = 'n'; + if (cr & UARTCR1_PE) { + if (cr & UARTCR1_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr & UARTCR1_M) + *bits = 9; + else + *bits = 8; + + bdh = readb(sport->port.membase + UARTBDH); + bdh &= UARTBDH_SBR_MASK; + bdl = readb(sport->port.membase + UARTBDL); + sbr = bdh; + sbr <<= 8; + sbr |= bdl; + brfa = readb(sport->port.membase + UARTCR4); + brfa &= UARTCR4_BRFA_MASK; + + uartclk = clk_get_rate(sport->clk); + /* + * baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk / (16 * (sbr + brfa / 32)); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console lpuart rounded baud rate" + "from %d to %d\n", baud_raw, *baud); +} + +static int __init lpuart_console_setup(struct console *co, char *options) +{ + struct lpuart_port *sport; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(lpuart_ports)) + co->index = 0; + + sport = lpuart_ports[co->index]; + if (sport == NULL) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + lpuart_console_get_options(sport, &baud, &parity, &bits); + + lpuart_setup_watermark(sport); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver lpuart_reg; +static struct console lpuart_console = { + .name = DEV_NAME, + .write = lpuart_console_write, + .device = uart_console_device, + .setup = lpuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lpuart_reg, +}; + +#define LPUART_CONSOLE (&lpuart_console) +#else +#define LPUART_CONSOLE NULL +#endif + +static struct uart_driver lpuart_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .nr = ARRAY_SIZE(lpuart_ports), + .cons = LPUART_CONSOLE, +}; + +static int lpuart_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct lpuart_port *sport; + struct resource *res; + int ret; + + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + pdev->dev.coherent_dma_mask = 0; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + sport->port.line = ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + sport->port.mapbase = res->start; + sport->port.membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sport->port.membase)) + return PTR_ERR(sport->port.membase); + + sport->port.dev = &pdev->dev; + sport->port.type = PORT_LPUART; + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->port.ops = &lpuart_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + + sport->clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sport->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret); + return ret; + } + + sport->port.uartclk = clk_get_rate(sport->clk); + + lpuart_ports[sport->port.line] = sport; + + platform_set_drvdata(pdev, &sport->port); + + ret = uart_add_one_port(&lpuart_reg, &sport->port); + if (ret) { + clk_disable_unprepare(sport->clk); + return ret; + } + + return 0; +} + +static int lpuart_remove(struct platform_device *pdev) +{ + struct lpuart_port *sport = platform_get_drvdata(pdev); + + uart_remove_one_port(&lpuart_reg, &sport->port); + + clk_disable_unprepare(sport->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int lpuart_suspend(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_suspend_port(&lpuart_reg, &sport->port); + + return 0; +} + +static int lpuart_resume(struct device *dev) +{ + struct lpuart_port *sport = dev_get_drvdata(dev); + + uart_resume_port(&lpuart_reg, &sport->port); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume); + +static struct platform_driver lpuart_driver = { + .probe = lpuart_probe, + .remove = lpuart_remove, + .driver = { + .name = "fsl-lpuart", + .owner = THIS_MODULE, + .of_match_table = lpuart_dt_ids, + .pm = &lpuart_pm_ops, + }, +}; + +static int __init lpuart_serial_init(void) +{ + int ret; + + pr_info("serial: Freescale lpuart driver\n"); + + ret = uart_register_driver(&lpuart_reg); + if (ret) + return ret; + + ret = platform_driver_register(&lpuart_driver); + if (ret) + uart_unregister_driver(&lpuart_reg); + + return ret; +} + +static void __exit lpuart_serial_exit(void) +{ + platform_driver_unregister(&lpuart_driver); + uart_unregister_driver(&lpuart_reg); +} + +module_init(lpuart_serial_init); +module_exit(lpuart_serial_exit); + +MODULE_DESCRIPTION("Freescale lpuart serial port driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 72b6334bcf1..67423805e6d 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -105,7 +105,7 @@ static const struct pci_device_id icom_pci_table[] = { {} }; -struct lookup_proc_table start_proc[4] = { +static struct lookup_proc_table start_proc[4] = { {NULL, ICOM_CONTROL_START_A}, {NULL, ICOM_CONTROL_START_B}, {NULL, ICOM_CONTROL_START_C}, @@ -113,14 +113,14 @@ struct lookup_proc_table start_proc[4] = { }; -struct lookup_proc_table stop_proc[4] = { +static struct lookup_proc_table stop_proc[4] = { {NULL, ICOM_CONTROL_STOP_A}, {NULL, ICOM_CONTROL_STOP_B}, {NULL, ICOM_CONTROL_STOP_C}, {NULL, ICOM_CONTROL_STOP_D} }; -struct lookup_int_table int_mask_tbl[4] = { +static struct lookup_int_table int_mask_tbl[4] = { {NULL, ICOM_INT_MASK_PRC_A}, {NULL, ICOM_INT_MASK_PRC_B}, {NULL, ICOM_INT_MASK_PRC_C}, @@ -297,25 +297,25 @@ static void stop_processor(struct icom_port *icom_port) spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(stop_proc)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) stop_proc[port].global_control_reg = &icom_port->global_reg->control; else stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; + temp = readl(stop_proc[port].global_control_reg); + temp = (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + writel(temp, stop_proc[port].global_control_reg); - if (port < 4) { - temp = readl(stop_proc[port].global_control_reg); - temp = - (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; - writel(temp, stop_proc[port].global_control_reg); - - /* write flush */ - readl(stop_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + /* write flush */ + readl(stop_proc[port].global_control_reg); +unlock: spin_unlock_irqrestore(&icom_lock, flags); } @@ -328,23 +328,25 @@ static void start_processor(struct icom_port *icom_port) spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(start_proc)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) start_proc[port].global_control_reg = &icom_port->global_reg->control; else start_proc[port].global_control_reg = &icom_port->global_reg->control_2; - if (port < 4) { - temp = readl(start_proc[port].global_control_reg); - temp = - (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; - writel(temp, start_proc[port].global_control_reg); - /* write flush */ - readl(start_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + temp = readl(start_proc[port].global_control_reg); + temp = (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + writel(temp, start_proc[port].global_control_reg); + /* write flush */ + readl(start_proc[port].global_control_reg); + +unlock: spin_unlock_irqrestore(&icom_lock, flags); } @@ -453,11 +455,11 @@ static void load_code(struct icom_port *icom_port) for (index = 0; index < fw->size; index++) new_page[index] = fw->data[index]; - release_firmware(fw); - writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length); writel(temp_pci, &icom_port->dram->mac_load_addr); + release_firmware(fw); + /*Setting the syncReg to 0x80 causes adapter to start downloading the personality code into adapter instruction RAM. Once code is loaded, it will begin executing and, based on @@ -557,6 +559,12 @@ static int startup(struct icom_port *icom_port) */ spin_lock_irqsave(&icom_lock, flags); port = icom_port->port; + if (port >= ARRAY_SIZE(int_mask_tbl)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } + if (port == 0 || port == 1) int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; else @@ -566,17 +574,14 @@ static int startup(struct icom_port *icom_port) writew(0x00FF, icom_port->int_reg); else writew(0x3F00, icom_port->int_reg); - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); +unlock: spin_unlock_irqrestore(&icom_lock, flags); return 0; } @@ -595,21 +600,23 @@ static void shutdown(struct icom_port *icom_port) * disable all interrupts */ port = icom_port->port; + if (port >= ARRAY_SIZE(int_mask_tbl)) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + goto unlock; + } if (port == 0 || port == 1) int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; else int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + +unlock: spin_unlock_irqrestore(&icom_lock, flags); /* @@ -734,7 +741,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) { short int count, rcv_buff; - struct tty_struct *tty = icom_port->uart_port.state->port.tty; + struct tty_port *port = &icom_port->uart_port.state->port; unsigned short int status; struct uart_icount *icount; unsigned long offset; @@ -761,7 +768,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) /* Block copy all but the last byte as this may have status */ if (count > 0) { first = icom_port->recv_buf[offset]; - tty_insert_flip_string(tty, icom_port->recv_buf + offset, count - 1); + tty_insert_flip_string(port, icom_port->recv_buf + offset, count - 1); } icount = &icom_port->uart_port.icount; @@ -812,7 +819,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) } - tty_insert_flip_char(tty, *(icom_port->recv_buf + offset + count - 1), flag); + tty_insert_flip_char(port, *(icom_port->recv_buf + offset + count - 1), flag); if (status & SA_FLAGS_OVERRUN) /* @@ -820,7 +827,7 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) * reported immediately, and doesn't * affect the current character */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); ignore_char: icom_port->statStg->rcv[rcv_buff].flags = 0; icom_port->statStg->rcv[rcv_buff].leLength = 0; @@ -834,7 +841,10 @@ ignore_char: status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); } icom_port->next_rcv = rcv_buff; - tty_flip_buffer_push(tty); + + spin_unlock(&icom_port->uart_port.lock); + tty_flip_buffer_push(port); + spin_lock(&icom_port->uart_port.lock); } static void process_interrupt(u16 port_int_reg, @@ -1087,8 +1097,7 @@ static void icom_close(struct uart_port *port) /* stop receiver */ cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE, - &ICOM_PORT->dram->CmdReg); + writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); shutdown(ICOM_PORT); @@ -1415,8 +1424,7 @@ static int icom_alloc_adapter(struct icom_adapter struct icom_adapter *cur_adapter_entry; struct list_head *tmp; - icom_adapter = (struct icom_adapter *) - kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); + icom_adapter = kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); if (!icom_adapter) { return -ENOMEM; @@ -1568,7 +1576,7 @@ static int icom_probe(struct pci_dev *dev, icom_port->uart_port.type = PORT_ICOM; icom_port->uart_port.iotype = UPIO_MEM; icom_port->uart_port.membase = - (char *) icom_adapter->base_addr_pci; + (unsigned char __iomem *)icom_adapter->base_addr_pci; icom_port->uart_port.fifosize = 16; icom_port->uart_port.ops = &icom_ops; icom_port->uart_port.line = diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 8cb6d8d66a1..59039097099 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -270,23 +270,6 @@ static void mrdy_assert(struct ifx_spi_device *ifx_dev) } /** - * ifx_spi_hangup - hang up an IFX device - * @ifx_dev: our SPI device - * - * Hang up the tty attached to the IFX device if one is currently - * open. If not take no action - */ -static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) -{ - struct tty_port *pport = &ifx_dev->tty_port; - struct tty_struct *tty = tty_port_tty_get(pport); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -} - -/** * ifx_spi_timeout - SPI timeout * @arg: our SPI device * @@ -298,7 +281,7 @@ static void ifx_spi_timeout(unsigned long arg) struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); mrdy_set_low(ifx_dev); clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); } @@ -443,25 +426,6 @@ static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, } /** - * ifx_spi_wakeup_serial - SPI space made - * @port_data: our SPI device - * - * We have emptied the FIFO enough that we want to get more data - * queued into it. Poke the line discipline via tty_wakeup so that - * it will feed us more bits - */ -static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) -{ - struct tty_struct *tty; - - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -} - -/** * ifx_spi_prepare_tx_buffer - prepare transmit frame * @ifx_dev: our SPI device * @@ -481,7 +445,6 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) unsigned char *tx_buffer; tx_buffer = ifx_dev->tx_buffer; - memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE); /* make room for required SPI header */ tx_buffer += IFX_SPI_HEADER_OVERHEAD; @@ -507,7 +470,7 @@ static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) tx_count += temp_count; if (temp_count == queue_length) /* poke port to get more data */ - ifx_spi_wakeup_serial(ifx_dev); + tty_port_tty_wakeup(&ifx_dev->tty_port); else /* more data in port, use next SPI message */ ifx_dev->spi_more = 1; } @@ -615,7 +578,7 @@ static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) tty->driver_data = ifx_dev; /* allows flip string push from int context */ - tty->low_latency = 1; + port->low_latency = 1; /* set flag to allows data transfer */ set_bit(IFX_SPI_STATE_IO_AVAILABLE, &ifx_dev->flags); @@ -670,12 +633,8 @@ static const struct tty_operations ifx_spi_serial_ops = { static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, unsigned char *chars, size_t size) { - struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_insert_flip_string(tty, chars, size); - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty_insert_flip_string(&ifx_dev->tty_port, chars, size); + tty_flip_buffer_push(&ifx_dev->tty_port); } /** @@ -688,8 +647,6 @@ static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, static void ifx_spi_complete(void *ctx) { struct ifx_spi_device *ifx_dev = ctx; - struct tty_struct *tty; - struct tty_ldisc *ldisc = NULL; int length; int actual_length; unsigned char more; @@ -767,15 +724,7 @@ complete_exit: */ ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (tty) { - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - ldisc->ops->write_wakeup(tty); - tty_ldisc_deref(ldisc); - } - tty_kref_put(tty); - } + tty_port_tty_wakeup(&ifx_dev->tty_port); } } } @@ -967,7 +916,7 @@ static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); if (!solreset) { /* unsolicited reset */ - ifx_spi_ttyhangup(ifx_dev); + tty_port_tty_hangup(&ifx_dev->tty_port, false); } } else { /* exited reset */ @@ -1059,7 +1008,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) return -ENODEV; } - pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; + pl_data = dev_get_platdata(&spi->dev); if (!pl_data) { dev_err(&spi->dev, "missing platform data!"); return -ENODEV; @@ -1330,30 +1279,6 @@ static void ifx_spi_spi_shutdown(struct spi_device *spi) */ /** - * ifx_spi_spi_suspend - suspend SPI on system suspend - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return 0; -} - -/** - * ifx_spi_spi_resume - resume SPI side on system resume - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_resume(struct spi_device *spi) -{ - return 0; -} - -/** * ifx_spi_pm_suspend - suspend modem on system suspend * @dev: device being suspended * @@ -1442,8 +1367,6 @@ static struct spi_driver ifx_spi_driver = { .probe = ifx_spi_spi_probe, .shutdown = ifx_spi_spi_shutdown, .remove = ifx_spi_spi_remove, - .suspend = ifx_spi_spi_suspend, - .resume = ifx_spi_spi_resume, .id_table = ifx_id_table }; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 59819121fe9..044e86d528a 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -47,11 +47,12 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> -#include <asm/io.h> #include <asm/irq.h> #include <linux/platform_data/serial-imx.h> +#include <linux/platform_data/dma-imx.h> /* Register definitions */ #define URXD0 0x0 /* Receiver Register */ @@ -73,102 +74,105 @@ #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/ /* UART Control Register Bit Fields.*/ -#define URXD_CHARRDY (1<<15) -#define URXD_ERR (1<<14) -#define URXD_OVRRUN (1<<13) -#define URXD_FRMERR (1<<12) -#define URXD_BRK (1<<11) -#define URXD_PRERR (1<<10) -#define UCR1_ADEN (1<<15) /* Auto detect interrupt */ -#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ -#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ -#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ -#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ -#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ -#define UCR1_IREN (1<<7) /* Infrared interface enable */ -#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ -#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ -#define UCR1_SNDBRK (1<<4) /* Send break */ -#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ -#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */ -#define UCR1_DOZE (1<<1) /* Doze */ -#define UCR1_UARTEN (1<<0) /* UART enabled */ -#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ -#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ -#define UCR2_CTSC (1<<13) /* CTS pin control */ -#define UCR2_CTS (1<<12) /* Clear to send */ -#define UCR2_ESCEN (1<<11) /* Escape enable */ -#define UCR2_PREN (1<<8) /* Parity enable */ -#define UCR2_PROE (1<<7) /* Parity odd/even */ -#define UCR2_STPB (1<<6) /* Stop */ -#define UCR2_WS (1<<5) /* Word size */ -#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ -#define UCR2_ATEN (1<<3) /* Aging Timer Enable */ -#define UCR2_TXEN (1<<2) /* Transmitter enabled */ -#define UCR2_RXEN (1<<1) /* Receiver enabled */ -#define UCR2_SRST (1<<0) /* SW reset */ -#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ -#define UCR3_PARERREN (1<<12) /* Parity enable */ -#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ -#define UCR3_DSR (1<<10) /* Data set ready */ -#define UCR3_DCD (1<<9) /* Data carrier detect */ -#define UCR3_RI (1<<8) /* Ring indicator */ -#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ -#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 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 */ -#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ -#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ -#define UCR4_INVR (1<<9) /* Inverted infrared reception */ -#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ -#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ -#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ -#define UCR4_IRSC (1<<5) /* IR special case */ -#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ -#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ -#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ -#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ -#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ -#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */ -#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ -#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) -#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ -#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ -#define USR1_RTSS (1<<14) /* RTS pin status */ -#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ -#define USR1_RTSD (1<<12) /* RTS delta */ -#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ -#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ -#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ -#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ -#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 */ -#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ -#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ -#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ -#define USR2_IDLE (1<<12) /* Idle condition */ -#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ -#define USR2_WAKE (1<<7) /* Wake */ -#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ -#define USR2_TXDC (1<<3) /* Transmitter complete */ -#define USR2_BRCD (1<<2) /* Break condition */ -#define USR2_ORE (1<<1) /* Overrun error */ -#define USR2_RDR (1<<0) /* Recv data ready */ -#define UTS_FRCPERR (1<<13) /* Force parity error */ -#define UTS_LOOP (1<<12) /* Loop tx and rx */ -#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ -#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ -#define UTS_TXFULL (1<<4) /* TxFIFO full */ -#define UTS_RXFULL (1<<3) /* RxFIFO full */ -#define UTS_SOFTRST (1<<0) /* Software reset */ +#define URXD_CHARRDY (1<<15) +#define URXD_ERR (1<<14) +#define URXD_OVRRUN (1<<13) +#define URXD_FRMERR (1<<12) +#define URXD_BRK (1<<11) +#define URXD_PRERR (1<<10) +#define UCR1_ADEN (1<<15) /* Auto detect interrupt */ +#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ +#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ +#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ +#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */ +#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ +#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ +#define UCR1_IREN (1<<7) /* Infrared interface enable */ +#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ +#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ +#define UCR1_SNDBRK (1<<4) /* Send break */ +#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ +#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */ +#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */ +#define UCR1_DOZE (1<<1) /* Doze */ +#define UCR1_UARTEN (1<<0) /* UART enabled */ +#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ +#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ +#define UCR2_CTSC (1<<13) /* CTS pin control */ +#define UCR2_CTS (1<<12) /* Clear to send */ +#define UCR2_ESCEN (1<<11) /* Escape enable */ +#define UCR2_PREN (1<<8) /* Parity enable */ +#define UCR2_PROE (1<<7) /* Parity odd/even */ +#define UCR2_STPB (1<<6) /* Stop */ +#define UCR2_WS (1<<5) /* Word size */ +#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ +#define UCR2_ATEN (1<<3) /* Aging Timer Enable */ +#define UCR2_TXEN (1<<2) /* Transmitter enabled */ +#define UCR2_RXEN (1<<1) /* Receiver enabled */ +#define UCR2_SRST (1<<0) /* SW reset */ +#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ +#define UCR3_PARERREN (1<<12) /* Parity enable */ +#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ +#define UCR3_DSR (1<<10) /* Data set ready */ +#define UCR3_DCD (1<<9) /* Data carrier detect */ +#define UCR3_RI (1<<8) /* Ring indicator */ +#define UCR3_ADNIMP (1<<7) /* Autobaud Detection Not Improved */ +#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 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 */ +#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ +#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ +#define UCR4_INVR (1<<9) /* Inverted infrared reception */ +#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ +#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ +#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ +#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */ +#define UCR4_IRSC (1<<5) /* IR special case */ +#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ +#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ +#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ +#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ +#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ +#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */ +#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ +#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) +#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ +#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ +#define USR1_RTSS (1<<14) /* RTS pin status */ +#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ +#define USR1_RTSD (1<<12) /* RTS delta */ +#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ +#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ +#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ +#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ +#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 */ +#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ +#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ +#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ +#define USR2_IDLE (1<<12) /* Idle condition */ +#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ +#define USR2_WAKE (1<<7) /* Wake */ +#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ +#define USR2_TXDC (1<<3) /* Transmitter complete */ +#define USR2_BRCD (1<<2) /* Break condition */ +#define USR2_ORE (1<<1) /* Overrun error */ +#define USR2_RDR (1<<0) /* Recv data ready */ +#define UTS_FRCPERR (1<<13) /* Force parity error */ +#define UTS_LOOP (1<<12) /* Loop tx and rx */ +#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ +#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ +#define UTS_TXFULL (1<<4) /* TxFIFO full */ +#define UTS_RXFULL (1<<3) /* RxFIFO full */ +#define UTS_SOFTRST (1<<0) /* Software reset */ /* We've been assigned a range on the "Low-density serial ports" major */ -#define SERIAL_IMX_MAJOR 207 -#define MINOR_START 16 +#define SERIAL_IMX_MAJOR 207 +#define MINOR_START 16 #define DEV_NAME "ttymxc" /* @@ -187,6 +191,7 @@ enum imx_uart_type { IMX1_UART, IMX21_UART, + IMX6Q_UART, }; /* device type dependent stuff */ @@ -199,8 +204,9 @@ struct imx_port { struct uart_port port; struct timer_list timer; unsigned int old_status; - int txirq,rxirq,rtsirq; + int txirq, rxirq, rtsirq; unsigned int have_rtscts:1; + unsigned int dte_mode:1; unsigned int use_irda:1; unsigned int irda_inv_rx:1; unsigned int irda_inv_tx:1; @@ -208,6 +214,18 @@ struct imx_port { struct clk *clk_ipg; struct clk *clk_per; const struct imx_uart_data *devdata; + + /* DMA fields */ + unsigned int dma_is_inited:1; + unsigned int dma_is_enabled:1; + unsigned int dma_is_rxing:1; + unsigned int dma_is_txing:1; + struct dma_chan *dma_chan_rx, *dma_chan_tx; + struct scatterlist rx_sgl, tx_sgl[2]; + void *rx_buf; + unsigned int tx_bytes; + unsigned int dma_tx_nents; + wait_queue_head_t dma_wait; }; struct imx_port_ucrs { @@ -231,6 +249,10 @@ static struct imx_uart_data imx_uart_devdata[] = { .uts_reg = IMX21_UTS, .devtype = IMX21_UART, }, + [IMX6Q_UART] = { + .uts_reg = IMX21_UTS, + .devtype = IMX6Q_UART, + }, }; static struct platform_device_id imx_uart_devtype[] = { @@ -241,12 +263,16 @@ static struct platform_device_id imx_uart_devtype[] = { .name = "imx21-uart", .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART], }, { + .name = "imx6q-uart", + .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART], + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, imx_uart_devtype); static struct of_device_id imx_uart_dt_ids[] = { + { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, { /* sentinel */ } @@ -268,9 +294,14 @@ static inline int is_imx21_uart(struct imx_port *sport) return sport->devdata->devtype == IMX21_UART; } +static inline int is_imx6q_uart(struct imx_port *sport) +{ + return sport->devdata->devtype == IMX6Q_UART; +} /* * Save and restore functions for UCR1, UCR2 and UCR3 registers */ +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE) static void imx_port_ucrs_save(struct uart_port *port, struct imx_port_ucrs *ucr) { @@ -288,6 +319,7 @@ static void imx_port_ucrs_restore(struct uart_port *port, writel(ucr->ucr2, port->membase + UCR2); writel(ucr->ucr3, port->membase + UCR3); } +#endif /* * Handle any change of modem status signal since we were last called. @@ -384,6 +416,13 @@ static void imx_stop_tx(struct uart_port *port) return; } + /* + * We are maybe in the SMP context, so if the DMA TX thread is running + * on other cpu, we have to wait for it to finish. + */ + if (sport->dma_is_enabled && sport->dma_is_txing) + return; + temp = readl(sport->port.membase + UCR1); writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); } @@ -396,8 +435,19 @@ static void imx_stop_rx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; + /* + * We are maybe in the SMP context, so if the DMA TX thread is running + * on other cpu, we have to wait for it to finish. + */ + if (sport->dma_is_enabled && sport->dma_is_rxing) + return; + temp = readl(sport->port.membase + UCR2); - writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2); + writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); + + /* disable the `Receiver Ready Interrrupt` */ + temp = readl(sport->port.membase + UCR1); + writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1); } /* @@ -431,6 +481,84 @@ static inline void imx_transmit_buffer(struct imx_port *sport) imx_stop_tx(&sport->port); } +static void dma_tx_callback(void *data) +{ + struct imx_port *sport = data; + struct scatterlist *sgl = &sport->tx_sgl[0]; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + + sport->dma_is_txing = 0; + + /* update the stat */ + spin_lock_irqsave(&sport->port.lock, flags); + xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx += sport->tx_bytes; + spin_unlock_irqrestore(&sport->port.lock, flags); + + dev_dbg(sport->port.dev, "we finish the TX DMA.\n"); + + uart_write_wakeup(&sport->port); + + if (waitqueue_active(&sport->dma_wait)) { + wake_up(&sport->dma_wait); + dev_dbg(sport->port.dev, "exit in %s.\n", __func__); + return; + } +} + +static void imx_dma_tx(struct imx_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + struct scatterlist *sgl = sport->tx_sgl; + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = sport->dma_chan_tx; + struct device *dev = sport->port.dev; + enum dma_status status; + int ret; + + status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL); + if (DMA_IN_PROGRESS == status) + return; + + sport->tx_bytes = uart_circ_chars_pending(xmit); + + if (xmit->tail > xmit->head && xmit->head > 0) { + sport->dma_tx_nents = 2; + sg_init_table(sgl, 2); + sg_set_buf(sgl, xmit->buf + xmit->tail, + UART_XMIT_SIZE - xmit->tail); + sg_set_buf(sgl + 1, xmit->buf, xmit->head); + } else { + sport->dma_tx_nents = 1; + sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); + } + + ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE); + if (ret == 0) { + dev_err(dev, "DMA mapping error for TX.\n"); + return; + } + desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "We cannot prepare for the TX slave dma!\n"); + return; + } + desc->callback = dma_tx_callback; + desc->callback_param = sport; + + dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n", + uart_circ_chars_pending(xmit)); + /* fire it */ + sport->dma_is_txing = 1; + dmaengine_submit(desc); + dma_async_issue_pending(chan); + return; +} + /* * interrupts disabled on entry */ @@ -439,6 +567,9 @@ static void imx_start_tx(struct uart_port *port) struct imx_port *sport = (struct imx_port *)port; unsigned long temp; + if (uart_circ_empty(&port->state->xmit)) + return; + if (USE_IRDA(sport)) { /* half duplex in IrDA mode; have to disable receive mode */ temp = readl(sport->port.membase + UCR4); @@ -449,9 +580,18 @@ static void imx_start_tx(struct uart_port *port) temp &= ~(UCR1_RRDYEN); writel(temp, sport->port.membase + UCR1); } + /* Clear any pending ORE flag before enabling interrupt */ + temp = readl(sport->port.membase + USR2); + writel(temp | USR2_ORE, sport->port.membase + USR2); - temp = readl(sport->port.membase + UCR1); - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_OREN; + writel(temp, sport->port.membase + UCR4); + + if (!sport->dma_is_enabled) { + temp = readl(sport->port.membase + UCR1); + writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + } if (USE_IRDA(sport)) { temp = readl(sport->port.membase + UCR1); @@ -463,6 +603,11 @@ static void imx_start_tx(struct uart_port *port) writel(temp, sport->port.membase + UCR4); } + if (sport->dma_is_enabled) { + imx_dma_tx(sport); + return; + } + if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY) imx_transmit_buffer(sport); } @@ -490,9 +635,8 @@ static irqreturn_t imx_txint(int irq, void *dev_id) struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; - spin_lock_irqsave(&sport->port.lock,flags); - if (sport->port.x_char) - { + spin_lock_irqsave(&sport->port.lock, flags); + if (sport->port.x_char) { /* Send next char */ writel(sport->port.x_char, sport->port.membase + URTX0); goto out; @@ -509,18 +653,18 @@ static irqreturn_t imx_txint(int irq, void *dev_id) uart_write_wakeup(&sport->port); out: - spin_unlock_irqrestore(&sport->port.lock,flags); + spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; } static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int rx,flg,ignored = 0; - struct tty_struct *tty = sport->port.state->port.tty; + unsigned int rx, flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; unsigned long flags, temp; - spin_lock_irqsave(&sport->port.lock,flags); + spin_lock_irqsave(&sport->port.lock, flags); while (readl(sport->port.membase + USR2) & USR2_RDR) { flg = TTY_NORMAL; @@ -570,24 +714,52 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) #endif } - tty_insert_flip_char(tty, rx, flg); + tty_insert_flip_char(port, rx, flg); } out: - spin_unlock_irqrestore(&sport->port.lock,flags); - tty_flip_buffer_push(tty); + spin_unlock_irqrestore(&sport->port.lock, flags); + tty_flip_buffer_push(port); return IRQ_HANDLED; } +static int start_rx_dma(struct imx_port *sport); +/* + * If the RXFIFO is filled with some data, and then we + * arise a DMA operation to receive them. + */ +static void imx_dma_rxint(struct imx_port *sport) +{ + unsigned long temp; + + temp = readl(sport->port.membase + USR2); + if ((temp & USR2_RDR) && !sport->dma_is_rxing) { + sport->dma_is_rxing = 1; + + /* disable the `Recerver Ready Interrrupt` */ + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RRDYEN); + writel(temp, sport->port.membase + UCR1); + + /* tell the DMA to receive the data. */ + start_rx_dma(sport); + } +} + static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int sts; + unsigned int sts2; sts = readl(sport->port.membase + USR1); - if (sts & USR1_RRDY) - imx_rxint(irq, dev_id); + if (sts & USR1_RRDY) { + if (sport->dma_is_enabled) + imx_dma_rxint(sport); + else + imx_rxint(irq, dev_id); + } if (sts & USR1_TRDY && readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) @@ -599,6 +771,13 @@ static irqreturn_t imx_int(int irq, void *dev_id) if (sts & USR1_AWAKE) writel(USR1_AWAKE, sport->port.membase + USR1); + sts2 = readl(sport->port.membase + USR2); + if (sts2 & USR2_ORE) { + dev_err(sport->port.dev, "Rx FIFO overrun\n"); + sport->port.icount.overrun++; + writel(sts2 | USR2_ORE, sport->port.membase + USR2); + } + return IRQ_HANDLED; } @@ -608,8 +787,15 @@ static irqreturn_t imx_int(int irq, void *dev_id) static unsigned int imx_tx_empty(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; + unsigned int ret; - return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; + ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; + + /* If the TX DMA is working, return 0. */ + if (sport->dma_is_enabled && sport->dma_is_txing) + ret = 0; + + return ret; } /* @@ -626,6 +812,9 @@ static unsigned int imx_get_mctrl(struct uart_port *port) if (readl(sport->port.membase + UCR2) & UCR2_CTS) tmp |= TIOCM_RTS; + if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP) + tmp |= TIOCM_LOOP; + return tmp; } @@ -637,9 +826,15 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; if (mctrl & TIOCM_RTS) - temp |= UCR2_CTS; + if (!sport->dma_is_enabled) + temp |= UCR2_CTS; writel(temp, sport->port.membase + UCR2); + + temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP; + if (mctrl & TIOCM_LOOP) + temp |= UTS_LOOP; + writel(temp, sport->port.membase + uts_reg(sport)); } /* @@ -654,7 +849,7 @@ static void imx_break_ctl(struct uart_port *port, int break_state) temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; - if ( break_state != 0 ) + if (break_state != 0) temp |= UCR1_SNDBRK; writel(temp, sport->port.membase + UCR1); @@ -676,15 +871,224 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) return 0; } +#define RX_BUF_SIZE (PAGE_SIZE) +static void imx_rx_dma_done(struct imx_port *sport) +{ + unsigned long temp; + + /* Enable this interrupt when the RXFIFO is empty. */ + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN; + writel(temp, sport->port.membase + UCR1); + + sport->dma_is_rxing = 0; + + /* Is the shutdown waiting for us? */ + if (waitqueue_active(&sport->dma_wait)) + wake_up(&sport->dma_wait); +} + +/* + * There are three kinds of RX DMA interrupts(such as in the MX6Q): + * [1] the RX DMA buffer is full. + * [2] the Aging timer expires(wait for 8 bytes long) + * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN). + * + * The [2] is trigger when a character was been sitting in the FIFO + * meanwhile [3] can wait for 32 bytes long when the RX line is + * on IDLE state and RxFIFO is empty. + */ +static void dma_rx_callback(void *data) +{ + struct imx_port *sport = data; + struct dma_chan *chan = sport->dma_chan_rx; + struct scatterlist *sgl = &sport->rx_sgl; + struct tty_port *port = &sport->port.state->port; + struct dma_tx_state state; + enum dma_status status; + unsigned int count; + + /* unmap it first */ + dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE); + + status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); + count = RX_BUF_SIZE - state.residue; + dev_dbg(sport->port.dev, "We get %d bytes.\n", count); + + if (count) { + tty_insert_flip_string(port, sport->rx_buf, count); + tty_flip_buffer_push(port); + + start_rx_dma(sport); + } else + imx_rx_dma_done(sport); +} + +static int start_rx_dma(struct imx_port *sport) +{ + struct scatterlist *sgl = &sport->rx_sgl; + struct dma_chan *chan = sport->dma_chan_rx; + struct device *dev = sport->port.dev; + struct dma_async_tx_descriptor *desc; + int ret; + + sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE); + ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE); + if (ret == 0) { + dev_err(dev, "DMA mapping error for RX.\n"); + return -EINVAL; + } + desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "We cannot prepare for the RX slave dma!\n"); + return -EINVAL; + } + desc->callback = dma_rx_callback; + desc->callback_param = sport; + + dev_dbg(dev, "RX: prepare for the DMA.\n"); + dmaengine_submit(desc); + dma_async_issue_pending(chan); + return 0; +} + +static void imx_uart_dma_exit(struct imx_port *sport) +{ + if (sport->dma_chan_rx) { + dma_release_channel(sport->dma_chan_rx); + sport->dma_chan_rx = NULL; + + kfree(sport->rx_buf); + sport->rx_buf = NULL; + } + + if (sport->dma_chan_tx) { + dma_release_channel(sport->dma_chan_tx); + sport->dma_chan_tx = NULL; + } + + sport->dma_is_inited = 0; +} + +static int imx_uart_dma_init(struct imx_port *sport) +{ + struct dma_slave_config slave_config = {}; + struct device *dev = sport->port.dev; + int ret; + + /* Prepare for RX : */ + sport->dma_chan_rx = dma_request_slave_channel(dev, "rx"); + if (!sport->dma_chan_rx) { + dev_dbg(dev, "cannot get the DMA channel.\n"); + ret = -EINVAL; + goto err; + } + + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = sport->port.mapbase + URXD0; + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_maxburst = RXTL; + ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); + if (ret) { + dev_err(dev, "error in RX dma configuration.\n"); + goto err; + } + + sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!sport->rx_buf) { + dev_err(dev, "cannot alloc DMA buffer.\n"); + ret = -ENOMEM; + goto err; + } + + /* Prepare for TX : */ + sport->dma_chan_tx = dma_request_slave_channel(dev, "tx"); + if (!sport->dma_chan_tx) { + dev_err(dev, "cannot get the TX DMA channel!\n"); + ret = -EINVAL; + goto err; + } + + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = sport->port.mapbase + URTX0; + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_maxburst = TXTL; + ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config); + if (ret) { + dev_err(dev, "error in TX dma configuration."); + goto err; + } + + sport->dma_is_inited = 1; + + return 0; +err: + imx_uart_dma_exit(sport); + return ret; +} + +static void imx_enable_dma(struct imx_port *sport) +{ + unsigned long temp; + + init_waitqueue_head(&sport->dma_wait); + + /* set UCR1 */ + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN | + /* wait for 32 idle frames for IDDMA interrupt */ + UCR1_ICD_REG(3); + writel(temp, sport->port.membase + UCR1); + + /* set UCR4 */ + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_IDDMAEN; + writel(temp, sport->port.membase + UCR4); + + sport->dma_is_enabled = 1; +} + +static void imx_disable_dma(struct imx_port *sport) +{ + unsigned long temp; + + /* clear UCR1 */ + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN); + writel(temp, sport->port.membase + UCR1); + + /* clear UCR2 */ + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_CTSC | UCR2_CTS); + writel(temp, sport->port.membase + UCR2); + + /* clear UCR4 */ + temp = readl(sport->port.membase + UCR4); + temp &= ~UCR4_IDDMAEN; + writel(temp, sport->port.membase + UCR4); + + sport->dma_is_enabled = 0; +} + /* half the RX buffer size */ #define CTSTL 16 static int imx_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - int retval; + int retval, i; unsigned long flags, temp; + retval = clk_prepare_enable(sport->clk_per); + if (retval) + goto error_out1; + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) { + clk_disable_unprepare(sport->clk_per); + goto error_out1; + } + imx_setup_ufcr(sport, 0); /* disable the DREN bit (Data Ready interrupt enable) before @@ -696,22 +1100,20 @@ static int imx_startup(struct uart_port *port) temp |= UCR4_IRSC; /* set the trigger level for CTS */ - temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); - temp |= CTSTL<< UCR4_CTSTL_SHF; + temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF); + temp |= CTSTL << UCR4_CTSTL_SHF; writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); - if (USE_IRDA(sport)) { - /* reset fifo's and state machines */ - int i = 100; - temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_SRST; - writel(temp, sport->port.membase + UCR2); - while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && - (--i > 0)) { - udelay(1); - } - } + /* Reset fifo's and state machines */ + i = 100; + + temp = readl(sport->port.membase + UCR2); + temp &= ~UCR2_SRST; + writel(temp, sport->port.membase + UCR2); + + while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0)) + udelay(1); /* * Allocate the IRQ(s) i.MX1 has three interrupts whereas later @@ -719,25 +1121,25 @@ static int imx_startup(struct uart_port *port) */ if (sport->txirq > 0) { retval = request_irq(sport->rxirq, imx_rxint, 0, - DRIVER_NAME, sport); + dev_name(port->dev), sport); if (retval) goto error_out1; retval = request_irq(sport->txirq, imx_txint, 0, - DRIVER_NAME, sport); + dev_name(port->dev), sport); if (retval) goto error_out2; /* do not use RTS IRQ on IrDA */ if (!USE_IRDA(sport)) { retval = request_irq(sport->rtsirq, imx_rtsint, 0, - DRIVER_NAME, sport); + dev_name(port->dev), sport); if (retval) goto error_out3; } } else { retval = request_irq(sport->port.irq, imx_int, 0, - DRIVER_NAME, sport); + dev_name(port->dev), sport); if (retval) { free_irq(sport->port.irq, sport); goto error_out1; @@ -762,20 +1164,13 @@ static int imx_startup(struct uart_port *port) temp = readl(sport->port.membase + UCR2); temp |= (UCR2_RXEN | UCR2_TXEN); + if (!sport->have_rtscts) + temp |= UCR2_IRTS; writel(temp, sport->port.membase + UCR2); - if (USE_IRDA(sport)) { - /* clear RX-FIFO */ - int i = 64; - while ((--i > 0) && - (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) { - barrier(); - } - } - - if (is_imx21_uart(sport)) { + if (!is_imx1_uart(sport)) { temp = readl(sport->port.membase + UCR3); - temp |= IMX21_UCR3_RXDMUXSEL; + temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP; writel(temp, sport->port.membase + UCR3); } @@ -799,11 +1194,11 @@ static int imx_startup(struct uart_port *port) * Enable modem status interrupts */ imx_enable_ms(&sport->port); - spin_unlock_irqrestore(&sport->port.lock,flags); + spin_unlock_irqrestore(&sport->port.lock, flags); if (USE_IRDA(sport)) { struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; + pdata = dev_get_platdata(sport->port.dev); sport->irda_inv_rx = pdata->irda_inv_rx; sport->irda_inv_tx = pdata->irda_inv_tx; sport->trcv_delay = pdata->transceiver_delay; @@ -829,6 +1224,15 @@ static void imx_shutdown(struct uart_port *port) unsigned long temp; unsigned long flags; + if (sport->dma_is_enabled) { + /* We have to wait for the DMA to finish. */ + wait_event(sport->dma_wait, + !sport->dma_is_rxing && !sport->dma_is_txing); + imx_stop_rx(port); + imx_disable_dma(sport); + imx_uart_dma_exit(sport); + } + spin_lock_irqsave(&sport->port.lock, flags); temp = readl(sport->port.membase + UCR2); temp &= ~(UCR2_TXEN); @@ -837,7 +1241,7 @@ static void imx_shutdown(struct uart_port *port) if (USE_IRDA(sport)) { struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; + pdata = dev_get_platdata(sport->port.dev); if (pdata->irda_enable) pdata->irda_enable(0); } @@ -870,6 +1274,19 @@ static void imx_shutdown(struct uart_port *port) writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); + + clk_disable_unprepare(sport->clk_per); + clk_disable_unprepare(sport->clk_ipg); +} + +static void imx_flush_buffer(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + + if (sport->dma_is_enabled) { + sport->tx_bytes = 0; + dmaengine_terminate_all(sport->dma_chan_tx); + } } static void @@ -909,9 +1326,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ucr2 = UCR2_SRST | UCR2_IRTS; if (termios->c_cflag & CRTSCTS) { - if( sport->have_rtscts ) { + if (sport->have_rtscts) { ucr2 &= ~UCR2_IRTS; ucr2 |= UCR2_CTSC; + + /* Can we enable the DMA support? */ + if (is_imx6q_uart(sport) && !uart_console(port) + && !sport->dma_is_inited) + imx_uart_dma_init(sport); } else { termios->c_cflag &= ~CRTSCTS; } @@ -969,12 +1391,12 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), sport->port.membase + UCR1); - while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) + while (!(readl(sport->port.membase + USR2) & USR2_TXDC)) barrier(); /* then, disable everything */ old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), + writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN), sport->port.membase + UCR2); old_txrxen &= (UCR2_TXEN | UCR2_RXEN); @@ -985,6 +1407,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, */ div = 1; } else { + /* custom-baudrate handling */ + div = sport->port.uartclk / (baud * 16); + if (baud == 38400 && quot != div) + baud = sport->port.uartclk / (quot * 16); + div = sport->port.uartclk / (baud * 16); if (div > 7) div = 7; @@ -1006,12 +1433,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, ufcr = readl(sport->port.membase + UFCR); ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); + if (sport->dte_mode) + ufcr |= UFCR_DCEDTE; writel(ufcr, sport->port.membase + UFCR); writel(num, sport->port.membase + UBIR); writel(denom, sport->port.membase + UBMR); - if (is_imx21_uart(sport)) + if (!is_imx1_uart(sport)) writel(sport->port.uartclk / div / 1000, sport->port.membase + IMX21_ONEMS); @@ -1023,6 +1452,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); + if (sport->dma_is_inited && !sport->dma_is_enabled) + imx_enable_dma(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -1034,44 +1465,13 @@ static const char *imx_type(struct uart_port *port) } /* - * Release the memory region(s) being used by 'port'. - */ -static void imx_release_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *mmres; - - mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mmres->start, resource_size(mmres)); -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int imx_request_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *mmres; - void *ret; - - mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mmres) - return -ENODEV; - - ret = request_mem_region(mmres->start, resource_size(mmres), "imx-uart"); - - return ret ? 0 : -EBUSY; -} - -/* * Configure/autoconfigure the port. */ static void imx_config_port(struct uart_port *port, int flags) { struct imx_port *sport = (struct imx_port *)port; - if (flags & UART_CONFIG_TYPE && - imx_request_port(&sport->port) == 0) + if (flags & UART_CONFIG_TYPE) sport->port.type = PORT_IMX; } @@ -1094,7 +1494,7 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser) ret = -EINVAL; if (sport->port.uartclk / 16 != ser->baud_base) ret = -EINVAL; - if ((void *)sport->port.mapbase != ser->iomem_base) + if (sport->port.mapbase != (unsigned long)ser->iomem_base) ret = -EINVAL; if (sport->port.iobase != ser->port) ret = -EINVAL; @@ -1178,10 +1578,9 @@ static struct uart_ops imx_pops = { .break_ctl = imx_break_ctl, .startup = imx_startup, .shutdown = imx_shutdown, + .flush_buffer = imx_flush_buffer, .set_termios = imx_set_termios, .type = imx_type, - .release_port = imx_release_port, - .request_port = imx_request_port, .config_port = imx_config_port, .verify_port = imx_verify_port, #if defined(CONFIG_CONSOLE_POLL) @@ -1212,9 +1611,25 @@ imx_console_write(struct console *co, const char *s, unsigned int count) struct imx_port *sport = imx_ports[co->index]; struct imx_port_ucrs old_ucr; unsigned int ucr1; - unsigned long flags; + unsigned long flags = 0; + int locked = 1; + int retval; - spin_lock_irqsave(&sport->port.lock, flags); + retval = clk_enable(sport->clk_per); + if (retval) + return; + retval = clk_enable(sport->clk_ipg); + if (retval) { + clk_disable(sport->clk_per); + return; + } + + if (sport->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock_irqsave(&sport->port.lock, flags); + else + spin_lock_irqsave(&sport->port.lock, flags); /* * First, save UCR1/2/3 and then disable interrupts @@ -1241,7 +1656,11 @@ imx_console_write(struct console *co, const char *s, unsigned int count) imx_port_ucrs_restore(&sport->port, &old_ucr); - spin_unlock_irqrestore(&sport->port.lock, flags); + if (locked) + spin_unlock_irqrestore(&sport->port.lock, flags); + + clk_disable(sport->clk_ipg); + clk_disable(sport->clk_per); } /* @@ -1255,7 +1674,7 @@ imx_console_get_options(struct imx_port *sport, int *baud, if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) { /* ok, the port was enabled */ - unsigned int ucr2, ubir,ubmr, uartclk; + unsigned int ucr2, ubir, ubmr, uartclk; unsigned int baud_raw; unsigned int ucfr_rfdiv; @@ -1301,8 +1720,8 @@ imx_console_get_options(struct imx_port *sport, int *baud, *baud = (baud_raw + 50) / 100 * 100; } - if(*baud != baud_raw) - printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n", + if (*baud != baud_raw) + pr_info("Console IMX rounded baud rate from %d to %d\n", baud_raw, *baud); } } @@ -1315,6 +1734,7 @@ imx_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; + int retval; /* * Check whether an invalid uart number has been specified, and @@ -1324,9 +1744,14 @@ imx_console_setup(struct console *co, char *options) if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) co->index = 0; sport = imx_ports[co->index]; - if(sport == NULL) + if (sport == NULL) return -ENODEV; + /* For setting the registers, we only need to enable the ipg clock. */ + retval = clk_prepare_enable(sport->clk_ipg); + if (retval) + goto error_console; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else @@ -1334,7 +1759,20 @@ imx_console_setup(struct console *co, char *options) imx_setup_ufcr(sport, 0); - return uart_set_options(&sport->port, co, baud, parity, bits, flow); + retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); + + clk_disable(sport->clk_ipg); + if (retval) { + clk_unprepare(sport->clk_ipg); + goto error_console; + } + + retval = clk_prepare(sport->clk_per); + if (retval) + clk_disable_unprepare(sport->clk_ipg); + +error_console: + return retval; } static struct uart_driver imx_reg; @@ -1423,6 +1861,9 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "fsl,irda-mode", NULL)) sport->use_irda = 1; + if (of_get_property(np, "fsl,dte-mode", NULL)) + sport->dte_mode = 1; + sport->devdata = of_id->data; return 0; @@ -1438,7 +1879,7 @@ static inline int serial_imx_probe_dt(struct imx_port *sport, static void serial_imx_probe_pdata(struct imx_port *sport, struct platform_device *pdev) { - struct imxuart_platform_data *pdata = pdev->dev.platform_data; + struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev); sport->port.line = pdev->id; sport->devdata = (struct imx_uart_data *) pdev->id_entry->driver_data; @@ -1456,13 +1897,11 @@ static void serial_imx_probe_pdata(struct imx_port *sport, static int serial_imx_probe(struct platform_device *pdev) { struct imx_port *sport; - struct imxuart_platform_data *pdata; void __iomem *base; int ret = 0; struct resource *res; - struct pinctrl *pinctrl; - sport = kzalloc(sizeof(*sport), GFP_KERNEL); + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); if (!sport) return -ENOMEM; @@ -1470,19 +1909,12 @@ static int serial_imx_probe(struct platform_device *pdev) if (ret > 0) serial_imx_probe_pdata(sport, pdev); else if (ret < 0) - goto free; + return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto free; - } - - base = ioremap(res->start, PAGE_SIZE); - if (!base) { - ret = -ENOMEM; - goto free; - } + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); sport->port.dev = &pdev->dev; sport->port.mapbase = res->start; @@ -1500,82 +1932,34 @@ static int serial_imx_probe(struct platform_device *pdev) sport->timer.function = imx_timeout; sport->timer.data = (unsigned long)sport; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret); - goto unmap; - } - sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(sport->clk_ipg)) { ret = PTR_ERR(sport->clk_ipg); dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret); - goto unmap; + return ret; } sport->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(sport->clk_per)) { ret = PTR_ERR(sport->clk_per); dev_err(&pdev->dev, "failed to get per clk: %d\n", ret); - goto unmap; + return ret; } - clk_prepare_enable(sport->clk_per); - clk_prepare_enable(sport->clk_ipg); - sport->port.uartclk = clk_get_rate(sport->clk_per); imx_ports[sport->port.line] = sport; - pdata = pdev->dev.platform_data; - if (pdata && pdata->init) { - ret = pdata->init(pdev); - if (ret) - goto clkput; - } - - ret = uart_add_one_port(&imx_reg, &sport->port); - if (ret) - goto deinit; platform_set_drvdata(pdev, sport); - return 0; -deinit: - if (pdata && pdata->exit) - pdata->exit(pdev); -clkput: - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); -unmap: - iounmap(sport->port.membase); -free: - kfree(sport); - - return ret; + return uart_add_one_port(&imx_reg, &sport->port); } static int serial_imx_remove(struct platform_device *pdev) { - struct imxuart_platform_data *pdata; struct imx_port *sport = platform_get_drvdata(pdev); - pdata = pdev->dev.platform_data; - - platform_set_drvdata(pdev, NULL); - - uart_remove_one_port(&imx_reg, &sport->port); - - clk_disable_unprepare(sport->clk_per); - clk_disable_unprepare(sport->clk_ipg); - - if (pdata && pdata->exit) - pdata->exit(pdev); - - iounmap(sport->port.membase); - kfree(sport); - - return 0; + return uart_remove_one_port(&imx_reg, &sport->port); } static struct platform_driver serial_imx_driver = { @@ -1596,7 +1980,7 @@ static int __init imx_serial_init(void) { int ret; - printk(KERN_INFO "Serial: IMX driver\n"); + pr_info("Serial: IMX driver\n"); ret = uart_register_driver(&imx_reg); if (ret) diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index d8f1d1d5447..6e4c715c5d2 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -1000,7 +1000,7 @@ ioc3_change_speed(struct uart_port *the_port, the_port->ignore_status_mask = N_ALL_INPUT; - state->port.tty->low_latency = 1; + state->port.low_latency = 1; if (iflag & IGNPAR) the_port->ignore_status_mask &= ~(N_PARITY_ERROR @@ -1393,7 +1393,6 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len) */ static int receive_chars(struct uart_port *the_port) { - struct tty_struct *tty; unsigned char ch[MAX_CHARS]; int read_count = 0, read_room, flip = 0; struct uart_state *state = the_port->state; @@ -1403,25 +1402,23 @@ static int receive_chars(struct uart_port *the_port) /* Make sure all the pointers are "good" ones */ if (!state) return 0; - if (!state->port.tty) - return 0; if (!(port->ip_flags & INPUT_ENABLE)) return 0; spin_lock_irqsave(&the_port->lock, pflags); - tty = state->port.tty; read_count = do_read(the_port, ch, MAX_CHARS); if (read_count > 0) { flip = 1; - read_room = tty_insert_flip_string(tty, ch, read_count); + read_room = tty_insert_flip_string(&state->port, ch, + read_count); the_port->icount.rx += read_count; } spin_unlock_irqrestore(&the_port->lock, pflags); if (flip) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&state->port); return read_count; } diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index 3e7da10cebb..1274499850f 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -297,7 +297,7 @@ struct ioc4_serial { struct ioc4_uartregs uart_1; struct ioc4_uartregs uart_2; struct ioc4_uartregs uart_3; -} ioc4_serial; +}; /* UART clock speed */ #define IOC4_SER_XIN_CLK_66 66666667 @@ -1740,7 +1740,7 @@ ioc4_change_speed(struct uart_port *the_port, the_port->ignore_status_mask = N_ALL_INPUT; - state->port.tty->low_latency = 1; + state->port.low_latency = 1; if (iflag & IGNPAR) the_port->ignore_status_mask &= ~(N_PARITY_ERROR @@ -2340,7 +2340,6 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf, */ static void receive_chars(struct uart_port *the_port) { - struct tty_struct *tty; unsigned char ch[IOC4_MAX_CHARS]; int read_count, request_count = IOC4_MAX_CHARS; struct uart_icount *icount; @@ -2350,26 +2349,23 @@ static void receive_chars(struct uart_port *the_port) /* Make sure all the pointers are "good" ones */ if (!state) return; - if (!state->port.tty) - return; spin_lock_irqsave(&the_port->lock, pflags); - tty = state->port.tty; - request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS); + request_count = tty_buffer_request_room(&state->port, IOC4_MAX_CHARS); if (request_count > 0) { icount = &the_port->icount; read_count = do_read(the_port, ch, request_count); if (read_count > 0) { - tty_insert_flip_string(tty, ch, read_count); + tty_insert_flip_string(&state->port, ch, read_count); icount->rx += read_count; } } spin_unlock_irqrestore(&the_port->lock, pflags); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&state->port); } /** @@ -2771,7 +2767,7 @@ ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) * called per card found from IOC4 master module. * @idd: Master module data for this IOC4 */ -int +static int ioc4_serial_attach_one(struct ioc4_driver_data *idd) { unsigned long tmp_addr1; @@ -2883,6 +2879,7 @@ ioc4_serial_attach_one(struct ioc4_driver_data *idd) /* error exits that give back resources */ out5: ioc4_serial_remove_one(idd); + return ret; out4: kfree(soft); out3: diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c index 7b1cda59ebb..99b7b869786 100644 --- a/drivers/tty/serial/ip22zilog.c +++ b/drivers/tty/serial/ip22zilog.c @@ -248,17 +248,12 @@ static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up, #define Rx_BRK 0x0100 /* BREAK event software flag. */ #define Rx_SYS 0x0200 /* SysRq event software flag. */ -static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up, +static bool ip22zilog_receive_chars(struct uart_ip22zilog_port *up, struct zilog_channel *channel) { - struct tty_struct *tty; unsigned char ch, flag; unsigned int r1; - - tty = NULL; - if (up->port.state != NULL && - up->port.state->port.tty != NULL) - tty = up->port.state->port.tty; + bool push = up->port.state != NULL; for (;;) { ch = readb(&channel->control); @@ -312,10 +307,10 @@ static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up if (uart_handle_sysrq_char(&up->port, ch)) continue; - if (tty) + if (push) uart_insert_char(&up->port, r1, Rx_OVR, ch, flag); } - return tty; + return push; } static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, @@ -438,21 +433,20 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) while (up) { struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - struct tty_struct *tty; unsigned char r3; + bool push = false; spin_lock(&up->port.lock); r3 = read_zsreg(channel, R3); /* Channel A */ - tty = NULL; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { writeb(RES_H_IUS, &channel->control); ZSDELAY(); ZS_WSYNC(channel); if (r3 & CHARxIP) - tty = ip22zilog_receive_chars(up, channel); + push = ip22zilog_receive_chars(up, channel); if (r3 & CHAEXT) ip22zilog_status_handle(up, channel); if (r3 & CHATxIP) @@ -460,22 +454,22 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) } spin_unlock(&up->port.lock); - if (tty) - tty_flip_buffer_push(tty); + if (push) + tty_flip_buffer_push(&up->port.state->port); /* Channel B */ up = up->next; channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + push = false; spin_lock(&up->port.lock); - tty = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { writeb(RES_H_IUS, &channel->control); ZSDELAY(); ZS_WSYNC(channel); if (r3 & CHBRxIP) - tty = ip22zilog_receive_chars(up, channel); + push = ip22zilog_receive_chars(up, channel); if (r3 & CHBEXT) ip22zilog_status_handle(up, channel); if (r3 & CHBTxIP) @@ -483,8 +477,8 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) } spin_unlock(&up->port.lock); - if (tty) - tty_flip_buffer_push(tty); + if (push) + tty_flip_buffer_push(&up->port.state->port); up = up->next; } @@ -609,6 +603,8 @@ static void ip22zilog_start_tx(struct uart_port *port) } else { struct circ_buf *xmit = &port->state->xmit; + if (uart_circ_empty(xmit)) + return; writeb(xmit->buf[xmit->tail], &channel->data); ZSDELAY(); ZS_WSYNC(channel); @@ -838,7 +834,7 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag, up->curregs[5] |= Tx8; up->parity_mask = 0xff; break; - }; + } up->curregs[4] &= ~0x0c; if (cflag & CSTOPB) up->curregs[4] |= SB2; @@ -856,7 +852,7 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag, up->port.read_status_mask = Rx_OVR; if (iflag & INPCK) up->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) + if (iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= BRK_ABRT; up->port.ignore_status_mask = 0; diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 4c00c5550b1..27bb75070c9 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -521,6 +521,7 @@ void jsm_input(struct jsm_channel *ch) { struct jsm_board *bd; struct tty_struct *tp; + struct tty_port *port; u32 rmask; u16 head; u16 tail; @@ -536,7 +537,8 @@ void jsm_input(struct jsm_channel *ch) if (!ch) return; - tp = ch->uart_port.state->port.tty; + port = &ch->uart_port.state->port; + tp = port->tty; bd = ch->ch_bd; if(!bd) @@ -594,13 +596,7 @@ void jsm_input(struct jsm_channel *ch) jsm_dbg(READ, &ch->ch_bd->pci_dev, "start 2\n"); - if (data_len <= 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_dbg(READ, &ch->ch_bd->pci_dev, "jsm_input 1\n"); - return; - } - - len = tty_buffer_request_room(tp, data_len); + len = tty_buffer_request_room(port, data_len); n = len; /* @@ -629,16 +625,16 @@ void jsm_input(struct jsm_channel *ch) * format it likes. */ if (*(ch->ch_equeue +tail +i) & UART_LSR_BI) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_BREAK); + tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_BREAK); else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY); + tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_PARITY); else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME); + tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_FRAME); else - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL); + tty_insert_flip_char(port, *(ch->ch_rqueue +tail +i), TTY_NORMAL); } } else { - tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ; + tty_insert_flip_string(port, ch->ch_rqueue + tail, s); } tail += s; n -= s; @@ -652,7 +648,7 @@ void jsm_input(struct jsm_channel *ch) spin_unlock_irqrestore(&ch->ch_lock, lock_flags); /* Tell the tty layer its okay to "eat" the data now */ - tty_flip_buffer_push(tp); + tty_flip_buffer_push(port); jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "finish\n"); } diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c index 6ac2b797a76..cfadf2971b1 100644 --- a/drivers/tty/serial/kgdb_nmi.c +++ b/drivers/tty/serial/kgdb_nmi.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/compiler.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/atomic.h> @@ -23,6 +22,7 @@ #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> +#include <linux/serial_core.h> #include <linux/interrupt.h> #include <linux/hrtimer.h> #include <linux/tick.h> @@ -44,13 +44,22 @@ MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)"); static bool kgdb_nmi_tty_enabled; +static int kgdb_nmi_console_setup(struct console *co, char *options) +{ + /* The NMI console uses the dbg_io_ops to issue console messages. To + * avoid duplicate messages during kdb sessions we must inform kdb's + * I/O utilities that messages sent to the console will automatically + * be displayed on the dbg_io. + */ + dbg_io_ops->is_console = true; + + return 0; +} + static void kgdb_nmi_console_write(struct console *co, const char *s, uint c) { int i; - if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0) - return; - for (i = 0; i < c; i++) dbg_io_ops->write_char(s[i]); } @@ -65,6 +74,7 @@ static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx) static struct console kgdb_nmi_console = { .name = "ttyNMI", + .setup = kgdb_nmi_console_setup, .write = kgdb_nmi_console_write, .device = kgdb_nmi_console_device, .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, @@ -80,29 +90,10 @@ static struct console kgdb_nmi_console = { struct kgdb_nmi_tty_priv { struct tty_port port; - struct tasklet_struct tlet; + struct timer_list timer; STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo; }; -static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port) -{ - return container_of(port, struct kgdb_nmi_tty_priv, port); -} - -/* - * Our debugging console is polled in a tasklet, so we'll check for input - * every tick. In HZ-less mode, we should program the next tick. We have - * to use the lowlevel stuff as no locks should be grabbed. - */ -#ifdef CONFIG_HIGH_RES_TIMERS -static void kgdb_tty_poke(void) -{ - tick_program_event(ktime_get(), 0); -} -#else -static inline void kgdb_tty_poke(void) {} -#endif - static struct tty_port *kgdb_nmi_port; static void kgdb_tty_recv(int ch) @@ -113,14 +104,13 @@ static void kgdb_tty_recv(int ch) if (!kgdb_nmi_port || ch < 0) return; /* - * Can't use port->tty->driver_data as tty might be not there. Tasklet + * Can't use port->tty->driver_data as tty might be not there. Timer * will check for tty and will get the ref, but here we don't have to * do that, and actually, we can't: we're in NMI context, no locks are * possible. */ - priv = kgdb_nmi_port_to_priv(kgdb_nmi_port); + priv = container_of(kgdb_nmi_port, struct kgdb_nmi_tty_priv, port); kfifo_in(&priv->fifo, &c, 1); - kgdb_tty_poke(); } static int kgdb_nmi_poll_one_knock(void) @@ -202,40 +192,37 @@ bool kgdb_nmi_poll_knock(void) static void kgdb_nmi_tty_receiver(unsigned long data) { struct kgdb_nmi_tty_priv *priv = (void *)data; - struct tty_struct *tty; char ch; - tasklet_schedule(&priv->tlet); + priv->timer.expires = jiffies + (HZ/100); + add_timer(&priv->timer); if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo))) return; - /* Port is there, but tty might be hung up, check. */ - tty = tty_port_tty_get(kgdb_nmi_port); - if (!tty) - return; - while (kfifo_out(&priv->fifo, &ch, 1)) - tty_insert_flip_char(priv->port.tty, ch, TTY_NORMAL); - tty_flip_buffer_push(priv->port.tty); - - tty_kref_put(tty); + tty_insert_flip_char(&priv->port, ch, TTY_NORMAL); + tty_flip_buffer_push(&priv->port); } static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty) { - struct kgdb_nmi_tty_priv *priv = tty->driver_data; + struct kgdb_nmi_tty_priv *priv = + container_of(port, struct kgdb_nmi_tty_priv, port); kgdb_nmi_port = port; - tasklet_schedule(&priv->tlet); + priv->timer.expires = jiffies + (HZ/100); + add_timer(&priv->timer); + return 0; } static void kgdb_nmi_tty_shutdown(struct tty_port *port) { - struct kgdb_nmi_tty_priv *priv = port->tty->driver_data; + struct kgdb_nmi_tty_priv *priv = + container_of(port, struct kgdb_nmi_tty_priv, port); - tasklet_kill(&priv->tlet); + del_timer(&priv->timer); kgdb_nmi_port = NULL; } @@ -254,7 +241,7 @@ static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty) return -ENOMEM; INIT_KFIFO(priv->fifo); - tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv); + setup_timer(&priv->timer, kgdb_nmi_tty_receiver, (unsigned long)priv); tty_port_init(&priv->port); priv->port.ops = &kgdb_nmi_tty_port_ops; tty->driver_data = priv; diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 10020547c60..a260cde743e 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -45,7 +45,7 @@ static int kgdboc_reset_connect(struct input_handler *handler, { input_reset_device(dev); - /* Retrun an error - we do not want to bind, just to reset */ + /* Return an error - we do not want to bind, just to reset */ return -ENODEV; } diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 02da071fe1e..88d01e0bb0c 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -162,21 +162,16 @@ lqasc_enable_ms(struct uart_port *port) static int lqasc_rx_chars(struct uart_port *port) { - struct tty_struct *tty = tty_port_tty_get(&port->state->port); + struct tty_port *tport = &port->state->port; unsigned int ch = 0, rsr = 0, fifocnt; - if (!tty) { - dev_dbg(port->dev, "%s:tty is busy now", __func__); - return -EBUSY; - } - fifocnt = - ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; + fifocnt = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; while (fifocnt--) { u8 flag = TTY_NORMAL; ch = ltq_r8(port->membase + LTQ_ASC_RBUF); rsr = (ltq_r32(port->membase + LTQ_ASC_STATE) & ASCSTATE_ANY) | UART_DUMMY_UER_RX; - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); port->icount.rx++; /* @@ -208,7 +203,7 @@ lqasc_rx_chars(struct uart_port *port) } if ((rsr & port->ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tport, ch, flag); if (rsr & ASCSTATE_ROE) /* @@ -216,11 +211,12 @@ lqasc_rx_chars(struct uart_port *port) * immediately, and doesn't affect the current * character */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); } + if (ch != 0) - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty_flip_buffer_push(tport); + return 0; } @@ -322,7 +318,7 @@ lqasc_startup(struct uart_port *port) struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); int retval; - if (ltq_port->clk) + if (!IS_ERR(ltq_port->clk)) clk_enable(ltq_port->clk); port->uartclk = clk_get_rate(ltq_port->fpiclk); @@ -390,7 +386,7 @@ lqasc_shutdown(struct uart_port *port) port->membase + LTQ_ASC_RXFCON); ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, port->membase + LTQ_ASC_TXFCON); - if (ltq_port->clk) + if (!IS_ERR(ltq_port->clk)) clk_disable(ltq_port->clk); } @@ -640,6 +636,9 @@ lqasc_console_setup(struct console *co, char *options) port = <q_port->port; + if (!IS_ERR(ltq_port->clk)) + clk_enable(ltq_port->clk); + port->uartclk = clk_get_rate(ltq_port->fpiclk); if (options) diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index 0e86bff3fe2..701644f0682 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -257,17 +257,8 @@ static void __serial_uart_flush(struct uart_port *port) static void __serial_lpc32xx_rx(struct uart_port *port) { + struct tty_port *tport = &port->state->port; unsigned int tmp, flag; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - - if (!tty) { - /* Discard data: no tty available */ - while (!(readl(LPC32XX_HSUART_FIFO(port->membase)) & - LPC32XX_HSU_RX_EMPTY)) - ; - - return; - } /* Read data from FIFO and push into terminal */ tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); @@ -281,15 +272,17 @@ static void __serial_lpc32xx_rx(struct uart_port *port) LPC32XX_HSUART_IIR(port->membase)); port->icount.frame++; flag = TTY_FRAME; - tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_insert_flip_char(tport, 0, TTY_FRAME); } - tty_insert_flip_char(tty, (tmp & 0xFF), flag); + tty_insert_flip_char(tport, (tmp & 0xFF), flag); tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); } - tty_flip_buffer_push(tty); - tty_kref_put(tty); + + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); } static void __serial_lpc32xx_tx(struct uart_port *port) @@ -332,7 +325,7 @@ exit_tx: static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); + struct tty_port *tport = &port->state->port; u32 status; spin_lock(&port->lock); @@ -356,18 +349,13 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) writel(LPC32XX_HSU_RX_OE_INT, LPC32XX_HSUART_IIR(port->membase)); port->icount.overrun++; - if (tty) { - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_schedule_flip(tty); - } + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + tty_schedule_flip(tport); } /* Data received? */ - if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) { + if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) __serial_lpc32xx_rx(port); - if (tty) - tty_flip_buffer_push(tty); - } /* Transmit data request? */ if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) { @@ -376,7 +364,6 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) } spin_unlock(&port->lock); - tty_kref_put(tty); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index b13949ad340..5702828fb62 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -266,9 +266,11 @@ static void m32r_sio_start_tx(struct uart_port *port) if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; + if (!uart_circ_empty(xmit)) { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } } while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); #else @@ -300,7 +302,7 @@ static void m32r_sio_enable_ms(struct uart_port *port) static void receive_chars(struct uart_sio_port *up, int *status) { - struct tty_struct *tty = up->port.state->port.tty; + struct tty_port *port = &up->port.state->port; unsigned char ch; unsigned char flag; int max_count = 256; @@ -355,7 +357,7 @@ static void receive_chars(struct uart_sio_port *up, int *status) if (uart_handle_sysrq_char(&up->port, ch)) goto ignore_char; if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(port, ch, flag); if (*status & UART_LSR_OE) { /* @@ -363,12 +365,15 @@ static void receive_chars(struct uart_sio_port *up, int *status) * immediately, and doesn't affect the current * character. */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); } ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); + + spin_unlock(&up->port.lock); + tty_flip_buffer_push(port); + spin_lock(&up->port.lock); } static void transmit_chars(struct uart_sio_port *up) @@ -734,7 +739,7 @@ static void m32r_sio_set_termios(struct uart_port *port, up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= UART_LSR_BI; /* diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 7ce3197087b..79f9a9eff54 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -179,8 +179,7 @@ static void max3100_work(struct work_struct *w); static void max3100_dowork(struct max3100_port *s) { - if (!s->force_end_work && !work_pending(&s->work) && - !freezing(current) && !s->suspending) + if (!s->force_end_work && !freezing(current) && !s->suspending) queue_work(s->workqueue, &s->work); } @@ -311,8 +310,8 @@ static void max3100_work(struct work_struct *w) } } - if (rxchars > 16 && s->port.state->port.tty != NULL) { - tty_flip_buffer_push(s->port.state->port.tty); + if (rxchars > 16) { + tty_flip_buffer_push(&s->port.state->port); rxchars = 0; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -324,8 +323,8 @@ static void max3100_work(struct work_struct *w) (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)))); - if (rxchars > 0 && s->port.state->port.tty != NULL) - tty_flip_buffer_push(s->port.state->port.tty); + if (rxchars > 0) + tty_flip_buffer_push(&s->port.state->port); } static irqreturn_t max3100_irq(int irqno, void *dev_id) @@ -530,7 +529,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, MAX3100_STATUS_OE; /* we are sending char from a workqueue so enable */ - s->port.state->port.tty->low_latency = 1; + s->port.state->port.low_latency = 1; if (s->poll_time > 0) del_timer_sync(&s->timer); @@ -779,8 +778,8 @@ static int max3100_probe(struct spi_device *spi) max3100s[i]->spi = spi; max3100s[i]->irq = spi->irq; spin_lock_init(&max3100s[i]->conf_lock); - dev_set_drvdata(&spi->dev, max3100s[i]); - pdata = spi->dev.platform_data; + spi_set_drvdata(spi, max3100s[i]); + pdata = dev_get_platdata(&spi->dev); max3100s[i]->crystal = pdata->crystal; max3100s[i]->loopback = pdata->loopback; max3100s[i]->poll_time = pdata->poll_time * HZ / 1000; @@ -820,7 +819,7 @@ static int max3100_probe(struct spi_device *spi) static int max3100_remove(struct spi_device *spi) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = spi_get_drvdata(spi); int i; mutex_lock(&max3100s_lock); @@ -850,11 +849,11 @@ static int max3100_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP -static int max3100_suspend(struct spi_device *spi, pm_message_t state) +static int max3100_suspend(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -875,9 +874,9 @@ static int max3100_suspend(struct spi_device *spi, pm_message_t state) return 0; } -static int max3100_resume(struct spi_device *spi) +static int max3100_resume(struct device *dev) { - struct max3100_port *s = dev_get_drvdata(&spi->dev); + struct max3100_port *s = dev_get_drvdata(dev); dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -895,21 +894,21 @@ static int max3100_resume(struct spi_device *spi) return 0; } +static SIMPLE_DEV_PM_OPS(max3100_pm_ops, max3100_suspend, max3100_resume); +#define MAX3100_PM_OPS (&max3100_pm_ops) + #else -#define max3100_suspend NULL -#define max3100_resume NULL +#define MAX3100_PM_OPS NULL #endif static struct spi_driver max3100_driver = { .driver = { .name = "max3100", .owner = THIS_MODULE, + .pm = MAX3100_PM_OPS, }, - .probe = max3100_probe, .remove = max3100_remove, - .suspend = max3100_suspend, - .resume = max3100_resume, }; module_spi_driver(max3100_driver); diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index a801f6872ca..ba285cd45b5 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,7 +1,7 @@ /* - * Maxim (Dallas) MAX3107/8 serial driver + * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver * - * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> + * Copyright (C) 2012-2014 Alexander Shiyan <shc_work@mail.ru> * * Based on max3100.c, by Christian Pellegrin <chripell@evolware.org> * Based on max3110.c, by Feng Tang <feng.tang@intel.com> @@ -13,20 +13,23 @@ * (at your option) any later version. */ -/* TODO: MAX3109 support (Dual) */ -/* TODO: MAX14830 support (Quad) */ - -#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> #include <linux/device.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> #include <linux/serial_core.h> #include <linux/serial.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/regmap.h> -#include <linux/gpio.h> #include <linux/spi/spi.h> -#include <linux/platform_data/max310x.h> +#include <linux/uaccess.h> +#define MAX310X_NAME "max310x" #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 @@ -37,7 +40,8 @@ #define MAX310X_IRQSTS_REG (0x02) /* IRQ status */ #define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */ #define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */ -#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */ +#define MAX310X_REG_05 (0x05) +#define MAX310X_SPCHR_IRQEN_REG MAX310X_REG_05 /* Special char IRQ en */ #define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */ #define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */ #define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */ @@ -63,8 +67,15 @@ #define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */ #define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ #define MAX310X_CLKSRC_REG (0x1e) /* Clock source */ -/* Only present in MAX3107 */ -#define MAX3107_REVID_REG (0x1f) /* Revision identification */ +#define MAX310X_REG_1F (0x1f) + +#define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */ + +#define MAX310X_GLOBALIRQ_REG MAX310X_REG_1F /* Global IRQ (RO) */ +#define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */ + +/* Extended registers */ +#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ /* IRQ register bits */ #define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ @@ -152,10 +163,6 @@ /* IRDA register bits */ #define MAX310X_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ #define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ -#define MAX310X_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ -#define MAX310X_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ -#define MAX310X_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ -#define MAX310X_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ /* Flow control trigger level register masks */ #define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ @@ -211,26 +218,6 @@ * XOFF2 */ -/* GPIO configuration register bits */ -#define MAX310X_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ -#define MAX310X_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ -#define MAX310X_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ -#define MAX310X_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ -#define MAX310X_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ -#define MAX310X_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ -#define MAX310X_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ -#define MAX310X_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ - -/* GPIO DATA register bits */ -#define MAX310X_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ -#define MAX310X_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ -#define MAX310X_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ -#define MAX310X_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ -#define MAX310X_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ -#define MAX310X_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ -#define MAX310X_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ -#define MAX310X_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ - /* PLL configuration register masks */ #define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */ #define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */ @@ -246,58 +233,209 @@ #define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ #define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ +/* Global commands */ +#define MAX310X_EXTREG_ENBL (0xce) +#define MAX310X_EXTREG_DSBL (0xcd) + /* Misc definitions */ #define MAX310X_FIFO_SIZE (128) +#define MAX310x_REV_MASK (0xfc) /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) -#define MAX3107_REV_MASK (0xfe) - -/* IRQ status bits definitions */ -#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \ - MAX310X_IRQ_TXEMPTY_BIT) -#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \ - MAX310X_IRQ_RXEMPTY_BIT) - -/* Supported chip types */ -enum { - MAX310X_TYPE_MAX3107 = 3107, - MAX310X_TYPE_MAX3108 = 3108, + +/* MAX3109 specific */ +#define MAX3109_REV_ID (0xc0) + +/* MAX14830 specific */ +#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */ +#define MAX14830_REV_ID (0xb0) + +struct max310x_devtype { + char name[9]; + int nr; + int (*detect)(struct device *); + void (*power)(struct uart_port *, int); }; -struct max310x_port { - struct uart_driver uart; +struct max310x_one { struct uart_port port; + struct work_struct tx_work; + struct work_struct md_work; +}; - const char *name; - int uartclk; - - unsigned int nr_gpio; +struct max310x_port { + struct uart_driver uart; + struct max310x_devtype *devtype; + struct regmap *regmap; + struct mutex mutex; + struct clk *clk; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif + struct max310x_one p[0]; +}; - struct regmap *regmap; - struct regmap_config regcfg; +static u8 max310x_port_read(struct uart_port *port, u8 reg) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; - struct workqueue_struct *wq; - struct work_struct tx_work; + regmap_read(s->regmap, port->iobase + reg, &val); + + return val; +} + +static void max310x_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, port->iobase + reg, val); +} + +static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, port->iobase + reg, mask, val); +} + +static int max3107_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; + + if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static int max3108_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + /* MAX3108 have not REV ID register, we just check default value + * from clocksource register to make sure everything works. + */ + ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); + if (ret) + return ret; + + if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) { + dev_err(dev, "%s not present\n", s->devtype->name); + return -ENODEV; + } + + return 0; +} + +static int max3109_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; - struct mutex max310x_mutex; + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; - struct max310x_pdata *pdata; + if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static void max310x_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_FORCESLEEP_BIT, + on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT); + if (on) + msleep(50); +} + +static int max14830_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, + MAX310X_EXTREG_ENBL); + if (ret) + return ret; + + regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); + regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); + if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static void max14830_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_BRGCFG_REG, + MAX14830_BRGCFG_CLKDIS_BIT, + on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT); + if (on) + msleep(50); +} + +static const struct max310x_devtype max3107_devtype = { + .name = "MAX3107", + .nr = 1, + .detect = max3107_detect, + .power = max310x_power, +}; + +static const struct max310x_devtype max3108_devtype = { + .name = "MAX3108", + .nr = 1, + .detect = max3108_detect, + .power = max310x_power, +}; + +static const struct max310x_devtype max3109_devtype = { + .name = "MAX3109", + .nr = 2, + .detect = max3109_detect, + .power = max310x_power, }; -static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) +static const struct max310x_devtype max14830_devtype = { + .name = "MAX14830", + .nr = 4, + .detect = max14830_detect, + .power = max14830_power, +}; + +static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: case MAX310X_STS_IRQSTS_REG: case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: - case MAX3107_REVID_REG: /* Only available on MAX3107 */ return false; default: break; @@ -308,7 +446,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) static bool max310x_reg_volatile(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: @@ -317,6 +455,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: case MAX310X_GPIODATA_REG: + case MAX310X_BRGDIVLSB_REG: + case MAX310X_REG_05: + case MAX310X_REG_1F: return true; default: break; @@ -327,7 +468,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) static bool max310x_reg_precious(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: @@ -340,42 +481,33 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) return false; } -static void max310x_set_baud(struct max310x_port *s, int baud) +static int max310x_set_baud(struct uart_port *port, int baud) { - unsigned int mode = 0, div = s->uartclk / baud; + unsigned int mode = 0, clk = port->uartclk, div = clk / baud; - if (!(div / 16)) { + /* Check for minimal value for divider */ + if (div < 16) + div = 16; + + if (clk % baud && (div / 16) < 0x8000) { /* Mode x2 */ mode = MAX310X_BRGCFG_2XMODE_BIT; - div = (s->uartclk * 2) / baud; - } - - if (!(div / 16)) { - /* Mode x4 */ - mode = MAX310X_BRGCFG_4XMODE_BIT; - div = (s->uartclk * 4) / baud; + clk = port->uartclk * 2; + div = clk / baud; + + if (clk % baud && (div / 16) < 0x8000) { + /* Mode x4 */ + mode = MAX310X_BRGCFG_4XMODE_BIT; + clk = port->uartclk * 4; + div = clk / baud; + } } - regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG, - ((div / 16) >> 8) & 0xff); - regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff); - regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode); -} - -static void max310x_wait_pll(struct max310x_port *s) -{ - int tryes = 1000; - - /* Wait for PLL only if crystal is used */ - if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) { - unsigned int sts = 0; + max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8); + max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16); + max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode); - while (tryes--) { - regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts); - if (sts & MAX310X_STS_CLKREADY_BIT) - break; - } - } + return DIV_ROUND_CLOSEST(clk, div); } static int max310x_update_best_err(unsigned long f, long *besterr) @@ -391,18 +523,19 @@ static int max310x_update_best_err(unsigned long f, long *besterr) return 1; } -static int max310x_set_ref_clk(struct max310x_port *s) +static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq, + bool xtal) { unsigned int div, clksrc, pllcfg = 0; long besterr = -1; - unsigned long fdiv, fmul, bestfreq = s->pdata->frequency; + unsigned long fdiv, fmul, bestfreq = freq; /* First, update error without PLL */ - max310x_update_best_err(s->pdata->frequency, &besterr); + max310x_update_best_err(freq, &besterr); /* Try all possible PLL dividers */ for (div = 1; (div <= 63) && besterr; div++) { - fdiv = DIV_ROUND_CLOSEST(s->pdata->frequency, div); + fdiv = DIV_ROUND_CLOSEST(freq, div); /* Try multiplier 6 */ fmul = fdiv * 6; @@ -435,10 +568,7 @@ static int max310x_set_ref_clk(struct max310x_port *s) } /* Configure clock source */ - if (s->pdata->driver_flags & MAX310X_EXT_CLK) - clksrc = MAX310X_CLKSRC_EXTCLK_BIT; - else - clksrc = MAX310X_CLKSRC_CRYST_BIT; + clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT; /* Configure PLL */ if (pllcfg) { @@ -449,53 +579,49 @@ static int max310x_set_ref_clk(struct max310x_port *s) regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc); - if (pllcfg) - max310x_wait_pll(s); - - dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq); + /* Wait for crystal */ + if (pllcfg && xtal) + msleep(10); return (int)bestfreq; } -static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) +static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) { - unsigned int sts = 0, ch = 0, flag; - struct tty_struct *tty = tty_port_tty_get(&s->port.state->port); - - if (!tty) - return; + unsigned int sts, ch, flag; - if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) { - dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + if (unlikely(rxlen >= port->fifosize)) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun\n", + port->line); + port->icount.buf_overrun++; /* Ensure sanity of RX level */ - rxlen = MAX310X_FIFO_SIZE; + rxlen = port->fifosize; } - dev_dbg(s->port.dev, "RX Len = %u\n", rxlen); - while (rxlen--) { - regmap_read(s->regmap, MAX310X_RHR_REG, &ch); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts); + ch = max310x_port_read(port, MAX310X_RHR_REG); + sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT | MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT; - s->port.icount.rx++; + port->icount.rx++; flag = TTY_NORMAL; if (unlikely(sts)) { if (sts & MAX310X_LSR_RXBRK_BIT) { - s->port.icount.brk++; - if (uart_handle_break(&s->port)) + port->icount.brk++; + if (uart_handle_break(port)) continue; } else if (sts & MAX310X_LSR_RXPAR_BIT) - s->port.icount.parity++; + port->icount.parity++; else if (sts & MAX310X_LSR_FRERR_BIT) - s->port.icount.frame++; + port->icount.frame++; else if (sts & MAX310X_LSR_RXOVR_BIT) - s->port.icount.overrun++; + port->icount.overrun++; - sts &= s->port.read_status_mask; + sts &= port->read_status_mask; if (sts & MAX310X_LSR_RXBRK_BIT) flag = TTY_BREAK; else if (sts & MAX310X_LSR_RXPAR_BIT) @@ -506,131 +632,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) flag = TTY_OVERRUN; } - if (uart_handle_sysrq_char(s->port, ch)) + if (uart_handle_sysrq_char(port, ch)) continue; - if (sts & s->port.ignore_status_mask) + if (sts & port->ignore_status_mask) continue; - uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT, - ch, flag); + uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag); } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); } -static void max310x_handle_tx(struct max310x_port *s) +static void max310x_handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &s->port.state->xmit; - unsigned int txlen = 0, to_send; + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send; - if (unlikely(s->port.x_char)) { - regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char); - s->port.icount.tx++; - s->port.x_char = 0; + if (unlikely(port->x_char)) { + max310x_port_write(port, MAX310X_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; /* Get length of data pending in circular buffer */ to_send = uart_circ_chars_pending(xmit); if (likely(to_send)) { /* Limit to size of TX FIFO */ - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen); - txlen = MAX310X_FIFO_SIZE - txlen; + txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + txlen = port->fifosize - txlen; to_send = (to_send > txlen) ? txlen : to_send; - dev_dbg(s->port.dev, "TX Len = %u\n", to_send); - /* Add data to send */ - s->port.icount.tx += to_send; + port->icount.tx += to_send; while (to_send--) { - regmap_write(s->regmap, MAX310X_THR_REG, - xmit->buf[xmit->tail]); + max310x_port_write(port, MAX310X_THR_REG, + xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - }; + } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); + uart_write_wakeup(port); } -static irqreturn_t max310x_ist(int irq, void *dev_id) +static void max310x_port_irq(struct max310x_port *s, int portno) { - struct max310x_port *s = (struct max310x_port *)dev_id; - unsigned int ists = 0, lsr = 0, rxlen = 0; + struct uart_port *port = &s->p[portno].port; - mutex_lock(&s->max310x_mutex); + do { + unsigned int ists, lsr, rxlen; - for (;;) { /* Read IRQ status & RX FIFO level */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr); - regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen); - if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen) + ists = max310x_port_read(port, MAX310X_IRQSTS_REG); + rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG); + if (!ists && !rxlen) break; - dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists); - - if (rxlen) - max310x_handle_rx(s, rxlen); - if (ists & MAX310X_IRQ_TX) - max310x_handle_tx(s); - if (ists & MAX310X_IRQ_CTS_BIT) - uart_handle_cts_change(&s->port, + if (ists & MAX310X_IRQ_CTS_BIT) { + lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); + uart_handle_cts_change(port, !!(lsr & MAX310X_LSR_CTS_BIT)); - } + } + if (rxlen) + max310x_handle_rx(port, rxlen); + if (ists & MAX310X_IRQ_TXEMPTY_BIT) { + mutex_lock(&s->mutex); + max310x_handle_tx(port); + mutex_unlock(&s->mutex); + } + } while (1); +} + +static irqreturn_t max310x_ist(int irq, void *dev_id) +{ + struct max310x_port *s = (struct max310x_port *)dev_id; - mutex_unlock(&s->max310x_mutex); + if (s->uart.nr > 1) { + do { + unsigned int val = ~0; + + WARN_ON_ONCE(regmap_read(s->regmap, + MAX310X_GLOBALIRQ_REG, &val)); + val = ((1 << s->uart.nr) - 1) & ~val; + if (!val) + break; + max310x_port_irq(s, fls(val) - 1); + } while (1); + } else + max310x_port_irq(s, 0); return IRQ_HANDLED; } static void max310x_wq_proc(struct work_struct *ws) { - struct max310x_port *s = container_of(ws, struct max310x_port, tx_work); + struct max310x_one *one = container_of(ws, struct max310x_one, tx_work); + struct max310x_port *s = dev_get_drvdata(one->port.dev); - mutex_lock(&s->max310x_mutex); - max310x_handle_tx(s); - mutex_unlock(&s->max310x_mutex); + mutex_lock(&s->mutex); + max310x_handle_tx(&one->port); + mutex_unlock(&s->mutex); } static void max310x_start_tx(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); - - queue_work(s->wq, &s->tx_work); -} + struct max310x_one *one = container_of(port, struct max310x_one, port); -static void max310x_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} - -static void max310x_stop_rx(struct uart_port *port) -{ - /* Do nothing */ + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); } static unsigned int max310x_tx_empty(struct uart_port *port) { - unsigned int val = 0; - struct max310x_port *s = container_of(port, struct max310x_port, port); + unsigned int lvl, sts; - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val); - mutex_unlock(&s->max310x_mutex); + lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + sts = max310x_port_read(port, MAX310X_IRQSTS_REG); - return val ? 0 : TIOCSER_TEMT; -} - -static void max310x_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ + return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0; } static unsigned int max310x_get_mctrl(struct uart_port *port) @@ -641,37 +765,39 @@ static unsigned int max310x_get_mctrl(struct uart_port *port) return TIOCM_DSR | TIOCM_CAR; } +static void max310x_md_proc(struct work_struct *ws) +{ + struct max310x_one *one = container_of(ws, struct max310x_one, md_work); + + max310x_port_update(&one->port, MAX310X_MODE2_REG, + MAX310X_MODE2_LOOPBACK_BIT, + (one->port.mctrl & TIOCM_LOOP) ? + MAX310X_MODE2_LOOPBACK_BIT : 0); +} + static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) { - /* DCD and DSR are not wired and CTS/RTS is hadnled automatically - * so do nothing - */ + struct max310x_one *one = container_of(port, struct max310x_one, port); + + schedule_work(&one->md_work); } static void max310x_break_ctl(struct uart_port *port, int break_state) { - struct max310x_port *s = container_of(port, struct max310x_port, port); - - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_LCR_REG, - MAX310X_LCR_TXBREAK_BIT, - break_state ? MAX310X_LCR_TXBREAK_BIT : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_LCR_REG, + MAX310X_LCR_TXBREAK_BIT, + break_state ? MAX310X_LCR_TXBREAK_BIT : 0); } static void max310x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct max310x_port *s = container_of(port, struct max310x_port, port); unsigned int lcr, flow = 0; int baud; - mutex_lock(&s->max310x_mutex); - /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; - termios->c_iflag &= ~IXANY; /* Word size */ switch (termios->c_cflag & CSIZE) { @@ -702,14 +828,14 @@ static void max310x_set_termios(struct uart_port *port, lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */ /* Update LCR register */ - regmap_write(s->regmap, MAX310X_LCR_REG, lcr); + max310x_port_write(port, MAX310X_LCR_REG, lcr); /* Set read status mask */ port->read_status_mask = MAX310X_LSR_RXOVR_BIT; if (termios->c_iflag & INPCK) port->read_status_mask |= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= MAX310X_LSR_RXBRK_BIT; /* Set status ignore mask */ @@ -723,8 +849,8 @@ static void max310x_set_termios(struct uart_port *port, MAX310X_LSR_RXBRK_BIT; /* Configure flow control */ - regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]); - regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); + max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]); + max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); if (termios->c_cflag & CRTSCTS) flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT | MAX310X_FLOWCTRL_AUTORTS_BIT; @@ -734,7 +860,7 @@ static void max310x_set_termios(struct uart_port *port, if (termios->c_iflag & IXOFF) flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT | MAX310X_FLOWCTRL_SWFLOWEN_BIT; - regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow); + max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow); /* Get baud rate generator configuration */ baud = uart_get_baud_rate(port, termios, old, @@ -742,103 +868,110 @@ static void max310x_set_termios(struct uart_port *port, port->uartclk / 4); /* Setup baudrate generator */ - max310x_set_baud(s, baud); + baud = max310x_set_baud(port, baud); /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); - - mutex_unlock(&s->max310x_mutex); } -static int max310x_startup(struct uart_port *port) +static int max310x_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) { - unsigned int val, line = port->line; - struct max310x_port *s = container_of(port, struct max310x_port, port); - - if (s->pdata->suspend) - s->pdata->suspend(0); +#if defined(TIOCSRS485) && defined(TIOCGRS485) + struct serial_rs485 rs485; + unsigned int val; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) + return -EFAULT; + if (rs485.delay_rts_before_send > 0x0f || + rs485.delay_rts_after_send > 0x0f) + return -ERANGE; + val = (rs485.delay_rts_before_send << 4) | + rs485.delay_rts_after_send; + max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val); + if (rs485.flags & SER_RS485_ENABLED) { + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + MAX310X_MODE1_TRNSCVCTRL_BIT); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, + MAX310X_MODE2_ECHOSUPR_BIT); + } else { + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, 0); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_ECHOSUPR_BIT, 0); + } + return 0; + case TIOCGRS485: + memset(&rs485, 0, sizeof(rs485)); + val = max310x_port_read(port, MAX310X_MODE1_REG); + rs485.flags = (val & MAX310X_MODE1_TRNSCVCTRL_BIT) ? + SER_RS485_ENABLED : 0; + rs485.flags |= SER_RS485_RTS_ON_SEND; + val = max310x_port_read(port, MAX310X_HDPIXDELAY_REG); + rs485.delay_rts_before_send = val >> 4; + rs485.delay_rts_after_send = val & 0x0f; + if (copy_to_user((void __user *)arg, &rs485, sizeof(rs485))) + return -EFAULT; + return 0; + default: + break; + } +#endif - mutex_lock(&s->max310x_mutex); + return -ENOIOCTLCMD; +} - /* Configure baud rate, 9600 as default */ - max310x_set_baud(s, 9600); +static int max310x_startup(struct uart_port *port) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + unsigned int val; - /* Configure LCR register, 8N1 mode by default */ - val = MAX310X_LCR_WORD_LEN_8; - regmap_write(s->regmap, MAX310X_LCR_REG, val); + s->devtype->power(port, 1); /* Configure MODE1 register */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) - ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); - - /* Configure MODE2 register */ - val = MAX310X_MODE2_RXEMPTINV_BIT; - if (s->pdata->uart_flags[line] & MAX310X_LOOPBACK) - val |= MAX310X_MODE2_LOOPBACK_BIT; - if (s->pdata->uart_flags[line] & MAX310X_ECHO_SUPRESS) - val |= MAX310X_MODE2_ECHOSUPR_BIT; - - /* Reset FIFOs */ - val |= MAX310X_MODE2_FIFORST_BIT; - regmap_write(s->regmap, MAX310X_MODE2_REG, val); - - /* Configure FIFO trigger level register */ - /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */ - val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64); - regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val); + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, 0); + + /* Configure MODE2 register & Reset FIFOs*/ + val = MAX310X_MODE2_RXEMPTINV_BIT | MAX310X_MODE2_FIFORST_BIT; + max310x_port_write(port, MAX310X_MODE2_REG, val); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_FIFORST_BIT, 0); /* Configure flow control levels */ /* Flow control halt level 96, resume level 48 */ - val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96); - regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val); - - /* Clear timeout register */ - regmap_write(s->regmap, MAX310X_RXTO_REG, 0); - - /* Configure LSR interrupt enable register */ - /* Enable RX timeout interrupt */ - val = MAX310X_LSR_RXTO_BIT; - regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val); - - /* Clear FIFO reset */ - regmap_update_bits(s->regmap, MAX310X_MODE2_REG, - MAX310X_MODE2_FIFORST_BIT, 0); - - /* Clear IRQ status register by reading it */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val); + max310x_port_write(port, MAX310X_FLOWLVL_REG, + MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96)); - /* Configure interrupt enable register */ - /* Enable CTS change interrupt */ - val = MAX310X_IRQ_CTS_BIT; - /* Enable RX, TX interrupts */ - val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX; - regmap_write(s->regmap, MAX310X_IRQEN_REG, val); + /* Clear IRQ status register */ + max310x_port_read(port, MAX310X_IRQSTS_REG); - mutex_unlock(&s->max310x_mutex); + /* Enable RX, TX, CTS change interrupts */ + val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT; + max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT); return 0; } static void max310x_shutdown(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); /* Disable all interrupts */ - mutex_lock(&s->max310x_mutex); - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_write(port, MAX310X_IRQEN_REG, 0); - if (s->pdata->suspend) - s->pdata->suspend(1); + s->devtype->power(port, 0); } static const char *max310x_type(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_MAX310X) ? s->name : NULL; + return (port->type == PORT_MAX310X) ? s->devtype->name : NULL; } static int max310x_request_port(struct uart_port *port) @@ -847,125 +980,102 @@ static int max310x_request_port(struct uart_port *port) return 0; } -static void max310x_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - static void max310x_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_MAX310X; } -static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser) +static int max310x_verify_port(struct uart_port *port, struct serial_struct *s) { - if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X)) - return 0; - if (ser->irq == port->irq) - return 0; + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; - return -EINVAL; + return 0; } -static struct uart_ops max310x_ops = { +static void max310x_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops max310x_ops = { .tx_empty = max310x_tx_empty, .set_mctrl = max310x_set_mctrl, .get_mctrl = max310x_get_mctrl, - .stop_tx = max310x_stop_tx, + .stop_tx = max310x_null_void, .start_tx = max310x_start_tx, - .stop_rx = max310x_stop_rx, - .enable_ms = max310x_enable_ms, + .stop_rx = max310x_null_void, + .enable_ms = max310x_null_void, .break_ctl = max310x_break_ctl, .startup = max310x_startup, .shutdown = max310x_shutdown, .set_termios = max310x_set_termios, .type = max310x_type, .request_port = max310x_request_port, - .release_port = max310x_release_port, + .release_port = max310x_null_void, .config_port = max310x_config_port, .verify_port = max310x_verify_port, + .ioctl = max310x_ioctl, }; -static int max310x_suspend(struct spi_device *spi, pm_message_t state) +static int __maybe_unused max310x_suspend(struct device *dev) { - int ret; - struct max310x_port *s = dev_get_drvdata(&spi->dev); - - dev_dbg(&spi->dev, "Suspend\n"); - - ret = uart_suspend_port(&s->uart, &s->port); - - mutex_lock(&s->max310x_mutex); - - /* Enable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - MAX310X_MODE1_FORCESLEEP_BIT); - - mutex_unlock(&s->max310x_mutex); + struct max310x_port *s = dev_get_drvdata(dev); + int i; - if (s->pdata->suspend) - s->pdata->suspend(1); + for (i = 0; i < s->uart.nr; i++) { + uart_suspend_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } - return ret; + return 0; } -static int max310x_resume(struct spi_device *spi) +static int __maybe_unused max310x_resume(struct device *dev) { - struct max310x_port *s = dev_get_drvdata(&spi->dev); - - dev_dbg(&spi->dev, "Resume\n"); - - if (s->pdata->suspend) - s->pdata->suspend(0); - - mutex_lock(&s->max310x_mutex); - - /* Disable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - 0); - - max310x_wait_pll(s); + struct max310x_port *s = dev_get_drvdata(dev); + int i; - mutex_unlock(&s->max310x_mutex); + for (i = 0; i < s->uart.nr; i++) { + s->devtype->power(&s->p[i].port, 1); + uart_resume_port(&s->uart, &s->p[i].port); + } - return uart_resume_port(&s->uart, &s->port); + return 0; } +static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); + #ifdef CONFIG_GPIOLIB static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { - unsigned int val = 0; + unsigned int val; struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val); - mutex_unlock(&s->max310x_mutex); + val = max310x_port_read(port, MAX310X_GPIODATA_REG); - return !!((val >> 4) & (1 << offset)); + return !!((val >> 4) & (1 << (offset % 4))); } static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); } static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0); return 0; } @@ -974,289 +1084,289 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, - 1 << offset); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), + 1 << (offset % 4)); return 0; } #endif -/* Generic platform data */ -static struct max310x_pdata generic_plat_data = { - .driver_flags = MAX310X_EXT_CLK, - .uart_flags[0] = MAX310X_ECHO_SUPRESS, - .frequency = 26000000, -}; - -static int max310x_probe(struct spi_device *spi) +static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, + struct regmap *regmap, int irq, unsigned long flags) { + int i, ret, fmin, fmax, freq, uartclk; + struct clk *clk_osc, *clk_xtal; struct max310x_port *s; - struct device *dev = &spi->dev; - int chiptype = spi_get_device_id(spi)->driver_data; - struct max310x_pdata *pdata = dev->platform_data; - unsigned int val = 0; - int ret; + bool xtal = false; - /* Check for IRQ */ - if (spi->irq <= 0) { - dev_err(dev, "No IRQ specified\n"); - return -ENOTSUPP; - } + if (IS_ERR(regmap)) + return PTR_ERR(regmap); /* Alloc port structure */ - s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL); + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL); if (!s) { dev_err(dev, "Error allocating port structure\n"); return -ENOMEM; } - dev_set_drvdata(dev, s); - if (!pdata) { - dev_warn(dev, "No platform data supplied, using defaults\n"); - pdata = &generic_plat_data; + clk_osc = devm_clk_get(dev, "osc"); + clk_xtal = devm_clk_get(dev, "xtal"); + if (!IS_ERR(clk_osc)) { + s->clk = clk_osc; + fmin = 500000; + fmax = 35000000; + } else if (!IS_ERR(clk_xtal)) { + s->clk = clk_xtal; + fmin = 1000000; + fmax = 4000000; + xtal = true; + } else if (PTR_ERR(clk_osc) == -EPROBE_DEFER || + PTR_ERR(clk_xtal) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else { + dev_err(dev, "Cannot get clock\n"); + return -EINVAL; } - s->pdata = pdata; - - /* Individual chip settings */ - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - s->name = "MAX3107"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1f; - break; - case MAX310X_TYPE_MAX3108: - s->name = "MAX3108"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1e; - break; - default: - dev_err(dev, "Unsupported chip type %i\n", chiptype); - return -ENOTSUPP; - } - - /* Check input frequency */ - if ((pdata->driver_flags & MAX310X_EXT_CLK) && - ((pdata->frequency < 500000) || (pdata->frequency > 35000000))) - goto err_freq; - /* Check frequency for quartz */ - if (!(pdata->driver_flags & MAX310X_EXT_CLK) && - ((pdata->frequency < 1000000) || (pdata->frequency > 4000000))) - goto err_freq; - mutex_init(&s->max310x_mutex); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; - /* Setup SPI bus */ - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - spi->max_speed_hz = 26000000; - spi_setup(spi); - - /* Setup regmap */ - s->regcfg.reg_bits = 8; - s->regcfg.val_bits = 8; - s->regcfg.read_flag_mask = 0x00; - s->regcfg.write_flag_mask = 0x80; - s->regcfg.cache_type = REGCACHE_RBTREE; - s->regcfg.writeable_reg = max3107_8_reg_writeable; - s->regcfg.volatile_reg = max310x_reg_volatile; - s->regcfg.precious_reg = max310x_reg_precious; - s->regmap = devm_regmap_init_spi(spi, &s->regcfg); - if (IS_ERR(s->regmap)) { - ret = PTR_ERR(s->regmap); - dev_err(dev, "Failed to initialize register map\n"); - goto err_out; + freq = clk_get_rate(s->clk); + /* Check frequency limits */ + if (freq < fmin || freq > fmax) { + ret = -ERANGE; + goto out_clk; } - /* Reset chip & check SPI function */ - ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); - if (ret) { - dev_err(dev, "SPI transfer failed\n"); - goto err_out; - } - /* Clear chip reset */ - regmap_write(s->regmap, MAX310X_MODE2_REG, 0); - - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - /* Check REV ID to ensure we are talking to what we expect */ - regmap_read(s->regmap, MAX3107_REVID_REG, &val); - if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) { - dev_err(dev, "%s ID 0x%02x does not match\n", - s->name, val); - ret = -ENODEV; - goto err_out; - } - break; - case MAX310X_TYPE_MAX3108: - /* MAX3108 have not REV ID register, we just check default value - * from clocksource register to make sure everything works. - */ - regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); - if (val != (MAX310X_CLKSRC_EXTCLK_BIT | - MAX310X_CLKSRC_PLLBYP_BIT)) { - dev_err(dev, "%s not present\n", s->name); - ret = -ENODEV; - goto err_out; - } - break; - } - - /* Board specific configure */ - if (pdata->init) - pdata->init(); - if (pdata->suspend) - pdata->suspend(0); - - /* Calculate referecne clock */ - s->uartclk = max310x_set_ref_clk(s); - - /* Disable all interrupts */ - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - - /* Setup MODE1 register */ - val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */ - if (pdata->driver_flags & MAX310X_AUTOSLEEP) - val = MAX310X_MODE1_AUTOSLEEP_BIT; - regmap_write(s->regmap, MAX310X_MODE1_REG, val); + s->regmap = regmap; + s->devtype = devtype; + dev_set_drvdata(dev, s); - /* Setup interrupt */ - ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), s); - if (ret) { - dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq); - goto err_out; + /* Check device to ensure we are talking to what we expect */ + ret = devtype->detect(dev); + if (ret) + goto out_clk; + + for (i = 0; i < devtype->nr; i++) { + unsigned int offs = i << 5; + + /* Reset port */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, + MAX310X_MODE2_RST_BIT); + /* Clear port reset */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0); + + /* Wait for port startup */ + do { + regmap_read(s->regmap, + MAX310X_BRGDIVLSB_REG + offs, &ret); + } while (ret != 0x01); + + regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs, + MAX310X_MODE1_AUTOSLEEP_BIT, + MAX310X_MODE1_AUTOSLEEP_BIT); } + uartclk = max310x_set_ref_clk(s, freq, xtal); + dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* Register UART driver */ s->uart.owner = THIS_MODULE; - s->uart.driver_name = dev_name(dev); s->uart.dev_name = "ttyMAX"; s->uart.major = MAX310X_MAJOR; s->uart.minor = MAX310X_MINOR; + s->uart.nr = devtype->nr; ret = uart_register_driver(&s->uart); if (ret) { dev_err(dev, "Registering UART driver failed\n"); - goto err_out; + goto out_clk; } - /* Initialize workqueue for start TX */ - s->wq = create_freezable_workqueue(dev_name(dev)); - INIT_WORK(&s->tx_work, max310x_wq_proc); - - /* Initialize UART port data */ - s->port.line = 0; - s->port.dev = dev; - s->port.irq = spi->irq; - s->port.type = PORT_MAX310X; - s->port.fifosize = MAX310X_FIFO_SIZE; - s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port.iotype = UPIO_PORT; - s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */ - s->port.uartclk = s->uartclk; - s->port.ops = &max310x_ops; - uart_add_one_port(&s->uart, &s->port); - #ifdef CONFIG_GPIOLIB /* Setup GPIO cotroller */ - if (pdata->gpio_base) { - s->gpio.owner = THIS_MODULE; - s->gpio.dev = dev; - s->gpio.label = dev_name(dev); - s->gpio.direction_input = max310x_gpio_direction_input; - s->gpio.get = max310x_gpio_get; - s->gpio.direction_output= max310x_gpio_direction_output; - s->gpio.set = max310x_gpio_set; - s->gpio.base = pdata->gpio_base; - s->gpio.ngpio = s->nr_gpio; - s->gpio.can_sleep = 1; - if (gpiochip_add(&s->gpio)) { - /* Indicate that we should not call gpiochip_remove */ - s->gpio.base = 0; - } - } else - dev_info(dev, "GPIO support not enabled\n"); + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = max310x_gpio_direction_input; + s->gpio.get = max310x_gpio_get; + s->gpio.direction_output= max310x_gpio_direction_output; + s->gpio.set = max310x_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr * 4; + s->gpio.can_sleep = 1; + ret = gpiochip_add(&s->gpio); + if (ret) + goto out_uart; #endif - /* Go to suspend mode */ - if (pdata->suspend) - pdata->suspend(1); + mutex_init(&s->mutex); + + for (i = 0; i < devtype->nr; i++) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_MAX310X; + s->p[i].port.fifosize = MAX310X_FIFO_SIZE; + s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.iobase = i * 0x20; + s->p[i].port.membase = (void __iomem *)~0; + s->p[i].port.uartclk = uartclk; + s->p[i].port.ops = &max310x_ops; + /* Disable all interrupts */ + max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0); + /* Clear IRQ status register */ + max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG); + /* Enable IRQ pin */ + max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG, + MAX310X_MODE1_IRQSEL_BIT, + MAX310X_MODE1_IRQSEL_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, max310x_wq_proc); + /* Initialize queue for changing mode */ + INIT_WORK(&s->p[i].md_work, max310x_md_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + devtype->power(&s->p[i].port, 0); + } - return 0; + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, + IRQF_ONESHOT | flags, dev_name(dev), s); + if (!ret) + return 0; + + dev_err(dev, "Unable to reguest IRQ %i\n", irq); + + mutex_destroy(&s->mutex); + +#ifdef CONFIG_GPIOLIB + WARN_ON(gpiochip_remove(&s->gpio)); -err_freq: - dev_err(dev, "Frequency parameter incorrect\n"); - ret = -EINVAL; +out_uart: +#endif + uart_unregister_driver(&s->uart); -err_out: - dev_set_drvdata(dev, NULL); +out_clk: + clk_disable_unprepare(s->clk); return ret; } -static int max310x_remove(struct spi_device *spi) +static int max310x_remove(struct device *dev) { - struct device *dev = &spi->dev; struct max310x_port *s = dev_get_drvdata(dev); - int ret = 0; + int i, ret = 0; + +#ifdef CONFIG_GPIOLIB + ret = gpiochip_remove(&s->gpio); + if (ret) + return ret; +#endif - dev_dbg(dev, "Removing port\n"); + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + cancel_work_sync(&s->p[i].md_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } - devm_free_irq(dev, s->port.irq, s); + mutex_destroy(&s->mutex); + uart_unregister_driver(&s->uart); + clk_disable_unprepare(s->clk); - destroy_workqueue(s->wq); + return ret; +} - uart_remove_one_port(&s->uart, &s->port); +static const struct of_device_id __maybe_unused max310x_dt_ids[] = { + { .compatible = "maxim,max3107", .data = &max3107_devtype, }, + { .compatible = "maxim,max3108", .data = &max3108_devtype, }, + { .compatible = "maxim,max3109", .data = &max3109_devtype, }, + { .compatible = "maxim,max14830", .data = &max14830_devtype }, + { } +}; +MODULE_DEVICE_TABLE(of, max310x_dt_ids); + +static struct regmap_config regcfg = { + .reg_bits = 8, + .val_bits = 8, + .write_flag_mask = 0x80, + .cache_type = REGCACHE_RBTREE, + .writeable_reg = max310x_reg_writeable, + .volatile_reg = max310x_reg_volatile, + .precious_reg = max310x_reg_precious, +}; - uart_unregister_driver(&s->uart); +#ifdef CONFIG_SPI_MASTER +static int max310x_spi_probe(struct spi_device *spi) +{ + struct max310x_devtype *devtype; + unsigned long flags = 0; + struct regmap *regmap; + int ret; -#ifdef CONFIG_GPIOLIB - if (s->pdata->gpio_base) { - ret = gpiochip_remove(&s->gpio); - if (ret) - dev_err(dev, "Failed to remove gpio chip: %d\n", ret); + /* Setup SPI bus */ + spi->bits_per_word = 8; + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 26000000; + ret = spi_setup(spi); + if (ret) + return ret; + + if (spi->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(max310x_dt_ids, &spi->dev); + + devtype = (struct max310x_devtype *)of_id->data; + } else { + const struct spi_device_id *id_entry = spi_get_device_id(spi); + + devtype = (struct max310x_devtype *)id_entry->driver_data; + flags = IRQF_TRIGGER_FALLING; } -#endif - dev_set_drvdata(dev, NULL); + regcfg.max_register = devtype->nr * 0x20 - 1; + regmap = devm_regmap_init_spi(spi, ®cfg); - if (s->pdata->suspend) - s->pdata->suspend(1); - if (s->pdata->exit) - s->pdata->exit(); + return max310x_probe(&spi->dev, devtype, regmap, spi->irq, flags); +} - return ret; +static int max310x_spi_remove(struct spi_device *spi) +{ + return max310x_remove(&spi->dev); } static const struct spi_device_id max310x_id_table[] = { - { "max3107", MAX310X_TYPE_MAX3107 }, - { "max3108", MAX310X_TYPE_MAX3108 }, + { "max3107", (kernel_ulong_t)&max3107_devtype, }, + { "max3108", (kernel_ulong_t)&max3108_devtype, }, + { "max3109", (kernel_ulong_t)&max3109_devtype, }, + { "max14830", (kernel_ulong_t)&max14830_devtype, }, { } }; MODULE_DEVICE_TABLE(spi, max310x_id_table); -static struct spi_driver max310x_driver = { +static struct spi_driver max310x_uart_driver = { .driver = { - .name = "max310x", - .owner = THIS_MODULE, + .name = MAX310X_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max310x_dt_ids), + .pm = &max310x_pm_ops, }, - .probe = max310x_probe, - .remove = max310x_remove, - .suspend = max310x_suspend, - .resume = max310x_resume, + .probe = max310x_spi_probe, + .remove = max310x_spi_remove, .id_table = max310x_id_table, }; -module_spi_driver(max310x_driver); +module_spi_driver(max310x_uart_driver); +#endif -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); MODULE_DESCRIPTION("MAX310X serial driver"); diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index fcd56ab6053..a6f085717f9 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -23,6 +23,8 @@ #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/platform_device.h> #include <asm/coldfire.h> #include <asm/mcfsim.h> #include <asm/mcfuart.h> @@ -55,6 +57,7 @@ struct mcf_uart { struct uart_port port; unsigned int sigs; /* Local copy of line sigs */ unsigned char imr; /* Local IMR mirror */ + struct serial_rs485 rs485; /* RS485 settings */ }; /****************************************************************************/ @@ -101,6 +104,12 @@ static void mcf_start_tx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + if (pp->rs485.flags & SER_RS485_ENABLED) { + /* Enable Transmitter */ + writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR); + /* Manually assert RTS */ + writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); + } pp->imr |= MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); } @@ -196,6 +205,7 @@ static void mcf_shutdown(struct uart_port *port) static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned long flags; unsigned int baud, baudclk; #if defined(CONFIG_M5272) @@ -238,6 +248,12 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr1 |= MCFUART_MR1_PARITYNONE; } + /* + * FIXME: port->read_status_mask and port->ignore_status_mask + * need to be initialized based on termios settings for + * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT + */ + if (termios->c_cflag & CSTOPB) mr2 |= MCFUART_MR2_STOP2; else @@ -248,6 +264,11 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr2 |= MCFUART_MR2_TXCTS; } + if (pp->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + mr2 |= MCFUART_MR2_TXRTS; + } + spin_lock_irqsave(&port->lock, flags); uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); @@ -310,7 +331,9 @@ static void mcf_rx_chars(struct mcf_uart *pp) uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); } - tty_flip_buffer_push(port->state->port.tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); } /****************************************************************************/ @@ -342,6 +365,10 @@ static void mcf_tx_chars(struct mcf_uart *pp) if (xmit->head == xmit->tail) { pp->imr &= ~MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); + /* Disable TX to negate RTS automatically */ + if (pp->rs485.flags & SER_RS485_ENABLED) + writeb(MCFUART_UCR_TXDISABLE, + port->membase + MCFUART_UCR); } } @@ -418,6 +445,58 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /****************************************************************************/ +/* Enable or disable the RS485 support */ +static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + unsigned char mr1, mr2; + + spin_lock_irqsave(&port->lock, flags); + /* Get mode registers */ + mr1 = readb(port->membase + MCFUART_UMR); + mr2 = readb(port->membase + MCFUART_UMR); + if (rs485->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + /* Automatically negate RTS after TX completes */ + mr2 |= MCFUART_MR2_TXRTS; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + mr2 &= ~MCFUART_MR2_TXRTS; + } + writeb(mr1, port->membase + MCFUART_UMR); + writeb(mr2, port->membase + MCFUART_UMR); + pp->rs485 = *rs485; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int mcf_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case TIOCSRS485: { + struct serial_rs485 rs485; + if (copy_from_user(&rs485, (struct serial_rs485 *)arg, + sizeof(struct serial_rs485))) + return -EFAULT; + mcf_config_rs485(port, &rs485); + break; + } + case TIOCGRS485: { + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485, + sizeof(struct serial_rs485))) + return -EFAULT; + break; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/****************************************************************************/ + /* * Define the basic serial functions we support. */ @@ -438,6 +517,7 @@ static const struct uart_ops mcf_uart_ops = { .release_port = mcf_release_port, .config_port = mcf_config_port, .verify_port = mcf_verify_port, + .ioctl = mcf_ioctl, }; static struct mcf_uart mcf_ports[4]; @@ -573,7 +653,7 @@ static struct uart_driver mcf_driver = { static int mcf_probe(struct platform_device *pdev) { - struct mcf_platform_uart *platp = pdev->dev.platform_data; + struct mcf_platform_uart *platp = dev_get_platdata(&pdev->dev); struct uart_port *port; int i; @@ -636,8 +716,10 @@ static int __init mcf_init(void) if (rc) return rc; rc = platform_driver_register(&mcf_platform_driver); - if (rc) + if (rc) { + uart_unregister_driver(&mcf_driver); return rc; + } return 0; } diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c new file mode 100644 index 00000000000..c9d18548783 --- /dev/null +++ b/drivers/tty/serial/men_z135_uart.c @@ -0,0 +1,867 @@ +/* + * MEN 16z135 High Speed UART + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ":" fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/serial_core.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/tty_flip.h> +#include <linux/bitops.h> +#include <linux/mcb.h> + +#define MEN_Z135_MAX_PORTS 12 +#define MEN_Z135_BASECLK 29491200 +#define MEN_Z135_FIFO_SIZE 1024 +#define MEN_Z135_NUM_MSI_VECTORS 2 +#define MEN_Z135_FIFO_WATERMARK 1020 + +#define MEN_Z135_STAT_REG 0x0 +#define MEN_Z135_RX_RAM 0x4 +#define MEN_Z135_TX_RAM 0x400 +#define MEN_Z135_RX_CTRL 0x800 +#define MEN_Z135_TX_CTRL 0x804 +#define MEN_Z135_CONF_REG 0x808 +#define MEN_Z135_UART_FREQ 0x80c +#define MEN_Z135_BAUD_REG 0x810 +#define MENZ135_TIMEOUT 0x814 + +#define MEN_Z135_MEM_SIZE 0x818 + +#define IS_IRQ(x) ((x) & 1) +#define IRQ_ID(x) (((x) >> 1) & 7) + +#define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */ +#define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */ +#define MEN_Z135_IER_RLSIEN BIT(2) /* Receiver Line Status IRQ */ +#define MEN_Z135_IER_MSIEN BIT(3) /* Modem Status IRQ */ +#define MEN_Z135_ALL_IRQS (MEN_Z135_IER_RXCIEN \ + | MEN_Z135_IER_RLSIEN \ + | MEN_Z135_IER_MSIEN \ + | MEN_Z135_IER_TXCIEN) + +#define MEN_Z135_MCR_DTR BIT(24) +#define MEN_Z135_MCR_RTS BIT(25) +#define MEN_Z135_MCR_OUT1 BIT(26) +#define MEN_Z135_MCR_OUT2 BIT(27) +#define MEN_Z135_MCR_LOOP BIT(28) +#define MEN_Z135_MCR_RCFC BIT(29) + +#define MEN_Z135_MSR_DCTS BIT(0) +#define MEN_Z135_MSR_DDSR BIT(1) +#define MEN_Z135_MSR_DRI BIT(2) +#define MEN_Z135_MSR_DDCD BIT(3) +#define MEN_Z135_MSR_CTS BIT(4) +#define MEN_Z135_MSR_DSR BIT(5) +#define MEN_Z135_MSR_RI BIT(6) +#define MEN_Z135_MSR_DCD BIT(7) + +#define MEN_Z135_LCR_SHIFT 8 /* LCR shift mask */ + +#define MEN_Z135_WL5 0 /* CS5 */ +#define MEN_Z135_WL6 1 /* CS6 */ +#define MEN_Z135_WL7 2 /* CS7 */ +#define MEN_Z135_WL8 3 /* CS8 */ + +#define MEN_Z135_STB_SHIFT 2 /* Stopbits */ +#define MEN_Z135_NSTB1 0 +#define MEN_Z135_NSTB2 1 + +#define MEN_Z135_PEN_SHIFT 3 /* Parity enable */ +#define MEN_Z135_PAR_DIS 0 +#define MEN_Z135_PAR_ENA 1 + +#define MEN_Z135_PTY_SHIFT 4 /* Parity type */ +#define MEN_Z135_PTY_ODD 0 +#define MEN_Z135_PTY_EVN 1 + +#define MEN_Z135_LSR_DR BIT(0) +#define MEN_Z135_LSR_OE BIT(1) +#define MEN_Z135_LSR_PE BIT(2) +#define MEN_Z135_LSR_FE BIT(3) +#define MEN_Z135_LSR_BI BIT(4) +#define MEN_Z135_LSR_THEP BIT(5) +#define MEN_Z135_LSR_TEXP BIT(6) +#define MEN_Z135_LSR_RXFIFOERR BIT(7) + +#define MEN_Z135_IRQ_ID_MST 0 +#define MEN_Z135_IRQ_ID_TSA 1 +#define MEN_Z135_IRQ_ID_RDA 2 +#define MEN_Z135_IRQ_ID_RLS 3 +#define MEN_Z135_IRQ_ID_CTI 6 + +#define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff) + +#define BYTES_TO_ALIGN(x) ((x) & 0x3) + +static int line; + +static int txlvl = 5; +module_param(txlvl, int, S_IRUGO); +MODULE_PARM_DESC(txlvl, "TX IRQ trigger level 0-7, default 5 (128 byte)"); + +static int rxlvl = 6; +module_param(rxlvl, int, S_IRUGO); +MODULE_PARM_DESC(rxlvl, "RX IRQ trigger level 0-7, default 6 (256 byte)"); + +static int align; +module_param(align, int, S_IRUGO); +MODULE_PARM_DESC(align, "Keep hardware FIFO write pointer aligned, default 0"); + +struct men_z135_port { + struct uart_port port; + struct mcb_device *mdev; + unsigned char *rxbuf; + u32 stat_reg; + spinlock_t lock; +}; +#define to_men_z135(port) container_of((port), struct men_z135_port, port) + +/** + * men_z135_reg_set() - Set value in register + * @uart: The UART port + * @addr: Register address + * @val: value to set + */ +static inline void men_z135_reg_set(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg |= val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_reg_clr() - Unset value in register + * @uart: The UART port + * @addr: Register address + * @val: value to clear + */ +static inline void men_z135_reg_clr(struct men_z135_port *uart, + u32 addr, u32 val) +{ + struct uart_port *port = &uart->port; + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&uart->lock, flags); + + reg = ioread32(port->membase + addr); + reg &= ~val; + iowrite32(reg, port->membase + addr); + + spin_unlock_irqrestore(&uart->lock, flags); +} + +/** + * men_z135_handle_modem_status() - Handle change of modem status + * @port: The UART port + * + * Handle change of modem status register. This is done by reading the "delta" + * versions of DCD (Data Carrier Detect) and CTS (Clear To Send). + */ +static void men_z135_handle_modem_status(struct men_z135_port *uart) +{ + if (uart->stat_reg & MEN_Z135_MSR_DDCD) + uart_handle_dcd_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_DCD); + if (uart->stat_reg & MEN_Z135_MSR_DCTS) + uart_handle_cts_change(&uart->port, + uart->stat_reg & ~MEN_Z135_MSR_CTS); +} + +static void men_z135_handle_lsr(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u8 lsr; + + lsr = (uart->stat_reg >> 16) & 0xff; + + if (lsr & MEN_Z135_LSR_OE) + port->icount.overrun++; + if (lsr & MEN_Z135_LSR_PE) + port->icount.parity++; + if (lsr & MEN_Z135_LSR_FE) + port->icount.frame++; + if (lsr & MEN_Z135_LSR_BI) { + port->icount.brk++; + uart_handle_break(port); + } +} + +/** + * get_rx_fifo_content() - Get the number of bytes in RX FIFO + * @uart: The UART port + * + * Read RXC register from hardware and return current FIFO fill size. + */ +static u16 get_rx_fifo_content(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + u32 stat_reg; + u16 rxc; + u8 rxc_lo; + u8 rxc_hi; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + rxc_lo = stat_reg >> 24; + rxc_hi = (stat_reg & 0xC0) >> 6; + + rxc = rxc_lo | (rxc_hi << 8); + + return rxc; +} + +/** + * men_z135_handle_rx() - RX tasklet routine + * @arg: Pointer to struct men_z135_port + * + * Copy from RX FIFO and acknowledge number of bytes copied. + */ +static void men_z135_handle_rx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct tty_port *tport = &port->state->port; + int copied; + u16 size; + int room; + + size = get_rx_fifo_content(uart); + + if (size == 0) + return; + + /* Avoid accidently accessing TX FIFO instead of RX FIFO. Last + * longword in RX FIFO cannot be read.(0x004-0x3FF) + */ + if (size > MEN_Z135_FIFO_WATERMARK) + size = MEN_Z135_FIFO_WATERMARK; + + room = tty_buffer_request_room(tport, size); + if (room != size) + dev_warn(&uart->mdev->dev, + "Not enough room in flip buffer, truncating to %d\n", + room); + + if (room == 0) + return; + + memcpy_fromio(uart->rxbuf, port->membase + MEN_Z135_RX_RAM, room); + /* Be sure to first copy all data and then acknowledge it */ + mb(); + iowrite32(room, port->membase + MEN_Z135_RX_CTRL); + + copied = tty_insert_flip_string(tport, uart->rxbuf, room); + if (copied != room) + dev_warn(&uart->mdev->dev, + "Only copied %d instead of %d bytes\n", + copied, room); + + port->icount.rx += copied; + + tty_flip_buffer_push(tport); + +} + +/** + * men_z135_handle_tx() - TX tasklet routine + * @arg: Pointer to struct men_z135_port + * + */ +static void men_z135_handle_tx(struct men_z135_port *uart) +{ + struct uart_port *port = &uart->port; + struct circ_buf *xmit = &port->state->xmit; + u32 txc; + u32 wptr; + int qlen; + int n; + int txfree; + int head; + int tail; + int s; + + if (uart_circ_empty(xmit)) + goto out; + + if (uart_tx_stopped(port)) + goto out; + + if (port->x_char) + goto out; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* calculate bytes to copy */ + qlen = uart_circ_chars_pending(xmit); + if (qlen <= 0) + goto out; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + wptr &= 0x3ff; + + if (txc > MEN_Z135_FIFO_WATERMARK) + txc = MEN_Z135_FIFO_WATERMARK; + + txfree = MEN_Z135_FIFO_WATERMARK - txc; + if (txfree <= 0) { + pr_err("Not enough room in TX FIFO have %d, need %d\n", + txfree, qlen); + goto irq_en; + } + + /* if we're not aligned, it's better to copy only 1 or 2 bytes and + * then the rest. + */ + if (align && qlen >= 3 && BYTES_TO_ALIGN(wptr)) + n = 4 - BYTES_TO_ALIGN(wptr); + else if (qlen > txfree) + n = txfree; + else + n = qlen; + + if (n <= 0) + goto irq_en; + + head = xmit->head & (UART_XMIT_SIZE - 1); + tail = xmit->tail & (UART_XMIT_SIZE - 1); + + s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; + n = min(n, s); + + memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n); + xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1); + mmiowb(); + + iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL); + + port->icount.tx += n; + +irq_en: + if (!uart_circ_empty(xmit)) + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + else + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); + +out: + return; + +} + +/** + * men_z135_intr() - Handle legacy IRQs + * @irq: The IRQ number + * @data: Pointer to UART port + * + * Check IIR register to see which tasklet to start. + */ +static irqreturn_t men_z135_intr(int irq, void *data) +{ + struct men_z135_port *uart = (struct men_z135_port *)data; + struct uart_port *port = &uart->port; + int irq_id; + + uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + /* IRQ pending is low active */ + if (IS_IRQ(uart->stat_reg)) + return IRQ_NONE; + + irq_id = IRQ_ID(uart->stat_reg); + switch (irq_id) { + case MEN_Z135_IRQ_ID_MST: + men_z135_handle_modem_status(uart); + break; + case MEN_Z135_IRQ_ID_TSA: + men_z135_handle_tx(uart); + break; + case MEN_Z135_IRQ_ID_CTI: + dev_dbg(&uart->mdev->dev, "Character Timeout Indication\n"); + /* Fallthrough */ + case MEN_Z135_IRQ_ID_RDA: + /* Reading data clears RX IRQ */ + men_z135_handle_rx(uart); + break; + case MEN_Z135_IRQ_ID_RLS: + men_z135_handle_lsr(uart); + break; + default: + dev_warn(&uart->mdev->dev, "Unknown IRQ id %d\n", irq_id); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +/** + * men_z135_request_irq() - Request IRQ for 16z135 core + * @uart: z135 private uart port structure + * + * Request an IRQ for 16z135 to use. First try using MSI, if it fails + * fall back to using legacy interrupts. + */ +static int men_z135_request_irq(struct men_z135_port *uart) +{ + struct device *dev = &uart->mdev->dev; + struct uart_port *port = &uart->port; + int err = 0; + + err = request_irq(port->irq, men_z135_intr, IRQF_SHARED, + "men_z135_intr", uart); + if (err) + dev_err(dev, "Error %d getting interrupt\n", err); + + return err; +} + +/** + * men_z135_tx_empty() - Handle tx_empty call + * @port: The UART port + * + * This function tests whether the TX FIFO and shifter for the port + * described by @port is empty. + */ +static unsigned int men_z135_tx_empty(struct uart_port *port) +{ + u32 wptr; + u16 txc; + + wptr = ioread32(port->membase + MEN_Z135_TX_CTRL); + txc = (wptr >> 16) & 0x3ff; + + if (txc == 0) + return TIOCSER_TEMT; + else + return 0; +} + +/** + * men_z135_set_mctrl() - Set modem control lines + * @port: The UART port + * @mctrl: The modem control lines + * + * This function sets the modem control lines for a port described by @port + * to the state described by @mctrl + */ +static void men_z135_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + if (mctrl & TIOCM_RTS) + conf_reg |= MEN_Z135_MCR_RTS; + if (mctrl & TIOCM_DTR) + conf_reg |= MEN_Z135_MCR_DTR; + if (mctrl & TIOCM_OUT1) + conf_reg |= MEN_Z135_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + conf_reg |= MEN_Z135_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + conf_reg |= MEN_Z135_MCR_LOOP; + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, conf_reg); +} + +/** + * men_z135_get_mctrl() - Get modem control lines + * @port: The UART port + * + * Retruns the current state of modem control inputs. + */ +static unsigned int men_z135_get_mctrl(struct uart_port *port) +{ + unsigned int mctrl = 0; + u32 stat_reg; + u8 msr; + + stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); + + msr = ~((stat_reg >> 8) & 0xff); + + if (msr & MEN_Z135_MSR_CTS) + mctrl |= TIOCM_CTS; + if (msr & MEN_Z135_MSR_DSR) + mctrl |= TIOCM_DSR; + if (msr & MEN_Z135_MSR_RI) + mctrl |= TIOCM_RI; + if (msr & MEN_Z135_MSR_DCD) + mctrl |= TIOCM_CAR; + + return mctrl; +} + +/** + * men_z135_stop_tx() - Stop transmitting characters + * @port: The UART port + * + * Stop transmitting characters. This might be due to CTS line becomming + * inactive or the tty layer indicating we want to stop transmission due to + * an XOFF character. + */ +static void men_z135_stop_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_TXCIEN); +} + +/** + * men_z135_start_tx() - Start transmitting characters + * @port: The UART port + * + * Start transmitting character. This actually doesn't transmit anything, but + * fires off the TX tasklet. + */ +static void men_z135_start_tx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_handle_tx(uart); +} + +/** + * men_z135_stop_rx() - Stop receiving characters + * @port: The UART port + * + * Stop receiving characters; the port is in the process of being closed. + */ +static void men_z135_stop_rx(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_RXCIEN); +} + +/** + * men_z135_enable_ms() - Enable Modem Status + * port: + * + * Enable Modem Status IRQ. + */ +static void men_z135_enable_ms(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + + men_z135_reg_set(uart, MEN_Z135_CONF_REG, MEN_Z135_IER_MSIEN); +} + +static int men_z135_startup(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + int err; + u32 conf_reg = 0; + + err = men_z135_request_irq(uart); + if (err) + return -ENODEV; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + + /* Activate all but TX space available IRQ */ + conf_reg |= MEN_Z135_ALL_IRQS & ~MEN_Z135_IER_TXCIEN; + conf_reg &= ~(0xff << 16); + conf_reg |= (txlvl << 16); + conf_reg |= (rxlvl << 20); + + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + return 0; +} + +static void men_z135_shutdown(struct uart_port *port) +{ + struct men_z135_port *uart = to_men_z135(port); + u32 conf_reg = 0; + + conf_reg |= MEN_Z135_ALL_IRQS; + + men_z135_reg_clr(uart, MEN_Z135_CONF_REG, conf_reg); + + free_irq(uart->port.irq, uart); +} + +static void men_z135_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud; + u32 conf_reg; + u32 bd_reg; + u32 uart_freq; + u8 lcr; + + conf_reg = ioread32(port->membase + MEN_Z135_CONF_REG); + lcr = LCR(conf_reg); + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= MEN_Z135_WL5; + break; + case CS6: + lcr |= MEN_Z135_WL6; + break; + case CS7: + lcr |= MEN_Z135_WL7; + break; + case CS8: + lcr |= MEN_Z135_WL8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= MEN_Z135_NSTB2 << MEN_Z135_STB_SHIFT; + + /* parity */ + if (termios->c_cflag & PARENB) { + lcr |= MEN_Z135_PAR_ENA << MEN_Z135_PEN_SHIFT; + + if (termios->c_cflag & PARODD) + lcr |= MEN_Z135_PTY_ODD << MEN_Z135_PTY_SHIFT; + else + lcr |= MEN_Z135_PTY_EVN << MEN_Z135_PTY_SHIFT; + } else + lcr |= MEN_Z135_PAR_DIS << MEN_Z135_PEN_SHIFT; + + termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + conf_reg |= lcr << MEN_Z135_LCR_SHIFT; + iowrite32(conf_reg, port->membase + MEN_Z135_CONF_REG); + + uart_freq = ioread32(port->membase + MEN_Z135_UART_FREQ); + if (uart_freq == 0) + uart_freq = MEN_Z135_BASECLK; + + baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); + + spin_lock(&port->lock); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + bd_reg = uart_freq / (4 * baud); + iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); + + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock(&port->lock); +} + +static const char *men_z135_type(struct uart_port *port) +{ + return KBUILD_MODNAME; +} + +static void men_z135_release_port(struct uart_port *port) +{ + iounmap(port->membase); + port->membase = NULL; + + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); +} + +static int men_z135_request_port(struct uart_port *port) +{ + int size = MEN_Z135_MEM_SIZE; + + if (!request_mem_region(port->mapbase, size, "men_z135_port")) + return -EBUSY; + + port->membase = ioremap(port->mapbase, MEN_Z135_MEM_SIZE); + if (port->membase == NULL) { + release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); + return -ENOMEM; + } + + return 0; +} + +static void men_z135_config_port(struct uart_port *port, int type) +{ + port->type = PORT_MEN_Z135; + men_z135_request_port(port); +} + +static int men_z135_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + return -EINVAL; +} + +static struct uart_ops men_z135_ops = { + .tx_empty = men_z135_tx_empty, + .set_mctrl = men_z135_set_mctrl, + .get_mctrl = men_z135_get_mctrl, + .stop_tx = men_z135_stop_tx, + .start_tx = men_z135_start_tx, + .stop_rx = men_z135_stop_rx, + .enable_ms = men_z135_enable_ms, + .startup = men_z135_startup, + .shutdown = men_z135_shutdown, + .set_termios = men_z135_set_termios, + .type = men_z135_type, + .release_port = men_z135_release_port, + .request_port = men_z135_request_port, + .config_port = men_z135_config_port, + .verify_port = men_z135_verify_port, +}; + +static struct uart_driver men_z135_driver = { + .owner = THIS_MODULE, + .driver_name = KBUILD_MODNAME, + .dev_name = "ttyHSU", + .major = 0, + .minor = 0, + .nr = MEN_Z135_MAX_PORTS, +}; + +/** + * men_z135_probe() - Probe a z135 instance + * @mdev: The MCB device + * @id: The MCB device ID + * + * men_z135_probe does the basic setup of hardware resources and registers the + * new uart port to the tty layer. + */ +static int men_z135_probe(struct mcb_device *mdev, + const struct mcb_device_id *id) +{ + struct men_z135_port *uart; + struct resource *mem; + struct device *dev; + int err; + + dev = &mdev->dev; + + uart = devm_kzalloc(dev, sizeof(struct men_z135_port), GFP_KERNEL); + if (!uart) + return -ENOMEM; + + uart->rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!uart->rxbuf) + return -ENOMEM; + + mem = &mdev->mem; + + mcb_set_drvdata(mdev, uart); + + uart->port.uartclk = MEN_Z135_BASECLK * 16; + uart->port.fifosize = MEN_Z135_FIFO_SIZE; + uart->port.iotype = UPIO_MEM; + uart->port.ops = &men_z135_ops; + uart->port.irq = mcb_get_irq(mdev); + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + uart->port.line = line++; + uart->port.dev = dev; + uart->port.type = PORT_MEN_Z135; + uart->port.mapbase = mem->start; + uart->port.membase = NULL; + uart->mdev = mdev; + + spin_lock_init(&uart->port.lock); + spin_lock_init(&uart->lock); + + err = uart_add_one_port(&men_z135_driver, &uart->port); + if (err) + goto err; + + return 0; + +err: + free_page((unsigned long) uart->rxbuf); + dev_err(dev, "Failed to add UART: %d\n", err); + + return err; +} + +/** + * men_z135_remove() - Remove a z135 instance from the system + * + * @mdev: The MCB device + */ +static void men_z135_remove(struct mcb_device *mdev) +{ + struct men_z135_port *uart = mcb_get_drvdata(mdev); + + line--; + uart_remove_one_port(&men_z135_driver, &uart->port); + free_page((unsigned long) uart->rxbuf); +} + +static const struct mcb_device_id men_z135_ids[] = { + { .device = 0x87 }, +}; +MODULE_DEVICE_TABLE(mcb, men_z135_ids); + +static struct mcb_driver mcb_driver = { + .driver = { + .name = "z135-uart", + .owner = THIS_MODULE, + }, + .probe = men_z135_probe, + .remove = men_z135_remove, + .id_table = men_z135_ids, +}; + +/** + * men_z135_init() - Driver Registration Routine + * + * men_z135_init is the first routine called when the driver is loaded. All it + * does is register with the legacy MEN Chameleon subsystem. + */ +static int __init men_z135_init(void) +{ + int err; + + err = uart_register_driver(&men_z135_driver); + if (err) { + pr_err("Failed to register UART: %d\n", err); + return err; + } + + err = mcb_register_driver(&mcb_driver); + if (err) { + pr_err("Failed to register MCB driver: %d\n", err); + uart_unregister_driver(&men_z135_driver); + return err; + } + + return 0; +} +module_init(men_z135_init); + +/** + * men_z135_exit() - Driver Exit Routine + * + * men_z135_exit is called just before the driver is removed from memory. + */ +static void __exit men_z135_exit(void) +{ + mcb_unregister_driver(&mcb_driver); + uart_unregister_driver(&men_z135_driver); +} +module_exit(men_z135_exit); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MEN 16z135 High Speed UART"); +MODULE_ALIAS("mcb:16z135"); diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index 2c01344dc33..445799dc984 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -21,6 +21,10 @@ * be triggered */ +#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/module.h> #include <linux/init.h> #include <linux/console.h> @@ -289,7 +293,7 @@ static void serial_hsu_enable_ms(struct uart_port *port) serial_out(up, UART_IER, up->ier); } -void hsu_dma_tx(struct uart_hsu_port *up) +static void hsu_dma_tx(struct uart_hsu_port *up) { struct circ_buf *xmit = &up->port.state->xmit; struct hsu_dma_buffer *dbuf = &up->txbuf; @@ -336,7 +340,8 @@ void hsu_dma_tx(struct uart_hsu_port *up) } /* The buffer is already cache coherent */ -void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) +static void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, + struct hsu_dma_buffer *dbuf) { dbuf->ofs = 0; @@ -382,17 +387,15 @@ static void serial_hsu_stop_tx(struct uart_port *port) /* This is always called in spinlock protected mode, so * modify timeout timer is safe here */ -void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) +static void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts, + unsigned long *flags) { struct hsu_dma_buffer *dbuf = &up->rxbuf; struct hsu_dma_chan *chan = up->rxc; struct uart_port *port = &up->port; - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; int count; - if (!tty) - return; - /* * First need to know how many is already transferred, * then check if its a timeout DMA irq, and return @@ -423,7 +426,7 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) * explicitly set tail to 0. So head will * always be greater than tail. */ - tty_insert_flip_string(tty, dbuf->buf, count); + tty_insert_flip_string(tport, dbuf->buf, count); port->icount.rx += count; dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, @@ -437,7 +440,9 @@ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) | (0x1 << 16) | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ); - tty_flip_buffer_push(tty); + spin_unlock_irqrestore(&up->port.lock, *flags); + tty_flip_buffer_push(tport); + spin_lock_irqsave(&up->port.lock, *flags); chan_writel(chan, HSU_CH_CR, 0x3); @@ -458,15 +463,12 @@ static void serial_hsu_stop_rx(struct uart_port *port) } } -static inline void receive_chars(struct uart_hsu_port *up, int *status) +static inline void receive_chars(struct uart_hsu_port *up, int *status, + unsigned long *flags) { - struct tty_struct *tty = up->port.state->port.tty; unsigned int ch, flag; unsigned int max_count = 256; - if (!tty) - return; - do { ch = serial_in(up, UART_RX); flag = TTY_NORMAL; @@ -522,7 +524,10 @@ static inline void receive_chars(struct uart_hsu_port *up, int *status) ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && max_count--); - tty_flip_buffer_push(tty); + + spin_unlock_irqrestore(&up->port.lock, *flags); + tty_flip_buffer_push(&up->port.state->port); + spin_lock_irqsave(&up->port.lock, *flags); } static void transmit_chars(struct uart_hsu_port *up) @@ -616,7 +621,7 @@ static irqreturn_t port_irq(int irq, void *dev_id) lsr = serial_in(up, UART_LSR); if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); + receive_chars(up, &lsr, &flags); check_modem_status(up); /* lsr will be renewed during the receive_chars */ @@ -646,7 +651,7 @@ static inline void dma_chan_irq(struct hsu_dma_chan *chan) /* Rx channel */ if (chan->dirt == DMA_FROM_DEVICE) - hsu_dma_rx(up, int_sts); + hsu_dma_rx(up, int_sts, &flags); /* Tx channel */ if (chan->dirt == DMA_TO_DEVICE) { @@ -972,7 +977,7 @@ serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= UART_LSR_BI; /* Characters to ignore */ @@ -1180,7 +1185,7 @@ static struct console serial_hsu_console = { #define SERIAL_HSU_CONSOLE NULL #endif -struct uart_ops serial_hsu_pops = { +static struct uart_ops serial_hsu_pops = { .tx_empty = serial_hsu_tx_empty, .set_mctrl = serial_hsu_set_mctrl, .get_mctrl = serial_hsu_get_mctrl, @@ -1255,13 +1260,8 @@ static int serial_hsu_resume(struct pci_dev *pdev) #ifdef CONFIG_PM_RUNTIME static int serial_hsu_runtime_idle(struct device *dev) { - int err; - - err = pm_schedule_suspend(dev, 500); - if (err) - return -EBUSY; - - return 0; + pm_schedule_suspend(dev, 500); + return -EBUSY; } static int serial_hsu_runtime_suspend(struct device *dev) @@ -1453,7 +1453,6 @@ static void serial_hsu_remove(struct pci_dev *pdev) uart_remove_one_port(&serial_hsu_reg, &up->port); } - pci_set_drvdata(pdev, NULL); free_irq(pdev->irq, priv); pci_disable_device(pdev); } @@ -1506,4 +1505,4 @@ module_init(hsu_pci_init); module_exit(hsu_pci_exit); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:medfield-hsu"); +MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 7c23c4f4c58..97888f4900e 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -84,16 +84,6 @@ static void mpc52xx_uart_of_enumerate(void); static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); - -/* Simple macro to test if a port is console or not. This one is taken - * for serial_core.c and maybe should be moved to serial_core.h ? */ -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) \ - ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -117,11 +107,22 @@ struct psc_ops { unsigned int (*set_baudrate)(struct uart_port *port, struct ktermios *new, struct ktermios *old); + int (*clock_alloc)(struct uart_port *port); + void (*clock_relse)(struct uart_port *port); int (*clock)(struct uart_port *port, int enable); int (*fifoc_init)(void); void (*fifoc_uninit)(void); void (*get_irq)(struct uart_port *, struct device_node *); irqreturn_t (*handle_irq)(struct uart_port *port); + u16 (*get_status)(struct uart_port *port); + u8 (*get_ipcr)(struct uart_port *port); + void (*command)(struct uart_port *port, u8 cmd); + void (*set_mode)(struct uart_port *port, u8 mr1, u8 mr2); + void (*set_rts)(struct uart_port *port, int state); + void (*enable_ms)(struct uart_port *port); + void (*set_sicr)(struct uart_port *port, u32 val); + void (*set_imr)(struct uart_port *port, u16 val); + u8 (*get_mr1)(struct uart_port *port); }; /* setting the prescaler and divisor reg is common for all chips */ @@ -134,6 +135,65 @@ static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, out_8(&psc->ctlr, divisor & 0xff); } +static u16 mpc52xx_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status); +} + +static u8 mpc52xx_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_ipcr); +} + +static void mpc52xx_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC(port)->command, cmd); +} + +static void mpc52xx_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&PSC(port)->mode, mr1); + out_8(&PSC(port)->mode, mr2); +} + +static void mpc52xx_psc_set_rts(struct uart_port *port, int state) +{ + if (state) + out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc52xx_psc_enable_ms(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC(port)->sicr, val); +} + +static void mpc52xx_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc52xx_psc_get_mr1(struct uart_port *port) +{ + out_8(&PSC(port)->command, MPC52xx_PSC_SEL_MODE_REG_1); + return in_8(&PSC(port)->mode); +} + #ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) @@ -304,6 +364,15 @@ static struct psc_ops mpc52xx_psc_ops = { .set_baudrate = mpc5200_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; static struct psc_ops mpc5200b_psc_ops = { @@ -325,6 +394,15 @@ static struct psc_ops mpc5200b_psc_ops = { .set_baudrate = mpc5200b_psc_set_baudrate, .get_irq = mpc52xx_psc_get_irq, .handle_irq = mpc52xx_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; #endif /* CONFIG_MPC52xx */ @@ -343,6 +421,7 @@ struct psc_fifoc { static struct psc_fifoc __iomem *psc_fifoc; static unsigned int psc_fifoc_irq; +static struct clk *psc_fifoc_clk; static void mpc512x_psc_fifo_init(struct uart_port *port) { @@ -490,36 +569,73 @@ static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port, /* Init PSC FIFO Controller */ static int __init mpc512x_psc_fifoc_init(void) { + int err; struct device_node *np; + struct clk *clk; + + /* default error code, potentially overwritten by clock calls */ + err = -ENODEV; np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-psc-fifo"); if (!np) { pr_err("%s: Can't find FIFOC node\n", __func__); - return -ENODEV; + goto out_err; + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + /* backwards compat with device trees that lack clock specs */ + clk = clk_get_sys(np->name, "ipg"); + } + if (IS_ERR(clk)) { + pr_err("%s: Can't lookup FIFO clock\n", __func__); + err = PTR_ERR(clk); + goto out_ofnode_put; } + if (clk_prepare_enable(clk)) { + pr_err("%s: Can't enable FIFO clock\n", __func__); + clk_put(clk); + goto out_ofnode_put; + } + psc_fifoc_clk = clk; psc_fifoc = of_iomap(np, 0); if (!psc_fifoc) { pr_err("%s: Can't map FIFOC\n", __func__); - of_node_put(np); - return -ENODEV; + goto out_clk_disable; } psc_fifoc_irq = irq_of_parse_and_map(np, 0); - of_node_put(np); if (psc_fifoc_irq == 0) { pr_err("%s: Can't get FIFOC irq\n", __func__); - iounmap(psc_fifoc); - return -ENODEV; + goto out_unmap; } + of_node_put(np); return 0; + +out_unmap: + iounmap(psc_fifoc); +out_clk_disable: + clk_disable_unprepare(psc_fifoc_clk); + clk_put(psc_fifoc_clk); +out_ofnode_put: + of_node_put(np); +out_err: + return err; } static void __exit mpc512x_psc_fifoc_uninit(void) { iounmap(psc_fifoc); + + /* disable the clock, errors are not fatal */ + if (psc_fifoc_clk) { + clk_disable_unprepare(psc_fifoc_clk); + clk_put(psc_fifoc_clk); + psc_fifoc_clk = NULL; + } } /* 512x specific interrupt handler. The caller holds the port lock */ @@ -540,31 +656,103 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) return IRQ_NONE; } -static int mpc512x_psc_clock(struct uart_port *port, int enable) +static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM]; +static struct clk *psc_ipg_clk[MPC52xx_PSC_MAXNUM]; + +/* called from within the .request_port() callback (allocation) */ +static int mpc512x_psc_alloc_clock(struct uart_port *port) { - struct clk *psc_clk; int psc_num; - char clk_name[10]; + struct clk *clk; + int err; + + psc_num = (port->mapbase & 0xf00) >> 8; + + clk = devm_clk_get(port->dev, "mclk"); + if (IS_ERR(clk)) { + dev_err(port->dev, "Failed to get MCLK!\n"); + err = PTR_ERR(clk); + goto out_err; + } + err = clk_prepare_enable(clk); + if (err) { + dev_err(port->dev, "Failed to enable MCLK!\n"); + goto out_err; + } + psc_mclk_clk[psc_num] = clk; + + clk = devm_clk_get(port->dev, "ipg"); + if (IS_ERR(clk)) { + dev_err(port->dev, "Failed to get IPG clock!\n"); + err = PTR_ERR(clk); + goto out_err; + } + err = clk_prepare_enable(clk); + if (err) { + dev_err(port->dev, "Failed to enable IPG clock!\n"); + goto out_err; + } + psc_ipg_clk[psc_num] = clk; + + return 0; + +out_err: + if (psc_mclk_clk[psc_num]) { + clk_disable_unprepare(psc_mclk_clk[psc_num]); + psc_mclk_clk[psc_num] = NULL; + } + if (psc_ipg_clk[psc_num]) { + clk_disable_unprepare(psc_ipg_clk[psc_num]); + psc_ipg_clk[psc_num] = NULL; + } + return err; +} + +/* called from within the .release_port() callback (release) */ +static void mpc512x_psc_relse_clock(struct uart_port *port) +{ + int psc_num; + struct clk *clk; + + psc_num = (port->mapbase & 0xf00) >> 8; + clk = psc_mclk_clk[psc_num]; + if (clk) { + clk_disable_unprepare(clk); + psc_mclk_clk[psc_num] = NULL; + } + if (psc_ipg_clk[psc_num]) { + clk_disable_unprepare(psc_ipg_clk[psc_num]); + psc_ipg_clk[psc_num] = NULL; + } +} + +/* implementation of the .clock() callback (enable/disable) */ +static int mpc512x_psc_endis_clock(struct uart_port *port, int enable) +{ + int psc_num; + struct clk *psc_clk; + int ret; if (uart_console(port)) return 0; psc_num = (port->mapbase & 0xf00) >> 8; - snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); - psc_clk = clk_get(port->dev, clk_name); - if (IS_ERR(psc_clk)) { + psc_clk = psc_mclk_clk[psc_num]; + if (!psc_clk) { dev_err(port->dev, "Failed to get PSC clock entry!\n"); return -ENODEV; } - dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); - - if (enable) - clk_enable(psc_clk); - else + dev_dbg(port->dev, "mclk %sable\n", enable ? "en" : "dis"); + if (enable) { + ret = clk_enable(psc_clk); + if (ret) + dev_err(port->dev, "Failed to enable MCLK!\n"); + return ret; + } else { clk_disable(psc_clk); - - return 0; + return 0; + } } static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) @@ -572,6 +760,248 @@ static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) port->irqflags = IRQF_SHARED; port->irq = psc_fifoc_irq; } +#endif + +#ifdef CONFIG_PPC_MPC512x + +#define PSC_5125(port) ((struct mpc5125_psc __iomem *)((port)->membase)) +#define FIFO_5125(port) ((struct mpc512x_psc_fifo __iomem *)(PSC_5125(port)+1)) + +static void mpc5125_psc_fifo_init(struct uart_port *port) +{ + /* /32 prescaler */ + out_8(&PSC_5125(port)->mpc52xx_psc_clock_select, 0xdd); + + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->txalarm, 1); + out_be32(&FIFO_5125(port)->tximr, 0); + + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_5125(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_5125(port)->rxalarm, 1); + out_be32(&FIFO_5125(port)->rximr, 0); + + out_be32(&FIFO_5125(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_5125(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc5125_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc5125_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc5125_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->rxsr) & + in_be32(&FIFO_5125(port)->rximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & + in_be32(&FIFO_5125(port)->tximr) & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc5125_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_5125(port)->txsr) & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc5125_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_5125(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->rximr, rx_fifo_imr); +} + +static void mpc5125_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_5125(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_5125(port)->tximr, tx_fifo_imr); +} + +static void mpc5125_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->rxisr, in_be32(&FIFO_5125(port)->rxisr)); +} + +static void mpc5125_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->txisr, in_be32(&FIFO_5125(port)->txisr)); +} + +static void mpc5125_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_5125(port)->txdata_8, c); +} + +static unsigned char mpc5125_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_5125(port)->rxdata_8); +} + +static void mpc5125_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_5125(port)->tximr) << 16 | + in_be32(&FIFO_5125(port)->rximr); + out_be32(&FIFO_5125(port)->tximr, 0); + out_be32(&FIFO_5125(port)->rximr, 0); +} + +static void mpc5125_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_5125(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_5125(port)->rximr, port->read_status_mask & 0x7f); +} + +static inline void mpc5125_set_divisor(struct mpc5125_psc __iomem *psc, + u8 prescaler, unsigned int divisor) +{ + /* select prescaler */ + out_8(&psc->mpc52xx_psc_clock_select, prescaler); + out_8(&psc->ctur, divisor >> 8); + out_8(&psc->ctlr, divisor & 0xff); +} + +static unsigned int mpc5125_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + + /* + * Calculate with a /16 prescaler here. + */ + + /* uartclk contains the ips freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (16 * 0xffff) + 1, + port->uartclk / 16); + divisor = (port->uartclk + 8 * baud) / (16 * baud); + + /* enable the /16 prescaler and set the divisor */ + mpc5125_set_divisor(PSC_5125(port), 0xdd, divisor); + return baud; +} + +/* + * MPC5125 have compatible PSC FIFO Controller. + * Special init not needed. + */ +static u16 mpc5125_psc_get_status(struct uart_port *port) +{ + return in_be16(&PSC_5125(port)->mpc52xx_psc_status); +} + +static u8 mpc5125_psc_get_ipcr(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mpc52xx_psc_ipcr); +} + +static void mpc5125_psc_command(struct uart_port *port, u8 cmd) +{ + out_8(&PSC_5125(port)->command, cmd); +} + +static void mpc5125_psc_set_mode(struct uart_port *port, u8 mr1, u8 mr2) +{ + out_8(&PSC_5125(port)->mr1, mr1); + out_8(&PSC_5125(port)->mr2, mr2); +} + +static void mpc5125_psc_set_rts(struct uart_port *port, int state) +{ + if (state & TIOCM_RTS) + out_8(&PSC_5125(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC_5125(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static void mpc5125_psc_enable_ms(struct uart_port *port) +{ + struct mpc5125_psc __iomem *psc = PSC_5125(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc5125_psc_set_sicr(struct uart_port *port, u32 val) +{ + out_be32(&PSC_5125(port)->sicr, val); +} + +static void mpc5125_psc_set_imr(struct uart_port *port, u16 val) +{ + out_be16(&PSC_5125(port)->mpc52xx_psc_imr, val); +} + +static u8 mpc5125_psc_get_mr1(struct uart_port *port) +{ + return in_8(&PSC_5125(port)->mr1); +} + +static struct psc_ops mpc5125_psc_ops = { + .fifo_init = mpc5125_psc_fifo_init, + .raw_rx_rdy = mpc5125_psc_raw_rx_rdy, + .raw_tx_rdy = mpc5125_psc_raw_tx_rdy, + .rx_rdy = mpc5125_psc_rx_rdy, + .tx_rdy = mpc5125_psc_tx_rdy, + .tx_empty = mpc5125_psc_tx_empty, + .stop_rx = mpc5125_psc_stop_rx, + .start_tx = mpc5125_psc_start_tx, + .stop_tx = mpc5125_psc_stop_tx, + .rx_clr_irq = mpc5125_psc_rx_clr_irq, + .tx_clr_irq = mpc5125_psc_tx_clr_irq, + .write_char = mpc5125_psc_write_char, + .read_char = mpc5125_psc_read_char, + .cw_disable_ints = mpc5125_psc_cw_disable_ints, + .cw_restore_ints = mpc5125_psc_cw_restore_ints, + .set_baudrate = mpc5125_psc_set_baudrate, + .clock_alloc = mpc512x_psc_alloc_clock, + .clock_relse = mpc512x_psc_relse_clock, + .clock = mpc512x_psc_endis_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc5125_psc_get_status, + .get_ipcr = mpc5125_psc_get_ipcr, + .command = mpc5125_psc_command, + .set_mode = mpc5125_psc_set_mode, + .set_rts = mpc5125_psc_set_rts, + .enable_ms = mpc5125_psc_enable_ms, + .set_sicr = mpc5125_psc_set_sicr, + .set_imr = mpc5125_psc_set_imr, + .get_mr1 = mpc5125_psc_get_mr1, +}; static struct psc_ops mpc512x_psc_ops = { .fifo_init = mpc512x_psc_fifo_init, @@ -590,13 +1020,25 @@ static struct psc_ops mpc512x_psc_ops = { .cw_disable_ints = mpc512x_psc_cw_disable_ints, .cw_restore_ints = mpc512x_psc_cw_restore_ints, .set_baudrate = mpc512x_psc_set_baudrate, - .clock = mpc512x_psc_clock, + .clock_alloc = mpc512x_psc_alloc_clock, + .clock_relse = mpc512x_psc_relse_clock, + .clock = mpc512x_psc_endis_clock, .fifoc_init = mpc512x_psc_fifoc_init, .fifoc_uninit = mpc512x_psc_fifoc_uninit, .get_irq = mpc512x_psc_get_irq, .handle_irq = mpc512x_psc_handle_irq, + .get_status = mpc52xx_psc_get_status, + .get_ipcr = mpc52xx_psc_get_ipcr, + .command = mpc52xx_psc_command, + .set_mode = mpc52xx_psc_set_mode, + .set_rts = mpc52xx_psc_set_rts, + .enable_ms = mpc52xx_psc_enable_ms, + .set_sicr = mpc52xx_psc_set_sicr, + .set_imr = mpc52xx_psc_set_imr, + .get_mr1 = mpc52xx_psc_get_mr1, }; -#endif +#endif /* CONFIG_PPC_MPC512x */ + static const struct psc_ops *psc_ops; @@ -613,17 +1055,14 @@ mpc52xx_uart_tx_empty(struct uart_port *port) static void mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { - if (mctrl & TIOCM_RTS) - out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); - else - out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); + psc_ops->set_rts(port, mctrl & TIOCM_RTS); } static unsigned int mpc52xx_uart_get_mctrl(struct uart_port *port) { unsigned int ret = TIOCM_DSR; - u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + u8 status = psc_ops->get_ipcr(port); if (!(status & MPC52xx_PSC_CTS)) ret |= TIOCM_CTS; @@ -673,15 +1112,7 @@ mpc52xx_uart_stop_rx(struct uart_port *port) static void mpc52xx_uart_enable_ms(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - - /* clear D_*-bits by reading them */ - in_8(&psc->mpc52xx_psc_ipcr); - /* enable CTS and DCD as IPC interrupts */ - out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); - - port->read_status_mask |= MPC52xx_PSC_IMR_IPC; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->enable_ms(port); } static void @@ -691,9 +1122,9 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) spin_lock_irqsave(&port->lock, flags); if (ctl == -1) - out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); + psc_ops->command(port, MPC52xx_PSC_START_BRK); else - out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); + psc_ops->command(port, MPC52xx_PSC_STOP_BRK); spin_unlock_irqrestore(&port->lock, flags); } @@ -701,7 +1132,6 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) static int mpc52xx_uart_startup(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); int ret; if (psc_ops->clock) { @@ -717,15 +1147,15 @@ mpc52xx_uart_startup(struct uart_port *port) return ret; /* Reset/activate the port, clear and enable interrupts */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); - out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ + psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */ psc_ops->fifo_init(port); - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); return 0; } @@ -733,19 +1163,20 @@ mpc52xx_uart_startup(struct uart_port *port) static void mpc52xx_uart_shutdown(struct uart_port *port) { - struct mpc52xx_psc __iomem *psc = PSC(port); - /* Shut down the port. Leave TX active if on a console port */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); if (!uart_console(port)) - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); port->read_status_mask = 0; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->set_imr(port, port->read_status_mask); if (psc_ops->clock) psc_ops->clock(port, 0); + /* Disable interrupt */ + psc_ops->cw_disable_ints(port); + /* Release interrupt */ free_irq(port->irq, port); } @@ -754,7 +1185,6 @@ static void mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, struct ktermios *old) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned long flags; unsigned char mr1, mr2; unsigned int j; @@ -818,13 +1248,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, "Some chars may have been lost.\n"); /* Reset the TX & RX */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); + psc_ops->command(port, MPC52xx_PSC_RST_RX); + psc_ops->command(port, MPC52xx_PSC_RST_TX); /* Send new mode settings */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - out_8(&psc->mode, mr1); - out_8(&psc->mode, mr2); + psc_ops->set_mode(port, mr1, mr2); baud = psc_ops->set_baudrate(port, new, old); /* Update the per-port timeout */ @@ -834,8 +1262,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, mpc52xx_uart_enable_ms(port); /* Reenable TX & RX */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_TX_ENABLE); + psc_ops->command(port, MPC52xx_PSC_RX_ENABLE); /* We're all set, release the lock */ spin_unlock_irqrestore(&port->lock, flags); @@ -854,6 +1282,9 @@ mpc52xx_uart_type(struct uart_port *port) static void mpc52xx_uart_release_port(struct uart_port *port) { + if (psc_ops->clock_relse) + psc_ops->clock_relse(port); + /* remapped by us ? */ if (port->flags & UPF_IOREMAP) { iounmap(port->membase); @@ -878,11 +1309,24 @@ mpc52xx_uart_request_port(struct uart_port *port) err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc), "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY; - if (err && (port->flags & UPF_IOREMAP)) { + if (err) + goto out_membase; + + if (psc_ops->clock_alloc) { + err = psc_ops->clock_alloc(port); + if (err) + goto out_mapregion; + } + + return 0; + +out_mapregion: + release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc)); +out_membase: + if (port->flags & UPF_IOREMAP) { iounmap(port->membase); port->membase = NULL; } - return err; } @@ -925,7 +1369,6 @@ static struct uart_ops mpc52xx_uart_ops = { .shutdown = mpc52xx_uart_shutdown, .set_termios = mpc52xx_uart_set_termios, /* .pm = mpc52xx_uart_pm, Not supported yet */ -/* .set_wake = mpc52xx_uart_set_wake, Not supported yet */ .type = mpc52xx_uart_type, .release_port = mpc52xx_uart_release_port, .request_port = mpc52xx_uart_request_port, @@ -941,7 +1384,7 @@ static struct uart_ops mpc52xx_uart_ops = { static inline int mpc52xx_uart_int_rx_chars(struct uart_port *port) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; unsigned char ch, flag; unsigned short status; @@ -963,7 +1406,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; - status = in_be16(&PSC(port)->mpc52xx_psc_status); + status = psc_ops->get_status(port); if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | @@ -983,23 +1426,23 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } /* Clear error condition */ - out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); + psc_ops->command(port, MPC52xx_PSC_RST_ERR_STAT); } - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tport, ch, flag); if (status & MPC52xx_PSC_SR_OE) { /* * Overrun is special, since it's * reported immediately, and doesn't * affect the current character */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); port->icount.overrun++; } } spin_unlock(&port->lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); spin_lock(&port->lock); return psc_ops->raw_rx_rdy(port); @@ -1066,7 +1509,7 @@ mpc5xxx_uart_process_int(struct uart_port *port) if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); - status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + status = psc_ops->get_ipcr(port); if (status & MPC52xx_PSC_D_DCD) uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); @@ -1107,14 +1550,12 @@ static void __init mpc52xx_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits, int *flow) { - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned char mr1; pr_debug("mpc52xx_console_get_options(port=%p)\n", port); /* Read the mode registers */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - mr1 = in_8(&psc->mode); + mr1 = psc_ops->get_mr1(port); /* CT{U,L}R are write-only ! */ *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; @@ -1304,6 +1745,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { #endif #ifdef CONFIG_PPC_MPC512x { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + { .compatible = "fsl,mpc5125-psc-uart", .data = &mpc5125_psc_ops, }, #endif {}, }; @@ -1372,15 +1814,14 @@ static int mpc52xx_uart_of_probe(struct platform_device *op) if (ret) return ret; - dev_set_drvdata(&op->dev, (void *)port); + platform_set_drvdata(op, (void *)port); return 0; } static int mpc52xx_uart_of_remove(struct platform_device *op) { - struct uart_port *port = dev_get_drvdata(&op->dev); - dev_set_drvdata(&op->dev, NULL); + struct uart_port *port = platform_get_drvdata(op); if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); @@ -1392,7 +1833,7 @@ mpc52xx_uart_of_remove(struct platform_device *op) static int mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = platform_get_drvdata(op); if (port) uart_suspend_port(&mpc52xx_uart_driver, port); @@ -1403,7 +1844,7 @@ mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) static int mpc52xx_uart_of_resume(struct platform_device *op) { - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + struct uart_port *port = platform_get_drvdata(op); if (port) uart_resume_port(&mpc52xx_uart_driver, port); @@ -1497,18 +1938,23 @@ mpc52xx_uart_init(void) if (psc_ops && psc_ops->fifoc_init) { ret = psc_ops->fifoc_init(); if (ret) - return ret; + goto err_init; } ret = platform_driver_register(&mpc52xx_uart_of_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", __FILE__, ret); - uart_unregister_driver(&mpc52xx_uart_driver); - return ret; + goto err_reg; } return 0; +err_reg: + if (psc_ops && psc_ops->fifoc_uninit) + psc_ops->fifoc_uninit(); +err_init: + uart_unregister_driver(&mpc52xx_uart_driver); + return ret; } static void __exit diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 6a9c6605666..759c6a6fa74 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -934,10 +934,10 @@ static int serial_polled; ****************************************************************************** */ -static int mpsc_rx_intr(struct mpsc_port_info *pi) +static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags) { struct mpsc_rx_desc *rxre; - struct tty_struct *tty = pi->port.state->port.tty; + struct tty_port *port = &pi->port.state->port; u32 cmdstat, bytes_in, i; int rc = 0; u8 *bp; @@ -968,10 +968,12 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi) } #endif /* Following use of tty struct directly is deprecated */ - if (unlikely(tty_buffer_request_room(tty, bytes_in) - < bytes_in)) { - if (tty->low_latency) - tty_flip_buffer_push(tty); + if (tty_buffer_request_room(port, bytes_in) < bytes_in) { + if (port->low_latency) { + spin_unlock_irqrestore(&pi->port.lock, *flags); + tty_flip_buffer_push(port); + spin_lock_irqsave(&pi->port.lock, *flags); + } /* * If this failed then we will throw away the bytes * but must do so to clear interrupts. @@ -1040,10 +1042,10 @@ static int mpsc_rx_intr(struct mpsc_port_info *pi) | SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && !(cmdstat & pi->port.ignore_status_mask)) { - tty_insert_flip_char(tty, *bp, flag); + tty_insert_flip_char(port, *bp, flag); } else { for (i=0; i<bytes_in; i++) - tty_insert_flip_char(tty, *bp++, TTY_NORMAL); + tty_insert_flip_char(port, *bp++, TTY_NORMAL); pi->port.icount.rx += bytes_in; } @@ -1081,7 +1083,9 @@ next_frame: if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) mpsc_start_rx(pi); - tty_flip_buffer_push(tty); + spin_unlock_irqrestore(&pi->port.lock, *flags); + tty_flip_buffer_push(port); + spin_lock_irqsave(&pi->port.lock, *flags); return rc; } @@ -1223,7 +1227,7 @@ static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) spin_lock_irqsave(&pi->port.lock, iflags); mpsc_sdma_intr_ack(pi); - if (mpsc_rx_intr(pi)) + if (mpsc_rx_intr(pi, &iflags)) rc = IRQ_HANDLED; if (mpsc_tx_intr(pi)) rc = IRQ_HANDLED; @@ -1454,7 +1458,7 @@ static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE | SDMA_DESC_CMDSTAT_FR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; /* Characters/events to ignore */ @@ -1885,7 +1889,7 @@ static int mpsc_shared_drv_probe(struct platform_device *dev) if (dev->id == 0) { if (!(rc = mpsc_shared_map_regs(dev))) { pdata = (struct mpsc_shared_pdata *) - dev->dev.platform_data; + dev_get_platdata(&dev->dev); mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; @@ -2026,7 +2030,7 @@ static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, { struct mpsc_pdata *pdata; - pdata = (struct mpsc_pdata *)pd->dev.platform_data; + pdata = dev_get_platdata(&pd->dev); pi->port.uartclk = pdata->brg_clk_freq; pi->port.iotype = UPIO_MEM; diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 58734d7e746..db0448ae59d 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -43,6 +43,7 @@ #include <linux/kthread.h> #include <linux/spi/spi.h> +#include <linux/pm.h> #include "mrst_max3110.h" @@ -61,6 +62,7 @@ struct uart_max3110 { struct task_struct *main_thread; struct task_struct *read_thread; struct mutex thread_mutex; + struct mutex io_mutex; u32 baud; u16 cur_conf; @@ -90,6 +92,7 @@ static int max3110_write_then_read(struct uart_max3110 *max, struct spi_transfer x; int ret; + mutex_lock(&max->io_mutex); spi_message_init(&message); memset(&x, 0, sizeof x); x.len = len; @@ -104,6 +107,7 @@ static int max3110_write_then_read(struct uart_max3110 *max, /* Do the i/o */ ret = spi_sync(spi, &message); + mutex_unlock(&max->io_mutex); return ret; } @@ -339,7 +343,7 @@ static int receive_chars(struct uart_max3110 *max, unsigned short *str, int len) { struct uart_port *port = &max->port; - struct tty_struct *tty; + struct tty_port *tport; char buf[M3110_RX_FIFO_DEPTH]; int r, w, usable; @@ -347,9 +351,7 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len) if (!port->state) return 0; - tty = tty_port_tty_get(&port->state->port); - if (!tty) - return 0; + tport = &port->state->port; for (r = 0, w = 0; r < len; r++) { if (str[r] & MAX3110_BREAK && @@ -364,20 +366,17 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len) } } - if (!w) { - tty_kref_put(tty); + if (!w) return 0; - } for (r = 0; w; r += usable, w -= usable) { - usable = tty_buffer_request_room(tty, w); + usable = tty_buffer_request_room(tport, w); if (usable) { - tty_insert_flip_string(tty, buf + r, usable); + tty_insert_flip_string(tport, buf + r, usable); port->icount.rx += usable; } } - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty_flip_buffer_push(tport); return r; } @@ -493,22 +492,12 @@ static int serial_m3110_startup(struct uart_port *port) | WC_BAUD_DR2; /* as we use thread to handle tx/rx, need set low latency */ - port->state->port.tty->low_latency = 1; + port->state->port.low_latency = 1; if (max->irq) { - max->read_thread = NULL; - ret = request_irq(max->irq, serial_m3110_irq, - IRQ_TYPE_EDGE_FALLING, "max3110", max); - if (ret) { - max->irq = 0; - pr_err(PR_FMT "unable to allocate IRQ, polling\n"); - } else { - /* Enable RX IRQ only */ - config |= WC_RXA_IRQ_ENABLE; - } - } - - if (max->irq == 0) { + /* Enable RX IRQ only */ + config |= WC_RXA_IRQ_ENABLE; + } else { /* If IRQ is disabled, start a read thread for input data */ max->read_thread = kthread_run(max3110_read_thread, max, "max3110_read"); @@ -522,8 +511,6 @@ static int serial_m3110_startup(struct uart_port *port) ret = max3110_out(max, config); if (ret) { - if (max->irq) - free_irq(max->irq, max); if (max->read_thread) kthread_stop(max->read_thread); max->read_thread = NULL; @@ -545,9 +532,6 @@ static void serial_m3110_shutdown(struct uart_port *port) max->read_thread = NULL; } - if (max->irq) - free_irq(max->irq, max); - /* Disable interrupts from this port */ config = WC_TAG | WC_SW_SHDI; max3110_out(max, config); @@ -718,7 +702,7 @@ static void serial_m3110_enable_ms(struct uart_port *port) { } -struct uart_ops serial_m3110_ops = { +static struct uart_ops serial_m3110_ops = { .tx_empty = serial_m3110_tx_empty, .set_mctrl = serial_m3110_set_mctrl, .get_mctrl = serial_m3110_get_mctrl, @@ -748,29 +732,37 @@ static struct uart_driver serial_m3110_reg = { .cons = &serial_m3110_console, }; -#ifdef CONFIG_PM -static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int serial_m3110_suspend(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); - disable_irq(max->irq); + if (max->irq > 0) + disable_irq(max->irq); uart_suspend_port(&serial_m3110_reg, &max->port); max3110_out(max, max->cur_conf | WC_SW_SHDI); return 0; } -static int serial_m3110_resume(struct spi_device *spi) +static int serial_m3110_resume(struct device *dev) { + struct spi_device *spi = to_spi_device(dev); struct uart_max3110 *max = spi_get_drvdata(spi); max3110_out(max, max->cur_conf); uart_resume_port(&serial_m3110_reg, &max->port); - enable_irq(max->irq); + if (max->irq > 0) + enable_irq(max->irq); return 0; } + +static SIMPLE_DEV_PM_OPS(serial_m3110_pm_ops, serial_m3110_suspend, + serial_m3110_resume); +#define SERIAL_M3110_PM_OPS (&serial_m3110_pm_ops) + #else -#define serial_m3110_suspend NULL -#define serial_m3110_resume NULL +#define SERIAL_M3110_PM_OPS NULL #endif static int serial_m3110_probe(struct spi_device *spi) @@ -802,6 +794,7 @@ static int serial_m3110_probe(struct spi_device *spi) max->irq = (u16)spi->irq; mutex_init(&max->thread_mutex); + mutex_init(&max->io_mutex); max->word_7bits = 0; max->parity = 0; @@ -839,11 +832,21 @@ static int serial_m3110_probe(struct spi_device *spi) goto err_kthread; } + if (max->irq) { + ret = request_irq(max->irq, serial_m3110_irq, + IRQ_TYPE_EDGE_FALLING, "max3110", max); + if (ret) { + max->irq = 0; + dev_warn(&spi->dev, + "unable to allocate IRQ, will use polling method\n"); + } + } + spi_set_drvdata(spi, max); pmax = max; /* Give membase a psudo value to pass serial_core's check */ - max->port.membase = (void *)0xff110000; + max->port.membase = (unsigned char __iomem *)0xff110000; uart_add_one_port(&serial_m3110_reg, &max->port); return 0; @@ -866,6 +869,9 @@ static int serial_m3110_remove(struct spi_device *dev) free_page((unsigned long)max->con_xmit.buf); + if (max->irq) + free_irq(max->irq, max); + if (max->main_thread) kthread_stop(max->main_thread); @@ -877,11 +883,10 @@ static struct spi_driver uart_max3110_driver = { .driver = { .name = "spi_max3111", .owner = THIS_MODULE, + .pm = SERIAL_M3110_PM_OPS, }, .probe = serial_m3110_probe, .remove = serial_m3110_remove, - .suspend = serial_m3110_suspend, - .resume = serial_m3110_resume, }; static int __init serial_m3110_init(void) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 95fd39be293..72000a6d5af 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -39,22 +39,31 @@ #include "msm_serial.h" +enum { + UARTDM_1P1 = 1, + UARTDM_1P2, + UARTDM_1P3, + UARTDM_1P4, +}; + struct msm_port { struct uart_port uart; char name[16]; struct clk *clk; struct clk *pclk; unsigned int imr; - unsigned int *gsbi_base; int is_uartdm; unsigned int old_snap_state; }; -static inline void wait_for_xmitr(struct uart_port *port, int bits) +static inline void wait_for_xmitr(struct uart_port *port) { - if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) - while ((msm_read(port, UART_ISR) & bits) != bits) - cpu_relax(); + while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { + if (msm_read(port, UART_ISR) & UART_ISR_TX_READY) + break; + udelay(1); + } + msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR); } static void msm_stop_tx(struct uart_port *port) @@ -91,14 +100,14 @@ static void msm_enable_ms(struct uart_port *port) static void handle_rx_dm(struct uart_port *port, unsigned int misr) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; unsigned int sr; int count = 0; struct msm_port *msm_port = UART_TO_MSM(port); if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); } @@ -132,12 +141,15 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr) port->icount.frame++; /* TODO: handle sysrq */ - tty_insert_flip_string(tty, (char *) &c, + tty_insert_flip_string(tport, (char *)&c, (count > 4) ? 4 : count); count -= 4; } - tty_flip_buffer_push(tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); + if (misr & (UART_IMR_RXSTALE)) msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); msm_write(port, 0xFFFFFF, UARTDM_DMRX); @@ -146,7 +158,7 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr) static void handle_rx(struct uart_port *port) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; unsigned int sr; /* @@ -155,7 +167,7 @@ static void handle_rx(struct uart_port *port) */ if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); } @@ -186,55 +198,72 @@ static void handle_rx(struct uart_port *port) } if (!uart_handle_sysrq_char(port, c)) - tty_insert_flip_char(tty, c, flag); + tty_insert_flip_char(tport, c, flag); } - tty_flip_buffer_push(tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); } -static void reset_dm_count(struct uart_port *port) +static void reset_dm_count(struct uart_port *port, int count) { - wait_for_xmitr(port, UART_ISR_TX_READY); - msm_write(port, 1, UARTDM_NCF_TX); + wait_for_xmitr(port); + msm_write(port, count, UARTDM_NCF_TX); + msm_read(port, UARTDM_NCF_TX); } static void handle_tx(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); - int sent_tx; + unsigned int tx_count, num_chars; + unsigned int tf_pointer = 0; + + tx_count = uart_circ_chars_pending(xmit); + tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail, + port->fifosize); if (port->x_char) { if (msm_port->is_uartdm) - reset_dm_count(port); + reset_dm_count(port, tx_count + 1); msm_write(port, port->x_char, msm_port->is_uartdm ? UARTDM_TF : UART_TF); port->icount.tx++; port->x_char = 0; + } else if (tx_count && msm_port->is_uartdm) { + reset_dm_count(port, tx_count); } - if (msm_port->is_uartdm) - reset_dm_count(port); + while (tf_pointer < tx_count) { + int i; + char buf[4] = { 0 }; + unsigned int *bf = (unsigned int *)&buf; - while (msm_read(port, UART_SR) & UART_SR_TX_READY) { - if (uart_circ_empty(xmit)) { - /* disable tx interrupts */ - msm_port->imr &= ~UART_IMR_TXLEV; - msm_write(port, msm_port->imr, UART_IMR); + if (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) break; - } - msm_write(port, xmit->buf[xmit->tail], - msm_port->is_uartdm ? UARTDM_TF : UART_TF); if (msm_port->is_uartdm) - reset_dm_count(port); + num_chars = min(tx_count - tf_pointer, + (unsigned int)sizeof(buf)); + else + num_chars = 1; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - sent_tx = 1; + for (i = 0; i < num_chars; i++) { + buf[i] = xmit->buf[xmit->tail + i]; + port->icount.tx++; + } + + msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF); + xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1); + tf_pointer += num_chars; } + /* disable tx interrupts if nothing more to send */ + if (uart_circ_empty(xmit)) + msm_stop_tx(port); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -286,6 +315,8 @@ static unsigned int msm_get_mctrl(struct uart_port *port) static void msm_reset(struct uart_port *port) { + struct msm_port *msm_port = UART_TO_MSM(port); + /* reset everything */ msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); @@ -293,9 +324,13 @@ static void msm_reset(struct uart_port *port) msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); + + /* Disable DM modes */ + if (msm_port->is_uartdm) + msm_write(port, 0, UARTDM_DMEN); } -void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int mr; mr = msm_read(port, UART_MR1); @@ -318,70 +353,60 @@ static void msm_break_ctl(struct uart_port *port, int break_ctl) msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); } +struct msm_baud_map { + u16 divisor; + u8 code; + u8 rxstale; +}; + +static const struct msm_baud_map * +msm_find_best_baud(struct uart_port *port, unsigned int baud) +{ + unsigned int i, divisor; + const struct msm_baud_map *entry; + 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 }, + }; + + divisor = uart_get_divisor(port, baud); + + for (i = 0, entry = table; i < ARRAY_SIZE(table); i++, entry++) + if (entry->divisor <= divisor) + break; + + return entry; /* Default to smallest divider */ +} + static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) { - unsigned int baud_code, rxstale, watermark; + unsigned int rxstale, watermark; struct msm_port *msm_port = UART_TO_MSM(port); + const struct msm_baud_map *entry; - switch (baud) { - case 300: - baud_code = UART_CSR_300; - rxstale = 1; - break; - case 600: - baud_code = UART_CSR_600; - rxstale = 1; - break; - case 1200: - baud_code = UART_CSR_1200; - rxstale = 1; - break; - case 2400: - baud_code = UART_CSR_2400; - rxstale = 1; - break; - case 4800: - baud_code = UART_CSR_4800; - rxstale = 1; - break; - case 9600: - baud_code = UART_CSR_9600; - rxstale = 2; - break; - case 14400: - baud_code = UART_CSR_14400; - rxstale = 3; - break; - case 19200: - baud_code = UART_CSR_19200; - rxstale = 4; - break; - case 28800: - baud_code = UART_CSR_28800; - rxstale = 6; - break; - case 38400: - baud_code = UART_CSR_38400; - rxstale = 8; - break; - case 57600: - baud_code = UART_CSR_57600; - rxstale = 16; - break; - case 115200: - default: - baud_code = UART_CSR_115200; - baud = 115200; - rxstale = 31; - break; - } + entry = msm_find_best_baud(port, baud); if (msm_port->is_uartdm) msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); - msm_write(port, baud_code, UART_CSR); + msm_write(port, entry->code, UART_CSR); /* RX stale watermark */ + rxstale = entry->rxstale; watermark = UART_IPR_STALE_LSB & rxstale; watermark |= UART_IPR_RXSTALE_LAST; watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); @@ -408,9 +433,8 @@ static void msm_init_clock(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); - clk_enable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_enable(msm_port->pclk); + clk_prepare_enable(msm_port->clk); + clk_prepare_enable(msm_port->pclk); msm_serial_set_mnd_regs(port); } @@ -486,7 +510,7 @@ static void msm_shutdown(struct uart_port *port) msm_port->imr = 0; msm_write(port, 0, UART_IMR); /* disable interrupts */ - clk_disable(msm_port->clk); + clk_disable_unprepare(msm_port->clk); free_irq(port->irq, port); } @@ -558,7 +582,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, port->read_status_mask = 0; if (termios->c_iflag & INPCK) port->read_status_mask |= UART_SR_PAR_FRAME_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= UART_SR_RX_BREAK; uart_update_timeout(port, termios->c_cflag, baud); @@ -574,9 +598,7 @@ static const char *msm_type(struct uart_port *port) static void msm_release_port(struct uart_port *port) { struct platform_device *pdev = to_platform_device(port->dev); - struct msm_port *msm_port = UART_TO_MSM(port); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -587,30 +609,12 @@ static void msm_release_port(struct uart_port *port) release_mem_region(port->mapbase, size); iounmap(port->membase); port->membase = NULL; - - if (msm_port->gsbi_base) { - iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base + - GSBI_CONTROL); - - gsbi_resource = platform_get_resource(pdev, - IORESOURCE_MEM, 1); - - if (unlikely(!gsbi_resource)) - return; - - size = resource_size(gsbi_resource); - release_mem_region(gsbi_resource->start, size); - iounmap(msm_port->gsbi_base); - msm_port->gsbi_base = NULL; - } } static int msm_request_port(struct uart_port *port) { - struct msm_port *msm_port = UART_TO_MSM(port); struct platform_device *pdev = to_platform_device(port->dev); struct resource *uart_resource; - struct resource *gsbi_resource; resource_size_t size; int ret; @@ -629,28 +633,8 @@ static int msm_request_port(struct uart_port *port) goto fail_release_port; } - gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - /* Is this a GSBI-based port? */ - if (gsbi_resource) { - size = resource_size(gsbi_resource); - - if (!request_mem_region(gsbi_resource->start, size, - "msm_serial")) { - ret = -EBUSY; - goto fail_release_port; - } - - msm_port->gsbi_base = ioremap(gsbi_resource->start, size); - if (!msm_port->gsbi_base) { - ret = -EBUSY; - goto fail_release_gsbi; - } - } - return 0; -fail_release_gsbi: - release_mem_region(gsbi_resource->start, size); fail_release_port: release_mem_region(port->mapbase, size); return ret; @@ -658,7 +642,6 @@ fail_release_port: static void msm_config_port(struct uart_port *port, int flags) { - struct msm_port *msm_port = UART_TO_MSM(port); int ret; if (flags & UART_CONFIG_TYPE) { port->type = PORT_MSM; @@ -666,10 +649,6 @@ static void msm_config_port(struct uart_port *port, int flags) if (ret) return; } - - if (msm_port->is_uartdm) - iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base + - GSBI_CONTROL); } static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) @@ -688,20 +667,129 @@ static void msm_power(struct uart_port *port, unsigned int state, switch (state) { case 0: - clk_enable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_enable(msm_port->pclk); + clk_prepare_enable(msm_port->clk); + clk_prepare_enable(msm_port->pclk); break; case 3: - clk_disable(msm_port->clk); - if (!IS_ERR(msm_port->pclk)) - clk_disable(msm_port->pclk); + clk_disable_unprepare(msm_port->clk); + clk_disable_unprepare(msm_port->pclk); break; default: printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); } } +#ifdef CONFIG_CONSOLE_POLL +static int msm_poll_init(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + /* Enable single character mode on RX FIFO */ + if (msm_port->is_uartdm >= UARTDM_1P4) + msm_write(port, UARTDM_DMEN_RX_SC_ENABLE, UARTDM_DMEN); + + return 0; +} + +static int msm_poll_get_char_single(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : UART_RF; + + if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) + return NO_POLL_CHAR; + else + return msm_read(port, rf_reg) & 0xff; +} + +static int msm_poll_get_char_dm_1p3(struct uart_port *port) +{ + int c; + static u32 slop; + static int count; + unsigned char *sp = (unsigned char *)&slop; + + /* Check if a previous read had more than one char */ + if (count) { + c = sp[sizeof(slop) - count]; + count--; + /* Or if FIFO is empty */ + } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) { + /* + * If RX packing buffer has less than a word, force stale to + * push contents into RX FIFO + */ + count = msm_read(port, UARTDM_RXFS); + count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK; + if (count) { + msm_write(port, UART_CR_CMD_FORCE_STALE, UART_CR); + slop = msm_read(port, UARTDM_RF); + c = sp[0]; + count--; + } else { + c = NO_POLL_CHAR; + } + /* FIFO has a word */ + } else { + slop = msm_read(port, UARTDM_RF); + c = sp[0]; + count = sizeof(slop) - 1; + } + + return c; +} + +static int msm_poll_get_char(struct uart_port *port) +{ + u32 imr; + int c; + struct msm_port *msm_port = UART_TO_MSM(port); + + /* Disable all interrupts */ + imr = msm_read(port, UART_IMR); + msm_write(port, 0, UART_IMR); + + if (msm_port->is_uartdm == UARTDM_1P3) + c = msm_poll_get_char_dm_1p3(port); + else + c = msm_poll_get_char_single(port); + + /* Enable interrupts */ + msm_write(port, imr, UART_IMR); + + return c; +} + +static void msm_poll_put_char(struct uart_port *port, unsigned char c) +{ + u32 imr; + struct msm_port *msm_port = UART_TO_MSM(port); + + /* Disable all interrupts */ + imr = msm_read(port, UART_IMR); + msm_write(port, 0, UART_IMR); + + if (msm_port->is_uartdm) + reset_dm_count(port, 1); + + /* Wait until FIFO is empty */ + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + cpu_relax(); + + /* Write a character */ + msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF); + + /* Wait until FIFO is empty */ + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + cpu_relax(); + + /* Enable interrupts */ + msm_write(port, imr, UART_IMR); + + return; +} +#endif + static struct uart_ops msm_uart_pops = { .tx_empty = msm_tx_empty, .set_mctrl = msm_set_mctrl, @@ -720,6 +808,11 @@ static struct uart_ops msm_uart_pops = { .config_port = msm_config_port, .verify_port = msm_verify_port, .pm = msm_power, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = msm_poll_init, + .poll_get_char = msm_poll_get_char, + .poll_put_char = msm_poll_put_char, +#endif }; static struct msm_port msm_uart_ports[] = { @@ -760,32 +853,63 @@ static inline struct uart_port *get_port_from_line(unsigned int line) } #ifdef CONFIG_SERIAL_MSM_CONSOLE - -static void msm_console_putchar(struct uart_port *port, int c) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - if (msm_port->is_uartdm) - reset_dm_count(port); - - while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) - ; - msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF); -} - static void msm_console_write(struct console *co, const char *s, unsigned int count) { + int i; struct uart_port *port; struct msm_port *msm_port; + int num_newlines = 0; + bool replaced = false; BUG_ON(co->index < 0 || co->index >= UART_NR); port = get_port_from_line(co->index); msm_port = UART_TO_MSM(port); + /* Account for newlines that will get a carriage return added */ + for (i = 0; i < count; i++) + if (s[i] == '\n') + num_newlines++; + count += num_newlines; + spin_lock(&port->lock); - uart_console_write(port, s, count, msm_console_putchar); + if (msm_port->is_uartdm) + reset_dm_count(port, count); + + i = 0; + while (i < count) { + int j; + unsigned int num_chars; + char buf[4] = { 0 }; + unsigned int *bf = (unsigned int *)&buf; + + if (msm_port->is_uartdm) + num_chars = min(count - i, (unsigned int)sizeof(buf)); + else + num_chars = 1; + + for (j = 0; j < num_chars; j++) { + char c = *s; + + if (c == '\n' && !replaced) { + buf[j] = '\r'; + j++; + replaced = true; + } + if (j < num_chars) { + buf[j] = c; + s++; + replaced = false; + } + } + + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + cpu_relax(); + + msm_write(port, *bf, msm_port->is_uartdm ? UARTDM_TF : UART_TF); + i += num_chars; + } spin_unlock(&port->lock); } @@ -859,11 +983,20 @@ static struct uart_driver msm_uart_driver = { static atomic_t msm_uart_next_id = ATOMIC_INIT(0); -static int __init msm_serial_probe(struct platform_device *pdev) +static const struct of_device_id msm_uartdm_table[] = { + { .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 }, + { .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 }, + { .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 }, + { .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 }, + { } +}; + +static int msm_serial_probe(struct platform_device *pdev) { struct msm_port *msm_port; struct resource *resource; struct uart_port *port; + const struct of_device_id *id; int irq; if (pdev->id == -1) @@ -878,25 +1011,23 @@ static int __init msm_serial_probe(struct platform_device *pdev) port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); - if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) - msm_port->is_uartdm = 1; + id = of_match_device(msm_uartdm_table, &pdev->dev); + if (id) + msm_port->is_uartdm = (unsigned long)id->data; else msm_port->is_uartdm = 0; - if (msm_port->is_uartdm) { - msm_port->clk = clk_get(&pdev->dev, "gsbi_uart_clk"); - msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk"); - } else { - msm_port->clk = clk_get(&pdev->dev, "uart_clk"); - msm_port->pclk = ERR_PTR(-ENOENT); - } + msm_port->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(msm_port->clk)) + return PTR_ERR(msm_port->clk); - if (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) && - msm_port->is_uartdm))) - return PTR_ERR(msm_port->clk); + if (msm_port->is_uartdm) { + msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(msm_port->pclk)) + return PTR_ERR(msm_port->pclk); - if (msm_port->is_uartdm) clk_set_rate(msm_port->clk, 1843200); + } port->uartclk = clk_get_rate(msm_port->clk); printk(KERN_INFO "uartclk = %d\n", port->uartclk); @@ -919,20 +1050,22 @@ static int __init msm_serial_probe(struct platform_device *pdev) static int msm_serial_remove(struct platform_device *pdev) { - struct msm_port *msm_port = platform_get_drvdata(pdev); + struct uart_port *port = platform_get_drvdata(pdev); - clk_put(msm_port->clk); + uart_remove_one_port(&msm_uart_driver, port); return 0; } static struct of_device_id msm_match_table[] = { { .compatible = "qcom,msm-uart" }, + { .compatible = "qcom,msm-uartdm" }, {} }; static struct platform_driver msm_platform_driver = { .remove = msm_serial_remove, + .probe = msm_serial_probe, .driver = { .name = "msm_serial", .owner = THIS_MODULE, @@ -948,7 +1081,7 @@ static int __init msm_serial_init(void) if (unlikely(ret)) return ret; - ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); + ret = platform_driver_register(&msm_platform_driver); if (unlikely(ret)) uart_unregister_driver(&msm_uart_driver); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index e4acef5de77..d98d45efdf8 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -38,19 +38,7 @@ #define UART_MR2_PARITY_MODE_SPACE 0x3 #define UART_MR2_PARITY_MODE 0x3 -#define UART_CSR 0x0008 -#define UART_CSR_115200 0xFF -#define UART_CSR_57600 0xEE -#define UART_CSR_38400 0xDD -#define UART_CSR_28800 0xCC -#define UART_CSR_19200 0xBB -#define UART_CSR_14400 0xAA -#define UART_CSR_9600 0x99 -#define UART_CSR_4800 0x77 -#define UART_CSR_2400 0x55 -#define UART_CSR_1200 0x44 -#define UART_CSR_600 0x33 -#define UART_CSR_300 0x22 +#define UART_CSR 0x0008 #define UART_TF 0x000C #define UARTDM_TF 0x0070 @@ -71,6 +59,8 @@ #define UART_CR_CMD_RESET_RFR (14 << 4) #define UART_CR_CMD_PROTECTION_EN (16 << 4) #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) +#define UART_CR_CMD_FORCE_STALE (4 << 8) +#define UART_CR_CMD_RESET_TX_READY (3 << 8) #define UART_CR_TX_DISABLE (1 << 3) #define UART_CR_TX_ENABLE (1 << 2) #define UART_CR_RX_DISABLE (1 << 1) @@ -119,10 +109,13 @@ #define UART_ISR 0x0014 #define UART_ISR_TX_READY (1 << 7) -#define GSBI_CONTROL 0x0 -#define GSBI_PROTOCOL_CODE 0x30 -#define GSBI_PROTOCOL_UART 0x40 -#define GSBI_PROTOCOL_IDLE 0x0 +#define UARTDM_RXFS 0x50 +#define UARTDM_RXFS_BUF_SHIFT 0x7 +#define UARTDM_RXFS_BUF_MASK 0x7 + +#define UARTDM_DMEN 0x3C +#define UARTDM_DMEN_RX_SC_ENABLE BIT(5) +#define UARTDM_DMEN_TX_SC_ENABLE BIT(4) #define UARTDM_DMRX 0x34 #define UARTDM_NCF_TX 0x40 @@ -151,6 +144,7 @@ static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) msm_write(port, 0xF1, UART_NREG); msm_write(port, 0x0F, UART_DREG); msm_write(port, 0x1A, UART_MNDREG); + port->uartclk = 1843200; } /* @@ -162,6 +156,7 @@ static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) msm_write(port, 0xF6, UART_NREG); msm_write(port, 0x0F, UART_DREG); msm_write(port, 0x0A, UART_MNDREG); + port->uartclk = 1843200; } static inline @@ -169,7 +164,7 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) { if (port->uartclk == 19200000) msm_serial_set_mnd_regs_tcxo(port); - else + else if (port->uartclk == 4800000) msm_serial_set_mnd_regs_tcxoby4(port); } diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 1fa92284ade..48e94961a9e 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -907,7 +907,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int error_f = 0; unsigned long flags; unsigned int flush; - struct tty_struct *tty; + struct tty_port *port; struct uart_port *uport; struct msm_hs_port *msm_uport; @@ -917,7 +917,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, spin_lock_irqsave(&uport->lock, flags); clk_enable(msm_uport->clk); - tty = uport->state->port.tty; + port = &uport->state->port; msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); @@ -926,7 +926,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, /* overflow is not connect to data in a FIFO */ if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && (uport->read_status_mask & CREAD))) { - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); uport->icount.buf_overrun++; error_f = 1; } @@ -939,7 +939,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, uport->icount.parity++; error_f = 1; if (uport->ignore_status_mask & IGNPAR) - tty_insert_flip_char(tty, 0, TTY_PARITY); + tty_insert_flip_char(port, 0, TTY_PARITY); } if (error_f) @@ -959,7 +959,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); if (0 != (uport->read_status_mask & CREAD)) { - retval = tty_insert_flip_string(tty, msm_uport->rx.buffer, + retval = tty_insert_flip_string(port, msm_uport->rx.buffer, rx_count); BUG_ON(retval != rx_count); } @@ -979,9 +979,8 @@ static void msm_hs_tty_flip_buffer_work(struct work_struct *work) { struct msm_hs_port *msm_uport = container_of(work, struct msm_hs_port, rx.tty_work); - struct tty_struct *tty = msm_uport->uport.state->port.tty; - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&msm_uport->uport.state->port); } /* @@ -1344,7 +1343,6 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) unsigned long flags; struct msm_hs_port *msm_uport = dev; struct uart_port *uport = &msm_uport->uport; - struct tty_struct *tty = NULL; spin_lock_irqsave(&uport->lock, flags); if (msm_uport->clk_state == MSM_HS_CLK_OFF) { @@ -1361,8 +1359,7 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) * optionally inject char into tty rx */ msm_hs_request_clock_on_locked(uport); if (msm_uport->rx_wakeup.inject_rx) { - tty = uport->state->port.tty; - tty_insert_flip_char(tty, + tty_insert_flip_char(&uport->state->port, msm_uport->rx_wakeup.rx_to_inject, TTY_NORMAL); queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); @@ -1400,7 +1397,7 @@ static int msm_hs_startup(struct uart_port *uport) /* do not let tty layer execute RX in global workqueue, use a * dedicated workqueue managed by this driver */ - uport->state->port.tty->low_latency = 1; + uport->state->port.low_latency = 1; /* turn on uart clk */ ret = msm_hs_init_clk_locked(uport); @@ -1621,7 +1618,7 @@ static int msm_hs_probe(struct platform_device *pdev) struct msm_hs_port *msm_uport; struct resource *resource; const struct msm_serial_hs_platform_data *pdata = - pdev->dev.platform_data; + dev_get_platdata(&pdev->dev); if (pdev->id < 0 || pdev->id >= UARTDM_NR) { printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); diff --git a/drivers/tty/serial/msm_smd_tty.c b/drivers/tty/serial/msm_smd_tty.c index 925d1fa153d..1238ac370bf 100644 --- a/drivers/tty/serial/msm_smd_tty.c +++ b/drivers/tty/serial/msm_smd_tty.c @@ -70,7 +70,7 @@ static void smd_tty_notify(void *priv, unsigned event) if (avail == 0) break; - avail = tty_prepare_flip_string(tty, &ptr, avail); + avail = tty_prepare_flip_string(&info->port, &ptr, avail); if (smd_read(info->ch, ptr, avail) != avail) { /* shouldn't be possible since we're in interrupt @@ -80,7 +80,7 @@ static void smd_tty_notify(void *priv, unsigned event) pr_err("OOPS - smd_tty_buffer mismatch?!"); } - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&info->port); } /* XXX only when writable and necessary */ @@ -90,13 +90,13 @@ static void smd_tty_notify(void *priv, unsigned event) static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) { + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); int i, res = 0; - int n = tty->index; const char *name = NULL; - struct smd_tty_info *info = smd_tty + n; for (i = 0; i < smd_tty_channels_len; i++) { - if (smd_tty_channels[i].id == n) { + if (smd_tty_channels[i].id == tty->index) { name = smd_tty_channels[i].name; break; } @@ -117,17 +117,13 @@ static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) static void smd_tty_port_shutdown(struct tty_port *tport) { - struct smd_tty_info *info; - struct tty_struct *tty = tty_port_tty_get(tport); + struct smd_tty_info *info = container_of(tport, struct smd_tty_info, + port); - info = tty->driver_data; if (info->ch) { smd_close(info->ch); info->ch = 0; } - - tty->driver_data = 0; - tty_kref_put(tty); } static int smd_tty_open(struct tty_struct *tty, struct file *f) diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index e2775b6df5a..be127d0da32 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -29,7 +29,7 @@ #include <asm/irq.h> #include <asm/parisc-device.h> -#ifdef CONFIG_MAGIC_SYSRQ +#if defined(CONFIG_SERIAL_MUX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #include <linux/sysrq.h> #define SUPPORT_SYSRQ #endif @@ -242,8 +242,8 @@ static void mux_write(struct uart_port *port) */ static void mux_read(struct uart_port *port) { + struct tty_port *tport = &port->state->port; int data; - struct tty_struct *tty = port->state->port.tty; __u32 start_count = port->icount.rx; while(1) { @@ -266,12 +266,11 @@ static void mux_read(struct uart_port *port) if (uart_handle_sysrq_char(port, data & 0xffu)) continue; - tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL); + tty_insert_flip_char(tport, data & 0xFF, TTY_NORMAL); } - if (start_count != port->icount.rx) { - tty_flip_buffer_push(tty); - } + if (start_count != port->icount.rx) + tty_flip_buffer_push(tport); } /** @@ -614,7 +613,7 @@ static void __exit mux_exit(void) { /* Delete the Mux timer. */ if(port_cnt > 0) { - del_timer(&mux_timer); + del_timer_sync(&mux_timer); #ifdef CONFIG_SERIAL_MUX_CONSOLE unregister_console(&mux_console); #endif diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index e55615eb34a..86de4477d98 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -32,14 +32,14 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> -#include <linux/pinctrl/consumer.h> #include <linux/of_device.h> #include <linux/dma-mapping.h> -#include <linux/fsl/mxs-dma.h> +#include <linux/dmaengine.h> #include <asm/cacheflush.h> #define MXS_AUART_PORTS 5 +#define MXS_AUART_FIFO_SIZE 16 #define AUART_CTRL0 0x00000000 #define AUART_CTRL0_SET 0x00000004 @@ -134,10 +134,10 @@ enum mxs_auart_type { struct mxs_auart_port { struct uart_port port; -#define MXS_AUART_DMA_CONFIG 0x1 #define MXS_AUART_DMA_ENABLED 0x2 #define MXS_AUART_DMA_TX_SYNC 2 /* bit 2 */ #define MXS_AUART_DMA_RX_READY 3 /* bit 3 */ +#define MXS_AUART_RTSCTS 4 /* bit 4 */ unsigned long flags; unsigned int ctrl; enum mxs_auart_type devtype; @@ -148,11 +148,6 @@ struct mxs_auart_port { struct device *dev; /* for DMA */ - struct mxs_dma_data dma_data; - int dma_channel_rx, dma_channel_tx; - int dma_irq_rx, dma_irq_tx; - int dma_channel; - struct scatterlist tx_sgl; struct dma_chan *tx_dma_chan; void *tx_dma_buf; @@ -205,7 +200,7 @@ static void dma_tx_callback(void *param) /* clear the bit used to serialize the DMA tx. */ clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); /* wake up the possible processes. */ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -280,7 +275,7 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s) mxs_auart_dma_tx(s, i); } else { clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); } return; } @@ -364,7 +359,6 @@ out: static void mxs_auart_rx_chars(struct mxs_auart_port *s) { - struct tty_struct *tty = s->port.state->port.tty; u32 stat = 0; for (;;) { @@ -375,7 +369,7 @@ static void mxs_auart_rx_chars(struct mxs_auart_port *s) } writel(stat, s->port.membase + AUART_STAT); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&s->port.state->port); } static int mxs_auart_request_port(struct uart_port *u) @@ -441,25 +435,11 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } -static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) -{ - struct mxs_auart_port *s = param; - - if (!mxs_dma_is_apbx(chan)) - return false; - - if (s->dma_channel == chan->chan_id) { - chan->private = &s->dma_data; - return true; - } - return false; -} - static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); static void dma_rx_callback(void *arg) { struct mxs_auart_port *s = (struct mxs_auart_port *) arg; - struct tty_struct *tty = s->port.state->port.tty; + struct tty_port *port = &s->port.state->port; int count; u32 stat; @@ -470,10 +450,10 @@ static void dma_rx_callback(void *arg) AUART_STAT_PERR | AUART_STAT_FERR); count = stat & AUART_STAT_RXCOUNT_MASK; - tty_insert_flip_string(tty, s->rx_dma_buf, count); + tty_insert_flip_string(port, s->rx_dma_buf, count); writel(stat, s->port.membase + AUART_STAT); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(port); /* start the next DMA for RX. */ mxs_auart_dma_prep_rx(s); @@ -546,21 +526,11 @@ static void mxs_auart_dma_exit(struct mxs_auart_port *s) static int mxs_auart_dma_init(struct mxs_auart_port *s) { - dma_cap_mask_t mask; - if (auart_dma_enabled(s)) return 0; - /* We do not get the right DMA channels. */ - if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1) - return -EINVAL; - /* init for RX */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - s->dma_channel = s->dma_channel_rx; - s->dma_data.chan_irq = s->dma_irq_rx; - s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx"); if (!s->rx_dma_chan) goto err_out; s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); @@ -568,9 +538,7 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s) goto err_out; /* init for TX */ - s->dma_channel = s->dma_channel_tx; - s->dma_data.chan_irq = s->dma_irq_tx; - s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx"); if (!s->tx_dma_chan) goto err_out; s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); @@ -581,6 +549,9 @@ static int mxs_auart_dma_init(struct mxs_auart_port *s) s->flags |= MXS_AUART_DMA_ENABLED; dev_dbg(s->dev, "enabled the DMA support."); + /* The DMA buffer is now the FIFO the TTY subsystem can use */ + s->port.fifosize = UART_XMIT_SIZE; + return 0; err_out: @@ -633,7 +604,7 @@ static void mxs_auart_settermios(struct uart_port *u, if (termios->c_iflag & INPCK) u->read_status_mask |= AUART_STAT_PERR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) u->read_status_mask |= AUART_STAT_BERR; /* @@ -672,7 +643,8 @@ static void mxs_auart_settermios(struct uart_port *u, * we can only implement the DMA support for auart * in mx28. */ - if (is_imx28_auart(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (is_imx28_auart(s) + && test_bit(MXS_AUART_RTSCTS, &s->flags)) { if (!mxs_auart_dma_init(s)) /* enable DMA tranfer */ ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE @@ -710,11 +682,18 @@ static void mxs_auart_settermios(struct uart_port *u, static irqreturn_t mxs_auart_irq_handle(int irq, void *context) { - u32 istatus, istat; + u32 istat; struct mxs_auart_port *s = context; u32 stat = readl(s->port.membase + AUART_STAT); - istatus = istat = readl(s->port.membase + AUART_INTR); + istat = readl(s->port.membase + AUART_INTR); + + /* ack irq */ + writel(istat & (AUART_INTR_RTIS + | AUART_INTR_TXIS + | AUART_INTR_RXIS + | AUART_INTR_CTSMIS), + s->port.membase + AUART_INTR_CLR); if (istat & AUART_INTR_CTSMIS) { uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS); @@ -734,12 +713,6 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) istat &= ~AUART_INTR_TXIS; } - writel(istatus & (AUART_INTR_RTIS - | AUART_INTR_TXIS - | AUART_INTR_RXIS - | AUART_INTR_CTSMIS), - s->port.membase + AUART_INTR_CLR); - return IRQ_HANDLED; } @@ -761,9 +734,12 @@ static void mxs_auart_reset(struct uart_port *u) static int mxs_auart_startup(struct uart_port *u) { + int ret; struct mxs_auart_port *s = to_auart_port(u); - clk_prepare_enable(s->clk); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR); @@ -772,6 +748,9 @@ static int mxs_auart_startup(struct uart_port *u) writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, u->membase + AUART_INTR); + /* Reset FIFO size (it could have changed if DMA was enabled) */ + u->fifosize = MXS_AUART_FIFO_SIZE; + /* * 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) @@ -882,9 +861,9 @@ auart_console_write(struct console *co, const char *str, unsigned int count) struct mxs_auart_port *s; struct uart_port *port; unsigned int old_ctrl0, old_ctrl2; - unsigned int to = 1000; + unsigned int to = 20000; - if (co->index > MXS_AUART_PORTS || co->index < 0) + if (co->index >= MXS_AUART_PORTS || co->index < 0) return; s = auart_port[co->index]; @@ -903,18 +882,23 @@ auart_console_write(struct console *co, const char *str, unsigned int count) uart_console_write(port, str, count, mxs_auart_console_putchar); - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ + /* Finally, wait for transmitter to become empty ... */ while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) { + udelay(1); if (!to--) break; - udelay(1); } - writel(old_ctrl0, port->membase + AUART_CTRL0); - writel(old_ctrl2, port->membase + AUART_CTRL2); + /* + * ... and restore the TCR if we waited long enough for the transmitter + * to be idle. This might keep the transmitter enabled although it is + * 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); + } clk_disable(s->clk); } @@ -976,7 +960,9 @@ auart_console_setup(struct console *co, char *options) if (!s) return -ENODEV; - clk_prepare_enable(s->clk); + ret = clk_prepare_enable(s->clk); + if (ret) + return ret; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -1021,7 +1007,6 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - u32 dma_channel[2]; int ret; if (!np) @@ -1035,20 +1020,9 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, } s->port.line = ret; - s->dma_irq_rx = platform_get_irq(pdev, 1); - s->dma_irq_tx = platform_get_irq(pdev, 2); - - ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", - dma_channel, 2); - if (ret == 0) { - s->dma_channel_rx = dma_channel[0]; - s->dma_channel_tx = dma_channel[1]; + if (of_get_property(np, "fsl,uart-has-rtscts", NULL)) + set_bit(MXS_AUART_RTSCTS, &s->flags); - s->flags |= MXS_AUART_DMA_CONFIG; - } else { - s->dma_channel_rx = -1; - s->dma_channel_tx = -1; - } return 0; } @@ -1060,7 +1034,6 @@ static int mxs_auart_probe(struct platform_device *pdev) u32 version; int ret = 0; struct resource *r; - struct pinctrl *pinctrl; s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); if (!s) { @@ -1074,12 +1047,6 @@ static int mxs_auart_probe(struct platform_device *pdev) else if (ret < 0) goto out_free; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto out_free; - } - if (of_id) { pdev->id_entry = of_id->data; s->devtype = pdev->id_entry->driver_data; @@ -1101,10 +1068,10 @@ static int mxs_auart_probe(struct platform_device *pdev) s->port.membase = ioremap(r->start, resource_size(r)); s->port.ops = &mxs_auart_ops; s->port.iotype = UPIO_MEM; - s->port.fifosize = 16; + 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 = get_device(&pdev->dev); + s->port.dev = s->dev = &pdev->dev; s->ctrl = 0; @@ -1135,7 +1102,6 @@ out_free_irq: auart_port[pdev->id] = NULL; free_irq(s->irq, s); out_free_clk: - put_device(s->dev); clk_put(s->clk); out_free: kfree(s); @@ -1151,7 +1117,6 @@ static int mxs_auart_remove(struct platform_device *pdev) auart_port[pdev->id] = NULL; - put_device(s->dev); clk_put(s->clk); free_irq(s->irq, s); kfree(s); diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c index d40da78e7c8..7a6745601d4 100644 --- a/drivers/tty/serial/netx-serial.c +++ b/drivers/tty/serial/netx-serial.c @@ -196,10 +196,9 @@ static void netx_txint(struct uart_port *port) uart_write_wakeup(port); } -static void netx_rxint(struct uart_port *port) +static void netx_rxint(struct uart_port *port, unsigned long *flags) { unsigned char rx, flg, status; - struct tty_struct *tty = port->state->port.tty; while (!(readl(port->membase + UART_FR) & FR_RXFE)) { rx = readl(port->membase + UART_DR); @@ -237,8 +236,9 @@ static void netx_rxint(struct uart_port *port) uart_insert_char(port, status, SR_OE, rx, flg); } - tty_flip_buffer_push(tty); - return; + spin_unlock_irqrestore(&port->lock, *flags); + tty_flip_buffer_push(&port->state->port); + spin_lock_irqsave(&port->lock, *flags); } static irqreturn_t netx_int(int irq, void *dev_id) @@ -252,7 +252,7 @@ static irqreturn_t netx_int(int irq, void *dev_id) status = readl(port->membase + UART_IIR) & IIR_MASK; while (status) { if (status & IIR_RIS) - netx_rxint(port); + netx_rxint(port, &flags); if (status & IIR_TIS) netx_txint(port); if (status & IIR_MIS) { @@ -419,7 +419,7 @@ netx_set_termios(struct uart_port *port, struct ktermios *termios, } port->read_status_mask = 0; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= SR_BE; if (termios->c_iflag & INPCK) port->read_status_mask |= SR_PE | SR_FE; @@ -695,8 +695,6 @@ static int serial_netx_remove(struct platform_device *pdev) { struct netx_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&netx_reg, &sport->port); diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index dd4c31d1aee..693bc6c2561 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -128,7 +128,7 @@ static void nwpserial_config_port(struct uart_port *port, int flags) static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) { struct nwpserial_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; + struct tty_port *port = &up->port.state->port; irqreturn_t ret; unsigned int iir; unsigned char ch; @@ -146,10 +146,13 @@ static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) up->port.icount.rx++; ch = dcr_read(up->dcr_host, UART_RX); if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) - tty_insert_flip_char(tty, ch, TTY_NORMAL); + tty_insert_flip_char(port, ch, TTY_NORMAL); } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR); - tty_flip_buffer_push(tty); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(port); + spin_lock(&up->port.lock); + ret = IRQ_HANDLED; /* clear interrupt */ @@ -199,7 +202,7 @@ static void nwpserial_shutdown(struct uart_port *port) dcr_write(up->dcr_host, UART_IER, up->ier); /* free irq */ - free_irq(up->port.irq, port); + free_irq(up->port.irq, up); } static int nwpserial_verify_port(struct uart_port *port, diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index e7cae1c2d7d..68d4455f3cf 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -9,20 +9,19 @@ * 2 of the License, or (at your option) any later version. * */ -#include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/serial_core.h> -#include <linux/serial_8250.h> #include <linux/serial_reg.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/of_serial.h> #include <linux/of_platform.h> #include <linux/nwpserial.h> #include <linux/clk.h> +#include "8250/8250.h" + struct of_serial_info { struct clk *clk; int type; @@ -45,8 +44,10 @@ void tegra_serial_handle_break(struct uart_port *p) udelay(1); } while (1); } -/* FIXME remove this export when tegra finishes conversion to open firmware */ -EXPORT_SYMBOL_GPL(tegra_serial_handle_break); +#else +static inline void tegra_serial_handle_break(struct uart_port *port) +{ +} #endif /* @@ -96,6 +97,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (of_property_read_u32(np, "reg-shift", &prop) == 0) port->regshift = prop; + /* Check for fifo size */ + if (of_property_read_u32(np, "fifo-size", &prop) == 0) + port->fifosize = prop; + port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { @@ -166,11 +171,22 @@ static int of_platform_serial_probe(struct platform_device *ofdev) #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: { - /* For now the of bindings don't support the extra - 8250 specific bits */ struct uart_8250_port port8250; memset(&port8250, 0, sizeof(port8250)); + port.type = port_type; port8250.port = port; + + if (port.fifosize) + port8250.capabilities = UART_CAP_FIFO; + + if (of_property_read_bool(ofdev->dev.of_node, + "auto-flow-control")) + port8250.capabilities |= UART_CAP_AFE; + + if (of_property_read_bool(ofdev->dev.of_node, + "has-hw-flow-control")) + port8250.port.flags |= UPF_HARD_FLOW; + ret = serial8250_register_8250_port(&port8250); break; } @@ -192,7 +208,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) info->type = port_type; info->line = ret; - dev_set_drvdata(&ofdev->dev, info); + platform_set_drvdata(ofdev, info); return 0; out: kfree(info); @@ -205,7 +221,7 @@ out: */ static int of_platform_serial_remove(struct platform_device *ofdev) { - struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); + struct of_serial_info *info = platform_get_drvdata(ofdev); switch (info->type) { #ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: @@ -240,6 +256,12 @@ static struct of_device_id of_platform_serial_table[] = { { .compatible = "ns16850", .data = (void *)PORT_16850, }, { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, + { .compatible = "altr,16550-FIFO32", + .data = (void *)PORT_ALTR_16550_F32, }, + { .compatible = "altr,16550-FIFO64", + .data = (void *)PORT_ALTR_16550_F64, }, + { .compatible = "altr,16550-FIFO128", + .data = (void *)PORT_ALTR_16550_F128, }, #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL { .compatible = "ibm,qpace-nwp-serial", .data = (void *)PORT_NWPSERIAL, }, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 57d6b29c039..d017cec8a34 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -39,10 +39,13 @@ #include <linux/irq.h> #include <linux/pm_runtime.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/gpio.h> -#include <linux/pinctrl/consumer.h> +#include <linux/of_gpio.h> #include <linux/platform_data/serial-omap.h> +#include <dt-bindings/gpio/gpio.h> + #define OMAP_MAX_HSUART_PORTS 6 #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) @@ -52,6 +55,11 @@ #define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_63 0x0603 +#define OMAP_UART_TX_WAKEUP_EN BIT(7) + +/* Feature flags */ +#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0) + #define UART_ERRATA_i202_MDR1_ACCESS BIT(0) #define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1) @@ -59,6 +67,7 @@ /* SCR register bitmasks */ #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) +#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) #define OMAP_UART_SCR_TX_EMPTY (1 << 3) /* FCR register bitmasks */ @@ -126,6 +135,7 @@ struct uart_omap_port { struct uart_port port; struct uart_omap_dma uart_dma; struct device *dev; + int wakeirq; unsigned char ier; unsigned char lcr; @@ -136,6 +146,7 @@ struct uart_omap_port { unsigned char dlh; unsigned char mdr1; unsigned char scr; + unsigned char wer; int use_dma; /* @@ -150,27 +161,25 @@ struct uart_omap_port { int context_loss_cnt; u32 errata; u8 wakeups_enabled; + u32 features; - int DTR_gpio; - int DTR_inverted; - int DTR_active; + struct serial_rs485 rs485; + int rts_gpio; struct pm_qos_request pm_qos_request; u32 latency; u32 calc_latency; struct work_struct qos_work; - struct pinctrl *pins; + bool is_suspending; }; -#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) +#define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; /* Forward declaration of functions */ static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1); -static struct workqueue_struct *serial_omap_uart_wq; - static inline unsigned int serial_in(struct uart_omap_port *up, int offset) { offset <<= up->port.regshift; @@ -193,37 +202,35 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) static int serial_omap_get_context_loss_count(struct uart_omap_port *up) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); if (!pdata || !pdata->get_context_loss_count) - return 0; + return -EINVAL; return pdata->get_context_loss_count(up->dev); } -static void serial_omap_set_forceidle(struct uart_omap_port *up) +static inline void serial_omap_enable_wakeirq(struct uart_omap_port *up, + bool enable) { - struct omap_uart_port_info *pdata = up->dev->platform_data; - - if (!pdata || !pdata->set_forceidle) + if (!up->wakeirq) return; - pdata->set_forceidle(up->dev); + if (enable) + enable_irq(up->wakeirq); + else + disable_irq_nosync(up->wakeirq); } -static void serial_omap_set_noidle(struct uart_omap_port *up) +static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); - if (!pdata || !pdata->set_noidle) + if (enable == up->wakeups_enabled) return; - pdata->set_noidle(up->dev); -} - -static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) -{ - struct omap_uart_port_info *pdata = up->dev->platform_data; + serial_omap_enable_wakeirq(up, enable); + up->wakeups_enabled = enable; if (!pdata || !pdata->enable_wakeup) return; @@ -232,28 +239,46 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) } /* + * serial_omap_baud_is_mode16 - check if baud rate is MODE16X + * @port: uart port info + * @baud: baudrate for which mode needs to be determined + * + * Returns true if baud rate is MODE16X and false if MODE13X + * Original table in OMAP TRM named "UART Mode Baud Rates, Divisor Values, + * and Error Rates" determines modes not for all common baud rates. + * E.g. for 1000000 baud rate mode must be 16x, but according to that + * table it's determined as 13x. + */ +static bool +serial_omap_baud_is_mode16(struct uart_port *port, unsigned int baud) +{ + unsigned int n13 = port->uartclk / (13 * baud); + unsigned int n16 = port->uartclk / (16 * baud); + int baudAbsDiff13 = baud - (port->uartclk / (13 * n13)); + int baudAbsDiff16 = baud - (port->uartclk / (16 * n16)); + if (baudAbsDiff13 < 0) + baudAbsDiff13 = -baudAbsDiff13; + if (baudAbsDiff16 < 0) + baudAbsDiff16 = -baudAbsDiff16; + + return (baudAbsDiff13 >= baudAbsDiff16); +} + +/* * serial_omap_get_divisor - calculate divisor value * @port: uart port info * @baud: baudrate for which divisor needs to be calculated. - * - * We have written our own function to get the divisor so as to support - * 13x mode. 3Mbps Baudrate as an different divisor. - * Reference OMAP TRM Chapter 17: - * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates - * referring to oversampling - divisor value - * baudrate 460,800 to 3,686,400 all have divisor 13 - * except 3,000,000 which has divisor value 16 */ static unsigned int serial_omap_get_divisor(struct uart_port *port, unsigned int baud) { - unsigned int divisor; + unsigned int mode; - if (baud > OMAP_MODE13X_SPEED && baud != 3000000) - divisor = 13; + if (!serial_omap_baud_is_mode16(port, baud)) + mode = 13; else - divisor = 16; - return port->uartclk/(baud * divisor); + mode = 16; + return port->uartclk/(mode * baud); } static void serial_omap_enable_ms(struct uart_port *port) @@ -272,14 +297,60 @@ static void serial_omap_enable_ms(struct uart_port *port) static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + int res; pm_runtime_get_sync(up->dev); + + /* Handle RS-485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + if (up->scr & OMAP_UART_SCR_TX_EMPTY) { + /* THR interrupt is fired when both TX FIFO and TX + * shift register are empty. This means there's nothing + * left to transmit now, so make sure the THR interrupt + * is fired when TX FIFO is below the trigger level, + * disable THR interrupts and toggle the RS-485 GPIO + * data direction pin if needed. + */ + up->scr &= ~OMAP_UART_SCR_TX_EMPTY; + serial_out(up, UART_OMAP_SCR, up->scr); + res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + if (gpio_get_value(up->rts_gpio) != res) { + if (up->rs485.delay_rts_after_send > 0) + mdelay(up->rs485.delay_rts_after_send); + gpio_set_value(up->rts_gpio, res); + } + } else { + /* We're asked to stop, but there's still stuff in the + * UART FIFO, so make sure the THR interrupt is fired + * when both TX FIFO and TX shift register are empty. + * The next THR interrupt (if no transmission is started + * in the meantime) will indicate the end of a + * transmission. Therefore we _don't_ disable THR + * interrupts in this situation. + */ + up->scr |= OMAP_UART_SCR_TX_EMPTY; + serial_out(up, UART_OMAP_SCR, up->scr); + return; + } + } + if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; serial_out(up, UART_IER, up->ier); } - serial_omap_set_forceidle(up); + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) { + /* + * Empty the RX FIFO, we are not interested in anything + * received during the half-duplex transmission. + */ + serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_RCVR); + /* Re-enable RX interrupts */ + up->ier |= UART_IER_RLSI | UART_IER_RDI; + up->port.read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + } pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -290,7 +361,7 @@ static void serial_omap_stop_rx(struct uart_port *port) struct uart_omap_port *up = to_uart_omap_port(port); pm_runtime_get_sync(up->dev); - up->ier &= ~UART_IER_RLSI; + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); up->port.read_status_mask &= ~UART_LSR_DR; serial_out(up, UART_IER, up->ier); pm_runtime_mark_last_busy(up->dev); @@ -302,9 +373,6 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) struct circ_buf *xmit = &up->port.state->xmit; int count; - if (!(lsr & UART_LSR_THRE)) - return; - if (up->port.x_char) { serial_out(up, UART_TX, up->port.x_char); up->port.icount.tx++; @@ -324,11 +392,8 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) break; } while (--count > 0); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { - spin_unlock(&up->port.lock); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); - spin_lock(&up->port.lock); - } if (uart_circ_empty(xmit)) serial_omap_stop_tx(&up->port); @@ -345,10 +410,30 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + int res; pm_runtime_get_sync(up->dev); + + /* Handle RS-485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + /* Fire THR interrupts when FIFO is below trigger level */ + up->scr &= ~OMAP_UART_SCR_TX_EMPTY; + serial_out(up, UART_OMAP_SCR, up->scr); + + /* if rts not already enabled */ + res = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0; + if (gpio_get_value(up->rts_gpio) != res) { + gpio_set_value(up->rts_gpio, res); + if (up->rs485.delay_rts_before_send > 0) + mdelay(up->rs485.delay_rts_before_send); + } + } + + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) + serial_omap_stop_rx(port); + serial_omap_enable_ier_thri(up); - serial_omap_set_noidle(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -483,7 +568,6 @@ static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr) static irqreturn_t serial_omap_irq(int irq, void *dev_id) { struct uart_omap_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; unsigned int iir, lsr; unsigned int type; irqreturn_t ret = IRQ_NONE; @@ -530,7 +614,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&up->port.state->port); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -604,16 +688,6 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) serial_out(up, UART_MCR, up->mcr); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); - - if (gpio_is_valid(up->DTR_gpio) && - !!(mctrl & TIOCM_DTR) != up->DTR_active) { - up->DTR_active = !up->DTR_active; - if (gpio_cansleep(up->DTR_gpio)) - schedule_work(&up->qos_work); - else - gpio_set_value(up->DTR_gpio, - up->DTR_active != up->DTR_inverted); - } } static void serial_omap_break_ctl(struct uart_port *port, int break_state) @@ -648,6 +722,17 @@ static int serial_omap_startup(struct uart_port *port) if (retval) return retval; + /* Optional wake-up IRQ */ + if (up->wakeirq) { + retval = request_irq(up->wakeirq, serial_omap_irq, + up->port.irqflags, up->name, up); + if (retval) { + free_irq(up->port.irq, up); + return retval; + } + disable_irq(up->wakeirq); + } + dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line); pm_runtime_get_sync(up->dev); @@ -690,7 +775,11 @@ static int serial_omap_startup(struct uart_port *port) serial_out(up, UART_IER, up->ier); /* Enable module level wake up */ - serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP); + up->wer = OMAP_UART_WER_MOD_WKUP; + if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP) + up->wer |= OMAP_UART_TX_WAKEUP_EN; + + serial_out(up, UART_OMAP_WER, up->wer); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -732,6 +821,8 @@ static void serial_omap_shutdown(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); free_irq(up->port.irq, up); + if (up->wakeirq) + free_irq(up->wakeirq, up); } static void serial_omap_uart_qos_work(struct work_struct *work) @@ -740,9 +831,6 @@ static void serial_omap_uart_qos_work(struct work_struct *work) qos_work); pm_qos_update_request(&up->pm_qos_request, up->latency); - if (gpio_is_valid(up->DTR_gpio)) - gpio_set_value_cansleep(up->DTR_gpio, - up->DTR_active != up->DTR_inverted); } static void @@ -776,6 +864,8 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, cval |= UART_LCR_PARITY; if (!(termios->c_cflag & PARODD)) cval |= UART_LCR_EPAR; + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; /* * Ask the core to calculate the divisor for us. @@ -845,7 +935,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_IER, up->ier); serial_out(up, UART_LCR, cval); /* reset DLAB */ up->lcr = cval; - up->scr = OMAP_UART_SCR_TX_EMPTY; + up->scr = 0; /* FIFOs and DMA Settings */ @@ -870,9 +960,18 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, /* FIFO ENABLE, DMA MODE */ up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK; + /* + * NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK + * sets Enables the granularity of 1 for TRIGGER RX + * level. Along with setting RX FIFO trigger level + * to 1 (as noted below, 16 characters) and TLR[3:0] + * to zero this will result RX FIFO threshold level + * to 1 character, instead of 16 as noted in comment + * below. + */ /* Set receive FIFO threshold to 16 characters and - * transmit FIFO threshold to 16 spaces + * transmit FIFO threshold to 32 spaces */ up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK; up->fcr &= ~OMAP_UART_FCR_TX_FIFO_TRIG_MASK; @@ -915,7 +1014,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_EFR, up->efr); serial_out(up, UART_LCR, cval); - if (baud > 230400 && baud != 3000000) + if (!serial_omap_baud_is_mode16(port, baud)) up->mdr1 = UART_OMAP_MDR1_13X_MODE; else up->mdr1 = UART_OMAP_MDR1_16X_MODE; @@ -994,15 +1093,6 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line); } -static int serial_omap_set_wake(struct uart_port *port, unsigned int state) -{ - struct uart_omap_port *up = to_uart_omap_port(port); - - serial_omap_enable_wakeup(up, state); - - return 0; -} - static void serial_omap_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) @@ -1250,6 +1340,85 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif +/* Enable or disable the rs485 support */ +static void +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct uart_omap_port *up = to_uart_omap_port(port); + unsigned long flags; + unsigned int mode; + int val; + + pm_runtime_get_sync(up->dev); + spin_lock_irqsave(&up->port.lock, flags); + + /* Disable interrupts from this port */ + mode = up->ier; + up->ier = 0; + serial_out(up, UART_IER, 0); + + /* store new config */ + up->rs485 = *rs485conf; + + /* + * Just as a precaution, only allow rs485 + * to be enabled if the gpio pin is valid + */ + if (gpio_is_valid(up->rts_gpio)) { + /* enable / disable rts */ + val = (up->rs485.flags & SER_RS485_ENABLED) ? + SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND; + val = (up->rs485.flags & val) ? 1 : 0; + gpio_set_value(up->rts_gpio, val); + } else + up->rs485.flags &= ~SER_RS485_ENABLED; + + /* Enable interrupts */ + up->ier = mode; + serial_out(up, UART_IER, up->ier); + + /* If RS-485 is disabled, make sure the THR interrupt is fired when + * TX FIFO is below the trigger level. + */ + if (!(up->rs485.flags & SER_RS485_ENABLED) && + (up->scr & OMAP_UART_SCR_TX_EMPTY)) { + up->scr &= ~OMAP_UART_SCR_TX_EMPTY; + serial_out(up, UART_OMAP_SCR, up->scr); + } + + spin_unlock_irqrestore(&up->port.lock, flags); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); +} + +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (void __user *) arg, + sizeof(rs485conf))) + return -EFAULT; + + serial_omap_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((void __user *) arg, + &(to_uart_omap_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1265,12 +1434,12 @@ static struct uart_ops serial_omap_pops = { .shutdown = serial_omap_shutdown, .set_termios = serial_omap_set_termios, .pm = serial_omap_pm, - .set_wake = serial_omap_set_wake, .type = serial_omap_type, .release_port = serial_omap_release_port, .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, + .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, @@ -1286,6 +1455,22 @@ static struct uart_driver serial_omap_reg = { }; #ifdef CONFIG_PM_SLEEP +static int serial_omap_prepare(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = true; + + return 0; +} + +static void serial_omap_complete(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = false; +} + static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); @@ -1293,6 +1478,11 @@ static int serial_omap_suspend(struct device *dev) uart_suspend_port(&serial_omap_reg, &up->port); flush_work(&up->qos_work); + if (device_may_wakeup(dev)) + serial_omap_enable_wakeup(up, true); + else + serial_omap_enable_wakeup(up, false); + return 0; } @@ -1300,18 +1490,24 @@ static int serial_omap_resume(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) + serial_omap_enable_wakeup(up, false); + uart_resume_port(&serial_omap_reg, &up->port); return 0; } -#endif +#else +#define serial_omap_prepare NULL +#define serial_omap_complete NULL +#endif /* CONFIG_PM_SLEEP */ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { u32 mvr, scheme; u16 revision, major, minor; - mvr = serial_in(up, UART_OMAP_MVER); + mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift)); /* Check revision register scheme */ scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; @@ -1350,9 +1546,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) case OMAP_UART_REV_52: up->errata |= (UART_ERRATA_i202_MDR1_ACCESS | UART_ERRATA_i291_DMA_FORCEIDLE); + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; case OMAP_UART_REV_63: up->errata |= UART_ERRATA_i202_MDR1_ACCESS; + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; default: break; @@ -1372,62 +1570,98 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) return omap_up_info; } -static int serial_omap_probe(struct platform_device *pdev) +static int serial_omap_probe_rs485(struct uart_omap_port *up, + struct device_node *np) { - struct uart_omap_port *up; - struct resource *mem, *irq; - struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + struct serial_rs485 *rs485conf = &up->rs485; + u32 rs485_delay[2]; + enum of_gpio_flags flags; int ret; - if (pdev->dev.of_node) - omap_up_info = of_get_uart_port_info(&pdev->dev); - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } + rs485conf->flags = 0; + up->rts_gpio = -EINVAL; - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "no irq resource?\n"); - return -ENODEV; - } + if (!np) + return 0; - if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), - pdev->dev.driver->name)) { - dev_err(&pdev->dev, "memory region already claimed\n"); - return -EBUSY; - } + if (of_property_read_bool(np, "rs485-rts-active-high")) + rs485conf->flags |= SER_RS485_RTS_ON_SEND; + else + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; - if (gpio_is_valid(omap_up_info->DTR_gpio) && - omap_up_info->DTR_present) { - ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial"); + /* check for tx enable gpio */ + up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags); + if (gpio_is_valid(up->rts_gpio)) { + ret = devm_gpio_request(up->dev, up->rts_gpio, "omap-serial"); if (ret < 0) return ret; - ret = gpio_direction_output(omap_up_info->DTR_gpio, - omap_up_info->DTR_inverted); + ret = gpio_direction_output(up->rts_gpio, + flags & SER_RS485_RTS_AFTER_SEND); if (ret < 0) return ret; + } else if (up->rts_gpio == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else { + up->rts_gpio = -EINVAL; + } + + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + } + + if (of_property_read_bool(np, "rs485-rx-during-tx")) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) + rs485conf->flags |= SER_RS485_ENABLED; + + return 0; +} + +static int serial_omap_probe(struct platform_device *pdev) +{ + struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); + struct uart_omap_port *up; + struct resource *mem; + void __iomem *base; + int uartirq = 0; + int wakeirq = 0; + int ret; + + /* The optional wakeirq may be specified in the board dts file */ + if (pdev->dev.of_node) { + uartirq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!uartirq) + return -EPROBE_DEFER; + wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1); + omap_up_info = of_get_uart_port_info(&pdev->dev); + pdev->dev.platform_data = omap_up_info; + } else { + uartirq = platform_get_irq(pdev, 0); + if (uartirq < 0) + return -EPROBE_DEFER; } up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; - if (gpio_is_valid(omap_up_info->DTR_gpio) && - omap_up_info->DTR_present) { - up->DTR_gpio = omap_up_info->DTR_gpio; - up->DTR_inverted = omap_up_info->DTR_inverted; - } else - up->DTR_gpio = -EINVAL; - up->DTR_active = 0; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); up->dev = &pdev->dev; up->port.dev = &pdev->dev; up->port.type = PORT_OMAP; up->port.iotype = UPIO_MEM; - up->port.irq = irq->start; + up->port.irq = uartirq; + up->wakeirq = wakeirq; + if (!up->wakeirq) + dev_info(up->port.dev, "no wakeirq for uart%d\n", + up->port.line); up->port.regshift = 2; up->port.fifosize = 64; @@ -1445,45 +1679,40 @@ static int serial_omap_probe(struct platform_device *pdev) goto err_port_line; } - up->pins = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(up->pins)) { - dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n", - up->port.line, PTR_ERR(up->pins)); - up->pins = NULL; - } + ret = serial_omap_probe_rs485(up, pdev->dev.of_node); + if (ret < 0) + goto err_rs485; sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; - up->port.membase = devm_ioremap(&pdev->dev, mem->start, - resource_size(mem)); - if (!up->port.membase) { - dev_err(&pdev->dev, "can't ioremap UART\n"); - ret = -ENOMEM; - goto err_ioremap; - } - + up->port.membase = base; up->port.flags = omap_up_info->flags; up->port.uartclk = omap_up_info->uartclk; if (!up->port.uartclk) { up->port.uartclk = DEFAULT_CLK_SPEED; - dev_warn(&pdev->dev, "No clock speed specified: using default:" - "%d\n", DEFAULT_CLK_SPEED); + dev_warn(&pdev->dev, + "No clock speed specified: using default: %d\n", + DEFAULT_CLK_SPEED); } up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; pm_qos_add_request(&up->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, up->latency); - serial_omap_uart_wq = create_singlethread_workqueue(up->name); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); platform_set_drvdata(pdev, up); - pm_runtime_enable(&pdev->dev); + if (omap_up_info->autosuspend_timeout == 0) + omap_up_info->autosuspend_timeout = -1; + + device_init_wakeup(up->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, omap_up_info->autosuspend_timeout); pm_runtime_irq_safe(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(up); @@ -1502,7 +1731,7 @@ static int serial_omap_probe(struct platform_device *pdev) err_add_port: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); -err_ioremap: +err_rs485: err_port_line: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); @@ -1517,6 +1746,7 @@ static int serial_omap_remove(struct platform_device *dev) pm_runtime_disable(up->dev); uart_remove_one_port(&serial_omap_reg, &up->port); pm_qos_remove_request(&up->pm_qos_request); + device_init_wakeup(&dev->dev, false); return 0; } @@ -1583,32 +1813,29 @@ static void serial_omap_restore_context(struct uart_omap_port *up) serial_omap_mdr1_errataset(up, up->mdr1); else serial_out(up, UART_OMAP_MDR1, up->mdr1); + serial_out(up, UART_OMAP_WER, up->wer); } static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - struct omap_uart_port_info *pdata = dev->platform_data; if (!up) return -EINVAL; - if (!pdata) - return 0; + /* + * When using 'no_console_suspend', the console UART must not be + * suspended. Since driver suspend is managed by runtime suspend, + * preventing runtime suspend (by returning error) will keep device + * active during suspend. + */ + if (up->is_suspending && !console_suspend_enabled && + uart_console(&up->port)) + return -EBUSY; up->context_loss_cnt = serial_omap_get_context_loss_count(up); - if (device_may_wakeup(dev)) { - if (!up->wakeups_enabled) { - serial_omap_enable_wakeup(up, true); - up->wakeups_enabled = true; - } - } else { - if (up->wakeups_enabled) { - serial_omap_enable_wakeup(up, false); - up->wakeups_enabled = false; - } - } + serial_omap_enable_wakeup(up, true); up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; schedule_work(&up->qos_work); @@ -1622,8 +1849,10 @@ static int serial_omap_runtime_resume(struct device *dev) int loss_cnt = serial_omap_get_context_loss_count(up); + serial_omap_enable_wakeup(up, false); + if (loss_cnt < 0) { - dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n", loss_cnt); serial_omap_restore_context(up); } else if (up->context_loss_cnt != loss_cnt) { @@ -1640,6 +1869,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume) SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend, serial_omap_runtime_resume, NULL) + .prepare = serial_omap_prepare, + .complete = serial_omap_complete, }; #if defined(CONFIG_OF) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 8318925fbf6..0cb6a8e52bd 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -14,18 +14,21 @@ *along with this program; if not, write to the Free Software *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ +#if defined(CONFIG_SERIAL_PCH_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif #include <linux/kernel.h> #include <linux/serial_reg.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/console.h> #include <linux/serial_core.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/dmi.h> -#include <linux/console.h> #include <linux/nmi.h> #include <linux/delay.h> @@ -214,6 +217,7 @@ enum { #define FRI2_64_UARTCLK 64000000 /* 64.0000 MHz */ #define FRI2_48_UARTCLK 48000000 /* 48.0000 MHz */ #define NTC1_UARTCLK 64000000 /* 64.0000 MHz */ +#define MINNOW_UARTCLK 50000000 /* 50.0000 MHz */ struct pch_uart_buffer { unsigned char *buf; @@ -228,7 +232,7 @@ struct eg20t_port { unsigned int iobase; struct pci_dev *pdev; int fifo_size; - int uartclk; + unsigned int uartclk; int start_tx; int start_rx; int tx_empty; @@ -253,6 +257,8 @@ struct eg20t_port { dma_addr_t rx_buf_dma; struct dentry *debugfs; +#define IRQ_NAME_SIZE 17 + char irq_name[IRQ_NAME_SIZE]; /* protect the eg20t_port private structure and io access to membase */ spinlock_t lock; @@ -369,31 +375,62 @@ static const struct file_operations port_regs_ops = { }; #endif /* CONFIG_DEBUG_FS */ +static struct dmi_system_id pch_uart_dmi_table[] = { + { + .ident = "CM-iTC", + { + DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"), + }, + (void *)CMITC_UARTCLK, + }, + { + .ident = "FRI2", + { + DMI_MATCH(DMI_BIOS_VERSION, "FRI2"), + }, + (void *)FRI2_64_UARTCLK, + }, + { + .ident = "Fish River Island II", + { + DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"), + }, + (void *)FRI2_48_UARTCLK, + }, + { + .ident = "COMe-mTT", + { + DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"), + }, + (void *)NTC1_UARTCLK, + }, + { + .ident = "nanoETXexpress-TT", + { + DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"), + }, + (void *)NTC1_UARTCLK, + }, + { + .ident = "MinnowBoard", + { + DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"), + }, + (void *)MINNOW_UARTCLK, + }, +}; + /* Return UART clock, checking for board specific clocks. */ -static int pch_uart_get_uartclk(void) +static unsigned int pch_uart_get_uartclk(void) { - const char *cmp; + const struct dmi_system_id *d; if (user_uartclk) return user_uartclk; - cmp = dmi_get_system_info(DMI_BOARD_NAME); - if (cmp && strstr(cmp, "CM-iTC")) - return CMITC_UARTCLK; - - cmp = dmi_get_system_info(DMI_BIOS_VERSION); - if (cmp && strnstr(cmp, "FRI2", 4)) - return FRI2_64_UARTCLK; - - cmp = dmi_get_system_info(DMI_PRODUCT_NAME); - if (cmp && strstr(cmp, "Fish River Island II")) - return FRI2_48_UARTCLK; - - /* Kontron COMe-mTT10 (nanoETXexpress-TT) */ - cmp = dmi_get_system_info(DMI_BOARD_NAME); - if (cmp && (strstr(cmp, "COMe-mTT") || - strstr(cmp, "nanoETXexpress-TT"))) - return NTC1_UARTCLK; + d = dmi_first_match(pch_uart_dmi_table); + if (d) + return (unsigned long)d->driver_data; return DEFAULT_UARTCLK; } @@ -414,7 +451,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, iowrite8(ier, priv->membase + UART_IER); } -static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, +static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud, unsigned int parity, unsigned int bits, unsigned int stb) { @@ -449,7 +486,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, lcr |= bits; lcr |= stb; - dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", + dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n", __func__, baud, div, lcr, jiffies); iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); iowrite8(dll, priv->membase + PCH_UART_DLL); @@ -553,12 +590,26 @@ static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, { int i; u8 rbr, lsr; + struct uart_port *port = &priv->port; lsr = ioread8(priv->membase + UART_LSR); for (i = 0, lsr = ioread8(priv->membase + UART_LSR); - i < rx_size && lsr & UART_LSR_DR; + i < rx_size && lsr & (UART_LSR_DR | UART_LSR_BI); lsr = ioread8(priv->membase + UART_LSR)) { rbr = ioread8(priv->membase + PCH_UART_RBR); + + if (lsr & UART_LSR_BI) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } +#ifdef SUPPORT_SYSRQ + if (port->sysrq) { + if (uart_handle_sysrq_char(port, rbr)) + continue; + } +#endif + buf[i++] = rbr; } return i; @@ -591,19 +642,11 @@ static void pch_uart_hal_set_break(struct eg20t_port *priv, int on) static int push_rx(struct eg20t_port *priv, const unsigned char *buf, int size) { - struct uart_port *port; - struct tty_struct *tty; - - port = &priv->port; - tty = tty_port_tty_get(&port->state->port); - if (!tty) { - dev_dbg(priv->port.dev, "%s:tty is busy now", __func__); - return -EBUSY; - } + struct uart_port *port = &priv->port; + struct tty_port *tport = &port->state->port; - tty_insert_flip_string(tty, buf, size); - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty_insert_flip_string(tport, buf, size); + tty_flip_buffer_push(tport); return 0; } @@ -626,29 +669,21 @@ static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf) static int dma_push_rx(struct eg20t_port *priv, int size) { - struct tty_struct *tty; int room; struct uart_port *port = &priv->port; + struct tty_port *tport = &port->state->port; - port = &priv->port; - tty = tty_port_tty_get(&port->state->port); - if (!tty) { - dev_dbg(priv->port.dev, "%s:tty is busy now", __func__); - return 0; - } - - room = tty_buffer_request_room(tty, size); + room = tty_buffer_request_room(tport, size); if (room < size) dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", size - room); if (!room) - return room; + return 0; - tty_insert_flip_string(tty, sg_virt(&priv->sg_rx), size); + tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size); port->icount.rx += room; - tty_kref_put(tty); return room; } @@ -743,19 +778,12 @@ static void pch_dma_rx_complete(void *arg) { struct eg20t_port *priv = arg; struct uart_port *port = &priv->port; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); int count; - if (!tty) { - dev_dbg(priv->port.dev, "%s:tty is busy now", __func__); - return; - } - dma_sync_sg_for_cpu(port->dev, &priv->sg_rx, 1, DMA_FROM_DEVICE); count = dma_push_rx(priv, priv->trigger_level); if (count) - tty_flip_buffer_push(tty); - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); async_tx_ack(priv->desc_rx); pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT | PCH_UART_HAL_RX_ERR_INT); @@ -1037,23 +1065,35 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) { - u8 fcr = ioread8(priv->membase + UART_FCR); - - /* Reset FIFO */ - fcr |= UART_FCR_CLEAR_RCVR; - iowrite8(fcr, priv->membase + UART_FCR); + struct uart_port *port = &priv->port; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + char *error_msg[5] = {}; + int i = 0; if (lsr & PCH_UART_LSR_ERR) - dev_err(&priv->pdev->dev, "Error data in FIFO\n"); + error_msg[i++] = "Error data in FIFO\n"; + + if (lsr & UART_LSR_FE) { + port->icount.frame++; + error_msg[i++] = " Framing Error\n"; + } - if (lsr & UART_LSR_FE) - dev_err(&priv->pdev->dev, "Framing Error\n"); + if (lsr & UART_LSR_PE) { + port->icount.parity++; + error_msg[i++] = " Parity Error\n"; + } - if (lsr & UART_LSR_PE) - dev_err(&priv->pdev->dev, "Parity Error\n"); + if (lsr & UART_LSR_OE) { + port->icount.overrun++; + error_msg[i++] = " Overrun Error\n"; + } - if (lsr & UART_LSR_OE) - dev_err(&priv->pdev->dev, "Overrun Error\n"); + if (tty == NULL) { + for (i = 0; error_msg[i] != NULL; i++) + dev_err(&priv->pdev->dev, error_msg[i]); + } else { + tty_kref_put(tty); + } } static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) @@ -1305,7 +1345,7 @@ static int pch_uart_startup(struct uart_port *port) return ret; ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED, - KBUILD_MODNAME, priv); + priv->irq_name, priv); if (ret < 0) return ret; @@ -1345,9 +1385,8 @@ static void pch_uart_shutdown(struct uart_port *port) static void pch_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - int baud; int rtn; - unsigned int parity, bits, stb; + unsigned int baud, parity, bits, stb; struct eg20t_port *priv; unsigned long flags; @@ -1471,38 +1510,20 @@ static int pch_uart_verify_port(struct uart_port *port, __func__); return -EOPNOTSUPP; #endif - dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n"); - if (!priv->use_dma) + if (!priv->use_dma) { pch_request_dma(port); - priv->use_dma = 1; + if (priv->chan_rx) + priv->use_dma = 1; + } + dev_info(priv->port.dev, "PCH UART: %s\n", + priv->use_dma ? + "Use DMA Mode" : "No DMA"); } return 0; } -static struct uart_ops pch_uart_ops = { - .tx_empty = pch_uart_tx_empty, - .set_mctrl = pch_uart_set_mctrl, - .get_mctrl = pch_uart_get_mctrl, - .stop_tx = pch_uart_stop_tx, - .start_tx = pch_uart_start_tx, - .stop_rx = pch_uart_stop_rx, - .enable_ms = pch_uart_enable_ms, - .break_ctl = pch_uart_break_ctl, - .startup = pch_uart_startup, - .shutdown = pch_uart_shutdown, - .set_termios = pch_uart_set_termios, -/* .pm = pch_uart_pm, Not supported yet */ -/* .set_wake = pch_uart_set_wake, Not supported yet */ - .type = pch_uart_type, - .release_port = pch_uart_release_port, - .request_port = pch_uart_request_port, - .config_port = pch_uart_config_port, - .verify_port = pch_uart_verify_port -}; - -#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE - +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE) /* * Wait for transmitter & holding register to empty */ @@ -1533,6 +1554,79 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } } } +#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */ + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for communicate via uart while + * in an interrupt or debug context. + */ +static int pch_uart_get_poll_char(struct uart_port *port) +{ + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + u8 lsr = ioread8(priv->membase + UART_LSR); + + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return ioread8(priv->membase + PCH_UART_RBR); +} + + +static void pch_uart_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = ioread8(priv->membase + UART_IER); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + + wait_for_xmitr(priv, UART_LSR_THRE); + /* + * Send the character out. + */ + iowrite8(c, priv->membase + PCH_UART_THR); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(priv, BOTH_EMPTY); + iowrite8(ier, priv->membase + UART_IER); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops pch_uart_ops = { + .tx_empty = pch_uart_tx_empty, + .set_mctrl = pch_uart_set_mctrl, + .get_mctrl = pch_uart_get_mctrl, + .stop_tx = pch_uart_stop_tx, + .start_tx = pch_uart_start_tx, + .stop_rx = pch_uart_stop_rx, + .enable_ms = pch_uart_enable_ms, + .break_ctl = pch_uart_break_ctl, + .startup = pch_uart_startup, + .shutdown = pch_uart_shutdown, + .set_termios = pch_uart_set_termios, +/* .pm = pch_uart_pm, Not supported yet */ + .type = pch_uart_type, + .release_port = pch_uart_release_port, + .request_port = pch_uart_request_port, + .config_port = pch_uart_config_port, + .verify_port = pch_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pch_uart_get_poll_char, + .poll_put_char = pch_uart_put_poll_char, +#endif +}; + +#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE static void pch_console_putchar(struct uart_port *port, int ch) { @@ -1564,7 +1658,8 @@ pch_console_write(struct console *co, const char *s, unsigned int count) local_irq_save(flags); if (priv->port.sysrq) { - spin_lock(&priv->lock); + /* call to uart_handle_sysrq_char already took the priv lock */ + priv_locked = 0; /* serial8250_handle_port() already took the port lock */ port_locked = 0; } else if (oops_in_progress) { @@ -1641,7 +1736,7 @@ static struct console pch_console = { #define PCH_CONSOLE (&pch_console) #else #define PCH_CONSOLE NULL -#endif +#endif /* CONFIG_SERIAL_PCH_UART_CONSOLE */ static struct uart_driver pch_uart_driver = { .owner = THIS_MODULE, @@ -1664,7 +1759,9 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, int fifosize; int port_type; struct pch_uart_driver_data *board; +#ifdef CONFIG_DEBUG_FS char name[32]; /* for debugfs file name */ +#endif board = &drv_dat[id->driver_data]; port_type = board->port_type; @@ -1718,6 +1815,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, priv->port.line = board->line_no; priv->trigger = PCH_UART_HAL_TRIGGER_M; + snprintf(priv->irq_name, IRQ_NAME_SIZE, + KBUILD_MODNAME ":" PCH_UART_DRIVER_DEVICE "%d", + priv->port.line); + spin_lock_init(&priv->port.lock); pci_set_drvdata(pdev, priv); @@ -1759,7 +1860,6 @@ static void pch_uart_exit_port(struct eg20t_port *priv) debugfs_remove(priv->debugfs); #endif uart_remove_one_port(&pch_uart_driver, &priv->port); - pci_set_drvdata(priv->pdev, NULL); free_page((unsigned long)priv->rxbuf.buf); } @@ -1813,7 +1913,7 @@ static int pch_uart_pci_resume(struct pci_dev *pdev) #define pch_uart_pci_resume NULL #endif -static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { +static const struct pci_device_id pch_uart_pci_id[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811), .driver_data = pch_et20t_uart0}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812), @@ -1901,6 +2001,8 @@ module_exit(pch_uart_module_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver"); +MODULE_DEVICE_TABLE(pci, pch_uart_pci_id); + module_param(default_baud, uint, S_IRUGO); MODULE_PARM_DESC(default_baud, "Default BAUD for initial driver state and console (default 9600)"); diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 333c8d012b0..f7ad5b90305 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -57,6 +57,8 @@ #include <linux/bitops.h> #include <linux/sysrq.h> #include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/sections.h> #include <asm/io.h> #include <asm/irq.h> @@ -227,19 +229,19 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable) write_zsreg(uap, R1, uap->curregs[1]); } -static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) +static bool pmz_receive_chars(struct uart_pmac_port *uap) { - struct tty_struct *tty = NULL; + struct tty_port *port; unsigned char ch, r1, drop, error, flag; int loops = 0; /* Sanity check, make sure the old bug is no longer happening */ - if (uap->port.state == NULL || uap->port.state->port.tty == NULL) { + if (uap->port.state == NULL) { WARN_ON(1); (void)read_zsdata(uap); - return NULL; + return false; } - tty = uap->port.state->port.tty; + port = &uap->port.state->port; while (1) { error = 0; @@ -309,10 +311,10 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) if (uap->port.ignore_status_mask == 0xff || (r1 & uap->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(port, ch, flag); } if (r1 & Rx_OVR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); next_char: /* We can get stuck in an infinite loop getting char 0 when the * line is in a wrong HW state, we break that here. @@ -328,11 +330,11 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) break; } - return tty; + return true; flood: pmz_interrupt_control(uap, 0); pmz_error("pmz: rx irq flood !\n"); - return tty; + return true; } static void pmz_status_handle(struct uart_pmac_port *uap) @@ -453,7 +455,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) struct uart_pmac_port *uap_a; struct uart_pmac_port *uap_b; int rc = IRQ_NONE; - struct tty_struct *tty; + bool push; u8 r3; uap_a = pmz_get_port_A(uap); @@ -466,7 +468,7 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) pmz_debug("irq, r3: %x\n", r3); #endif /* Channel A */ - tty = NULL; + push = false; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { if (!ZS_IS_OPEN(uap_a)) { pmz_debug("ChanA interrupt while not open !\n"); @@ -477,21 +479,21 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) if (r3 & CHAEXT) pmz_status_handle(uap_a); if (r3 & CHARxIP) - tty = pmz_receive_chars(uap_a); + push = pmz_receive_chars(uap_a); if (r3 & CHATxIP) pmz_transmit_chars(uap_a); rc = IRQ_HANDLED; } skip_a: spin_unlock(&uap_a->port.lock); - if (tty != NULL) - tty_flip_buffer_push(tty); + if (push) + tty_flip_buffer_push(&uap->port.state->port); if (!uap_b) goto out; spin_lock(&uap_b->port.lock); - tty = NULL; + push = false; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { if (!ZS_IS_OPEN(uap_b)) { pmz_debug("ChanB interrupt while not open !\n"); @@ -502,15 +504,15 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) if (r3 & CHBEXT) pmz_status_handle(uap_b); if (r3 & CHBRxIP) - tty = pmz_receive_chars(uap_b); + push = pmz_receive_chars(uap_b); if (r3 & CHBTxIP) pmz_transmit_chars(uap_b); rc = IRQ_HANDLED; } skip_b: spin_unlock(&uap_b->port.lock); - if (tty != NULL) - tty_flip_buffer_push(tty); + if (push) + tty_flip_buffer_push(&uap->port.state->port); out: return rc; @@ -651,6 +653,8 @@ static void pmz_start_tx(struct uart_port *port) } else { struct circ_buf *xmit = &port->state->xmit; + if (uart_circ_empty(xmit)) + goto out; write_zsdata(uap, xmit->buf[xmit->tail]); zssync(uap); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -659,6 +663,7 @@ static void pmz_start_tx(struct uart_port *port) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); } + out: pmz_debug("pmz: start_tx() done.\n"); } @@ -1072,7 +1077,7 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, uap->curregs[5] |= Tx8; uap->parity_mask = 0xff; break; - }; + } uap->curregs[4] &= ~(SB_MASK); if (cflag & CSTOPB) uap->curregs[4] |= SB2; @@ -1090,7 +1095,7 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, uap->port.read_status_mask = Rx_OVR; if (iflag & INPCK) uap->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) + if (iflag & (IGNBRK | BRKINT | PARMRK)) uap->port.read_status_mask |= BRK_ABRT; uap->port.ignore_status_mask = 0; @@ -1798,7 +1803,6 @@ static int __exit pmz_detach(struct platform_device *pdev) uart_remove_one_port(&pmz_uart_reg, &uap->port); - platform_set_drvdata(pdev, NULL); uap->port.dev = NULL; return 0; @@ -2051,6 +2055,9 @@ static int __init pmz_console_init(void) /* Probe ports */ pmz_probe(); + if (pmz_ports_count == 0) + return -ENODEV; + /* TODO: Autoprobe console based on OF */ /* pmz_console.index = i; */ register_console(&pmz_console); diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c index 0aa75a97531..2ba24a45c97 100644 --- a/drivers/tty/serial/pnx8xxx_uart.c +++ b/drivers/tty/serial/pnx8xxx_uart.c @@ -181,7 +181,6 @@ static void pnx8xxx_enable_ms(struct uart_port *port) static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) { - struct tty_struct *tty = sport->port.state->port.tty; unsigned int status, ch, flg; status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | @@ -238,7 +237,10 @@ static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); } - tty_flip_buffer_push(tty); + + spin_unlock(&sport->port.lock); + tty_flip_buffer_push(&sport->port.state->port); + spin_lock(&sport->port.lock); } static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) @@ -475,7 +477,7 @@ pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, sport->port.read_status_mask |= FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) sport->port.read_status_mask |= ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); @@ -802,8 +804,6 @@ static int pnx8xxx_serial_remove(struct platform_device *pdev) { struct pnx8xxx_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&pnx8xxx_reg, &sport->port); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 2764828251f..c638c53cd2b 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -98,7 +98,6 @@ static void serial_pxa_stop_rx(struct uart_port *port) static inline void receive_chars(struct uart_pxa_port *up, int *status) { - struct tty_struct *tty = up->port.state->port.tty; unsigned int ch, flag; int max_count = 256; @@ -168,7 +167,7 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status) ignore_char: *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&up->port.state->port); /* work around Errata #20 according to * Intel(R) PXA27x Processor Family @@ -333,31 +332,6 @@ static void serial_pxa_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } -#if 0 -static void serial_pxa_dma_init(struct pxa_uart *up) -{ - up->rxdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up); - if (up->rxdma < 0) - goto out; - up->txdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up); - if (up->txdma < 0) - goto err_txdma; - up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL); - if (!up->dmadesc) - goto err_alloc; - - /* ... */ -err_alloc: - pxa_free_dma(up->txdma); -err_rxdma: - pxa_free_dma(up->rxdma); -out: - return; -} -#endif - static int serial_pxa_startup(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; @@ -518,7 +492,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (termios->c_iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= UART_LSR_BI; /* @@ -673,8 +647,7 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) unsigned long flags; int locked = 1; - clk_prepare_enable(up->clk); - + clk_enable(up->clk); local_irq_save(flags); if (up->port.sysrq) locked = 0; @@ -701,8 +674,8 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock(&up->port.lock); local_irq_restore(flags); + clk_disable(up->clk); - clk_disable_unprepare(up->clk); } #ifdef CONFIG_CONSOLE_POLL @@ -738,13 +711,8 @@ static void serial_pxa_put_poll_char(struct uart_port *port, wait_for_xmitr(up); /* * Send the character out. - * If a LF, also do CR... */ serial_out(up, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up); - serial_out(up, UART_TX, 13); - } /* * Finally, wait for transmitter to become empty @@ -792,7 +760,7 @@ static struct console serial_pxa_console = { #define PXA_CONSOLE NULL #endif -struct uart_ops serial_pxa_pops = { +static struct uart_ops serial_pxa_pops = { .tx_empty = serial_pxa_tx_empty, .set_mctrl = serial_pxa_set_mctrl, .get_mctrl = serial_pxa_get_mctrl, @@ -899,6 +867,12 @@ static int serial_pxa_probe(struct platform_device *dev) goto err_free; } + ret = clk_prepare(sport->clk); + if (ret) { + clk_put(sport->clk); + goto err_free; + } + sport->port.type = PORT_PXA; sport->port.iotype = UPIO_MEM; sport->port.mapbase = mmres->start; @@ -930,6 +904,7 @@ static int serial_pxa_probe(struct platform_device *dev) return 0; err_clk: + clk_unprepare(sport->clk); clk_put(sport->clk); err_free: kfree(sport); @@ -940,9 +915,9 @@ static int serial_pxa_remove(struct platform_device *dev) { struct uart_pxa_port *sport = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - uart_remove_one_port(&serial_pxa_reg, &sport->port); + + clk_unprepare(sport->clk); clk_put(sport->clk); kfree(sport); @@ -963,7 +938,7 @@ static struct platform_driver serial_pxa_driver = { }, }; -int __init serial_pxa_init(void) +static int __init serial_pxa_init(void) { int ret; @@ -978,7 +953,7 @@ int __init serial_pxa_init(void) return ret; } -void __exit serial_pxa_exit(void) +static void __exit serial_pxa_exit(void) { platform_driver_unregister(&serial_pxa_driver); uart_unregister_driver(&serial_pxa_reg); diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c new file mode 100644 index 00000000000..056f91b3a4c --- /dev/null +++ b/drivers/tty/serial/rp2.c @@ -0,0 +1,887 @@ +/* + * Driver for Comtrol RocketPort EXPRESS/INFINITY cards + * + * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com> + * + * Inspired by, and loosely based on: + * + * ar933x_uart.c + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> + * + * rocketport_infinity_express-linux-1.20.tar.gz + * Copyright (C) 2004-2011 Comtrol, Inc. + * + * 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. + */ + +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/completion.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/sysrq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/types.h> + +#define DRV_NAME "rp2" + +#define RP2_FW_NAME "rp2.fw" +#define RP2_UCODE_BYTES 0x3f + +#define PORTS_PER_ASIC 16 +#define ALL_PORTS_MASK (BIT(PORTS_PER_ASIC) - 1) + +#define UART_CLOCK 44236800 +#define DEFAULT_BAUD_DIV (UART_CLOCK / (9600 * 16)) +#define FIFO_SIZE 512 + +/* BAR0 registers */ +#define RP2_FPGA_CTL0 0x110 +#define RP2_FPGA_CTL1 0x11c +#define RP2_IRQ_MASK 0x1ec +#define RP2_IRQ_MASK_EN_m BIT(0) +#define RP2_IRQ_STATUS 0x1f0 + +/* BAR1 registers */ +#define RP2_ASIC_SPACING 0x1000 +#define RP2_ASIC_OFFSET(i) ((i) << ilog2(RP2_ASIC_SPACING)) + +#define RP2_PORT_BASE 0x000 +#define RP2_PORT_SPACING 0x040 + +#define RP2_UCODE_BASE 0x400 +#define RP2_UCODE_SPACING 0x80 + +#define RP2_CLK_PRESCALER 0xc00 +#define RP2_CH_IRQ_STAT 0xc04 +#define RP2_CH_IRQ_MASK 0xc08 +#define RP2_ASIC_IRQ 0xd00 +#define RP2_ASIC_IRQ_EN_m BIT(20) +#define RP2_GLOBAL_CMD 0xd0c +#define RP2_ASIC_CFG 0xd04 + +/* port registers */ +#define RP2_DATA_DWORD 0x000 + +#define RP2_DATA_BYTE 0x008 +#define RP2_DATA_BYTE_ERR_PARITY_m BIT(8) +#define RP2_DATA_BYTE_ERR_OVERRUN_m BIT(9) +#define RP2_DATA_BYTE_ERR_FRAMING_m BIT(10) +#define RP2_DATA_BYTE_BREAK_m BIT(11) + +/* This lets uart_insert_char() drop bytes received on a !CREAD port */ +#define RP2_DUMMY_READ BIT(16) + +#define RP2_DATA_BYTE_EXCEPTION_MASK (RP2_DATA_BYTE_ERR_PARITY_m | \ + RP2_DATA_BYTE_ERR_OVERRUN_m | \ + RP2_DATA_BYTE_ERR_FRAMING_m | \ + RP2_DATA_BYTE_BREAK_m) + +#define RP2_RX_FIFO_COUNT 0x00c +#define RP2_TX_FIFO_COUNT 0x00e + +#define RP2_CHAN_STAT 0x010 +#define RP2_CHAN_STAT_RXDATA_m BIT(0) +#define RP2_CHAN_STAT_DCD_m BIT(3) +#define RP2_CHAN_STAT_DSR_m BIT(4) +#define RP2_CHAN_STAT_CTS_m BIT(5) +#define RP2_CHAN_STAT_RI_m BIT(6) +#define RP2_CHAN_STAT_OVERRUN_m BIT(13) +#define RP2_CHAN_STAT_DSR_CHANGED_m BIT(16) +#define RP2_CHAN_STAT_CTS_CHANGED_m BIT(17) +#define RP2_CHAN_STAT_CD_CHANGED_m BIT(18) +#define RP2_CHAN_STAT_RI_CHANGED_m BIT(22) +#define RP2_CHAN_STAT_TXEMPTY_m BIT(25) + +#define RP2_CHAN_STAT_MS_CHANGED_MASK (RP2_CHAN_STAT_DSR_CHANGED_m | \ + RP2_CHAN_STAT_CTS_CHANGED_m | \ + RP2_CHAN_STAT_CD_CHANGED_m | \ + RP2_CHAN_STAT_RI_CHANGED_m) + +#define RP2_TXRX_CTL 0x014 +#define RP2_TXRX_CTL_MSRIRQ_m BIT(0) +#define RP2_TXRX_CTL_RXIRQ_m BIT(2) +#define RP2_TXRX_CTL_RX_TRIG_s 3 +#define RP2_TXRX_CTL_RX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s) +#define RP2_TXRX_CTL_RX_TRIG_1 (0x1 << RP2_TXRX_CTL_RX_TRIG_s) +#define RP2_TXRX_CTL_RX_TRIG_256 (0x2 << RP2_TXRX_CTL_RX_TRIG_s) +#define RP2_TXRX_CTL_RX_TRIG_448 (0x3 << RP2_TXRX_CTL_RX_TRIG_s) +#define RP2_TXRX_CTL_RX_EN_m BIT(5) +#define RP2_TXRX_CTL_RTSFLOW_m BIT(6) +#define RP2_TXRX_CTL_DTRFLOW_m BIT(7) +#define RP2_TXRX_CTL_TX_TRIG_s 16 +#define RP2_TXRX_CTL_TX_TRIG_m (0x3 << RP2_TXRX_CTL_RX_TRIG_s) +#define RP2_TXRX_CTL_DSRFLOW_m BIT(18) +#define RP2_TXRX_CTL_TXIRQ_m BIT(19) +#define RP2_TXRX_CTL_CTSFLOW_m BIT(23) +#define RP2_TXRX_CTL_TX_EN_m BIT(24) +#define RP2_TXRX_CTL_RTS_m BIT(25) +#define RP2_TXRX_CTL_DTR_m BIT(26) +#define RP2_TXRX_CTL_LOOP_m BIT(27) +#define RP2_TXRX_CTL_BREAK_m BIT(28) +#define RP2_TXRX_CTL_CMSPAR_m BIT(29) +#define RP2_TXRX_CTL_nPARODD_m BIT(30) +#define RP2_TXRX_CTL_PARENB_m BIT(31) + +#define RP2_UART_CTL 0x018 +#define RP2_UART_CTL_MODE_s 0 +#define RP2_UART_CTL_MODE_m (0x7 << RP2_UART_CTL_MODE_s) +#define RP2_UART_CTL_MODE_rs232 (0x1 << RP2_UART_CTL_MODE_s) +#define RP2_UART_CTL_FLUSH_RX_m BIT(3) +#define RP2_UART_CTL_FLUSH_TX_m BIT(4) +#define RP2_UART_CTL_RESET_CH_m BIT(5) +#define RP2_UART_CTL_XMIT_EN_m BIT(6) +#define RP2_UART_CTL_DATABITS_s 8 +#define RP2_UART_CTL_DATABITS_m (0x3 << RP2_UART_CTL_DATABITS_s) +#define RP2_UART_CTL_DATABITS_8 (0x3 << RP2_UART_CTL_DATABITS_s) +#define RP2_UART_CTL_DATABITS_7 (0x2 << RP2_UART_CTL_DATABITS_s) +#define RP2_UART_CTL_DATABITS_6 (0x1 << RP2_UART_CTL_DATABITS_s) +#define RP2_UART_CTL_DATABITS_5 (0x0 << RP2_UART_CTL_DATABITS_s) +#define RP2_UART_CTL_STOPBITS_m BIT(10) + +#define RP2_BAUD 0x01c + +/* ucode registers */ +#define RP2_TX_SWFLOW 0x02 +#define RP2_TX_SWFLOW_ena 0x81 +#define RP2_TX_SWFLOW_dis 0x9d + +#define RP2_RX_SWFLOW 0x0c +#define RP2_RX_SWFLOW_ena 0x81 +#define RP2_RX_SWFLOW_dis 0x8d + +#define RP2_RX_FIFO 0x37 +#define RP2_RX_FIFO_ena 0x08 +#define RP2_RX_FIFO_dis 0x81 + +static struct uart_driver rp2_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyRP", + .nr = CONFIG_SERIAL_RP2_NR_UARTS, +}; + +struct rp2_card; + +struct rp2_uart_port { + struct uart_port port; + int idx; + int ignore_rx; + struct rp2_card *card; + void __iomem *asic_base; + void __iomem *base; + void __iomem *ucode; +}; + +struct rp2_card { + struct pci_dev *pdev; + struct rp2_uart_port *ports; + int n_ports; + int initialized_ports; + int minor_start; + int smpte; + void __iomem *bar0; + void __iomem *bar1; + spinlock_t card_lock; + struct completion fw_loaded; +}; + +#define RP_ID(prod) PCI_VDEVICE(RP, (prod)) +#define RP_CAP(ports, smpte) (((ports) << 8) | ((smpte) << 0)) + +static inline void rp2_decode_cap(const struct pci_device_id *id, + int *ports, int *smpte) +{ + *ports = id->driver_data >> 8; + *smpte = id->driver_data & 0xff; +} + +static DEFINE_SPINLOCK(rp2_minor_lock); +static int rp2_minor_next; + +static int rp2_alloc_ports(int n_ports) +{ + int ret = -ENOSPC; + + spin_lock(&rp2_minor_lock); + if (rp2_minor_next + n_ports <= CONFIG_SERIAL_RP2_NR_UARTS) { + /* sorry, no support for hot unplugging individual cards */ + ret = rp2_minor_next; + rp2_minor_next += n_ports; + } + spin_unlock(&rp2_minor_lock); + + return ret; +} + +static inline struct rp2_uart_port *port_to_up(struct uart_port *port) +{ + return container_of(port, struct rp2_uart_port, port); +} + +static void rp2_rmw(struct rp2_uart_port *up, int reg, + u32 clr_bits, u32 set_bits) +{ + u32 tmp = readl(up->base + reg); + tmp &= ~clr_bits; + tmp |= set_bits; + writel(tmp, up->base + reg); +} + +static void rp2_rmw_clr(struct rp2_uart_port *up, int reg, u32 val) +{ + rp2_rmw(up, reg, val, 0); +} + +static void rp2_rmw_set(struct rp2_uart_port *up, int reg, u32 val) +{ + rp2_rmw(up, reg, 0, val); +} + +static void rp2_mask_ch_irq(struct rp2_uart_port *up, int ch_num, + int is_enabled) +{ + unsigned long flags, irq_mask; + + spin_lock_irqsave(&up->card->card_lock, flags); + + irq_mask = readl(up->asic_base + RP2_CH_IRQ_MASK); + if (is_enabled) + irq_mask &= ~BIT(ch_num); + else + irq_mask |= BIT(ch_num); + writel(irq_mask, up->asic_base + RP2_CH_IRQ_MASK); + + spin_unlock_irqrestore(&up->card->card_lock, flags); +} + +static unsigned int rp2_uart_tx_empty(struct uart_port *port) +{ + struct rp2_uart_port *up = port_to_up(port); + unsigned long tx_fifo_bytes, flags; + + /* + * This should probably check the transmitter, not the FIFO. + * But the TXEMPTY bit doesn't seem to work unless the TX IRQ is + * enabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + tx_fifo_bytes = readw(up->base + RP2_TX_FIFO_COUNT); + spin_unlock_irqrestore(&up->port.lock, flags); + + return tx_fifo_bytes ? 0 : TIOCSER_TEMT; +} + +static unsigned int rp2_uart_get_mctrl(struct uart_port *port) +{ + struct rp2_uart_port *up = port_to_up(port); + u32 status; + + status = readl(up->base + RP2_CHAN_STAT); + return ((status & RP2_CHAN_STAT_DCD_m) ? TIOCM_CAR : 0) | + ((status & RP2_CHAN_STAT_DSR_m) ? TIOCM_DSR : 0) | + ((status & RP2_CHAN_STAT_CTS_m) ? TIOCM_CTS : 0) | + ((status & RP2_CHAN_STAT_RI_m) ? TIOCM_RI : 0); +} + +static void rp2_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + rp2_rmw(port_to_up(port), RP2_TXRX_CTL, + RP2_TXRX_CTL_DTR_m | RP2_TXRX_CTL_RTS_m | RP2_TXRX_CTL_LOOP_m, + ((mctrl & TIOCM_DTR) ? RP2_TXRX_CTL_DTR_m : 0) | + ((mctrl & TIOCM_RTS) ? RP2_TXRX_CTL_RTS_m : 0) | + ((mctrl & TIOCM_LOOP) ? RP2_TXRX_CTL_LOOP_m : 0)); +} + +static void rp2_uart_start_tx(struct uart_port *port) +{ + rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m); +} + +static void rp2_uart_stop_tx(struct uart_port *port) +{ + rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_TXIRQ_m); +} + +static void rp2_uart_stop_rx(struct uart_port *port) +{ + rp2_rmw_clr(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_RXIRQ_m); +} + +static void rp2_uart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + rp2_rmw(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_BREAK_m, + break_state ? RP2_TXRX_CTL_BREAK_m : 0); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void rp2_uart_enable_ms(struct uart_port *port) +{ + rp2_rmw_set(port_to_up(port), RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m); +} + +static void __rp2_uart_set_termios(struct rp2_uart_port *up, + unsigned long cfl, + unsigned long ifl, + unsigned int baud_div) +{ + /* baud rate divisor (calculated elsewhere). 0 = divide-by-1 */ + writew(baud_div - 1, up->base + RP2_BAUD); + + /* data bits and stop bits */ + rp2_rmw(up, RP2_UART_CTL, + RP2_UART_CTL_STOPBITS_m | RP2_UART_CTL_DATABITS_m, + ((cfl & CSTOPB) ? RP2_UART_CTL_STOPBITS_m : 0) | + (((cfl & CSIZE) == CS8) ? RP2_UART_CTL_DATABITS_8 : 0) | + (((cfl & CSIZE) == CS7) ? RP2_UART_CTL_DATABITS_7 : 0) | + (((cfl & CSIZE) == CS6) ? RP2_UART_CTL_DATABITS_6 : 0) | + (((cfl & CSIZE) == CS5) ? RP2_UART_CTL_DATABITS_5 : 0)); + + /* parity and hardware flow control */ + rp2_rmw(up, RP2_TXRX_CTL, + RP2_TXRX_CTL_PARENB_m | RP2_TXRX_CTL_nPARODD_m | + RP2_TXRX_CTL_CMSPAR_m | RP2_TXRX_CTL_DTRFLOW_m | + RP2_TXRX_CTL_DSRFLOW_m | RP2_TXRX_CTL_RTSFLOW_m | + RP2_TXRX_CTL_CTSFLOW_m, + ((cfl & PARENB) ? RP2_TXRX_CTL_PARENB_m : 0) | + ((cfl & PARODD) ? 0 : RP2_TXRX_CTL_nPARODD_m) | + ((cfl & CMSPAR) ? RP2_TXRX_CTL_CMSPAR_m : 0) | + ((cfl & CRTSCTS) ? (RP2_TXRX_CTL_RTSFLOW_m | + RP2_TXRX_CTL_CTSFLOW_m) : 0)); + + /* XON/XOFF software flow control */ + writeb((ifl & IXON) ? RP2_TX_SWFLOW_ena : RP2_TX_SWFLOW_dis, + up->ucode + RP2_TX_SWFLOW); + writeb((ifl & IXOFF) ? RP2_RX_SWFLOW_ena : RP2_RX_SWFLOW_dis, + up->ucode + RP2_RX_SWFLOW); +} + +static void rp2_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + struct rp2_uart_port *up = port_to_up(port); + unsigned long flags; + unsigned int baud, baud_div; + + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + baud_div = uart_get_divisor(port, baud); + + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + + /* ignore all characters if CREAD is not set */ + port->ignore_status_mask = (new->c_cflag & CREAD) ? 0 : RP2_DUMMY_READ; + + __rp2_uart_set_termios(up, new->c_cflag, new->c_iflag, baud_div); + uart_update_timeout(port, new->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void rp2_rx_chars(struct rp2_uart_port *up) +{ + u16 bytes = readw(up->base + RP2_RX_FIFO_COUNT); + struct tty_port *port = &up->port.state->port; + + for (; bytes != 0; bytes--) { + u32 byte = readw(up->base + RP2_DATA_BYTE) | RP2_DUMMY_READ; + char ch = byte & 0xff; + + if (likely(!(byte & RP2_DATA_BYTE_EXCEPTION_MASK))) { + if (!uart_handle_sysrq_char(&up->port, ch)) + uart_insert_char(&up->port, byte, 0, ch, + TTY_NORMAL); + } else { + char flag = TTY_NORMAL; + + if (byte & RP2_DATA_BYTE_BREAK_m) + flag = TTY_BREAK; + else if (byte & RP2_DATA_BYTE_ERR_FRAMING_m) + flag = TTY_FRAME; + else if (byte & RP2_DATA_BYTE_ERR_PARITY_m) + flag = TTY_PARITY; + uart_insert_char(&up->port, byte, + RP2_DATA_BYTE_ERR_OVERRUN_m, ch, flag); + } + up->port.icount.rx++; + } + + spin_unlock(&up->port.lock); + tty_flip_buffer_push(port); + spin_lock(&up->port.lock); +} + +static void rp2_tx_chars(struct rp2_uart_port *up) +{ + u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT); + struct circ_buf *xmit = &up->port.state->xmit; + + if (uart_tx_stopped(&up->port)) { + rp2_uart_stop_tx(&up->port); + return; + } + + for (; max_tx != 0; max_tx--) { + if (up->port.x_char) { + writeb(up->port.x_char, up->base + RP2_DATA_BYTE); + up->port.x_char = 0; + up->port.icount.tx++; + continue; + } + if (uart_circ_empty(xmit)) { + rp2_uart_stop_tx(&up->port); + break; + } + writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +static void rp2_ch_interrupt(struct rp2_uart_port *up) +{ + u32 status; + + spin_lock(&up->port.lock); + + /* + * The IRQ status bits are clear-on-write. Other status bits in + * this register aren't, so it's harmless to write to them. + */ + status = readl(up->base + RP2_CHAN_STAT); + writel(status, up->base + RP2_CHAN_STAT); + + if (status & RP2_CHAN_STAT_RXDATA_m) + rp2_rx_chars(up); + if (status & RP2_CHAN_STAT_TXEMPTY_m) + rp2_tx_chars(up); + if (status & RP2_CHAN_STAT_MS_CHANGED_MASK) + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + + spin_unlock(&up->port.lock); +} + +static int rp2_asic_interrupt(struct rp2_card *card, unsigned int asic_id) +{ + void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id); + int ch, handled = 0; + unsigned long status = readl(base + RP2_CH_IRQ_STAT) & + ~readl(base + RP2_CH_IRQ_MASK); + + for_each_set_bit(ch, &status, PORTS_PER_ASIC) { + rp2_ch_interrupt(&card->ports[ch]); + handled++; + } + return handled; +} + +static irqreturn_t rp2_uart_interrupt(int irq, void *dev_id) +{ + struct rp2_card *card = dev_id; + int handled; + + handled = rp2_asic_interrupt(card, 0); + if (card->n_ports >= PORTS_PER_ASIC) + handled += rp2_asic_interrupt(card, 1); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static inline void rp2_flush_fifos(struct rp2_uart_port *up) +{ + rp2_rmw_set(up, RP2_UART_CTL, + RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m); + readl(up->base + RP2_UART_CTL); + udelay(10); + rp2_rmw_clr(up, RP2_UART_CTL, + RP2_UART_CTL_FLUSH_RX_m | RP2_UART_CTL_FLUSH_TX_m); +} + +static int rp2_uart_startup(struct uart_port *port) +{ + struct rp2_uart_port *up = port_to_up(port); + + rp2_flush_fifos(up); + rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_MSRIRQ_m, RP2_TXRX_CTL_RXIRQ_m); + rp2_rmw(up, RP2_TXRX_CTL, RP2_TXRX_CTL_RX_TRIG_m, + RP2_TXRX_CTL_RX_TRIG_1); + rp2_rmw(up, RP2_CHAN_STAT, 0, 0); + rp2_mask_ch_irq(up, up->idx, 1); + + return 0; +} + +static void rp2_uart_shutdown(struct uart_port *port) +{ + struct rp2_uart_port *up = port_to_up(port); + unsigned long flags; + + rp2_uart_break_ctl(port, 0); + + spin_lock_irqsave(&port->lock, flags); + rp2_mask_ch_irq(up, up->idx, 0); + rp2_rmw(up, RP2_CHAN_STAT, 0, 0); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *rp2_uart_type(struct uart_port *port) +{ + return (port->type == PORT_RP2) ? "RocketPort 2 UART" : NULL; +} + +static void rp2_uart_release_port(struct uart_port *port) +{ + /* Nothing to release ... */ +} + +static int rp2_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void rp2_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_RP2; +} + +static int rp2_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_RP2) + return -EINVAL; + + return 0; +} + +static const struct uart_ops rp2_uart_ops = { + .tx_empty = rp2_uart_tx_empty, + .set_mctrl = rp2_uart_set_mctrl, + .get_mctrl = rp2_uart_get_mctrl, + .stop_tx = rp2_uart_stop_tx, + .start_tx = rp2_uart_start_tx, + .stop_rx = rp2_uart_stop_rx, + .enable_ms = rp2_uart_enable_ms, + .break_ctl = rp2_uart_break_ctl, + .startup = rp2_uart_startup, + .shutdown = rp2_uart_shutdown, + .set_termios = rp2_uart_set_termios, + .type = rp2_uart_type, + .release_port = rp2_uart_release_port, + .request_port = rp2_uart_request_port, + .config_port = rp2_uart_config_port, + .verify_port = rp2_uart_verify_port, +}; + +static void rp2_reset_asic(struct rp2_card *card, unsigned int asic_id) +{ + void __iomem *base = card->bar1 + RP2_ASIC_OFFSET(asic_id); + u32 clk_cfg; + + writew(1, base + RP2_GLOBAL_CMD); + readw(base + RP2_GLOBAL_CMD); + msleep(100); + writel(0, base + RP2_CLK_PRESCALER); + + /* TDM clock configuration */ + clk_cfg = readw(base + RP2_ASIC_CFG); + clk_cfg = (clk_cfg & ~BIT(8)) | BIT(9); + writew(clk_cfg, base + RP2_ASIC_CFG); + + /* IRQ routing */ + writel(ALL_PORTS_MASK, base + RP2_CH_IRQ_MASK); + writel(RP2_ASIC_IRQ_EN_m, base + RP2_ASIC_IRQ); +} + +static void rp2_init_card(struct rp2_card *card) +{ + writel(4, card->bar0 + RP2_FPGA_CTL0); + writel(0, card->bar0 + RP2_FPGA_CTL1); + + rp2_reset_asic(card, 0); + if (card->n_ports >= PORTS_PER_ASIC) + rp2_reset_asic(card, 1); + + writel(RP2_IRQ_MASK_EN_m, card->bar0 + RP2_IRQ_MASK); +} + +static void rp2_init_port(struct rp2_uart_port *up, const struct firmware *fw) +{ + int i; + + writel(RP2_UART_CTL_RESET_CH_m, up->base + RP2_UART_CTL); + readl(up->base + RP2_UART_CTL); + udelay(1); + + writel(0, up->base + RP2_TXRX_CTL); + writel(0, up->base + RP2_UART_CTL); + readl(up->base + RP2_UART_CTL); + udelay(1); + + rp2_flush_fifos(up); + + for (i = 0; i < min_t(int, fw->size, RP2_UCODE_BYTES); i++) + writeb(fw->data[i], up->ucode + i); + + __rp2_uart_set_termios(up, CS8 | CREAD | CLOCAL, 0, DEFAULT_BAUD_DIV); + rp2_uart_set_mctrl(&up->port, 0); + + writeb(RP2_RX_FIFO_ena, up->ucode + RP2_RX_FIFO); + rp2_rmw(up, RP2_UART_CTL, RP2_UART_CTL_MODE_m, + RP2_UART_CTL_XMIT_EN_m | RP2_UART_CTL_MODE_rs232); + rp2_rmw_set(up, RP2_TXRX_CTL, + RP2_TXRX_CTL_TX_EN_m | RP2_TXRX_CTL_RX_EN_m); +} + +static void rp2_remove_ports(struct rp2_card *card) +{ + int i; + + for (i = 0; i < card->initialized_ports; i++) + uart_remove_one_port(&rp2_uart_driver, &card->ports[i].port); + card->initialized_ports = 0; +} + +static void rp2_fw_cb(const struct firmware *fw, void *context) +{ + struct rp2_card *card = context; + resource_size_t phys_base; + int i, rc = -ENOENT; + + if (!fw) { + dev_err(&card->pdev->dev, "cannot find '%s' firmware image\n", + RP2_FW_NAME); + goto no_fw; + } + + phys_base = pci_resource_start(card->pdev, 1); + + for (i = 0; i < card->n_ports; i++) { + struct rp2_uart_port *rp = &card->ports[i]; + struct uart_port *p; + int j = (unsigned)i % PORTS_PER_ASIC; + + rp->asic_base = card->bar1; + rp->base = card->bar1 + RP2_PORT_BASE + j*RP2_PORT_SPACING; + rp->ucode = card->bar1 + RP2_UCODE_BASE + j*RP2_UCODE_SPACING; + rp->card = card; + rp->idx = j; + + p = &rp->port; + p->line = card->minor_start + i; + p->dev = &card->pdev->dev; + p->type = PORT_RP2; + p->iotype = UPIO_MEM32; + p->uartclk = UART_CLOCK; + p->regshift = 2; + p->fifosize = FIFO_SIZE; + p->ops = &rp2_uart_ops; + p->irq = card->pdev->irq; + p->membase = rp->base; + p->mapbase = phys_base + RP2_PORT_BASE + j*RP2_PORT_SPACING; + + if (i >= PORTS_PER_ASIC) { + rp->asic_base += RP2_ASIC_SPACING; + rp->base += RP2_ASIC_SPACING; + rp->ucode += RP2_ASIC_SPACING; + p->mapbase += RP2_ASIC_SPACING; + } + + rp2_init_port(rp, fw); + rc = uart_add_one_port(&rp2_uart_driver, p); + if (rc) { + dev_err(&card->pdev->dev, + "error registering port %d: %d\n", i, rc); + rp2_remove_ports(card); + break; + } + card->initialized_ports++; + } + + release_firmware(fw); +no_fw: + /* + * rp2_fw_cb() is called from a workqueue long after rp2_probe() + * has already returned success. So if something failed here, + * we'll just leave the now-dormant device in place until somebody + * unbinds it. + */ + if (rc) + dev_warn(&card->pdev->dev, "driver initialization failed\n"); + + complete(&card->fw_loaded); +} + +static int rp2_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct rp2_card *card; + struct rp2_uart_port *ports; + void __iomem * const *bars; + int rc; + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + pci_set_drvdata(pdev, card); + spin_lock_init(&card->card_lock); + init_completion(&card->fw_loaded); + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + rc = pcim_iomap_regions_request_all(pdev, 0x03, DRV_NAME); + if (rc) + return rc; + + bars = pcim_iomap_table(pdev); + card->bar0 = bars[0]; + card->bar1 = bars[1]; + card->pdev = pdev; + + rp2_decode_cap(id, &card->n_ports, &card->smpte); + dev_info(&pdev->dev, "found new card with %d ports\n", card->n_ports); + + card->minor_start = rp2_alloc_ports(card->n_ports); + if (card->minor_start < 0) { + dev_err(&pdev->dev, + "too many ports (try increasing CONFIG_SERIAL_RP2_NR_UARTS)\n"); + return -EINVAL; + } + + rp2_init_card(card); + + ports = devm_kzalloc(&pdev->dev, sizeof(*ports) * card->n_ports, + GFP_KERNEL); + if (!ports) + return -ENOMEM; + card->ports = ports; + + rc = devm_request_irq(&pdev->dev, pdev->irq, rp2_uart_interrupt, + IRQF_SHARED, DRV_NAME, card); + if (rc) + return rc; + + /* + * Only catastrophic errors (e.g. ENOMEM) are reported here. + * If the FW image is missing, we'll find out in rp2_fw_cb() + * and print an error message. + */ + rc = request_firmware_nowait(THIS_MODULE, 1, RP2_FW_NAME, &pdev->dev, + GFP_KERNEL, card, rp2_fw_cb); + if (rc) + return rc; + dev_dbg(&pdev->dev, "waiting for firmware blob...\n"); + + return 0; +} + +static void rp2_remove(struct pci_dev *pdev) +{ + struct rp2_card *card = pci_get_drvdata(pdev); + + wait_for_completion(&card->fw_loaded); + rp2_remove_ports(card); +} + +static const struct pci_device_id rp2_pci_tbl[] = { + + /* RocketPort INFINITY cards */ + + { RP_ID(0x0040), RP_CAP(8, 0) }, /* INF Octa, RJ45, selectable */ + { RP_ID(0x0041), RP_CAP(32, 0) }, /* INF 32, ext interface */ + { RP_ID(0x0042), RP_CAP(8, 0) }, /* INF Octa, ext interface */ + { RP_ID(0x0043), RP_CAP(16, 0) }, /* INF 16, ext interface */ + { RP_ID(0x0044), RP_CAP(4, 0) }, /* INF Quad, DB, selectable */ + { RP_ID(0x0045), RP_CAP(8, 0) }, /* INF Octa, DB, selectable */ + { RP_ID(0x0046), RP_CAP(4, 0) }, /* INF Quad, ext interface */ + { RP_ID(0x0047), RP_CAP(4, 0) }, /* INF Quad, RJ45 */ + { RP_ID(0x004a), RP_CAP(4, 0) }, /* INF Plus, Quad */ + { RP_ID(0x004b), RP_CAP(8, 0) }, /* INF Plus, Octa */ + { RP_ID(0x004c), RP_CAP(8, 0) }, /* INF III, Octa */ + { RP_ID(0x004d), RP_CAP(4, 0) }, /* INF III, Quad */ + { RP_ID(0x004e), RP_CAP(2, 0) }, /* INF Plus, 2, RS232 */ + { RP_ID(0x004f), RP_CAP(2, 1) }, /* INF Plus, 2, SMPTE */ + { RP_ID(0x0050), RP_CAP(4, 0) }, /* INF Plus, Quad, RJ45 */ + { RP_ID(0x0051), RP_CAP(8, 0) }, /* INF Plus, Octa, RJ45 */ + { RP_ID(0x0052), RP_CAP(8, 1) }, /* INF Octa, SMPTE */ + + /* RocketPort EXPRESS cards */ + + { RP_ID(0x0060), RP_CAP(8, 0) }, /* EXP Octa, RJ45, selectable */ + { RP_ID(0x0061), RP_CAP(32, 0) }, /* EXP 32, ext interface */ + { RP_ID(0x0062), RP_CAP(8, 0) }, /* EXP Octa, ext interface */ + { RP_ID(0x0063), RP_CAP(16, 0) }, /* EXP 16, ext interface */ + { RP_ID(0x0064), RP_CAP(4, 0) }, /* EXP Quad, DB, selectable */ + { RP_ID(0x0065), RP_CAP(8, 0) }, /* EXP Octa, DB, selectable */ + { RP_ID(0x0066), RP_CAP(4, 0) }, /* EXP Quad, ext interface */ + { RP_ID(0x0067), RP_CAP(4, 0) }, /* EXP Quad, RJ45 */ + { RP_ID(0x0068), RP_CAP(8, 0) }, /* EXP Octa, RJ11 */ + { RP_ID(0x0072), RP_CAP(8, 1) }, /* EXP Octa, SMPTE */ + { } +}; +MODULE_DEVICE_TABLE(pci, rp2_pci_tbl); + +static struct pci_driver rp2_pci_driver = { + .name = DRV_NAME, + .id_table = rp2_pci_tbl, + .probe = rp2_probe, + .remove = rp2_remove, +}; + +static int __init rp2_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&rp2_uart_driver); + if (rc) + return rc; + + rc = pci_register_driver(&rp2_pci_driver); + if (rc) { + uart_unregister_driver(&rp2_uart_driver); + return rc; + } + + return 0; +} + +static void __exit rp2_uart_exit(void) +{ + pci_unregister_driver(&rp2_pci_driver); + uart_unregister_driver(&rp2_uart_driver); +} + +module_init(rp2_uart_init); +module_exit(rp2_uart_exit); + +MODULE_DESCRIPTION("Comtrol RocketPort EXPRESS/INFINITY driver"); +MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(RP2_FW_NAME); diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c index 5d4b9b449b4..753d4525b36 100644 --- a/drivers/tty/serial/sa1100.c +++ b/drivers/tty/serial/sa1100.c @@ -188,7 +188,6 @@ static void sa1100_enable_ms(struct uart_port *port) static void sa1100_rx_chars(struct sa1100_port *sport) { - struct tty_struct *tty = sport->port.state->port.tty; unsigned int status, ch, flg; status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | @@ -233,7 +232,10 @@ sa1100_rx_chars(struct sa1100_port *sport) status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | UTSR0_TO_SM(UART_GET_UTSR0(sport)); } - tty_flip_buffer_push(tty); + + spin_unlock(&sport->port.lock); + tty_flip_buffer_push(&sport->port.state->port); + spin_lock(&sport->port.lock); } static void sa1100_tx_chars(struct sa1100_port *sport) @@ -645,7 +647,10 @@ void sa1100_register_uart_fns(struct sa1100_port_fns *fns) sa1100_pops.set_mctrl = fns->set_mctrl; sa1100_pops.pm = fns->pm; - sa1100_pops.set_wake = fns->set_wake; + /* + * FIXME: fns->set_wake is unused - this should be called from + * the suspend() callback if device_may_wakeup(dev)) is set. + */ } void __init sa1100_register_uart(int idx, int port) @@ -865,8 +870,6 @@ static int sa1100_serial_remove(struct platform_device *pdev) { struct sa1100_port *sport = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (sport) uart_remove_one_port(&sa1100_reg, &sport->port); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index e514b3a4dc5..c1d3ebdf3b9 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -39,6 +39,7 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/serial_s3c.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/cpufreq.h> @@ -46,14 +47,35 @@ #include <asm/irq.h> -#include <mach/hardware.h> -#include <mach/map.h> - -#include <plat/regs-serial.h> +#ifdef CONFIG_SAMSUNG_CLOCK #include <plat/clock.h> +#endif #include "samsung.h" +#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \ + defined(CONFIG_DEBUG_LL) && \ + !defined(MODULE) + +extern void printascii(const char *); + +__printf(1, 2) +static void dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vscnprintf(buff, sizeof(buff), fmt, va); + va_end(va); + + printascii(buff); +} + +#else +#define dbg(fmt, ...) do { if (0) no_printk(fmt, ##__VA_ARGS__); } while (0) +#endif + /* UART name and device definitions */ #define S3C24XX_SERIAL_NAME "ttySAC" @@ -221,7 +243,6 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - struct tty_struct *tty = port->state->port.tty; unsigned int ufcon, ch, flag, ufstat, uerstat; unsigned long flags; int max_count = 64; @@ -251,6 +272,8 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; + spin_unlock_irqrestore(&port->lock, + flags); goto out; } continue; @@ -299,10 +322,11 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) ignore_char: continue; } - tty_flip_buffer_push(tty); - out: spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(&port->state->port); + + out: return IRQ_HANDLED; } @@ -406,7 +430,14 @@ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) { - /* todo - possibly remove AFC and do manual CTS */ + unsigned int umcon = rd_regl(port, S3C2410_UMCON); + + if (mctrl & TIOCM_RTS) + umcon |= S3C2410_UMCOM_RTS_LOW; + else + umcon &= ~S3C2410_UMCOM_RTS_LOW; + + wr_regl(port, S3C2410_UMCON, umcon); } static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) @@ -448,6 +479,8 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) /* Clear pending interrupts and mask all interrupts */ if (s3c24xx_serial_has_interrupt_mask(port)) { + free_irq(port->irq, ourport); + wr_regl(port, S3C64XX_UINTP, 0xf); wr_regl(port, S3C64XX_UINTM, 0xf); } @@ -458,8 +491,8 @@ static int s3c24xx_serial_startup(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); int ret; - dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", - port->mapbase, port->membase); + dbg("s3c24xx_serial_startup: port=%p (%08llx,%p)\n", + port, (unsigned long long)port->mapbase, port->membase); rx_enabled(port) = 1; @@ -504,8 +537,10 @@ static int s3c64xx_serial_startup(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); int ret; - dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n", - port->mapbase, port->membase); + dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n", + port, (unsigned long long)port->mapbase, port->membase); + + wr_regl(port, S3C64XX_UINTM, 0xf); ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, s3c24xx_serial_portname(port), ourport); @@ -769,8 +804,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (termios->c_cflag & CSTOPB) ulcon |= S3C2410_LCON_STOPB; - umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; - if (termios->c_cflag & PARENB) { if (termios->c_cflag & PARODD) ulcon |= S3C2410_LCON_PODD; @@ -787,6 +820,15 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); + + umcon = rd_regl(port, S3C2410_UMCON); + if (termios->c_cflag & CRTSCTS) { + umcon |= S3C2410_UMCOM_AFC; + /* Disable RTS when RX FIFO contains 63 bytes */ + umcon &= ~S3C2412_UMCON_AFC_8; + } else { + umcon &= ~S3C2410_UMCOM_AFC; + } wr_regl(port, S3C2410_UMCON, umcon); if (ourport->info->has_divslot) @@ -896,7 +938,7 @@ console_initcall(s3c24xx_serial_console_init); #define S3C24XX_SERIAL_CONSOLE NULL #endif -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) static int s3c24xx_serial_get_poll_char(struct uart_port *port); static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c); @@ -920,7 +962,7 @@ static struct uart_ops s3c24xx_serial_ops = { .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, -#ifdef CONFIG_CONSOLE_POLL +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL) .poll_get_char = s3c24xx_serial_get_poll_char, .poll_put_char = s3c24xx_serial_put_poll_char, #endif @@ -1141,10 +1183,15 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, return -EINVAL; } - dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); + dbg("resource %pR)\n", res); + + port->membase = devm_ioremap(port->dev, res->start, resource_size(res)); + if (!port->membase) { + dev_err(port->dev, "failed to remap controller address\n"); + return -EBUSY; + } port->mapbase = res->start; - port->membase = S3C_VA_UART + (res->start & 0xfffff); ret = platform_get_irq(platdev, 0); if (ret < 0) port->irq = 0; @@ -1159,6 +1206,18 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->tx_irq = ret; ourport->clk = clk_get(&platdev->dev, "uart"); + if (IS_ERR(ourport->clk)) { + pr_err("%s: Controller clock not found\n", + dev_name(&platdev->dev)); + return PTR_ERR(ourport->clk); + } + + ret = clk_prepare_enable(ourport->clk); + if (ret) { + pr_err("uart: clock failed to prepare+enable: %d\n", ret); + clk_put(ourport->clk); + return ret; + } /* Keep all interrupts masked and cleared */ if (s3c24xx_serial_has_interrupt_mask(port)) { @@ -1167,7 +1226,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, wr_regl(port, S3C64XX_UINTSP, 0xf); } - dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", + dbg("port: map=%08x, mem=%p, irq=%d (%d,%d), clock=%u\n", port->mapbase, port->membase, port->irq, ourport->rx_irq, ourport->tx_irq, port->uartclk); @@ -1176,6 +1235,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, return 0; } +#ifdef CONFIG_SAMSUNG_CLOCK static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, struct device_attribute *attr, char *buf) @@ -1191,7 +1251,7 @@ static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, } static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); - +#endif /* Device driver serial port probe */ @@ -1229,8 +1289,8 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) ourport->baudclk = ERR_PTR(-EINVAL); ourport->info = ourport->drv_data->info; - ourport->cfg = (pdev->dev.platform_data) ? - (struct s3c2410_uartcfg *)pdev->dev.platform_data : + ourport->cfg = (dev_get_platdata(&pdev->dev)) ? + dev_get_platdata(&pdev->dev) : ourport->drv_data->def_cfg; ourport->port.fifosize = (ourport->info->fifosize) ? @@ -1245,13 +1305,30 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) if (ret < 0) goto probe_err; + if (!s3c24xx_uart_drv.state) { + ret = uart_register_driver(&s3c24xx_uart_drv); + if (ret < 0) { + pr_err("Failed to register Samsung UART driver\n"); + return ret; + } + } + dbg("%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(pdev, &ourport->port); + /* + * Deactivate the clock enabled in s3c24xx_serial_init_port here, + * so that a potential re-enablement through the pm-callback overlaps + * and keeps the clock enabled in this case. + */ + clk_disable_unprepare(ourport->clk); + +#ifdef CONFIG_SAMSUNG_CLOCK ret = device_create_file(&pdev->dev, &dev_attr_clock_source); if (ret < 0) dev_err(&pdev->dev, "failed to add clock source attr.\n"); +#endif ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) @@ -1269,10 +1346,14 @@ static int s3c24xx_serial_remove(struct platform_device *dev) if (port) { s3c24xx_serial_cpufreq_deregister(to_ourport(port)); +#ifdef CONFIG_SAMSUNG_CLOCK device_remove_file(&dev->dev, &dev_attr_clock_source); +#endif uart_remove_one_port(&s3c24xx_uart_drv, port); } + uart_unregister_driver(&s3c24xx_uart_drv); + return 0; } @@ -1304,9 +1385,29 @@ static int s3c24xx_serial_resume(struct device *dev) return 0; } +static int s3c24xx_serial_resume_noirq(struct device *dev) +{ + struct uart_port *port = s3c24xx_dev_to_port(dev); + + if (port) { + /* restore IRQ mask */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + unsigned int uintm = 0xf; + if (tx_enabled(port)) + uintm &= ~S3C64XX_UINTM_TXD_MSK; + if (rx_enabled(port)) + uintm &= ~S3C64XX_UINTM_RXD_MSK; + wr_regl(port, S3C64XX_UINTM, uintm); + } + } + + return 0; +} + static const struct dev_pm_ops s3c24xx_serial_pm_ops = { .suspend = s3c24xx_serial_suspend, .resume = s3c24xx_serial_resume, + .resume_noirq = s3c24xx_serial_resume_noirq, }; #define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops) @@ -1340,6 +1441,13 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; } +static bool +s3c24xx_port_configured(unsigned int ucon) +{ + /* consider the serial port configured if the tx/rx mode set */ + return (ucon & 0xf) != 0; +} + #ifdef CONFIG_CONSOLE_POLL /* * Console polling routines for writing and reading from the uart while @@ -1361,11 +1469,16 @@ static int s3c24xx_serial_get_poll_char(struct uart_port *port) static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c) { - unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ufcon = rd_regl(port, S3C2410_UFCON); + unsigned int ucon = rd_regl(port, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; while (!s3c24xx_serial_console_txrdy(port, ufcon)) cpu_relax(); - wr_regb(cons_uart, S3C2410_UTXH, c); + wr_regb(port, S3C2410_UTXH, c); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1373,16 +1486,23 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, static void s3c24xx_serial_console_putchar(struct uart_port *port, int ch) { - unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + unsigned int ufcon = rd_regl(port, S3C2410_UFCON); + while (!s3c24xx_serial_console_txrdy(port, ufcon)) - barrier(); - wr_regb(cons_uart, S3C2410_UTXH, ch); + cpu_relax(); + wr_regb(port, S3C2410_UTXH, ch); } static void s3c24xx_serial_console_write(struct console *co, const char *s, unsigned int count) { + unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + + /* not possible to xmit on unconfigured port */ + if (!s3c24xx_port_configured(ucon)) + return; + uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); } @@ -1406,9 +1526,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", port, ulcon, ucon, ubrdiv); - if ((ucon & 0xf) != 0) { - /* consider the serial port configured if the tx/rx mode set */ - + if (s3c24xx_port_configured(ucon)) { switch (ulcon & S3C2410_LCON_CSMASK) { case S3C2410_LCON_CS5: *bits = 5; @@ -1652,9 +1770,7 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif -#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \ - defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) || \ - defined(CONFIG_SOC_EXYNOS5440) +#if defined(CONFIG_ARCH_EXYNOS) static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { .info = &(struct s3c24xx_uart_info) { .name = "Samsung Exynos4 UART", @@ -1724,8 +1840,6 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); -#else -#define s3c24xx_uart_dt_match NULL #endif static struct platform_driver samsung_serial_driver = { @@ -1736,32 +1850,11 @@ static struct platform_driver samsung_serial_driver = { .name = "samsung-uart", .owner = THIS_MODULE, .pm = SERIAL_SAMSUNG_PM_OPS, - .of_match_table = s3c24xx_uart_dt_match, + .of_match_table = of_match_ptr(s3c24xx_uart_dt_match), }, }; -/* module initialisation code */ - -static int __init s3c24xx_serial_modinit(void) -{ - int ret; - - ret = uart_register_driver(&s3c24xx_uart_drv); - if (ret < 0) { - pr_err("Failed to register Samsung UART driver\n"); - return ret; - } - - return platform_driver_register(&samsung_serial_driver); -} - -static void __exit s3c24xx_serial_modexit(void) -{ - uart_unregister_driver(&s3c24xx_uart_drv); -} - -module_init(s3c24xx_serial_modinit); -module_exit(s3c24xx_serial_modexit); +module_platform_driver(samsung_serial_driver); MODULE_ALIAS("platform:samsung-uart"); MODULE_DESCRIPTION("Samsung SoC Serial port driver"); diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index 1a4bca3e417..eb071dd19b2 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -1,3 +1,6 @@ +#ifndef __SAMSUNG_H +#define __SAMSUNG_H + /* * Driver for Samsung SoC onboard UARTs. * @@ -63,12 +66,13 @@ struct s3c24xx_uart_port { /* conversion functions */ -#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev) +#define s3c24xx_dev_to_port(__dev) dev_get_drvdata(__dev) /* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) -#define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg))) +#define portaddrl(port, reg) \ + ((unsigned long *)(unsigned long)((port)->membase + (reg))) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) @@ -76,22 +80,4 @@ struct s3c24xx_uart_port { #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) -#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG - -extern void printascii(const char *); - -static void dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsprintf(buff, fmt, va); - va_end(va); - - printascii(buff); -} - -#else -#define dbg(x...) do { } while (0) #endif diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index f76b1688c5c..771f361c47e 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -384,7 +384,7 @@ static void sbd_receive_chars(struct sbd_port *sport) uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag); } - tty_flip_buffer_push(uport->state->port.tty); + tty_flip_buffer_push(&uport->state->port); } static void sbd_transmit_chars(struct sbd_port *sport) @@ -596,7 +596,7 @@ static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, if (termios->c_iflag & INPCK) uport->read_status_mask |= M_DUART_FRM_ERR | M_DUART_PARITY_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) uport->read_status_mask |= M_DUART_RCVD_BRK; uport->ignore_status_mask = 0; diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c new file mode 100644 index 00000000000..1b6a77c4b2c --- /dev/null +++ b/drivers/tty/serial/sc16is7xx.c @@ -0,0 +1,1277 @@ +/* + * SC16IS7xx tty serial driver - Copyright (C) 2014 GridPoint + * Author: Jon Ringle <jringle@gridpoint.com> + * + * Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/uaccess.h> + +#define SC16IS7XX_NAME "sc16is7xx" + +/* SC16IS7XX register definitions */ +#define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */ +#define SC16IS7XX_THR_REG (0x00) /* TX FIFO */ +#define SC16IS7XX_IER_REG (0x01) /* Interrupt enable */ +#define SC16IS7XX_IIR_REG (0x02) /* Interrupt Identification */ +#define SC16IS7XX_FCR_REG (0x02) /* FIFO control */ +#define SC16IS7XX_LCR_REG (0x03) /* Line Control */ +#define SC16IS7XX_MCR_REG (0x04) /* Modem Control */ +#define SC16IS7XX_LSR_REG (0x05) /* Line Status */ +#define SC16IS7XX_MSR_REG (0x06) /* Modem Status */ +#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */ +#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */ +#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */ +#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction + * - only on 75x/76x + */ +#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State + * - only on 75x/76x + */ +#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable + * - only on 75x/76x + */ +#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control + * - only on 75x/76x + */ +#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */ + +/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */ +#define SC16IS7XX_TCR_REG (0x06) /* Transmit control */ +#define SC16IS7XX_TLR_REG (0x07) /* Trigger level */ + +/* Special Register set: Only if ((LCR[7] == 1) && (LCR != 0xBF)) */ +#define SC16IS7XX_DLL_REG (0x00) /* Divisor Latch Low */ +#define SC16IS7XX_DLH_REG (0x01) /* Divisor Latch High */ + +/* Enhanced Register set: Only if (LCR == 0xBF) */ +#define SC16IS7XX_EFR_REG (0x02) /* Enhanced Features */ +#define SC16IS7XX_XON1_REG (0x04) /* Xon1 word */ +#define SC16IS7XX_XON2_REG (0x05) /* Xon2 word */ +#define SC16IS7XX_XOFF1_REG (0x06) /* Xoff1 word */ +#define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */ + +/* IER register bits */ +#define SC16IS7XX_IER_RDI_BIT (1 << 0) /* Enable RX data interrupt */ +#define SC16IS7XX_IER_THRI_BIT (1 << 1) /* Enable TX holding register + * interrupt */ +#define SC16IS7XX_IER_RLSI_BIT (1 << 2) /* Enable RX line status + * interrupt */ +#define SC16IS7XX_IER_MSI_BIT (1 << 3) /* Enable Modem status + * interrupt */ + +/* IER register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_IER_SLEEP_BIT (1 << 4) /* Enable Sleep mode */ +#define SC16IS7XX_IER_XOFFI_BIT (1 << 5) /* Enable Xoff interrupt */ +#define SC16IS7XX_IER_RTSI_BIT (1 << 6) /* Enable nRTS interrupt */ +#define SC16IS7XX_IER_CTSI_BIT (1 << 7) /* Enable nCTS interrupt */ + +/* FCR register bits */ +#define SC16IS7XX_FCR_FIFO_BIT (1 << 0) /* Enable FIFO */ +#define SC16IS7XX_FCR_RXRESET_BIT (1 << 1) /* Reset RX FIFO */ +#define SC16IS7XX_FCR_TXRESET_BIT (1 << 2) /* Reset TX FIFO */ +#define SC16IS7XX_FCR_RXLVLL_BIT (1 << 6) /* RX Trigger level LSB */ +#define SC16IS7XX_FCR_RXLVLH_BIT (1 << 7) /* RX Trigger level MSB */ + +/* FCR register bits - write only if (EFR[4] == 1) */ +#define SC16IS7XX_FCR_TXLVLL_BIT (1 << 4) /* TX Trigger level LSB */ +#define SC16IS7XX_FCR_TXLVLH_BIT (1 << 5) /* TX Trigger level MSB */ + +/* IIR register bits */ +#define SC16IS7XX_IIR_NO_INT_BIT (1 << 0) /* No interrupts pending */ +#define SC16IS7XX_IIR_ID_MASK 0x3e /* Mask for the interrupt ID */ +#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */ +#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */ +#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */ +#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */ +#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state + * - only on 75x/76x + */ +#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */ +#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state + * from active (LOW) + * to inactive (HIGH) + */ +/* LCR register bits */ +#define SC16IS7XX_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define SC16IS7XX_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define SC16IS7XX_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define SC16IS7XX_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define SC16IS7XX_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define SC16IS7XX_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define SC16IS7XX_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define SC16IS7XX_LCR_DLAB_BIT (1 << 7) /* Divisor Latch enable */ +#define SC16IS7XX_LCR_WORD_LEN_5 (0x00) +#define SC16IS7XX_LCR_WORD_LEN_6 (0x01) +#define SC16IS7XX_LCR_WORD_LEN_7 (0x02) +#define SC16IS7XX_LCR_WORD_LEN_8 (0x03) +#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special + * reg set */ +#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced + * reg set */ + +/* MCR register bits */ +#define SC16IS7XX_MCR_DTR_BIT (1 << 0) /* DTR complement + * - only on 75x/76x + */ +#define SC16IS7XX_MCR_RTS_BIT (1 << 1) /* RTS complement */ +#define SC16IS7XX_MCR_TCRTLR_BIT (1 << 2) /* TCR/TLR register enable */ +#define SC16IS7XX_MCR_LOOP_BIT (1 << 4) /* Enable loopback test mode */ +#define SC16IS7XX_MCR_XONANY_BIT (1 << 5) /* Enable Xon Any + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_IRDA_BIT (1 << 6) /* Enable IrDA mode + * - write enabled + * if (EFR[4] == 1) + */ +#define SC16IS7XX_MCR_CLKSEL_BIT (1 << 7) /* Divide clock by 4 + * - write enabled + * if (EFR[4] == 1) + */ + +/* LSR register bits */ +#define SC16IS7XX_LSR_DR_BIT (1 << 0) /* Receiver data ready */ +#define SC16IS7XX_LSR_OE_BIT (1 << 1) /* Overrun Error */ +#define SC16IS7XX_LSR_PE_BIT (1 << 2) /* Parity Error */ +#define SC16IS7XX_LSR_FE_BIT (1 << 3) /* Frame Error */ +#define SC16IS7XX_LSR_BI_BIT (1 << 4) /* Break Interrupt */ +#define SC16IS7XX_LSR_BRK_ERROR_MASK 0x1E /* BI, FE, PE, OE bits */ +#define SC16IS7XX_LSR_THRE_BIT (1 << 5) /* TX holding register empty */ +#define SC16IS7XX_LSR_TEMT_BIT (1 << 6) /* Transmitter empty */ +#define SC16IS7XX_LSR_FIFOE_BIT (1 << 7) /* Fifo Error */ + +/* MSR register bits */ +#define SC16IS7XX_MSR_DCTS_BIT (1 << 0) /* Delta CTS Clear To Send */ +#define SC16IS7XX_MSR_DDSR_BIT (1 << 1) /* Delta DSR Data Set Ready + * or (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DRI_BIT (1 << 2) /* Delta RI Ring Indicator + * or (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DCD_BIT (1 << 3) /* Delta CD Carrier Detect + * or (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CTS_BIT (1 << 0) /* CTS */ +#define SC16IS7XX_MSR_DSR_BIT (1 << 1) /* DSR (IO4) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_RI_BIT (1 << 2) /* RI (IO7) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_CD_BIT (1 << 3) /* CD (IO6) + * - only on 75x/76x + */ +#define SC16IS7XX_MSR_DELTA_MASK 0x0F /* Any of the delta bits! */ + +/* + * TCR register bits + * TCR trigger levels are available from 0 to 60 characters with a granularity + * of four. + * The programmer must program the TCR such that TCR[3:0] > TCR[7:4]. There is + * no built-in hardware check to make sure this condition is met. Also, the TCR + * must be programmed with this condition before auto RTS or software flow + * control is enabled to avoid spurious operation of the device. + */ +#define SC16IS7XX_TCR_RX_HALT(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TCR_RX_RESUME(words) ((((words) / 4) & 0x0f) << 4) + +/* + * TLR register bits + * If TLR[3:0] or TLR[7:4] are logical 0, the selectable trigger levels via the + * FIFO Control Register (FCR) are used for the transmit and receive FIFO + * trigger levels. Trigger levels from 4 characters to 60 characters are + * available with a granularity of four. + * + * When the trigger level setting in TLR is zero, the SC16IS740/750/760 uses the + * trigger level setting defined in FCR. If TLR has non-zero trigger level value + * the trigger level defined in FCR is discarded. This applies to both transmit + * FIFO and receive FIFO trigger level setting. + * + * When TLR is used for RX trigger level control, FCR[7:6] should be left at the + * default state, that is, '00'. + */ +#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0) +#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4) + +/* IOControl register bits (Only 750/760) */ +#define SC16IS7XX_IOCONTROL_LATCH_BIT (1 << 0) /* Enable input latching */ +#define SC16IS7XX_IOCONTROL_GPIO_BIT (1 << 1) /* Enable GPIO[7:4] */ +#define SC16IS7XX_IOCONTROL_SRESET_BIT (1 << 3) /* Software Reset */ + +/* EFCR register bits */ +#define SC16IS7XX_EFCR_9BIT_MODE_BIT (1 << 0) /* Enable 9-bit or Multidrop + * mode (RS485) */ +#define SC16IS7XX_EFCR_RXDISABLE_BIT (1 << 1) /* Disable receiver */ +#define SC16IS7XX_EFCR_TXDISABLE_BIT (1 << 2) /* Disable transmitter */ +#define SC16IS7XX_EFCR_AUTO_RS485_BIT (1 << 4) /* Auto RS485 RTS direction */ +#define SC16IS7XX_EFCR_RTS_INVERT_BIT (1 << 5) /* RTS output inversion */ +#define SC16IS7XX_EFCR_IRDA_MODE_BIT (1 << 7) /* IrDA mode + * 0 = rate upto 115.2 kbit/s + * - Only 750/760 + * 1 = rate upto 1.152 Mbit/s + * - Only 760 + */ + +/* EFR register bits */ +#define SC16IS7XX_EFR_AUTORTS_BIT (1 << 6) /* Auto RTS flow ctrl enable */ +#define SC16IS7XX_EFR_AUTOCTS_BIT (1 << 7) /* Auto CTS flow ctrl enable */ +#define SC16IS7XX_EFR_XOFF2_DETECT_BIT (1 << 5) /* Enable Xoff2 detection */ +#define SC16IS7XX_EFR_ENABLE_BIT (1 << 4) /* Enable enhanced functions + * and writing to IER[7:4], + * FCR[5:4], MCR[7:5] + */ +#define SC16IS7XX_EFR_SWFLOW3_BIT (1 << 3) /* SWFLOW bit 3 */ +#define SC16IS7XX_EFR_SWFLOW2_BIT (1 << 2) /* SWFLOW bit 2 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no transmitter flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ +#define SC16IS7XX_EFR_SWFLOW1_BIT (1 << 1) /* SWFLOW bit 2 */ +#define SC16IS7XX_EFR_SWFLOW0_BIT (1 << 0) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * 10 -> receiver compares + * XON1 and XOFF1 + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* Misc definitions */ +#define SC16IS7XX_FIFO_SIZE (64) +#define SC16IS7XX_REG_SHIFT 2 + +struct sc16is7xx_devtype { + char name[10]; + int nr_gpio; + int nr_uart; +}; + +struct sc16is7xx_one { + struct uart_port port; + struct work_struct tx_work; + struct work_struct md_work; + + struct serial_rs485 rs485; +}; + +struct sc16is7xx_port { + struct uart_driver uart; + struct sc16is7xx_devtype *devtype; + struct regmap *regmap; + struct mutex mutex; + struct clk *clk; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; +#endif + unsigned char buf[SC16IS7XX_FIFO_SIZE]; + struct sc16is7xx_one p[0]; +}; + +#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e))) + +static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; + + regmap_read(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, &val); + + return val; +} + +static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, val); +} + +static void sc16is7xx_port_update(struct uart_port *port, u8 reg, + u8 mask, u8 val) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, + (reg << SC16IS7XX_REG_SHIFT) | port->line, + mask, val); +} + + +static void sc16is7xx_power(struct uart_port *port, int on) +{ + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_SLEEP_BIT, + on ? 0 : SC16IS7XX_IER_SLEEP_BIT); +} + +static const struct sc16is7xx_devtype sc16is74x_devtype = { + .name = "SC16IS74X", + .nr_gpio = 0, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is750_devtype = { + .name = "SC16IS750", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is752_devtype = { + .name = "SC16IS752", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static const struct sc16is7xx_devtype sc16is760_devtype = { + .name = "SC16IS760", + .nr_gpio = 8, + .nr_uart = 1, +}; + +static const struct sc16is7xx_devtype sc16is762_devtype = { + .name = "SC16IS762", + .nr_gpio = 8, + .nr_uart = 2, +}; + +static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + case SC16IS7XX_IIR_REG: + case SC16IS7XX_LSR_REG: + case SC16IS7XX_MSR_REG: + case SC16IS7XX_TXLVL_REG: + case SC16IS7XX_RXLVL_REG: + case SC16IS7XX_IOSTATE_REG: + return true; + default: + break; + } + + return false; +} + +static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg) +{ + switch (reg >> SC16IS7XX_REG_SHIFT) { + case SC16IS7XX_RHR_REG: + return true; + default: + break; + } + + return false; +} + +static int sc16is7xx_set_baud(struct uart_port *port, int baud) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + u8 lcr; + u8 prescaler = 0; + unsigned long clk = port->uartclk, div = clk / 16 / baud; + + if (div > 0xffff) { + prescaler = SC16IS7XX_MCR_CLKSEL_BIT; + div /= 4; + } + + lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Enable enhanced features */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_CLKSEL_BIT, + prescaler); + + /* Open the LCR divisors for configuration */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_A); + + /* Write the new divisor */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256); + sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256); + regcache_cache_bypass(s->regmap, false); + + /* Put LCR back to the normal mode */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + return DIV_ROUND_CLOSEST(clk / 16, div); +} + +static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, + unsigned int iir) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lsr = 0, ch, flag, bytes_read, i; + bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false; + + if (unlikely(rxlen >= sizeof(s->buf))) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun: %d\n", + port->line, rxlen); + port->icount.buf_overrun++; + /* Ensure sanity of RX level */ + rxlen = sizeof(s->buf); + } + + while (rxlen) { + /* Only read lsr if there are possible errors in FIFO */ + if (read_lsr) { + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_FIFOE_BIT)) + read_lsr = false; /* No errors left in FIFO */ + } else + lsr = 0; + + if (read_lsr) { + s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); + bytes_read = 1; + } else { + regcache_cache_bypass(s->regmap, true); + regmap_raw_read(s->regmap, SC16IS7XX_RHR_REG, + s->buf, rxlen); + regcache_cache_bypass(s->regmap, false); + bytes_read = rxlen; + } + + lsr &= SC16IS7XX_LSR_BRK_ERROR_MASK; + + port->icount.rx++; + flag = TTY_NORMAL; + + if (unlikely(lsr)) { + if (lsr & SC16IS7XX_LSR_BI_BIT) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (lsr & SC16IS7XX_LSR_PE_BIT) + port->icount.parity++; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + port->icount.frame++; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + port->icount.overrun++; + + lsr &= port->read_status_mask; + if (lsr & SC16IS7XX_LSR_BI_BIT) + flag = TTY_BREAK; + else if (lsr & SC16IS7XX_LSR_PE_BIT) + flag = TTY_PARITY; + else if (lsr & SC16IS7XX_LSR_FE_BIT) + flag = TTY_FRAME; + else if (lsr & SC16IS7XX_LSR_OE_BIT) + flag = TTY_OVERRUN; + } + + for (i = 0; i < bytes_read; ++i) { + ch = s->buf[i]; + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (lsr & port->ignore_status_mask) + continue; + + uart_insert_char(port, lsr, SC16IS7XX_LSR_OE_BIT, ch, + flag); + } + rxlen -= bytes_read; + } + + tty_flip_buffer_push(&port->state->port); +} + +static void sc16is7xx_handle_tx(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send, i; + + if (unlikely(port->x_char)) { + sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + /* Get length of data pending in circular buffer */ + to_send = uart_circ_chars_pending(xmit); + if (likely(to_send)) { + /* Limit to size of TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + to_send = (to_send > txlen) ? txlen : to_send; + + /* Add data to send */ + port->icount.tx += to_send; + + /* Convert to linear buffer */ + for (i = 0; i < to_send; ++i) { + s->buf[i] = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + regcache_cache_bypass(s->regmap, true); + regmap_raw_write(s->regmap, SC16IS7XX_THR_REG, s->buf, to_send); + regcache_cache_bypass(s->regmap, false); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +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; + + iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); + if (iir & SC16IS7XX_IIR_NO_INT_BIT) + break; + + iir &= SC16IS7XX_IIR_ID_MASK; + + switch (iir) { + case SC16IS7XX_IIR_RDI_SRC: + case SC16IS7XX_IIR_RLSE_SRC: + case SC16IS7XX_IIR_RTOI_SRC: + case SC16IS7XX_IIR_XOFFI_SRC: + rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG); + 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_CTS_BIT)); + break; + case SC16IS7XX_IIR_THRI_SRC: + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(port); + mutex_unlock(&s->mutex); + break; + default: + dev_err_ratelimited(port->dev, + "Port %i: Unexpected interrupt: %x", + port->line, iir); + break; + } + } while (1); +} + +static irqreturn_t sc16is7xx_ist(int irq, void *dev_id) +{ + struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id; + int i; + + for (i = 0; i < s->uart.nr; ++i) + sc16is7xx_port_irq(s, i); + + return IRQ_HANDLED; +} + +static void sc16is7xx_wq_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, tx_work); + struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev); + + mutex_lock(&s->mutex); + sc16is7xx_handle_tx(&one->port); + mutex_unlock(&s->mutex); +} + +static void sc16is7xx_stop_tx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + struct circ_buf *xmit = &one->port.state->xmit; + + /* handle rs485 */ + if (one->rs485.flags & SER_RS485_ENABLED) { + /* do nothing if current tx not yet completed */ + int lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + if (!(lsr & SC16IS7XX_LSR_TEMT_BIT)) + return; + + if (uart_circ_empty(xmit) && + (one->rs485.delay_rts_after_send > 0)) + mdelay(one->rs485.delay_rts_after_send); + } + + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_IER_THRI_BIT, + 0); +} + +static void sc16is7xx_stop_rx(struct uart_port* port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->port.read_status_mask &= ~SC16IS7XX_LSR_DR_BIT; + sc16is7xx_port_update(port, SC16IS7XX_IER_REG, + SC16IS7XX_LSR_DR_BIT, + 0); +} + +static void sc16is7xx_start_tx(struct uart_port *port) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + /* handle rs485 */ + if ((one->rs485.flags & SER_RS485_ENABLED) && + (one->rs485.delay_rts_before_send > 0)) { + mdelay(one->rs485.delay_rts_before_send); + } + + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); +} + +static unsigned int sc16is7xx_tx_empty(struct uart_port *port) +{ + unsigned int lvl, lsr; + + lvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG); + + return ((lsr & SC16IS7XX_LSR_THRE_BIT) && !lvl) ? TIOCSER_TEMT : 0; +} + +static unsigned int sc16is7xx_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void sc16is7xx_md_proc(struct work_struct *ws) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(ws, md_work); + + sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_LOOP_BIT, + (one->port.mctrl & TIOCM_LOOP) ? + SC16IS7XX_MCR_LOOP_BIT : 0); +} + +static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + schedule_work(&one->md_work); +} + +static void sc16is7xx_break_ctl(struct uart_port *port, int break_state) +{ + sc16is7xx_port_update(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_TXBREAK_BIT, + break_state ? SC16IS7XX_LCR_TXBREAK_BIT : 0); +} + +static void sc16is7xx_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int lcr, flow = 0; + int baud; + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Word size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = SC16IS7XX_LCR_WORD_LEN_5; + break; + case CS6: + lcr = SC16IS7XX_LCR_WORD_LEN_6; + break; + case CS7: + lcr = SC16IS7XX_LCR_WORD_LEN_7; + break; + case CS8: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + break; + default: + lcr = SC16IS7XX_LCR_WORD_LEN_8; + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* Parity */ + if (termios->c_cflag & PARENB) { + lcr |= SC16IS7XX_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + lcr |= SC16IS7XX_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= SC16IS7XX_LCR_STOPLEN_BIT; /* 2 stops */ + + /* Set read status mask */ + port->read_status_mask = SC16IS7XX_LSR_OE_BIT; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SC16IS7XX_LSR_PE_BIT | + SC16IS7XX_LSR_FE_BIT; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SC16IS7XX_LSR_BI_BIT; + + /* Set status ignore mask */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) + port->ignore_status_mask |= SC16IS7XX_LSR_BI_BIT; + if (!(termios->c_cflag & CREAD)) + port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK; + + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + /* Configure flow control */ + regcache_cache_bypass(s->regmap, true); + sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]); + sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]); + if (termios->c_cflag & CRTSCTS) + flow |= SC16IS7XX_EFR_AUTOCTS_BIT | + SC16IS7XX_EFR_AUTORTS_BIT; + if (termios->c_iflag & IXON) + flow |= SC16IS7XX_EFR_SWFLOW3_BIT; + if (termios->c_iflag & IXOFF) + flow |= SC16IS7XX_EFR_SWFLOW1_BIT; + + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, flow); + regcache_cache_bypass(s->regmap, false); + + /* Update LCR register */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); + + /* Get baud rate generator configuration */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 4 / 0xffff, + port->uartclk / 16); + + /* Setup baudrate generator */ + baud = sc16is7xx_set_baud(port, baud); + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); +} + +#if defined(TIOCSRS485) && defined(TIOCGRS485) +static void sc16is7xx_config_rs485(struct uart_port *port, + struct serial_rs485 *rs485) +{ + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); + + one->rs485 = *rs485; + + if (one->rs485.flags & SER_RS485_ENABLED) { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + SC16IS7XX_EFCR_AUTO_RS485_BIT); + } else { + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_AUTO_RS485_BIT, + 0); + } +} +#endif + +static int sc16is7xx_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ +#if defined(TIOCSRS485) && defined(TIOCGRS485) + struct serial_rs485 rs485; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485, (void __user *)arg, sizeof(rs485))) + return -EFAULT; + + sc16is7xx_config_rs485(port, &rs485); + return 0; + case TIOCGRS485: + if (copy_to_user((void __user *)arg, + &(to_sc16is7xx_one(port, port)->rs485), + sizeof(rs485))) + return -EFAULT; + return 0; + default: + break; + } +#endif + + return -ENOIOCTLCMD; +} + +static int sc16is7xx_startup(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + unsigned int val; + + sc16is7xx_power(port, 1); + + /* Reset FIFOs*/ + val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT; + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val); + udelay(5); + sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, + SC16IS7XX_FCR_FIFO_BIT); + + /* Enable EFR */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, + SC16IS7XX_LCR_CONF_MODE_B); + + regcache_cache_bypass(s->regmap, true); + + /* Enable write access to enhanced features and internal clock div */ + sc16is7xx_port_write(port, SC16IS7XX_EFR_REG, + SC16IS7XX_EFR_ENABLE_BIT); + + /* Enable TCR/TLR */ + sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, + SC16IS7XX_MCR_TCRTLR_BIT, + SC16IS7XX_MCR_TCRTLR_BIT); + + /* Configure flow control levels */ + /* Flow control halt level 48, resume level 24 */ + sc16is7xx_port_write(port, SC16IS7XX_TCR_REG, + SC16IS7XX_TCR_RX_RESUME(24) | + SC16IS7XX_TCR_RX_HALT(48)); + + regcache_cache_bypass(s->regmap, false); + + /* Now, initialize the UART */ + sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8); + + /* Enable the Rx and Tx FIFO */ + sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT, + 0); + + /* Enable RX, TX, CTS change interrupts */ + val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_THRI_BIT | + SC16IS7XX_IER_CTSI_BIT; + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val); + + return 0; +} + +static void sc16is7xx_shutdown(struct uart_port *port) +{ + /* Disable all interrupts */ + sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + + sc16is7xx_power(port, 0); +} + +static const char *sc16is7xx_type(struct uart_port *port) +{ + struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + + return (port->type == PORT_SC16IS7XX) ? s->devtype->name : NULL; +} + +static int sc16is7xx_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +static void sc16is7xx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SC16IS7XX; +} + +static int sc16is7xx_verify_port(struct uart_port *port, + struct serial_struct *s) +{ + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_SC16IS7XX)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; + + return 0; +} + +static void sc16is7xx_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0); +} + +static void sc16is7xx_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops sc16is7xx_ops = { + .tx_empty = sc16is7xx_tx_empty, + .set_mctrl = sc16is7xx_set_mctrl, + .get_mctrl = sc16is7xx_get_mctrl, + .stop_tx = sc16is7xx_stop_tx, + .start_tx = sc16is7xx_start_tx, + .stop_rx = sc16is7xx_stop_rx, + .enable_ms = sc16is7xx_null_void, + .break_ctl = sc16is7xx_break_ctl, + .startup = sc16is7xx_startup, + .shutdown = sc16is7xx_shutdown, + .set_termios = sc16is7xx_set_termios, + .type = sc16is7xx_type, + .request_port = sc16is7xx_request_port, + .release_port = sc16is7xx_null_void, + .config_port = sc16is7xx_config_port, + .verify_port = sc16is7xx_verify_port, + .ioctl = sc16is7xx_ioctl, + .pm = sc16is7xx_pm, +}; + +#ifdef CONFIG_GPIOLIB +static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int val; + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG); + + return !!(val & BIT(offset)); +} + +static void sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); +} + +static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0); + + return 0; +} + +static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int val) +{ + struct sc16is7xx_port *s = container_of(chip, struct sc16is7xx_port, + gpio); + struct uart_port *port = &s->p[0].port; + + sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset), + val ? BIT(offset) : 0); + sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), + BIT(offset)); + + return 0; +} +#endif + +static int sc16is7xx_probe(struct device *dev, + struct sc16is7xx_devtype *devtype, + struct regmap *regmap, int irq, unsigned long flags) +{ + unsigned long freq, *pfreq = dev_get_platdata(dev); + struct clk *clk; + int i, ret; + struct sc16is7xx_port *s; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Alloc port structure */ + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct sc16is7xx_one) * devtype->nr_uart, + GFP_KERNEL); + if (!s) { + dev_err(dev, "Error allocating port structure\n"); + return -ENOMEM; + } + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + if (pfreq) + freq = *pfreq; + else + return PTR_ERR(clk); + } else { + freq = clk_get_rate(clk); + } + + s->regmap = regmap; + s->devtype = devtype; + dev_set_drvdata(dev, s); + + /* Register UART driver */ + s->uart.owner = THIS_MODULE; + s->uart.dev_name = "ttySC"; + s->uart.nr = devtype->nr_uart; + ret = uart_register_driver(&s->uart); + if (ret) { + dev_err(dev, "Registering UART driver failed\n"); + goto out_clk; + } + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) { + /* Setup GPIO cotroller */ + s->gpio.owner = THIS_MODULE; + s->gpio.dev = dev; + s->gpio.label = dev_name(dev); + s->gpio.direction_input = sc16is7xx_gpio_direction_input; + s->gpio.get = sc16is7xx_gpio_get; + s->gpio.direction_output = sc16is7xx_gpio_direction_output; + s->gpio.set = sc16is7xx_gpio_set; + s->gpio.base = -1; + s->gpio.ngpio = devtype->nr_gpio; + s->gpio.can_sleep = 1; + ret = gpiochip_add(&s->gpio); + if (ret) + goto out_uart; + } +#endif + + mutex_init(&s->mutex); + + for (i = 0; i < devtype->nr_uart; ++i) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_SC16IS7XX; + s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE; + s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.uartclk = freq; + s->p[i].port.ops = &sc16is7xx_ops; + /* Disable all interrupts */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0); + /* Disable TX/RX */ + sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG, + SC16IS7XX_EFCR_RXDISABLE_BIT | + SC16IS7XX_EFCR_TXDISABLE_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, sc16is7xx_wq_proc); + /* Initialize queue for changing mode */ + INIT_WORK(&s->p[i].md_work, sc16is7xx_md_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + sc16is7xx_power(&s->p[i].port, 0); + } + + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_ist, + IRQF_ONESHOT | flags, dev_name(dev), s); + if (!ret) + return 0; + + mutex_destroy(&s->mutex); + +#ifdef CONFIG_GPIOLIB + if (devtype->nr_gpio) + WARN_ON(gpiochip_remove(&s->gpio)); + +out_uart: +#endif + uart_unregister_driver(&s->uart); + +out_clk: + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static int sc16is7xx_remove(struct device *dev) +{ + struct sc16is7xx_port *s = dev_get_drvdata(dev); + int i, ret = 0; + +#ifdef CONFIG_GPIOLIB + if (s->devtype->nr_gpio) { + ret = gpiochip_remove(&s->gpio); + if (ret) + return ret; + } +#endif + + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + cancel_work_sync(&s->p[i].md_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + sc16is7xx_power(&s->p[i].port, 0); + } + + mutex_destroy(&s->mutex); + uart_unregister_driver(&s->uart); + if (!IS_ERR(s->clk)) + clk_disable_unprepare(s->clk); + + return ret; +} + +static const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = { + { .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, }, + { .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, }, + { .compatible = "nxp,sc16is752", .data = &sc16is752_devtype, }, + { .compatible = "nxp,sc16is760", .data = &sc16is760_devtype, }, + { .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids); + +static struct regmap_config regcfg = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = sc16is7xx_regmap_volatile, + .precious_reg = sc16is7xx_regmap_precious, +}; + +static int sc16is7xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct sc16is7xx_devtype *devtype; + unsigned long flags = 0; + struct regmap *regmap; + + if (i2c->dev.of_node) { + const struct of_device_id *of_id = + of_match_device(sc16is7xx_dt_ids, &i2c->dev); + + devtype = (struct sc16is7xx_devtype *)of_id->data; + } else { + devtype = (struct sc16is7xx_devtype *)id->driver_data; + flags = IRQF_TRIGGER_FALLING; + } + + regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) | + (devtype->nr_uart - 1); + regmap = devm_regmap_init_i2c(i2c, ®cfg); + + return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq, flags); +} + +static int sc16is7xx_i2c_remove(struct i2c_client *client) +{ + return sc16is7xx_remove(&client->dev); +} + +static const struct i2c_device_id sc16is7xx_i2c_id_table[] = { + { "sc16is74x", (kernel_ulong_t)&sc16is74x_devtype, }, + { "sc16is750", (kernel_ulong_t)&sc16is750_devtype, }, + { "sc16is752", (kernel_ulong_t)&sc16is752_devtype, }, + { "sc16is760", (kernel_ulong_t)&sc16is760_devtype, }, + { "sc16is762", (kernel_ulong_t)&sc16is762_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc16is7xx_i2c_id_table); + +static struct i2c_driver sc16is7xx_i2c_uart_driver = { + .driver = { + .name = SC16IS7XX_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sc16is7xx_dt_ids), + }, + .probe = sc16is7xx_i2c_probe, + .remove = sc16is7xx_i2c_remove, + .id_table = sc16is7xx_i2c_id_table, +}; +module_i2c_driver(sc16is7xx_i2c_uart_driver); +MODULE_ALIAS("i2c:sc16is7xx"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>"); +MODULE_DESCRIPTION("SC16IS7XX serial driver"); diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c deleted file mode 100644 index aced1dd923d..00000000000 --- a/drivers/tty/serial/sc26xx.c +++ /dev/null @@ -1,749 +0,0 @@ -/* - * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices. - * - * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de) - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/major.h> -#include <linux/circ_buf.h> -#include <linux/serial.h> -#include <linux/sysrq.h> -#include <linux/console.h> -#include <linux/spinlock.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/irq.h> -#include <linux/io.h> - -#warning "Please try migrate to use new driver SCCNXP and report the status" \ - "in the linux-serial mailing list." - -#if defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include <linux/serial_core.h> - -#define SC26XX_MAJOR 204 -#define SC26XX_MINOR_START 205 -#define SC26XX_NR 2 - -struct uart_sc26xx_port { - struct uart_port port[2]; - u8 dsr_mask[2]; - u8 cts_mask[2]; - u8 dcd_mask[2]; - u8 ri_mask[2]; - u8 dtr_mask[2]; - u8 rts_mask[2]; - u8 imr; -}; - -/* register common to both ports */ -#define RD_ISR 0x14 -#define RD_IPR 0x34 - -#define WR_ACR 0x10 -#define WR_IMR 0x14 -#define WR_OPCR 0x34 -#define WR_OPR_SET 0x38 -#define WR_OPR_CLR 0x3C - -/* access common register */ -#define READ_SC(p, r) readb((p)->membase + RD_##r) -#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r) - -/* register per port */ -#define RD_PORT_MRx 0x00 -#define RD_PORT_SR 0x04 -#define RD_PORT_RHR 0x0c - -#define WR_PORT_MRx 0x00 -#define WR_PORT_CSR 0x04 -#define WR_PORT_CR 0x08 -#define WR_PORT_THR 0x0c - -/* SR bits */ -#define SR_BREAK (1 << 7) -#define SR_FRAME (1 << 6) -#define SR_PARITY (1 << 5) -#define SR_OVERRUN (1 << 4) -#define SR_TXRDY (1 << 2) -#define SR_RXRDY (1 << 0) - -#define CR_RES_MR (1 << 4) -#define CR_RES_RX (2 << 4) -#define CR_RES_TX (3 << 4) -#define CR_STRT_BRK (6 << 4) -#define CR_STOP_BRK (7 << 4) -#define CR_DIS_TX (1 << 3) -#define CR_ENA_TX (1 << 2) -#define CR_DIS_RX (1 << 1) -#define CR_ENA_RX (1 << 0) - -/* ISR bits */ -#define ISR_RXRDYB (1 << 5) -#define ISR_TXRDYB (1 << 4) -#define ISR_RXRDYA (1 << 1) -#define ISR_TXRDYA (1 << 0) - -/* IMR bits */ -#define IMR_RXRDY (1 << 1) -#define IMR_TXRDY (1 << 0) - -/* access port register */ -static inline u8 read_sc_port(struct uart_port *p, u8 reg) -{ - return readb(p->membase + p->line * 0x20 + reg); -} - -static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val) -{ - writeb(val, p->membase + p->line * 0x20 + reg); -} - -#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r) -#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v) - -static void sc26xx_enable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr |= mask << (line * 4); - WRITE_SC(port, IMR, up->imr); -} - -static void sc26xx_disable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr &= ~(mask << (line * 4)); - WRITE_SC(port, IMR, up->imr); -} - -static struct tty_struct *receive_chars(struct uart_port *port) -{ - struct tty_struct *tty = NULL; - int limit = 10000; - unsigned char ch; - char flag; - u8 status; - - if (port->state != NULL) /* Unopened serial console */ - tty = port->state->port.tty; - - while (limit-- > 0) { - status = READ_SC_PORT(port, SR); - if (!(status & SR_RXRDY)) - break; - ch = READ_SC_PORT(port, RHR); - - flag = TTY_NORMAL; - port->icount.rx++; - - if (unlikely(status & (SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN))) { - if (status & SR_BREAK) { - status &= ~(SR_PARITY | SR_FRAME); - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (status & SR_PARITY) - port->icount.parity++; - else if (status & SR_FRAME) - port->icount.frame++; - if (status & SR_OVERRUN) - port->icount.overrun++; - - status &= port->read_status_mask; - if (status & SR_BREAK) - flag = TTY_BREAK; - else if (status & SR_PARITY) - flag = TTY_PARITY; - else if (status & SR_FRAME) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - continue; - - if (status & port->ignore_status_mask) - continue; - - tty_insert_flip_char(tty, ch, flag); - } - return tty; -} - -static void transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit; - - if (!port->state) - return; - - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - sc26xx_disable_irq(port, IMR_TXRDY); - return; - } - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) - break; - - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static irqreturn_t sc26xx_interrupt(int irq, void *dev_id) -{ - struct uart_sc26xx_port *up = dev_id; - struct tty_struct *tty; - unsigned long flags; - u8 isr; - - spin_lock_irqsave(&up->port[0].lock, flags); - - tty = NULL; - isr = READ_SC(&up->port[0], ISR); - if (isr & ISR_TXRDYA) - transmit_chars(&up->port[0]); - if (isr & ISR_RXRDYA) - tty = receive_chars(&up->port[0]); - - spin_unlock(&up->port[0].lock); - - if (tty) - tty_flip_buffer_push(tty); - - spin_lock(&up->port[1].lock); - - tty = NULL; - if (isr & ISR_TXRDYB) - transmit_chars(&up->port[1]); - if (isr & ISR_RXRDYB) - tty = receive_chars(&up->port[1]); - - spin_unlock_irqrestore(&up->port[1].lock, flags); - - if (tty) - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - -/* port->lock is not held. */ -static unsigned int sc26xx_tx_empty(struct uart_port *port) -{ - return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0; -} - -/* port->lock held by caller. */ -static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - if (up->dtr_mask[line]) { - if (mctrl & TIOCM_DTR) - WRITE_SC(port, OPR_SET, up->dtr_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->dtr_mask[line]); - } - if (up->rts_mask[line]) { - if (mctrl & TIOCM_RTS) - WRITE_SC(port, OPR_SET, up->rts_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->rts_mask[line]); - } -} - -/* port->lock is held by caller and interrupts are disabled. */ -static unsigned int sc26xx_get_mctrl(struct uart_port *port) -{ - struct uart_sc26xx_port *up; - int line = port->line; - unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - u8 ipr; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - ipr = READ_SC(port, IPR) ^ 0xff; - - if (up->dsr_mask[line]) { - mctrl &= ~TIOCM_DSR; - mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0; - } - if (up->cts_mask[line]) { - mctrl &= ~TIOCM_CTS; - mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0; - } - if (up->dcd_mask[line]) { - mctrl &= ~TIOCM_CAR; - mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0; - } - if (up->ri_mask[line]) { - mctrl &= ~TIOCM_RNG; - mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0; - } - return mctrl; -} - -/* port->lock held by caller. */ -static void sc26xx_stop_tx(struct uart_port *port) -{ - return; -} - -/* port->lock held by caller. */ -static void sc26xx_start_tx(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { - sc26xx_enable_irq(port, IMR_TXRDY); - break; - } - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } -} - -/* port->lock held by caller. */ -static void sc26xx_stop_rx(struct uart_port *port) -{ -} - -/* port->lock held by caller. */ -static void sc26xx_enable_ms(struct uart_port *port) -{ -} - -/* port->lock is not held. */ -static void sc26xx_break_ctl(struct uart_port *port, int break_state) -{ - if (break_state == -1) - WRITE_SC_PORT(port, CR, CR_STRT_BRK); - else - WRITE_SC_PORT(port, CR, CR_STOP_BRK); -} - -/* port->lock is not held. */ -static int sc26xx_startup(struct uart_port *port) -{ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - WRITE_SC(port, OPCR, 0); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - /* start rx/tx */ - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - - /* enable irqs */ - sc26xx_enable_irq(port, IMR_RXRDY); - return 0; -} - -/* port->lock is not held. */ -static void sc26xx_shutdown(struct uart_port *port) -{ - /* disable interrupst */ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - - /* stop tx/rx */ - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); -} - -/* port->lock is not held. */ -static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - unsigned int quot = uart_get_divisor(port, baud); - unsigned int iflag, cflag; - unsigned long flags; - u8 mr1, mr2, csr; - - spin_lock_irqsave(&port->lock, flags); - - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); - - iflag = termios->c_iflag; - cflag = termios->c_cflag; - - port->read_status_mask = SR_OVERRUN; - if (iflag & INPCK) - port->read_status_mask |= SR_PARITY | SR_FRAME; - if (iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SR_BREAK; - - port->ignore_status_mask = 0; - if (iflag & IGNBRK) - port->ignore_status_mask |= SR_BREAK; - if ((cflag & CREAD) == 0) - port->ignore_status_mask |= SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN; - - switch (cflag & CSIZE) { - case CS5: - mr1 = 0x00; - break; - case CS6: - mr1 = 0x01; - break; - case CS7: - mr1 = 0x02; - break; - default: - case CS8: - mr1 = 0x03; - break; - } - mr2 = 0x07; - if (cflag & CSTOPB) - mr2 = 0x0f; - if (cflag & PARENB) { - if (cflag & PARODD) - mr1 |= (1 << 2); - } else - mr1 |= (2 << 3); - - switch (baud) { - case 50: - csr = 0x00; - break; - case 110: - csr = 0x11; - break; - case 134: - csr = 0x22; - break; - case 200: - csr = 0x33; - break; - case 300: - csr = 0x44; - break; - case 600: - csr = 0x55; - break; - case 1200: - csr = 0x66; - break; - case 2400: - csr = 0x88; - break; - case 4800: - csr = 0x99; - break; - default: - case 9600: - csr = 0xbb; - break; - case 19200: - csr = 0xcc; - break; - } - - WRITE_SC_PORT(port, CR, CR_RES_MR); - WRITE_SC_PORT(port, MRx, mr1); - WRITE_SC_PORT(port, MRx, mr2); - - WRITE_SC(port, ACR, 0x80); - WRITE_SC_PORT(port, CSR, csr); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - /* XXX */ - uart_update_timeout(port, cflag, - (port->uartclk / (16 * quot))); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *sc26xx_type(struct uart_port *port) -{ - return "SC26XX"; -} - -static void sc26xx_release_port(struct uart_port *port) -{ -} - -static int sc26xx_request_port(struct uart_port *port) -{ - return 0; -} - -static void sc26xx_config_port(struct uart_port *port, int flags) -{ -} - -static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops sc26xx_ops = { - .tx_empty = sc26xx_tx_empty, - .set_mctrl = sc26xx_set_mctrl, - .get_mctrl = sc26xx_get_mctrl, - .stop_tx = sc26xx_stop_tx, - .start_tx = sc26xx_start_tx, - .stop_rx = sc26xx_stop_rx, - .enable_ms = sc26xx_enable_ms, - .break_ctl = sc26xx_break_ctl, - .startup = sc26xx_startup, - .shutdown = sc26xx_shutdown, - .set_termios = sc26xx_set_termios, - .type = sc26xx_type, - .release_port = sc26xx_release_port, - .request_port = sc26xx_request_port, - .config_port = sc26xx_config_port, - .verify_port = sc26xx_verify_port, -}; - -static struct uart_port *sc26xx_port; - -#ifdef CONFIG_SERIAL_SC26XX_CONSOLE -static void sc26xx_console_putchar(struct uart_port *port, char c) -{ - unsigned long flags; - int limit = 1000000; - - spin_lock_irqsave(&port->lock, flags); - - while (limit-- > 0) { - if (READ_SC_PORT(port, SR) & SR_TXRDY) { - WRITE_SC_PORT(port, THR, c); - break; - } - udelay(2); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void sc26xx_console_write(struct console *con, const char *s, unsigned n) -{ - struct uart_port *port = sc26xx_port; - int i; - - for (i = 0; i < n; i++) { - if (*s == '\n') - sc26xx_console_putchar(port, '\r'); - sc26xx_console_putchar(port, *s++); - } -} - -static int __init sc26xx_console_setup(struct console *con, char *options) -{ - struct uart_port *port = sc26xx_port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (port->type != PORT_SC26XX) - return -1; - - printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, con, baud, parity, bits, flow); -} - -static struct uart_driver sc26xx_reg; -static struct console sc26xx_console = { - .name = "ttySC", - .write = sc26xx_console_write, - .device = uart_console_device, - .setup = sc26xx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sc26xx_reg, -}; -#define SC26XX_CONSOLE &sc26xx_console -#else -#define SC26XX_CONSOLE NULL -#endif - -static struct uart_driver sc26xx_reg = { - .owner = THIS_MODULE, - .driver_name = "SC26xx", - .dev_name = "ttySC", - .major = SC26XX_MAJOR, - .minor = SC26XX_MINOR_START, - .nr = SC26XX_NR, - .cons = SC26XX_CONSOLE, -}; - -static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) -{ - unsigned int bit = (flags >> bitpos) & 15; - - return bit ? (1 << (bit - 1)) : 0; -} - -static void sc26xx_init_masks(struct uart_sc26xx_port *up, - int line, unsigned int data) -{ - up->dtr_mask[line] = sc26xx_flags2mask(data, 0); - up->rts_mask[line] = sc26xx_flags2mask(data, 4); - up->dsr_mask[line] = sc26xx_flags2mask(data, 8); - up->cts_mask[line] = sc26xx_flags2mask(data, 12); - up->dcd_mask[line] = sc26xx_flags2mask(data, 16); - up->ri_mask[line] = sc26xx_flags2mask(data, 20); -} - -static int sc26xx_probe(struct platform_device *dev) -{ - struct resource *res; - struct uart_sc26xx_port *up; - unsigned int *sc26xx_data = dev->dev.platform_data; - int err; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - up = kzalloc(sizeof *up, GFP_KERNEL); - if (unlikely(!up)) - return -ENOMEM; - - up->port[0].line = 0; - up->port[0].ops = &sc26xx_ops; - up->port[0].type = PORT_SC26XX; - up->port[0].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[0].mapbase = res->start; - up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40); - up->port[0].iotype = UPIO_MEM; - up->port[0].irq = platform_get_irq(dev, 0); - - up->port[0].dev = &dev->dev; - - sc26xx_init_masks(up, 0, sc26xx_data[0]); - - sc26xx_port = &up->port[0]; - - up->port[1].line = 1; - up->port[1].ops = &sc26xx_ops; - up->port[1].type = PORT_SC26XX; - up->port[1].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[1].mapbase = up->port[0].mapbase; - up->port[1].membase = up->port[0].membase; - up->port[1].iotype = UPIO_MEM; - up->port[1].irq = up->port[0].irq; - - up->port[1].dev = &dev->dev; - - sc26xx_init_masks(up, 1, sc26xx_data[1]); - - err = uart_register_driver(&sc26xx_reg); - if (err) - goto out_free_port; - - sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor; - - err = uart_add_one_port(&sc26xx_reg, &up->port[0]); - if (err) - goto out_unregister_driver; - - err = uart_add_one_port(&sc26xx_reg, &up->port[1]); - if (err) - goto out_remove_port0; - - err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up); - if (err) - goto out_remove_ports; - - dev_set_drvdata(&dev->dev, up); - return 0; - -out_remove_ports: - uart_remove_one_port(&sc26xx_reg, &up->port[1]); -out_remove_port0: - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - -out_unregister_driver: - uart_unregister_driver(&sc26xx_reg); - -out_free_port: - kfree(up); - sc26xx_port = NULL; - return err; -} - - -static int __exit sc26xx_driver_remove(struct platform_device *dev) -{ - struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); - - free_irq(up->port[0].irq, up); - - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - uart_remove_one_port(&sc26xx_reg, &up->port[1]); - - uart_unregister_driver(&sc26xx_reg); - - kfree(up); - sc26xx_port = NULL; - - dev_set_drvdata(&dev->dev, NULL); - return 0; -} - -static struct platform_driver sc26xx_driver = { - .probe = sc26xx_probe, - .remove = sc26xx_driver_remove, - .driver = { - .name = "SC26xx", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(sc26xx_driver); - -MODULE_AUTHOR("Thomas Bogendörfer"); -MODULE_DESCRIPTION("SC681/SC2692 serial driver"); -MODULE_VERSION("1.0"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:SC26xx"); diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 418b495e323..e84b6a3bdd1 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -15,6 +15,8 @@ #define SUPPORT_SYSRQ #endif +#include <linux/clk.h> +#include <linux/err.h> #include <linux/module.h> #include <linux/device.h> #include <linux/console.h> @@ -23,8 +25,10 @@ #include <linux/io.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/spinlock.h> #include <linux/platform_device.h> -#include <linux/platform_data/sccnxp.h> +#include <linux/platform_data/serial-sccnxp.h> +#include <linux/regulator/consumer.h> #define SCCNXP_NAME "uart-sccnxp" #define SCCNXP_MAJOR 204 @@ -91,65 +95,131 @@ #define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0) #define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0) -/* Supported chip types */ -enum { - SCCNXP_TYPE_SC2681 = 2681, - SCCNXP_TYPE_SC2691 = 2691, - SCCNXP_TYPE_SC2692 = 2692, - SCCNXP_TYPE_SC2891 = 2891, - SCCNXP_TYPE_SC2892 = 2892, - SCCNXP_TYPE_SC28202 = 28202, - SCCNXP_TYPE_SC68681 = 68681, - SCCNXP_TYPE_SC68692 = 68692, +#define SCCNXP_HAVE_IO 0x00000001 +#define SCCNXP_HAVE_MR0 0x00000002 + +struct sccnxp_chip { + const char *name; + unsigned int nr; + unsigned long freq_min; + unsigned long freq_std; + unsigned long freq_max; + unsigned int flags; + unsigned int fifosize; }; struct sccnxp_port { struct uart_driver uart; struct uart_port port[SCCNXP_MAX_UARTS]; + bool opened[SCCNXP_MAX_UARTS]; - const char *name; int irq; - u8 imr; - u8 addr_mask; - int freq_std; - int flags; -#define SCCNXP_HAVE_IO 0x00000001 -#define SCCNXP_HAVE_MR0 0x00000002 + struct sccnxp_chip *chip; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE struct console console; #endif - struct mutex sccnxp_mutex; + spinlock_t lock; + + bool poll; + struct timer_list timer; struct sccnxp_pdata pdata; + + struct regulator *regulator; }; -static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift) -{ - return readb(base + (reg << shift)); -} +static const struct sccnxp_chip sc2681 = { + .name = "SC2681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; -static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v) -{ - writeb(v, base + (reg << shift)); -} +static const struct sccnxp_chip sc2691 = { + .name = "SC2691", + .nr = 1, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = 0, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2692 = { + .name = "SC2692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc2891 = { + .name = "SC2891", + .nr = 1, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc2892 = { + .name = "SC2892", + .nr = 2, + .freq_min = 100000, + .freq_std = 3686400, + .freq_max = 8000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 16, +}; + +static const struct sccnxp_chip sc28202 = { + .name = "SC28202", + .nr = 2, + .freq_min = 1000000, + .freq_std = 14745600, + .freq_max = 50000000, + .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, + .fifosize = 256, +}; + +static const struct sccnxp_chip sc68681 = { + .name = "SC68681", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; + +static const struct sccnxp_chip sc68692 = { + .name = "SC68692", + .nr = 2, + .freq_min = 1000000, + .freq_std = 3686400, + .freq_max = 4000000, + .flags = SCCNXP_HAVE_IO, + .fifosize = 3, +}; static inline u8 sccnxp_read(struct uart_port *port, u8 reg) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - return sccnxp_raw_read(port->membase, reg & s->addr_mask, - port->regshift); + return readb(port->membase + (reg << port->regshift)); } static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) { - struct sccnxp_port *s = dev_get_drvdata(port->dev); - - sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v); + writeb(v, port->membase + (reg << port->regshift)); } static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) @@ -174,14 +244,12 @@ static int sccnxp_update_best_err(int a, int b, int *besterr) return 1; } -struct baud_table { +static const struct { u8 csr; u8 acr; u8 mr0; int baud; -}; - -const struct baud_table baud_std[] = { +} baud_std[] = { { 0, ACR_BAUD0, MR0_BAUD_NORMAL, 50, }, { 0, ACR_BAUD1, MR0_BAUD_NORMAL, 75, }, { 1, ACR_BAUD0, MR0_BAUD_NORMAL, 110, }, @@ -217,13 +285,14 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) { struct sccnxp_port *s = dev_get_drvdata(port->dev); int div_std, tmp_baud, bestbaud = baud, besterr = -1; + struct sccnxp_chip *chip = s->chip; u8 i, acr = 0, csr = 0, mr0 = 0; /* Find best baud from table */ for (i = 0; baud_std[i].baud && besterr; i++) { - if (baud_std[i].mr0 && !(s->flags & SCCNXP_HAVE_MR0)) + if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0)) continue; - div_std = DIV_ROUND_CLOSEST(s->freq_std, baud_std[i].baud); + div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud); tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std); if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) { acr = baud_std[i].acr; @@ -233,7 +302,7 @@ static int sccnxp_set_baud(struct uart_port *port, int baud) } } - if (s->flags & SCCNXP_HAVE_MR0) { + if (chip->flags & SCCNXP_HAVE_MR0) { /* Enable FIFO, set half level for TX */ mr0 |= MR0_FIFO | MR0_TXLVL; /* Update MR0 */ @@ -285,10 +354,6 @@ static void sccnxp_handle_rx(struct uart_port *port) { u8 sr; unsigned int ch, flag; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - - if (!tty) - return; for (;;) { sr = sccnxp_port_read(port, SCCNXP_SR_REG); @@ -304,14 +369,19 @@ static void sccnxp_handle_rx(struct uart_port *port) if (unlikely(sr)) { if (sr & SR_BRK) { port->icount.brk++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_BREAK_RESET); if (uart_handle_break(port)) continue; } else if (sr & SR_PE) port->icount.parity++; else if (sr & SR_FE) port->icount.frame++; - else if (sr & SR_OVR) + else if (sr & SR_OVR) { port->icount.overrun++; + sccnxp_port_write(port, SCCNXP_CR_REG, + CR_CMD_STATUS_RESET); + } sr &= port->read_status_mask; if (sr & SR_BRK) @@ -333,9 +403,7 @@ static void sccnxp_handle_rx(struct uart_port *port) uart_insert_char(port, sr, SR_OVR, ch, flag); } - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&port->state->port); } static void sccnxp_handle_tx(struct uart_port *port) @@ -357,7 +425,7 @@ static void sccnxp_handle_tx(struct uart_port *port) sccnxp_disable_irq(port, IMR_TXRDY); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); } return; @@ -377,31 +445,46 @@ static void sccnxp_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static irqreturn_t sccnxp_ist(int irq, void *dev_id) +static void sccnxp_handle_events(struct sccnxp_port *s) { int i; u8 isr; - struct sccnxp_port *s = (struct sccnxp_port *)dev_id; - - mutex_lock(&s->sccnxp_mutex); - for (;;) { + do { isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG); isr &= s->imr; if (!isr) break; - dev_dbg(s->port[0].dev, "IRQ status: 0x%02x\n", isr); - for (i = 0; i < s->uart.nr; i++) { - if (isr & ISR_RXRDY(i)) + if (s->opened[i] && (isr & ISR_RXRDY(i))) sccnxp_handle_rx(&s->port[i]); - if (isr & ISR_TXRDY(i)) + if (s->opened[i] && (isr & ISR_TXRDY(i))) sccnxp_handle_tx(&s->port[i]); } - } + } while (1); +} + +static void sccnxp_timer(unsigned long data) +{ + struct sccnxp_port *s = (struct sccnxp_port *)data; + unsigned long flags; - mutex_unlock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); + + mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us)); +} + +static irqreturn_t sccnxp_ist(int irq, void *dev_id) +{ + struct sccnxp_port *s = (struct sccnxp_port *)dev_id; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + sccnxp_handle_events(s); + spin_unlock_irqrestore(&s->lock, flags); return IRQ_HANDLED; } @@ -409,16 +492,17 @@ static irqreturn_t sccnxp_ist(int irq, void *dev_id) static void sccnxp_start_tx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Set direction to output */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 1); sccnxp_enable_irq(port, IMR_TXRDY); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_stop_tx(struct uart_port *port) @@ -429,20 +513,22 @@ static void sccnxp_stop_tx(struct uart_port *port) static void sccnxp_stop_rx(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_tx_empty(struct uart_port *port) { u8 val; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); val = sccnxp_port_read(port, SCCNXP_SR_REG); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return (val & SR_TXEMT) ? TIOCSER_TEMT : 0; } @@ -455,28 +541,30 @@ static void sccnxp_enable_ms(struct uart_port *port) static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR); sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static unsigned int sccnxp_get_mctrl(struct uart_port *port) { u8 bitmask, ipr; + unsigned long flags; struct sccnxp_port *s = dev_get_drvdata(port->dev); unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - if (!(s->flags & SCCNXP_HAVE_IO)) + if (!(s->chip->flags & SCCNXP_HAVE_IO)) return mctrl; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG); @@ -505,7 +593,7 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0; } - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); return mctrl; } @@ -513,21 +601,23 @@ static unsigned int sccnxp_get_mctrl(struct uart_port *port) static void sccnxp_break_ctl(struct uart_port *port, int break_state) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); sccnxp_port_write(port, SCCNXP_CR_REG, break_state ? CR_CMD_START_BREAK : CR_CMD_STOP_BREAK); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static void sccnxp_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; u8 mr1, mr2; int baud; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; @@ -575,41 +665,45 @@ static void sccnxp_set_termios(struct uart_port *port, port->read_status_mask = SR_OVR; if (termios->c_iflag & INPCK) port->read_status_mask |= SR_PE | SR_FE; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= SR_BRK; /* Set status ignore mask */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNBRK) port->ignore_status_mask |= SR_BRK; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; if (!(termios->c_cflag & CREAD)) port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK; /* Setup baudrate */ baud = uart_get_baud_rate(port, termios, old, 50, - (s->flags & SCCNXP_HAVE_MR0) ? + (s->chip->flags & SCCNXP_HAVE_MR0) ? 230400 : 38400); baud = sccnxp_set_baud(port, baud); /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); + /* Report actual baudrate back to core */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); /* Enable RX & TX */ sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_startup(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); - if (s->flags & SCCNXP_HAVE_IO) { + if (s->chip->flags & SCCNXP_HAVE_IO) { /* Outputs are controlled manually */ sccnxp_write(port, SCCNXP_OPCR_REG, 0); } @@ -626,7 +720,9 @@ static int sccnxp_startup(struct uart_port *port) /* Enable RX interrupt */ sccnxp_enable_irq(port, IMR_RXRDY); - mutex_unlock(&s->sccnxp_mutex); + s->opened[port->line] = 1; + + spin_unlock_irqrestore(&s->lock, flags); return 0; } @@ -634,8 +730,11 @@ static int sccnxp_startup(struct uart_port *port) static void sccnxp_shutdown(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); - mutex_lock(&s->sccnxp_mutex); + s->opened[port->line] = 0; /* Disable interrupts */ sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY); @@ -644,17 +743,17 @@ static void sccnxp_shutdown(struct uart_port *port) sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE); /* Leave direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(port, DIR_OP, 0); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static const char *sccnxp_type(struct uart_port *port) { struct sccnxp_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_SC26XX) ? s->name : NULL; + return (port->type == PORT_SC26XX) ? s->chip->name : NULL; } static void sccnxp_release_port(struct uart_port *port) @@ -721,10 +820,11 @@ static void sccnxp_console_write(struct console *co, const char *c, unsigned n) { struct sccnxp_port *s = (struct sccnxp_port *)co->data; struct uart_port *port = &s->port[co->index]; + unsigned long flags; - mutex_lock(&s->sccnxp_mutex); + spin_lock_irqsave(&s->lock, flags); uart_console_write(port, c, n, sccnxp_console_putchar); - mutex_unlock(&s->sccnxp_mutex); + spin_unlock_irqrestore(&s->lock, flags); } static int sccnxp_console_setup(struct console *co, char *options) @@ -740,21 +840,31 @@ static int sccnxp_console_setup(struct console *co, char *options) } #endif +static const struct platform_device_id sccnxp_id_table[] = { + { .name = "sc2681", .driver_data = (kernel_ulong_t)&sc2681, }, + { .name = "sc2691", .driver_data = (kernel_ulong_t)&sc2691, }, + { .name = "sc2692", .driver_data = (kernel_ulong_t)&sc2692, }, + { .name = "sc2891", .driver_data = (kernel_ulong_t)&sc2891, }, + { .name = "sc2892", .driver_data = (kernel_ulong_t)&sc2892, }, + { .name = "sc28202", .driver_data = (kernel_ulong_t)&sc28202, }, + { .name = "sc68681", .driver_data = (kernel_ulong_t)&sc68681, }, + { .name = "sc68692", .driver_data = (kernel_ulong_t)&sc68692, }, + { } +}; +MODULE_DEVICE_TABLE(platform, sccnxp_id_table); + static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - int chiptype = pdev->id_entry->driver_data; struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); - int i, ret, fifosize, freq_min, freq_max; + int i, ret, uartclk; struct sccnxp_port *s; void __iomem *membase; + struct clk *clk; - if (!res) { - dev_err(&pdev->dev, "Missing memory resource data\n"); - return -EADDRNOTAVAIL; - } - - dev_set_name(&pdev->dev, SCCNXP_NAME); + membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(membase)) + return PTR_ERR(membase); s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL); if (!s) { @@ -763,129 +873,62 @@ static int sccnxp_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, s); - mutex_init(&s->sccnxp_mutex); - - /* Individual chip settings */ - switch (chiptype) { - case SCCNXP_TYPE_SC2681: - s->name = "SC2681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2691: - s->name = "SC2691"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x07; - s->flags = 0; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2692: - s->name = "SC2692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC2891: - s->name = "SC2891"; - s->uart.nr = 1; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC2892: - s->name = "SC2892"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 16; - freq_min = 100000; - freq_max = 8000000; - break; - case SCCNXP_TYPE_SC28202: - s->name = "SC28202"; - s->uart.nr = 2; - s->freq_std = 14745600; - s->addr_mask = 0x7f; - s->flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0; - fifosize = 256; - freq_min = 1000000; - freq_max = 50000000; - break; - case SCCNXP_TYPE_SC68681: - s->name = "SC68681"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - case SCCNXP_TYPE_SC68692: - s->name = "SC68692"; - s->uart.nr = 2; - s->freq_std = 3686400; - s->addr_mask = 0x0f; - s->flags = SCCNXP_HAVE_IO; - fifosize = 3; - freq_min = 1000000; - freq_max = 4000000; - break; - default: - dev_err(&pdev->dev, "Unsupported chip type %i\n", chiptype); - ret = -ENOTSUPP; - goto err_out; - } + spin_lock_init(&s->lock); - if (!pdata) { - dev_warn(&pdev->dev, - "No platform data supplied, using defaults\n"); - s->pdata.frequency = s->freq_std; - } else - memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; - s->irq = platform_get_irq(pdev, 0); - if (s->irq <= 0) { - dev_err(&pdev->dev, "Missing irq resource data\n"); - ret = -ENXIO; - goto err_out; - } + s->regulator = devm_regulator_get(&pdev->dev, "vcc"); + if (!IS_ERR(s->regulator)) { + ret = regulator_enable(s->regulator); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable regulator: %i\n", ret); + return ret; + } + } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_out; + } + dev_notice(&pdev->dev, "Using default clock frequency\n"); + uartclk = s->chip->freq_std; + } else + uartclk = clk_get_rate(clk); /* Check input frequency */ - if ((s->pdata.frequency < freq_min) || - (s->pdata.frequency > freq_max)) { + if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) { dev_err(&pdev->dev, "Frequency out of bounds\n"); ret = -EINVAL; goto err_out; } - membase = devm_request_and_ioremap(&pdev->dev, res); - if (!membase) { - dev_err(&pdev->dev, "Failed to ioremap\n"); - ret = -EIO; - goto err_out; + if (pdata) + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + + if (s->pdata.poll_time_us) { + dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", + s->pdata.poll_time_us); + s->poll = 1; + } + + if (!s->poll) { + s->irq = platform_get_irq(pdev, 0); + if (s->irq < 0) { + dev_err(&pdev->dev, "Missing irq resource data\n"); + ret = -ENXIO; + goto err_out; + } } s->uart.owner = THIS_MODULE; s->uart.dev_name = "ttySC"; s->uart.major = SCCNXP_MAJOR; s->uart.minor = SCCNXP_MINOR; + s->uart.nr = s->chip->nr; #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE s->uart.cons = &s->console; s->uart.cons->device = uart_console_device; @@ -907,17 +950,17 @@ static int sccnxp_probe(struct platform_device *pdev) s->port[i].dev = &pdev->dev; s->port[i].irq = s->irq; s->port[i].type = PORT_SC26XX; - s->port[i].fifosize = fifosize; + s->port[i].fifosize = s->chip->fifosize; s->port[i].flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; s->port[i].iotype = UPIO_MEM; s->port[i].mapbase = res->start; s->port[i].membase = membase; s->port[i].regshift = s->pdata.reg_shift; - s->port[i].uartclk = s->pdata.frequency; + s->port[i].uartclk = uartclk; s->port[i].ops = &sccnxp_ops; uart_add_one_port(&s->uart, &s->port[i]); /* Set direction to input */ - if (s->flags & SCCNXP_HAVE_IO) + if (s->chip->flags & SCCNXP_HAVE_IO) sccnxp_set_bit(&s->port[i], DIR_OP, 0); } @@ -925,20 +968,28 @@ static int sccnxp_probe(struct platform_device *pdev) s->imr = 0; sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0); - /* Board specific configure */ - if (s->pdata.init) - s->pdata.init(); - - ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, sccnxp_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&pdev->dev), s); - if (!ret) + if (!s->poll) { + ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL, + sccnxp_ist, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&pdev->dev), s); + if (!ret) + return 0; + + dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); + } else { + init_timer(&s->timer); + setup_timer(&s->timer, sccnxp_timer, (unsigned long)s); + mod_timer(&s->timer, jiffies + + usecs_to_jiffies(s->pdata.poll_time_us)); return 0; + } - dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq); - + uart_unregister_driver(&s->uart); err_out: - platform_set_drvdata(pdev, NULL); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return ret; } @@ -948,33 +999,22 @@ static int sccnxp_remove(struct platform_device *pdev) int i; struct sccnxp_port *s = platform_get_drvdata(pdev); - devm_free_irq(&pdev->dev, s->irq, s); + if (!s->poll) + devm_free_irq(&pdev->dev, s->irq, s); + else + del_timer_sync(&s->timer); for (i = 0; i < s->uart.nr; i++) uart_remove_one_port(&s->uart, &s->port[i]); uart_unregister_driver(&s->uart); - platform_set_drvdata(pdev, NULL); - if (s->pdata.exit) - s->pdata.exit(); + if (!IS_ERR(s->regulator)) + return regulator_disable(s->regulator); return 0; } -static const struct platform_device_id sccnxp_id_table[] = { - { "sc2681", SCCNXP_TYPE_SC2681 }, - { "sc2691", SCCNXP_TYPE_SC2691 }, - { "sc2692", SCCNXP_TYPE_SC2692 }, - { "sc2891", SCCNXP_TYPE_SC2891 }, - { "sc2892", SCCNXP_TYPE_SC2892 }, - { "sc28202", SCCNXP_TYPE_SC28202 }, - { "sc68681", SCCNXP_TYPE_SC68681 }, - { "sc68692", SCCNXP_TYPE_SC68692 }, - { }, -}; -MODULE_DEVICE_TABLE(platform, sccnxp_id_table); - static struct platform_driver sccnxp_uart_driver = { .driver = { .name = SCCNXP_NAME, diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c new file mode 100644 index 00000000000..d5c2a287b7e --- /dev/null +++ b/drivers/tty/serial/serial-tegra.c @@ -0,0 +1,1402 @@ +/* + * serial_tegra.c + * + * High-speed serial driver for NVIDIA Tegra SoCs + * + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pagemap.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/serial.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/termios.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#define TEGRA_UART_TYPE "TEGRA_UART" +#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE) +#define BYTES_TO_ALIGN(x) ((unsigned long)(x) & 0x3) + +#define TEGRA_UART_RX_DMA_BUFFER_SIZE 4096 +#define TEGRA_UART_LSR_TXFIFO_FULL 0x100 +#define TEGRA_UART_IER_EORD 0x20 +#define TEGRA_UART_MCR_RTS_EN 0x40 +#define TEGRA_UART_MCR_CTS_EN 0x20 +#define TEGRA_UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \ + UART_LSR_PE | UART_LSR_FE) +#define TEGRA_UART_IRDA_CSR 0x08 +#define TEGRA_UART_SIR_ENABLED 0x80 + +#define TEGRA_UART_TX_PIO 1 +#define TEGRA_UART_TX_DMA 2 +#define TEGRA_UART_MIN_DMA 16 +#define TEGRA_UART_FIFO_SIZE 32 + +/* + * Tx fifo trigger level setting in tegra uart is in + * reverse way then conventional uart. + */ +#define TEGRA_UART_TX_TRIG_16B 0x00 +#define TEGRA_UART_TX_TRIG_8B 0x10 +#define TEGRA_UART_TX_TRIG_4B 0x20 +#define TEGRA_UART_TX_TRIG_1B 0x30 + +#define TEGRA_UART_MAXIMUM 5 + +/* Default UART setting when started: 115200 no parity, stop, 8 data bits */ +#define TEGRA_UART_DEFAULT_BAUD 115200 +#define TEGRA_UART_DEFAULT_LSR UART_LCR_WLEN8 + +/* Tx transfer mode */ +#define TEGRA_TX_PIO 1 +#define TEGRA_TX_DMA 2 + +/** + * tegra_uart_chip_data: SOC specific data. + * + * @tx_fifo_full_status: Status flag available for checking tx fifo full. + * @allow_txfifo_reset_fifo_mode: allow_tx fifo reset with fifo mode or not. + * Tegra30 does not allow this. + * @support_clk_src_div: Clock source support the clock divider. + */ +struct tegra_uart_chip_data { + bool tx_fifo_full_status; + bool allow_txfifo_reset_fifo_mode; + bool support_clk_src_div; +}; + +struct tegra_uart_port { + struct uart_port uport; + const struct tegra_uart_chip_data *cdata; + + struct clk *uart_clk; + struct reset_control *rst; + unsigned int current_baud; + + /* Register shadow */ + unsigned long fcr_shadow; + unsigned long mcr_shadow; + unsigned long lcr_shadow; + unsigned long ier_shadow; + bool rts_active; + + int tx_in_progress; + unsigned int tx_bytes; + + bool enable_modem_interrupt; + + bool rx_timeout; + int rx_in_progress; + int symb_bit; + + struct dma_chan *rx_dma_chan; + struct dma_chan *tx_dma_chan; + dma_addr_t rx_dma_buf_phys; + dma_addr_t tx_dma_buf_phys; + unsigned char *rx_dma_buf_virt; + unsigned char *tx_dma_buf_virt; + struct dma_async_tx_descriptor *tx_dma_desc; + struct dma_async_tx_descriptor *rx_dma_desc; + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + int tx_bytes_requested; + int rx_bytes_requested; +}; + +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup); +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup); + +static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup, + unsigned long reg) +{ + return readl(tup->uport.membase + (reg << tup->uport.regshift)); +} + +static inline void tegra_uart_write(struct tegra_uart_port *tup, unsigned val, + unsigned long reg) +{ + writel(val, tup->uport.membase + (reg << tup->uport.regshift)); +} + +static inline struct tegra_uart_port *to_tegra_uport(struct uart_port *u) +{ + return container_of(u, struct tegra_uart_port, uport); +} + +static unsigned int tegra_uart_get_mctrl(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + /* + * RI - Ring detector is active + * CD/DCD/CAR - Carrier detect is always active. For some reason + * linux has different names for carrier detect. + * DSR - Data Set ready is active as the hardware doesn't support it. + * Don't know if the linux support this yet? + * CTS - Clear to send. Always set to active, as the hardware handles + * CTS automatically. + */ + if (tup->enable_modem_interrupt) + return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS; + return TIOCM_CTS; +} + +static void set_rts(struct tegra_uart_port *tup, bool active) +{ + unsigned long mcr; + + mcr = tup->mcr_shadow; + if (active) + mcr |= TEGRA_UART_MCR_RTS_EN; + else + mcr &= ~TEGRA_UART_MCR_RTS_EN; + if (mcr != tup->mcr_shadow) { + tegra_uart_write(tup, mcr, UART_MCR); + tup->mcr_shadow = mcr; + } + return; +} + +static void set_dtr(struct tegra_uart_port *tup, bool active) +{ + unsigned long mcr; + + mcr = tup->mcr_shadow; + if (active) + mcr |= UART_MCR_DTR; + else + mcr &= ~UART_MCR_DTR; + if (mcr != tup->mcr_shadow) { + tegra_uart_write(tup, mcr, UART_MCR); + tup->mcr_shadow = mcr; + } + return; +} + +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); + + dtr_enable = !!(mctrl & TIOCM_DTR); + set_dtr(tup, dtr_enable); + return; +} + +static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned long lcr; + + lcr = tup->lcr_shadow; + if (break_ctl) + lcr |= UART_LCR_SBC; + else + lcr &= ~UART_LCR_SBC; + tegra_uart_write(tup, lcr, UART_LCR); + tup->lcr_shadow = lcr; +} + +/* Wait for a symbol-time. */ +static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup, + unsigned int syms) +{ + if (tup->current_baud) + udelay(DIV_ROUND_UP(syms * tup->symb_bit * 1000000, + tup->current_baud)); +} + +static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits) +{ + unsigned long fcr = tup->fcr_shadow; + + if (tup->cdata->allow_txfifo_reset_fifo_mode) { + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + tegra_uart_write(tup, fcr, UART_FCR); + } else { + fcr &= ~UART_FCR_ENABLE_FIFO; + tegra_uart_write(tup, fcr, UART_FCR); + udelay(60); + fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + tegra_uart_write(tup, fcr, UART_FCR); + fcr |= UART_FCR_ENABLE_FIFO; + tegra_uart_write(tup, fcr, UART_FCR); + } + + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); + + /* Wait for the flush to propagate. */ + tegra_uart_wait_sym_time(tup, 1); +} + +static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud) +{ + unsigned long rate; + unsigned int divisor; + unsigned long lcr; + int ret; + + if (tup->current_baud == baud) + return 0; + + if (tup->cdata->support_clk_src_div) { + rate = baud * 16; + ret = clk_set_rate(tup->uart_clk, rate); + if (ret < 0) { + dev_err(tup->uport.dev, + "clk_set_rate() failed for rate %lu\n", rate); + return ret; + } + divisor = 1; + } else { + rate = clk_get_rate(tup->uart_clk); + divisor = DIV_ROUND_CLOSEST(rate, baud * 16); + } + + lcr = tup->lcr_shadow; + lcr |= UART_LCR_DLAB; + tegra_uart_write(tup, lcr, UART_LCR); + + tegra_uart_write(tup, divisor & 0xFF, UART_TX); + tegra_uart_write(tup, ((divisor >> 8) & 0xFF), UART_IER); + + lcr &= ~UART_LCR_DLAB; + tegra_uart_write(tup, lcr, UART_LCR); + + /* Dummy read to ensure the write is posted */ + tegra_uart_read(tup, UART_SCR); + + tup->current_baud = baud; + + /* wait two character intervals at new rate */ + tegra_uart_wait_sym_time(tup, 2); + return 0; +} + +static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup, + unsigned long lsr) +{ + char flag = TTY_NORMAL; + + if (unlikely(lsr & TEGRA_UART_LSR_ANY)) { + if (lsr & UART_LSR_OE) { + /* Overrrun error */ + flag |= TTY_OVERRUN; + tup->uport.icount.overrun++; + dev_err(tup->uport.dev, "Got overrun errors\n"); + } else if (lsr & UART_LSR_PE) { + /* Parity error */ + flag |= TTY_PARITY; + tup->uport.icount.parity++; + dev_err(tup->uport.dev, "Got Parity errors\n"); + } else if (lsr & UART_LSR_FE) { + flag |= TTY_FRAME; + tup->uport.icount.frame++; + dev_err(tup->uport.dev, "Got frame errors\n"); + } else if (lsr & UART_LSR_BI) { + dev_err(tup->uport.dev, "Got Break\n"); + tup->uport.icount.brk++; + /* If FIFO read error without any data, reset Rx FIFO */ + if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR); + } + } + return flag; +} + +static int tegra_uart_request_port(struct uart_port *u) +{ + return 0; +} + +static void tegra_uart_release_port(struct uart_port *u) +{ + /* Nothing to do here */ +} + +static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + int i; + + for (i = 0; i < max_bytes; i++) { + BUG_ON(uart_circ_empty(xmit)); + if (tup->cdata->tx_fifo_full_status) { + unsigned long lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & TEGRA_UART_LSR_TXFIFO_FULL)) + break; + } + tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + tup->uport.icount.tx++; + } +} + +static void tegra_uart_start_pio_tx(struct tegra_uart_port *tup, + unsigned int bytes) +{ + if (bytes > TEGRA_UART_MIN_DMA) + bytes = TEGRA_UART_MIN_DMA; + + tup->tx_in_progress = TEGRA_UART_TX_PIO; + tup->tx_bytes = bytes; + tup->ier_shadow |= UART_IER_THRI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); +} + +static void tegra_uart_tx_dma_complete(void *args) +{ + struct tegra_uart_port *tup = args; + struct circ_buf *xmit = &tup->uport.state->xmit; + struct dma_tx_state state; + unsigned long flags; + int count; + + dmaengine_tx_status(tup->tx_dma_chan, tup->rx_cookie, &state); + count = tup->tx_bytes_requested - state.residue; + async_tx_ack(tup->tx_dma_desc); + spin_lock_irqsave(&tup->uport.lock, flags); + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + tup->tx_in_progress = 0; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&tup->uport); + tegra_uart_start_next_tx(tup); + spin_unlock_irqrestore(&tup->uport.lock, flags); +} + +static int tegra_uart_start_tx_dma(struct tegra_uart_port *tup, + unsigned long count) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + dma_addr_t tx_phys_addr; + + dma_sync_single_for_device(tup->uport.dev, tup->tx_dma_buf_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); + + tup->tx_bytes = count & ~(0xF); + tx_phys_addr = tup->tx_dma_buf_phys + xmit->tail; + tup->tx_dma_desc = dmaengine_prep_slave_single(tup->tx_dma_chan, + tx_phys_addr, tup->tx_bytes, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!tup->tx_dma_desc) { + dev_err(tup->uport.dev, "Not able to get desc for Tx\n"); + return -EIO; + } + + tup->tx_dma_desc->callback = tegra_uart_tx_dma_complete; + tup->tx_dma_desc->callback_param = tup; + tup->tx_in_progress = TEGRA_UART_TX_DMA; + tup->tx_bytes_requested = tup->tx_bytes; + tup->tx_cookie = dmaengine_submit(tup->tx_dma_desc); + dma_async_issue_pending(tup->tx_dma_chan); + return 0; +} + +static void tegra_uart_start_next_tx(struct tegra_uart_port *tup) +{ + unsigned long tail; + unsigned long count; + struct circ_buf *xmit = &tup->uport.state->xmit; + + tail = (unsigned long)&xmit->buf[xmit->tail]; + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (!count) + return; + + if (count < TEGRA_UART_MIN_DMA) + tegra_uart_start_pio_tx(tup, count); + else if (BYTES_TO_ALIGN(tail) > 0) + tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail)); + else + tegra_uart_start_tx_dma(tup, count); +} + +/* Called by serial core driver with u->lock taken. */ +static void tegra_uart_start_tx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct circ_buf *xmit = &u->state->xmit; + + if (!uart_circ_empty(xmit) && !tup->tx_in_progress) + tegra_uart_start_next_tx(tup); +} + +static unsigned int tegra_uart_tx_empty(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&u->lock, flags); + if (!tup->tx_in_progress) { + unsigned long lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) + ret = TIOCSER_TEMT; + } + spin_unlock_irqrestore(&u->lock, flags); + return ret; +} + +static void tegra_uart_stop_tx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct circ_buf *xmit = &tup->uport.state->xmit; + struct dma_tx_state state; + int count; + + dmaengine_terminate_all(tup->tx_dma_chan); + dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state); + count = tup->tx_bytes_requested - state.residue; + async_tx_ack(tup->tx_dma_desc); + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + tup->tx_in_progress = 0; + return; +} + +static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) +{ + struct circ_buf *xmit = &tup->uport.state->xmit; + + tegra_uart_fill_tx_fifo(tup, tup->tx_bytes); + tup->tx_in_progress = 0; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&tup->uport); + tegra_uart_start_next_tx(tup); + return; +} + +static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, + struct tty_port *tty) +{ + do { + char flag = TTY_NORMAL; + unsigned long lsr = 0; + unsigned char ch; + + lsr = tegra_uart_read(tup, UART_LSR); + if (!(lsr & UART_LSR_DR)) + break; + + flag = tegra_uart_decode_rx_error(tup, lsr); + ch = (unsigned char) tegra_uart_read(tup, UART_RX); + tup->uport.icount.rx++; + + if (!uart_handle_sysrq_char(&tup->uport, ch) && tty) + tty_insert_flip_char(tty, ch, flag); + } while (1); + + return; +} + +static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, + struct tty_port *tty, int count) +{ + int copied; + + tup->uport.icount.rx += count; + if (!tty) { + dev_err(tup->uport.dev, "No tty port\n"); + return; + } + dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys, + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE); + copied = tty_insert_flip_string(tty, + ((unsigned char *)(tup->rx_dma_buf_virt)), count); + if (copied != count) { + WARN_ON(1); + dev_err(tup->uport.dev, "RxData copy to tty layer failed\n"); + } + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, + TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); +} + +static void tegra_uart_rx_dma_complete(void *args) +{ + struct tegra_uart_port *tup = args; + struct uart_port *u = &tup->uport; + int count = tup->rx_bytes_requested; + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + struct tty_port *port = &u->state->port; + unsigned long flags; + + async_tx_ack(tup->rx_dma_desc); + spin_lock_irqsave(&u->lock, flags); + + /* Deactivate flow control to stop sender */ + if (tup->rts_active) + set_rts(tup, false); + + /* If we are here, DMA is stopped */ + if (count) + tegra_uart_copy_rx_to_tty(tup, port, count); + + tegra_uart_handle_rx_pio(tup, port); + if (tty) { + spin_unlock_irqrestore(&u->lock, flags); + tty_flip_buffer_push(port); + spin_lock_irqsave(&u->lock, flags); + tty_kref_put(tty); + } + tegra_uart_start_rx_dma(tup); + + /* Activate flow control to start transfer */ + if (tup->rts_active) + set_rts(tup, true); + + spin_unlock_irqrestore(&u->lock, flags); +} + +static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, + unsigned long *flags) +{ + struct dma_tx_state state; + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); + struct tty_port *port = &tup->uport.state->port; + struct uart_port *u = &tup->uport; + int count; + + /* Deactivate flow control to stop sender */ + if (tup->rts_active) + set_rts(tup, false); + + dmaengine_terminate_all(tup->rx_dma_chan); + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + count = tup->rx_bytes_requested - state.residue; + + /* If we are here, DMA is stopped */ + if (count) + tegra_uart_copy_rx_to_tty(tup, port, count); + + tegra_uart_handle_rx_pio(tup, port); + if (tty) { + spin_unlock_irqrestore(&u->lock, *flags); + tty_flip_buffer_push(port); + spin_lock_irqsave(&u->lock, *flags); + tty_kref_put(tty); + } + tegra_uart_start_rx_dma(tup); + + if (tup->rts_active) + set_rts(tup, true); +} + +static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup) +{ + unsigned int count = TEGRA_UART_RX_DMA_BUFFER_SIZE; + + tup->rx_dma_desc = dmaengine_prep_slave_single(tup->rx_dma_chan, + tup->rx_dma_buf_phys, count, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!tup->rx_dma_desc) { + dev_err(tup->uport.dev, "Not able to get desc for Rx\n"); + return -EIO; + } + + tup->rx_dma_desc->callback = tegra_uart_rx_dma_complete; + tup->rx_dma_desc->callback_param = tup; + dma_sync_single_for_device(tup->uport.dev, tup->rx_dma_buf_phys, + count, DMA_TO_DEVICE); + tup->rx_bytes_requested = count; + tup->rx_cookie = dmaengine_submit(tup->rx_dma_desc); + dma_async_issue_pending(tup->rx_dma_chan); + return 0; +} + +static void tegra_uart_handle_modem_signal_change(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned long msr; + + msr = tegra_uart_read(tup, UART_MSR); + if (!(msr & UART_MSR_ANY_DELTA)) + return; + + if (msr & UART_MSR_TERI) + tup->uport.icount.rng++; + if (msr & UART_MSR_DDSR) + tup->uport.icount.dsr++; + /* We may only get DDCD when HW init and reset */ + if (msr & UART_MSR_DDCD) + uart_handle_dcd_change(&tup->uport, msr & UART_MSR_DCD); + /* Will start/stop_tx accordingly */ + if (msr & UART_MSR_DCTS) + uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS); + return; +} + +static irqreturn_t tegra_uart_isr(int irq, void *data) +{ + struct tegra_uart_port *tup = data; + struct uart_port *u = &tup->uport; + unsigned long iir; + unsigned long ier; + bool is_rx_int = false; + unsigned long flags; + + spin_lock_irqsave(&u->lock, flags); + while (1) { + iir = tegra_uart_read(tup, UART_IIR); + if (iir & UART_IIR_NO_INT) { + if (is_rx_int) { + tegra_uart_handle_rx_dma(tup, &flags); + if (tup->rx_in_progress) { + ier = tup->ier_shadow; + ier |= (UART_IER_RLSI | UART_IER_RTOIE | + TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + } + } + spin_unlock_irqrestore(&u->lock, flags); + return IRQ_HANDLED; + } + + switch ((iir >> 1) & 0x7) { + case 0: /* Modem signal change interrupt */ + tegra_uart_handle_modem_signal_change(u); + break; + + case 1: /* Transmit interrupt only triggered when using PIO */ + tup->ier_shadow &= ~UART_IER_THRI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + tegra_uart_handle_tx_pio(tup); + break; + + case 4: /* End of data */ + case 6: /* Rx timeout */ + case 2: /* Receive */ + if (!is_rx_int) { + is_rx_int = true; + /* Disable Rx interrupts */ + ier = tup->ier_shadow; + ier |= UART_IER_RDI; + tegra_uart_write(tup, ier, UART_IER); + ier &= ~(UART_IER_RDI | UART_IER_RLSI | + UART_IER_RTOIE | TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + } + break; + + case 3: /* Receive error */ + tegra_uart_decode_rx_error(tup, + tegra_uart_read(tup, UART_LSR)); + break; + + case 5: /* break nothing to handle */ + case 7: /* break nothing to handle */ + break; + } + } +} + +static void tegra_uart_stop_rx(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + struct tty_struct *tty; + struct tty_port *port = &u->state->port; + struct dma_tx_state state; + unsigned long ier; + int count; + + if (tup->rts_active) + set_rts(tup, false); + + if (!tup->rx_in_progress) + return; + + tty = tty_port_tty_get(&tup->uport.state->port); + + tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ + + ier = tup->ier_shadow; + ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | + TEGRA_UART_IER_EORD); + tup->ier_shadow = ier; + tegra_uart_write(tup, ier, UART_IER); + tup->rx_in_progress = 0; + if (tup->rx_dma_chan) { + dmaengine_terminate_all(tup->rx_dma_chan); + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + async_tx_ack(tup->rx_dma_desc); + count = tup->rx_bytes_requested - state.residue; + tegra_uart_copy_rx_to_tty(tup, port, count); + tegra_uart_handle_rx_pio(tup, port); + } else { + tegra_uart_handle_rx_pio(tup, port); + } + if (tty) { + tty_flip_buffer_push(port); + tty_kref_put(tty); + } + return; +} + +static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) +{ + unsigned long flags; + unsigned long char_time = DIV_ROUND_UP(10000000, tup->current_baud); + unsigned long fifo_empty_time = tup->uport.fifosize * char_time; + unsigned long wait_time; + unsigned long lsr; + unsigned long msr; + unsigned long mcr; + + /* Disable interrupts */ + tegra_uart_write(tup, 0, UART_IER); + + lsr = tegra_uart_read(tup, UART_LSR); + if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { + msr = tegra_uart_read(tup, UART_MSR); + mcr = tegra_uart_read(tup, UART_MCR); + if ((mcr & TEGRA_UART_MCR_CTS_EN) && (msr & UART_MSR_CTS)) + dev_err(tup->uport.dev, + "Tx Fifo not empty, CTS disabled, waiting\n"); + + /* Wait for Tx fifo to be empty */ + while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) { + wait_time = min(fifo_empty_time, 100lu); + udelay(wait_time); + fifo_empty_time -= wait_time; + if (!fifo_empty_time) { + msr = tegra_uart_read(tup, UART_MSR); + mcr = tegra_uart_read(tup, UART_MCR); + if ((mcr & TEGRA_UART_MCR_CTS_EN) && + (msr & UART_MSR_CTS)) + dev_err(tup->uport.dev, + "Slave not ready\n"); + break; + } + lsr = tegra_uart_read(tup, UART_LSR); + } + } + + spin_lock_irqsave(&tup->uport.lock, flags); + /* Reset the Rx and Tx FIFOs */ + tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); + tup->current_baud = 0; + spin_unlock_irqrestore(&tup->uport.lock, flags); + + clk_disable_unprepare(tup->uart_clk); +} + +static int tegra_uart_hw_init(struct tegra_uart_port *tup) +{ + int ret; + + tup->fcr_shadow = 0; + tup->mcr_shadow = 0; + tup->lcr_shadow = 0; + tup->ier_shadow = 0; + tup->current_baud = 0; + + clk_prepare_enable(tup->uart_clk); + + /* Reset the UART controller to clear all previous status.*/ + reset_control_assert(tup->rst); + udelay(10); + reset_control_deassert(tup->rst); + + tup->rx_in_progress = 0; + tup->tx_in_progress = 0; + + /* + * Set the trigger level + * + * For PIO mode: + * + * For receive, this will interrupt the CPU after that many number of + * bytes are received, for the remaining bytes the receive timeout + * interrupt is received. Rx high watermark is set to 4. + * + * For transmit, if the trasnmit interrupt is enabled, this will + * interrupt the CPU when the number of entries in the FIFO reaches the + * low watermark. Tx low watermark is set to 16 bytes. + * + * For DMA mode: + * + * Set the Tx trigger to 16. This should match the DMA burst size that + * programmed in the DMA registers. + */ + tup->fcr_shadow = UART_FCR_ENABLE_FIFO; + tup->fcr_shadow |= UART_FCR_R_TRIG_01; + tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B; + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + + /* + * Initialize the UART with default configuration + * (115200, N, 8, 1) so that the receive DMA buffer may be + * enqueued + */ + tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR; + tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD); + tup->fcr_shadow |= UART_FCR_DMA_SELECT; + tegra_uart_write(tup, tup->fcr_shadow, UART_FCR); + + ret = tegra_uart_start_rx_dma(tup); + if (ret < 0) { + dev_err(tup->uport.dev, "Not able to start Rx DMA\n"); + return ret; + } + tup->rx_in_progress = 1; + + /* + * Enable IE_RXS for the receive status interrupts like line errros. + * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd. + * + * If using DMA mode, enable EORD instead of receive interrupt which + * will interrupt after the UART is done with the receive instead of + * the interrupt when the FIFO "threshold" is reached. + * + * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when + * the DATA is sitting in the FIFO and couldn't be transferred to the + * DMA as the DMA size alignment(4 bytes) is not met. EORD will be + * triggered when there is a pause of the incomming data stream for 4 + * characters long. + * + * For pauses in the data which is not aligned to 4 bytes, we get + * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first + * then the EORD. + */ + tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + return 0; +} + +static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + unsigned char *dma_buf; + dma_addr_t dma_phys; + int ret; + struct dma_slave_config dma_sconfig; + + dma_chan = dma_request_slave_channel_reason(tup->uport.dev, + dma_to_memory ? "rx" : "tx"); + if (IS_ERR(dma_chan)) { + ret = PTR_ERR(dma_chan); + dev_err(tup->uport.dev, + "DMA channel alloc failed: %d\n", ret); + return ret; + } + + if (dma_to_memory) { + dma_buf = dma_alloc_coherent(tup->uport.dev, + TEGRA_UART_RX_DMA_BUFFER_SIZE, + &dma_phys, GFP_KERNEL); + if (!dma_buf) { + dev_err(tup->uport.dev, + "Not able to allocate the dma buffer\n"); + dma_release_channel(dma_chan); + return -ENOMEM; + } + } else { + dma_phys = dma_map_single(tup->uport.dev, + tup->uport.state->xmit.buf, UART_XMIT_SIZE, + DMA_TO_DEVICE); + dma_buf = tup->uport.state->xmit.buf; + } + + if (dma_to_memory) { + dma_sconfig.src_addr = tup->uport.mapbase; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.src_maxburst = 4; + } else { + dma_sconfig.dst_addr = tup->uport.mapbase; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + dma_sconfig.dst_maxburst = 16; + } + + ret = dmaengine_slave_config(dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tup->uport.dev, + "Dma slave config failed, err = %d\n", ret); + goto scrub; + } + + if (dma_to_memory) { + tup->rx_dma_chan = dma_chan; + tup->rx_dma_buf_virt = dma_buf; + tup->rx_dma_buf_phys = dma_phys; + } else { + tup->tx_dma_chan = dma_chan; + tup->tx_dma_buf_virt = dma_buf; + tup->tx_dma_buf_phys = dma_phys; + } + return 0; + +scrub: + dma_release_channel(dma_chan); + return ret; +} + +static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup, + bool dma_to_memory) +{ + struct dma_chan *dma_chan; + + if (dma_to_memory) { + dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE, + tup->rx_dma_buf_virt, tup->rx_dma_buf_phys); + dma_chan = tup->rx_dma_chan; + tup->rx_dma_chan = NULL; + tup->rx_dma_buf_phys = 0; + tup->rx_dma_buf_virt = NULL; + } else { + dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys, + UART_XMIT_SIZE, DMA_TO_DEVICE); + dma_chan = tup->tx_dma_chan; + tup->tx_dma_chan = NULL; + tup->tx_dma_buf_phys = 0; + tup->tx_dma_buf_virt = NULL; + } + dma_release_channel(dma_chan); +} + +static int tegra_uart_startup(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + int ret; + + ret = tegra_uart_dma_channel_allocate(tup, false); + if (ret < 0) { + dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret); + return ret; + } + + ret = tegra_uart_dma_channel_allocate(tup, true); + if (ret < 0) { + dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret); + goto fail_rx_dma; + } + + ret = tegra_uart_hw_init(tup); + if (ret < 0) { + dev_err(u->dev, "Uart HW init failed, err = %d\n", ret); + goto fail_hw_init; + } + + ret = request_irq(u->irq, tegra_uart_isr, 0, + dev_name(u->dev), tup); + if (ret < 0) { + dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq); + goto fail_hw_init; + } + return 0; + +fail_hw_init: + tegra_uart_dma_channel_free(tup, true); +fail_rx_dma: + tegra_uart_dma_channel_free(tup, false); + return ret; +} + +static void tegra_uart_shutdown(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + tegra_uart_hw_deinit(tup); + + tup->rx_in_progress = 0; + tup->tx_in_progress = 0; + + tegra_uart_dma_channel_free(tup, true); + tegra_uart_dma_channel_free(tup, false); + free_irq(u->irq, tup); +} + +static void tegra_uart_enable_ms(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + if (tup->enable_modem_interrupt) { + tup->ier_shadow |= UART_IER_MSI; + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + } +} + +static void tegra_uart_set_termios(struct uart_port *u, + struct ktermios *termios, struct ktermios *oldtermios) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + unsigned int baud; + unsigned long flags; + unsigned int lcr; + int symb_bit = 1; + struct clk *parent_clk = clk_get_parent(tup->uart_clk); + unsigned long parent_clk_rate = clk_get_rate(parent_clk); + int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF; + + max_divider *= 16; + spin_lock_irqsave(&u->lock, flags); + + /* Changing configuration, it is safe to stop any rx now */ + if (tup->rts_active) + set_rts(tup, false); + + /* Clear all interrupts as configuration is going to be change */ + tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER); + tegra_uart_read(tup, UART_IER); + tegra_uart_write(tup, 0, UART_IER); + tegra_uart_read(tup, UART_IER); + + /* Parity */ + lcr = tup->lcr_shadow; + lcr &= ~UART_LCR_PARITY; + + /* CMSPAR isn't supported by this driver */ + termios->c_cflag &= ~CMSPAR; + + if ((termios->c_cflag & PARENB) == PARENB) { + symb_bit++; + if (termios->c_cflag & PARODD) { + lcr |= UART_LCR_PARITY; + lcr &= ~UART_LCR_EPAR; + lcr &= ~UART_LCR_SPAR; + } else { + lcr |= UART_LCR_PARITY; + lcr |= UART_LCR_EPAR; + lcr &= ~UART_LCR_SPAR; + } + } + + lcr &= ~UART_LCR_WLEN8; + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr |= UART_LCR_WLEN5; + symb_bit += 5; + break; + case CS6: + lcr |= UART_LCR_WLEN6; + symb_bit += 6; + break; + case CS7: + lcr |= UART_LCR_WLEN7; + symb_bit += 7; + break; + default: + lcr |= UART_LCR_WLEN8; + symb_bit += 8; + break; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) { + lcr |= UART_LCR_STOP; + symb_bit += 2; + } else { + lcr &= ~UART_LCR_STOP; + symb_bit++; + } + + tegra_uart_write(tup, lcr, UART_LCR); + tup->lcr_shadow = lcr; + tup->symb_bit = symb_bit; + + /* Baud rate. */ + baud = uart_get_baud_rate(u, termios, oldtermios, + parent_clk_rate/max_divider, + parent_clk_rate/16); + spin_unlock_irqrestore(&u->lock, flags); + tegra_set_baudrate(tup, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + spin_lock_irqsave(&u->lock, flags); + + /* Flow control */ + if (termios->c_cflag & CRTSCTS) { + tup->mcr_shadow |= TEGRA_UART_MCR_CTS_EN; + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); + /* if top layer has asked to set rts active then do so here */ + if (tup->rts_active) + set_rts(tup, true); + } else { + tup->mcr_shadow &= ~TEGRA_UART_MCR_CTS_EN; + tup->mcr_shadow &= ~TEGRA_UART_MCR_RTS_EN; + tegra_uart_write(tup, tup->mcr_shadow, UART_MCR); + } + + /* update the port timeout based on new settings */ + uart_update_timeout(u, termios->c_cflag, baud); + + /* Make sure all write has completed */ + tegra_uart_read(tup, UART_IER); + + /* Reenable interrupt */ + tegra_uart_write(tup, tup->ier_shadow, UART_IER); + tegra_uart_read(tup, UART_IER); + + spin_unlock_irqrestore(&u->lock, flags); + return; +} + +/* + * Flush any TX data submitted for DMA and PIO. Called when the + * TX circular buffer is reset. + */ +static void tegra_uart_flush_buffer(struct uart_port *u) +{ + struct tegra_uart_port *tup = to_tegra_uport(u); + + tup->tx_bytes = 0; + if (tup->tx_dma_chan) + dmaengine_terminate_all(tup->tx_dma_chan); + return; +} + +static const char *tegra_uart_type(struct uart_port *u) +{ + return TEGRA_UART_TYPE; +} + +static struct uart_ops tegra_uart_ops = { + .tx_empty = tegra_uart_tx_empty, + .set_mctrl = tegra_uart_set_mctrl, + .get_mctrl = tegra_uart_get_mctrl, + .stop_tx = tegra_uart_stop_tx, + .start_tx = tegra_uart_start_tx, + .stop_rx = tegra_uart_stop_rx, + .flush_buffer = tegra_uart_flush_buffer, + .enable_ms = tegra_uart_enable_ms, + .break_ctl = tegra_uart_break_ctl, + .startup = tegra_uart_startup, + .shutdown = tegra_uart_shutdown, + .set_termios = tegra_uart_set_termios, + .type = tegra_uart_type, + .request_port = tegra_uart_request_port, + .release_port = tegra_uart_release_port, +}; + +static struct uart_driver tegra_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "tegra_hsuart", + .dev_name = "ttyTHS", + .cons = NULL, + .nr = TEGRA_UART_MAXIMUM, +}; + +static int tegra_uart_parse_dt(struct platform_device *pdev, + struct tegra_uart_port *tup) +{ + struct device_node *np = pdev->dev.of_node; + int port; + + port = of_alias_get_id(np, "serial"); + if (port < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port); + return port; + } + tup->uport.line = port; + + tup->enable_modem_interrupt = of_property_read_bool(np, + "nvidia,enable-modem-interrupt"); + return 0; +} + +static struct tegra_uart_chip_data tegra20_uart_chip_data = { + .tx_fifo_full_status = false, + .allow_txfifo_reset_fifo_mode = true, + .support_clk_src_div = false, +}; + +static struct tegra_uart_chip_data tegra30_uart_chip_data = { + .tx_fifo_full_status = true, + .allow_txfifo_reset_fifo_mode = false, + .support_clk_src_div = true, +}; + +static struct of_device_id tegra_uart_of_match[] = { + { + .compatible = "nvidia,tegra30-hsuart", + .data = &tegra30_uart_chip_data, + }, { + .compatible = "nvidia,tegra20-hsuart", + .data = &tegra20_uart_chip_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, tegra_uart_of_match); + +static int tegra_uart_probe(struct platform_device *pdev) +{ + struct tegra_uart_port *tup; + struct uart_port *u; + struct resource *resource; + int ret; + const struct tegra_uart_chip_data *cdata; + const struct of_device_id *match; + + match = of_match_device(tegra_uart_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + + tup = devm_kzalloc(&pdev->dev, sizeof(*tup), GFP_KERNEL); + if (!tup) { + dev_err(&pdev->dev, "Failed to allocate memory for tup\n"); + return -ENOMEM; + } + + ret = tegra_uart_parse_dt(pdev, tup); + if (ret < 0) + return ret; + + u = &tup->uport; + u->dev = &pdev->dev; + u->ops = &tegra_uart_ops; + u->type = PORT_TEGRA; + u->fifosize = 32; + tup->cdata = cdata; + + platform_set_drvdata(pdev, tup); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!resource) { + dev_err(&pdev->dev, "No IO memory resource\n"); + return -ENODEV; + } + + u->mapbase = resource->start; + u->membase = devm_ioremap_resource(&pdev->dev, resource); + if (IS_ERR(u->membase)) + return PTR_ERR(u->membase); + + tup->uart_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tup->uart_clk)) { + dev_err(&pdev->dev, "Couldn't get the clock\n"); + return PTR_ERR(tup->uart_clk); + } + + tup->rst = devm_reset_control_get(&pdev->dev, "serial"); + if (IS_ERR(tup->rst)) { + dev_err(&pdev->dev, "Couldn't get the reset\n"); + return PTR_ERR(tup->rst); + } + + u->iotype = UPIO_MEM32; + u->irq = platform_get_irq(pdev, 0); + u->regshift = 2; + ret = uart_add_one_port(&tegra_uart_driver, u); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add uart port, err %d\n", ret); + return ret; + } + return ret; +} + +static int tegra_uart_remove(struct platform_device *pdev) +{ + struct tegra_uart_port *tup = platform_get_drvdata(pdev); + struct uart_port *u = &tup->uport; + + uart_remove_one_port(&tegra_uart_driver, u); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_uart_suspend(struct device *dev) +{ + struct tegra_uart_port *tup = dev_get_drvdata(dev); + struct uart_port *u = &tup->uport; + + return uart_suspend_port(&tegra_uart_driver, u); +} + +static int tegra_uart_resume(struct device *dev) +{ + struct tegra_uart_port *tup = dev_get_drvdata(dev); + struct uart_port *u = &tup->uport; + + return uart_resume_port(&tegra_uart_driver, u); +} +#endif + +static const struct dev_pm_ops tegra_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_uart_suspend, tegra_uart_resume) +}; + +static struct platform_driver tegra_uart_platform_driver = { + .probe = tegra_uart_probe, + .remove = tegra_uart_remove, + .driver = { + .name = "serial-tegra", + .of_match_table = tegra_uart_of_match, + .pm = &tegra_uart_pm_ops, + }, +}; + +static int __init tegra_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&tegra_uart_driver); + if (ret < 0) { + pr_err("Could not register %s driver\n", + tegra_uart_driver.driver_name); + return ret; + } + + ret = platform_driver_register(&tegra_uart_platform_driver); + if (ret < 0) { + pr_err("Uart platform driver register failed, e = %d\n", ret); + uart_unregister_driver(&tegra_uart_driver); + return ret; + } + return 0; +} + +static void __exit tegra_uart_exit(void) +{ + pr_info("Unloading tegra uart driver\n"); + platform_driver_unregister(&tegra_uart_platform_driver); + uart_unregister_driver(&tegra_uart_driver); +} + +module_init(tegra_uart_init); +module_exit(tegra_uart_exit); + +MODULE_ALIAS("platform:serial-tegra"); +MODULE_DESCRIPTION("High speed UART driver for tegra chipset"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2c7230aaefd..fbf6c5ad222 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -50,16 +50,11 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); -static void uart_change_pm(struct uart_state *state, int pm_state); +static void uart_change_pm(struct uart_state *state, + enum uart_pm_state pm_state); static void uart_port_shutdown(struct tty_port *port); @@ -94,8 +89,7 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (!uart_circ_empty(&state->xmit) && state->xmit.buf && - !tty->stopped && !tty->hw_stopped) + if (!tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); } @@ -143,6 +137,11 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, return 1; /* + * Make sure the device is in D0 state. + */ + uart_change_pm(state, UART_PM_STATE_ON); + + /* * Initialise and allocate the transmit and temporary * buffer. */ @@ -175,8 +174,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (tty->termios.c_cflag & CBAUD) uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - - if (tty_port_cts_enabled(port)) { + /* + * if hw support flow control without software intervention, + * then skip the below check + */ + if (tty_port_cts_enabled(port) && + !(uport->flags & UPF_HARD_FLOW)) { spin_lock_irq(&uport->lock); if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) tty->hw_stopped = 1; @@ -831,25 +834,29 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, * If we fail to request resources for the * new port, try to restore the old settings. */ - if (retval && old_type != PORT_UNKNOWN) { + if (retval) { uport->iobase = old_iobase; uport->type = old_type; uport->hub6 = old_hub6; uport->iotype = old_iotype; uport->regshift = old_shift; uport->mapbase = old_mapbase; - retval = uport->ops->request_port(uport); - /* - * If we failed to restore the old settings, - * we fail like this. - */ - if (retval) - uport->type = PORT_UNKNOWN; - /* - * We failed anyway. - */ - retval = -EBUSY; + if (old_type != PORT_UNKNOWN) { + retval = uport->ops->request_port(uport); + /* + * If we failed to restore the old settings, + * we fail like this. + */ + if (retval) + uport->type = PORT_UNKNOWN; + + /* + * We failed anyway. + */ + retval = -EBUSY; + } + /* Added to return the correct error -Ram Gupta */ goto exit; } @@ -866,9 +873,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port, port->closing_wait = closing_wait; if (new_info->xmit_fifo_size) uport->fifosize = new_info->xmit_fifo_size; - if (port->tty) - port->tty->low_latency = - (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; + port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; @@ -1308,9 +1313,10 @@ static void uart_set_termios(struct tty_struct *tty, } /* - * In 2.4.5, calls to this will be serialized via the BKL in - * linux/drivers/char/tty_io.c:tty_release() - * linux/drivers/char/tty_io.c:do_tty_handup() + * 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) { @@ -1325,9 +1331,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uport = state->uart_port; port = &state->port; - pr_debug("uart_close(%d) called\n", uport->line); + pr_debug("uart_close(%d) called\n", uport ? uport->line : -1); - if (tty_port_close_start(port, tty, filp) == 0) + if (!port->count || tty_port_close_start(port, tty, filp) == 0) return; /* @@ -1365,7 +1371,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&port->lock, flags); } else if (!uart_console(uport)) { spin_unlock_irqrestore(&port->lock, flags); - uart_change_pm(state, 3); + uart_change_pm(state, UART_PM_STATE_OFF); spin_lock_irqsave(&port->lock, flags); } @@ -1437,10 +1443,9 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) } /* - * This is called with the BKL held in - * linux/drivers/char/tty_io.c:do_tty_hangup() - * We're called from the eventd thread, so we can sleep for - * a _short_ time only. + * Calls to uart_hangup() are serialised by the tty_lock in + * drivers/tty/tty_io.c:do_tty_hangup() + * This runs from a workqueue and can sleep for a _short_ time only. */ static void uart_hangup(struct tty_struct *tty) { @@ -1459,6 +1464,8 @@ static void uart_hangup(struct tty_struct *tty) clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); spin_unlock_irqrestore(&port->lock, flags); tty_port_tty_set(port, NULL); + if (!uart_console(state->uart_port)) + uart_change_pm(state, UART_PM_STATE_OFF); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); } @@ -1521,8 +1528,8 @@ static void uart_dtr_rts(struct tty_port *port, int onoff) } /* - * calls to uart_open are serialised by the BKL in - * fs/char_dev.c:chrdev_open() + * Calls to uart_open are serialised by the tty_lock in + * drivers/tty/tty_io.c:tty_open() * Note that if this fails, then uart_close() _will_ be called. * * In time, we want to scrap the "opening nonpresent ports" @@ -1564,7 +1571,8 @@ static int uart_open(struct tty_struct *tty, struct file *filp) */ tty->driver_data = state; state->uart_port->state = state; - tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; + state->port.low_latency = + (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty_port_tty_set(port, tty); /* @@ -1576,12 +1584,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) } /* - * Make sure the device is in D0 state. - */ - if (port->count == 1) - uart_change_pm(state, 0); - - /* * Start up the serial port. */ retval = uart_startup(tty, state, 0); @@ -1620,7 +1622,7 @@ 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; - int pm_state; + enum uart_pm_state pm_state; struct uart_port *uport = state->uart_port; char stat_buf[32]; unsigned int status; @@ -1645,12 +1647,12 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) if (capable(CAP_SYS_ADMIN)) { mutex_lock(&port->mutex); pm_state = state->pm_state; - if (pm_state) - uart_change_pm(state, 0); + if (pm_state != UART_PM_STATE_ON) + uart_change_pm(state, UART_PM_STATE_ON); spin_lock_irq(&uport->lock); status = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); - if (pm_state) + if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, pm_state); mutex_unlock(&port->mutex); @@ -1711,7 +1713,7 @@ static int uart_proc_show(struct seq_file *m, void *v) static int uart_proc_open(struct inode *inode, struct file *file) { - return single_open(file, uart_proc_show, PDE(inode)->data); + return single_open(file, uart_proc_show, PDE_DATA(inode)); } static const struct file_operations uart_proc_fops = { @@ -1768,7 +1770,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) } /** - * uart_parse_options - Parse serial port baud/parity/bits/flow contro. + * uart_parse_options - Parse serial port baud/parity/bits/flow control. * @options: pointer to option string * @baud: pointer to an 'int' variable for the baud rate. * @parity: pointer to an 'int' variable for the parity. @@ -1836,9 +1838,13 @@ uart_set_options(struct uart_port *port, struct console *co, /* * Ensure that the serial console lock is initialised * early. + * If this port is a console, then the spinlock is already + * initialised. */ - spin_lock_init(&port->lock); - lockdep_set_class(&port->lock, &port_lock_key); + if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) { + spin_lock_init(&port->lock); + lockdep_set_class(&port->lock, &port_lock_key); + } memset(&termios, 0, sizeof(struct ktermios)); @@ -1897,7 +1903,8 @@ EXPORT_SYMBOL_GPL(uart_set_options); * * Locking: port->mutex has to be held */ -static void uart_change_pm(struct uart_state *state, int pm_state) +static void uart_change_pm(struct uart_state *state, + enum uart_pm_state pm_state) { struct uart_port *port = state->uart_port; @@ -1940,6 +1947,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); + if (console_suspend_enabled || !uart_console(uport)) uport->suspended = 1; @@ -1982,7 +1991,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) console_stop(uport->cons); if (console_suspend_enabled || !uart_console(uport)) - uart_change_pm(state, 3); + uart_change_pm(state, UART_PM_STATE_OFF); mutex_unlock(&port->mutex); @@ -2005,9 +2014,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) disable_irq_wake(uport->irq); uport->irq_wake = 0; } + put_device(tty_dev); mutex_unlock(&port->mutex); return 0; } + put_device(tty_dev); uport->suspended = 0; /* @@ -2027,7 +2038,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) termios = port->tty->termios; if (console_suspend_enabled) - uart_change_pm(state, 0); + uart_change_pm(state, UART_PM_STATE_ON); uport->ops->set_termios(uport, &termios, NULL); if (console_suspend_enabled) console_start(uport->cons); @@ -2037,7 +2048,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) const struct uart_ops *ops = uport->ops; int ret; - uart_change_pm(state, 0); + uart_change_pm(state, UART_PM_STATE_ON); spin_lock_irq(&uport->lock); ops->set_mctrl(uport, 0); spin_unlock_irq(&uport->lock); @@ -2096,12 +2107,12 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; } - printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", + printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n", port->dev ? dev_name(port->dev) : "", port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line, - address, port->irq, uart_type(port)); + address, port->irq, port->uartclk / 16, uart_type(port)); } static void @@ -2137,7 +2148,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, uart_report_port(drv, port); /* Power up port for set_mctrl() */ - uart_change_pm(state, 0); + uart_change_pm(state, UART_PM_STATE_ON); /* * Ensure that the modem control lines are de-activated. @@ -2161,7 +2172,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, * console if we have one. */ if (!uart_console(port)) - uart_change_pm(state, 3); + uart_change_pm(state, UART_PM_STATE_OFF); } } @@ -2232,6 +2243,9 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) return; port = state->uart_port; + + if (ch == '\n') + port->ops->poll_put_char(port, '\r'); port->ops->poll_put_char(port, ch); } #endif @@ -2588,7 +2602,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) } state->uart_port = uport; - state->pm_state = -1; + state->pm_state = UART_PM_STATE_UNDEFINED; uport->cons = drv->cons; uport->state = state; @@ -2606,7 +2620,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) /* * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. + * setserial to be used to alter this port's parameters. */ tty_dev = tty_port_register_device_attr(port, drv->tty_driver, uport->line, uport->dev, port, tty_dev_attr_groups); @@ -2642,6 +2656,8 @@ 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 tty_struct *tty; + int ret = 0; BUG_ON(in_interrupt()); @@ -2656,6 +2672,11 @@ 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) { + mutex_unlock(&port->mutex); + ret = -EINVAL; + goto out; + } uport->flags |= UPF_DEAD; mutex_unlock(&port->mutex); @@ -2664,8 +2685,17 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) */ tty_unregister_device(drv->tty_driver, uport->line); - if (port->tty) + tty = tty_port_tty_get(port); + if (tty) { tty_vhangup(port->tty); + tty_kref_put(tty); + } + + /* + * If the port is used as a console, unregister it + */ + if (uart_console(uport)) + unregister_console(uport->cons); /* * Free the port IO and memory resources, if any. @@ -2679,9 +2709,10 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) uport->type = PORT_UNKNOWN; state->uart_port = NULL; +out: mutex_unlock(&port_mutex); - return 0; + return ret; } /* @@ -2715,22 +2746,17 @@ EXPORT_SYMBOL(uart_match_port); */ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { - struct uart_state *state = uport->state; - struct tty_port *port = &state->port; - struct tty_ldisc *ld = NULL; - struct pps_event_time ts; + struct tty_port *port = &uport->state->port; struct tty_struct *tty = port->tty; + struct tty_ldisc *ld = tty ? tty_ldisc_ref(tty) : NULL; - if (tty) - ld = tty_ldisc_ref(tty); - if (ld && ld->ops->dcd_change) - pps_get_ts(&ts); + if (ld) { + if (ld->ops->dcd_change) + ld->ops->dcd_change(tty, status); + tty_ldisc_deref(ld); + } uport->icount.dcd++; -#ifdef CONFIG_HARD_PPS - if ((uport->flags & UPF_HARDPPS_CD) && status) - hardpps(); -#endif if (port->flags & ASYNC_CHECK_CD) { if (status) @@ -2738,11 +2764,6 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) else if (tty) tty_hangup(tty); } - - if (ld && ld->ops->dcd_change) - ld->ops->dcd_change(tty, status, &ts); - if (ld) - tty_ldisc_deref(ld); } EXPORT_SYMBOL_GPL(uart_handle_dcd_change); @@ -2758,7 +2779,9 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->icount.cts++; - if (tty_port_cts_enabled(port)) { + /* skip below code if the hw flow control is supported */ + if (tty_port_cts_enabled(port) && + !(uport->flags & UPF_HARD_FLOW)) { if (tty->hw_stopped) { if (status) { tty->hw_stopped = 0; @@ -2790,10 +2813,10 @@ EXPORT_SYMBOL_GPL(uart_handle_cts_change); void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; if ((status & port->ignore_status_mask & ~overrun) == 0) - if (tty_insert_flip_char(tty, ch, flag) == 0) + if (tty_insert_flip_char(tport, ch, flag) == 0) ++port->icount.buf_overrun; /* @@ -2801,7 +2824,7 @@ void uart_insert_char(struct uart_port *port, unsigned int status, * it doesn't affect the current character. */ if (status & ~port->ignore_status_mask & overrun) - if (tty_insert_flip_char(tty, 0, TTY_OVERRUN) == 0) + if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0) ++port->icount.buf_overrun; } EXPORT_SYMBOL_GPL(uart_insert_char); diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index 9bd004f9da8..5c79bdab985 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -153,7 +153,6 @@ static void ks8695uart_disable_ms(struct uart_port *port) static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, lsr, flg, max_count = 256; status = UART_GET_LSR(port); /* clears pending LSR interrupts */ @@ -200,7 +199,7 @@ static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id) ignore_char: status = UART_GET_LSR(port); } - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); return IRQ_HANDLED; } @@ -438,7 +437,7 @@ static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *term port->read_status_mask = URLS_URROE; if (termios->c_iflag & INPCK) port->read_status_mask |= (URLS_URFE | URLS_URPE); - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= URLS_URBI; /* diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c new file mode 100644 index 00000000000..bf9560ffe3f --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -0,0 +1,143 @@ +/* + * Helpers for controlling modem lines via GPIO + * + * Copyright (C) 2014 Paratronic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <uapi/asm-generic/termios.h> + +#include "serial_mctrl_gpio.h" + +struct mctrl_gpios { + struct gpio_desc *gpio[UART_GPIO_MAX]; +}; + +static const struct { + const char *name; + unsigned int mctrl; + bool dir_out; +} mctrl_gpios_desc[UART_GPIO_MAX] = { + { "cts", TIOCM_CTS, false, }, + { "dsr", TIOCM_DSR, false, }, + { "dcd", TIOCM_CD, false, }, + { "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) +{ + enum mctrl_gpio_idx i; + + if (IS_ERR_OR_NULL(gpios)) + return; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (!IS_ERR_OR_NULL(gpios->gpio[i]) && + mctrl_gpios_desc[i].dir_out) + gpiod_set_value(gpios->gpio[i], + !!(mctrl & mctrl_gpios_desc[i].mctrl)); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_set); + +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx) +{ + if (!IS_ERR_OR_NULL(gpios) && !IS_ERR_OR_NULL(gpios->gpio[gidx])) + return gpios->gpio[gidx]; + else + return NULL; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod); + +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) +{ + enum mctrl_gpio_idx i; + + /* + * return it unchanged if the structure is not allocated + */ + if (IS_ERR_OR_NULL(gpios)) + return *mctrl; + + for (i = 0; i < UART_GPIO_MAX; i++) { + if (!IS_ERR_OR_NULL(gpios->gpio[i]) && + !mctrl_gpios_desc[i].dir_out) { + if (gpiod_get_value(gpios->gpio[i])) + *mctrl |= mctrl_gpios_desc[i].mctrl; + else + *mctrl &= ~mctrl_gpios_desc[i].mctrl; + } + } + + return *mctrl; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_get); + +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +{ + struct mctrl_gpios *gpios; + enum mctrl_gpio_idx i; + int err; + + gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); + if (!gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < UART_GPIO_MAX; i++) { + gpios->gpio[i] = devm_gpiod_get_index(dev, + mctrl_gpios_desc[i].name, + idx); + + /* + * The GPIOs are maybe not all filled, + * this is not an error. + */ + if (IS_ERR_OR_NULL(gpios->gpio[i])) + continue; + + if (mctrl_gpios_desc[i].dir_out) + err = gpiod_direction_output(gpios->gpio[i], 0); + else + err = gpiod_direction_input(gpios->gpio[i]); + if (err) { + dev_dbg(dev, "Unable to set direction for %s GPIO", + mctrl_gpios_desc[i].name); + devm_gpiod_put(dev, gpios->gpio[i]); + gpios->gpio[i] = NULL; + } + } + + return gpios; +} +EXPORT_SYMBOL_GPL(mctrl_gpio_init); + +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + if (IS_ERR_OR_NULL(gpios)) + return; + + for (i = 0; i < UART_GPIO_MAX; i++) + if (!IS_ERR_OR_NULL(gpios->gpio[i])) + devm_gpiod_put(dev, gpios->gpio[i]); + devm_kfree(dev, gpios); +} +EXPORT_SYMBOL_GPL(mctrl_gpio_free); diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h new file mode 100644 index 00000000000..400ba0494a1 --- /dev/null +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -0,0 +1,110 @@ +/* + * Helpers for controlling modem lines via GPIO + * + * Copyright (C) 2014 Paratronic S.A. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SERIAL_MCTRL_GPIO__ +#define __SERIAL_MCTRL_GPIO__ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> + +enum mctrl_gpio_idx { + UART_GPIO_CTS, + UART_GPIO_DSR, + UART_GPIO_DCD, + UART_GPIO_RNG, + UART_GPIO_RI = UART_GPIO_RNG, + UART_GPIO_RTS, + UART_GPIO_DTR, + UART_GPIO_OUT1, + UART_GPIO_OUT2, + UART_GPIO_MAX, +}; + +/* + * Opaque descriptor for modem lines controlled by GPIOs + */ +struct mctrl_gpios; + +#ifdef CONFIG_GPIOLIB + +/* + * Set state of the modem control output lines via GPIOs. + */ +void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl); + +/* + * Get state of the modem control output lines from GPIOs. + * The mctrl flags are updated and returned. + */ +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl); + +/* + * Returns the associated struct gpio_desc to the modem line gidx + */ +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx); + +/* + * Request and set direction of modem control lines GPIOs. + * devm_* functions are used, so there's no need to call mctrl_gpio_free(). + * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on + * allocation error. + */ +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx); + +/* + * Free the mctrl_gpios structure. + * Normally, this function will not be called, as the GPIOs will + * be disposed of by the resource management code. + */ +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios); + +#else /* GPIOLIB */ + +static inline +void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) +{ +} + +static inline +unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) +{ + return *mctrl; +} + +static inline +struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, + enum mctrl_gpio_idx gidx) +{ + return ERR_PTR(-ENOSYS); +} + +static inline +struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +{ + return ERR_PTR(-ENOSYS); +} + +static inline +void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) +{ +} + +#endif /* GPIOLIB */ + +#endif diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index b52b21aeb25..ea8546092c7 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -277,7 +277,6 @@ static void serial_txx9_initialize(struct uart_port *port) static inline void receive_chars(struct uart_txx9_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.state->port.tty; unsigned char ch; unsigned int disr = *status; int max_count = 256; @@ -346,7 +345,7 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status) disr = sio_in(up, TXX9_SIDISR); } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&up->port.state->port); spin_lock(&up->port.lock); *status = disr; } @@ -536,13 +535,8 @@ static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) wait_for_xmitr(up); /* * Send the character out. - * If a LF, also do CR... */ sio_out(up, TXX9_SITFIFO, c); - if (c == 10) { - wait_for_xmitr(up); - sio_out(up, TXX9_SITFIFO, 13); - } /* * Finally, wait for transmitter to become empty @@ -703,7 +697,7 @@ serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS; if (termios->c_iflag & INPCK) up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= TXX9_SIDISR_UBRK; /* @@ -1098,7 +1092,7 @@ static void serial_txx9_unregister_port(int line) */ static int serial_txx9_probe(struct platform_device *dev) { - struct uart_port *p = dev->dev.platform_data; + struct uart_port *p = dev_get_platdata(&dev->dev); struct uart_port port; int ret, i; @@ -1221,8 +1215,6 @@ static void pciserial_txx9_remove_one(struct pci_dev *dev) { struct uart_txx9_port *up = pci_get_drvdata(dev); - pci_set_drvdata(dev, NULL); - if (up) { serial_txx9_unregister_port(up->port.line); pci_disable_device(dev); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 61477567423..88236da0ddf 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -23,35 +23,35 @@ #undef DEBUG -#include <linux/module.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/ctype.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> #include <linux/errno.h> -#include <linux/sh_dma.h> -#include <linux/timer.h> +#include <linux/init.h> #include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/sysrq.h> #include <linux/ioport.h> +#include <linux/major.h> +#include <linux/module.h> #include <linux/mm.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/console.h> -#include <linux/platform_device.h> -#include <linux/serial_sci.h> #include <linux/notifier.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> -#include <linux/cpufreq.h> -#include <linux/clk.h> -#include <linux/ctype.h> -#include <linux/err.h> -#include <linux/dmaengine.h> -#include <linux/dma-mapping.h> #include <linux/scatterlist.h> +#include <linux/serial.h> +#include <linux/serial_sci.h> +#include <linux/sh_dma.h> #include <linux/slab.h> -#include <linux/gpio.h> +#include <linux/string.h> +#include <linux/sysrq.h> +#include <linux/timer.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #ifdef CONFIG_SUPERH #include <asm/sh_bios.h> @@ -59,11 +59,32 @@ #include "sh-sci.h" +/* Offsets into the sci_port->irqs array */ +enum { + SCIx_ERI_IRQ, + SCIx_RXI_IRQ, + SCIx_TXI_IRQ, + SCIx_BRI_IRQ, + SCIx_NR_IRQS, + + SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ +}; + +#define SCIx_IRQ_IS_MUXED(port) \ + ((port)->irqs[SCIx_ERI_IRQ] == \ + (port)->irqs[SCIx_RXI_IRQ]) || \ + ((port)->irqs[SCIx_ERI_IRQ] && \ + ((port)->irqs[SCIx_RXI_IRQ] < 0)) + struct sci_port { struct uart_port port; /* Platform configuration */ struct plat_sci_port *cfg; + int overrun_bit; + unsigned int error_mask; + unsigned int sampling_rate; + /* Break timer */ struct timer_list break_timer; @@ -74,8 +95,8 @@ struct sci_port { /* Function clock */ struct clk *fclk; + int irqs[SCIx_NR_IRQS]; char *irqstr[SCIx_NR_IRQS]; - char *gpiostr[SCIx_NR_FNS]; struct dma_chan *chan_tx; struct dma_chan *chan_rx; @@ -146,6 +167,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -165,6 +187,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -183,6 +206,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -201,6 +225,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = { 0x3c, 16 }, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -220,6 +245,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -238,6 +264,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, /* @@ -256,6 +283,26 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = { 0x20, 16 }, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, + }, + + /* + * Common HSCIF definitions. + */ + [SCIx_HSCIF_REGTYPE] = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x04, 8 }, + [SCSCR] = { 0x08, 16 }, + [SCxTDR] = { 0x0c, 8 }, + [SCxSR] = { 0x10, 16 }, + [SCxRDR] = { 0x14, 8 }, + [SCFCR] = { 0x18, 16 }, + [SCFDR] = { 0x1c, 16 }, + [SCTFDR] = sci_reg_invalid, + [SCRFDR] = sci_reg_invalid, + [SCSPTR] = { 0x20, 16 }, + [SCLSR] = { 0x24, 16 }, + [HSSRR] = { 0x40, 16 }, }, /* @@ -275,6 +322,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = { 0x24, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -294,6 +342,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = { 0x20, 16 }, [SCSPTR] = { 0x24, 16 }, [SCLSR] = { 0x28, 16 }, + [HSSRR] = sci_reg_invalid, }, /* @@ -313,6 +362,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCRFDR] = sci_reg_invalid, [SCSPTR] = sci_reg_invalid, [SCLSR] = sci_reg_invalid, + [HSSRR] = sci_reg_invalid, }, }; @@ -374,8 +424,11 @@ static int sci_probe_regmap(struct plat_sci_port *cfg) */ cfg->regtype = SCIx_SH4_SCIF_REGTYPE; break; + case PORT_HSCIF: + cfg->regtype = SCIx_HSCIF_REGTYPE; + break; default: - printk(KERN_ERR "Can't probe register map for given port\n"); + pr_err("Can't probe register map for given port\n"); return -EINVAL; } @@ -389,9 +442,9 @@ static void sci_port_enable(struct sci_port *sci_port) pm_runtime_get_sync(sci_port->port.dev); - clk_enable(sci_port->iclk); + clk_prepare_enable(sci_port->iclk); sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - clk_enable(sci_port->fclk); + clk_prepare_enable(sci_port->fclk); } static void sci_port_disable(struct sci_port *sci_port) @@ -399,8 +452,16 @@ static void sci_port_disable(struct sci_port *sci_port) if (!sci_port->port.dev) return; - clk_disable(sci_port->fclk); - clk_disable(sci_port->iclk); + /* Cancel the break timer to ensure that the timer handler will not try + * to access the hardware with clocks and power disabled. Reset the + * break flag to make the break debouncing state machine ready for the + * next break. + */ + del_timer_sync(&sci_port->break_timer); + sci_port->break_flag = 0; + + clk_disable_unprepare(sci_port->fclk); + clk_disable_unprepare(sci_port->iclk); pm_runtime_put_sync(sci_port->port.dev); } @@ -525,7 +586,7 @@ static inline int sci_rxd_in(struct uart_port *port) return 1; /* Cast for ARM damage */ - return !!__raw_readb((void __iomem *)s->cfg->port_reg); + return !!__raw_readb((void __iomem *)(uintptr_t)s->cfg->port_reg); } /* ********************************************************************** * @@ -596,7 +657,7 @@ static void sci_transmit_chars(struct uart_port *port) static void sci_receive_chars(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; int i, count, copied = 0; unsigned short status; unsigned char flag; @@ -607,7 +668,7 @@ static void sci_receive_chars(struct uart_port *port) while (1) { /* Don't copy more bytes than there is room for in the buffer */ - count = tty_buffer_request_room(tty, sci_rxfill(port)); + count = tty_buffer_request_room(tport, sci_rxfill(port)); /* If for any reason we can't copy more data, we're done! */ if (count == 0) @@ -619,7 +680,7 @@ static void sci_receive_chars(struct uart_port *port) sci_port->break_flag) count = 0; else - tty_insert_flip_char(tty, c, TTY_NORMAL); + tty_insert_flip_char(tport, c, TTY_NORMAL); } else { for (i = 0; i < count; i++) { char c = serial_port_in(port, SCxRDR); @@ -661,7 +722,7 @@ static void sci_receive_chars(struct uart_port *port) } else flag = TTY_NORMAL; - tty_insert_flip_char(tty, c, flag); + tty_insert_flip_char(tport, c, flag); } } @@ -674,7 +735,7 @@ static void sci_receive_chars(struct uart_port *port) if (copied) { /* Tell the rest of the system the news. New characters! */ - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); } else { serial_port_in(port, SCxSR); /* dummy read */ serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); @@ -701,8 +762,6 @@ static void sci_break_timer(unsigned long data) { struct sci_port *port = (struct sci_port *)data; - sci_port_enable(port); - if (sci_rxd_in(&port->port) == 0) { port->break_flag = 1; sci_schedule_break_timer(port); @@ -712,30 +771,24 @@ static void sci_break_timer(unsigned long data) sci_schedule_break_timer(port); } else port->break_flag = 0; - - sci_port_disable(port); } static int sci_handle_errors(struct uart_port *port) { int copied = 0; unsigned short status = serial_port_in(port, SCxSR); - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); - /* - * Handle overruns, if supported. - */ - if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) { - if (status & (1 << s->cfg->overrun_bit)) { - port->icount.overrun++; + /* Handle overruns */ + if (status & (1 << s->overrun_bit)) { + port->icount.overrun++; - /* overrun error */ - if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) - copied++; + /* overrun error */ + if (tty_insert_flip_char(tport, 0, TTY_OVERRUN)) + copied++; - dev_notice(port->dev, "overrun error"); - } + dev_notice(port->dev, "overrun error\n"); } if (status & SCxSR_FER(port)) { @@ -755,7 +808,7 @@ static int sci_handle_errors(struct uart_port *port) dev_dbg(port->dev, "BREAK detected\n"); - if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + if (tty_insert_flip_char(tport, 0, TTY_BREAK)) copied++; } @@ -763,7 +816,7 @@ static int sci_handle_errors(struct uart_port *port) /* frame error */ port->icount.frame++; - if (tty_insert_flip_char(tty, 0, TTY_FRAME)) + if (tty_insert_flip_char(tport, 0, TTY_FRAME)) copied++; dev_notice(port->dev, "frame error\n"); @@ -774,21 +827,21 @@ static int sci_handle_errors(struct uart_port *port) /* parity error */ port->icount.parity++; - if (tty_insert_flip_char(tty, 0, TTY_PARITY)) + if (tty_insert_flip_char(tport, 0, TTY_PARITY)) copied++; - dev_notice(port->dev, "parity error"); + dev_notice(port->dev, "parity error\n"); } if (copied) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); return copied; } static int sci_handle_fifo_overrun(struct uart_port *port) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; int copied = 0; @@ -797,13 +850,13 @@ static int sci_handle_fifo_overrun(struct uart_port *port) if (!reg->size) return 0; - if ((serial_port_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) { + if ((serial_port_in(port, SCLSR) & (1 << s->overrun_bit))) { serial_port_out(port, SCLSR, 0); port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_flip_buffer_push(tty); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + tty_flip_buffer_push(tport); dev_notice(port->dev, "overrun error\n"); copied++; @@ -816,7 +869,7 @@ static int sci_handle_breaks(struct uart_port *port) { int copied = 0; unsigned short status = serial_port_in(port, SCxSR); - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); if (uart_handle_break(port)) @@ -831,14 +884,14 @@ static int sci_handle_breaks(struct uart_port *port) port->icount.brk++; /* Notify of BREAK */ - if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + if (tty_insert_flip_char(tport, 0, TTY_BREAK)) copied++; dev_dbg(port->dev, "BREAK detected\n"); } if (copied) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); copied += sci_handle_fifo_overrun(port); @@ -858,7 +911,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) /* Disable future Rx interrupts */ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { disable_irq_nosync(irq); - scr |= 0x4000; + scr |= SCSCR_RDRQE; } else { scr &= ~SCSCR_RIE; } @@ -988,8 +1041,7 @@ static int sci_notifier(struct notifier_block *self, sci_port = container_of(self, struct sci_port, freq_transition); - if ((phase == CPUFREQ_POSTCHANGE) || - (phase == CPUFREQ_RESUMECHANGE)) { + if (phase == CPUFREQ_POSTCHANGE) { struct uart_port *port = &sci_port->port; spin_lock_irqsave(&port->lock, flags); @@ -1043,19 +1095,19 @@ static int sci_request_irq(struct sci_port *port) for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { struct sci_irq_desc *desc; - unsigned int irq; + int irq; if (SCIx_IRQ_IS_MUXED(port)) { i = SCIx_MUX_IRQ; irq = up->irq; } else { - irq = port->cfg->irqs[i]; + irq = port->irqs[i]; /* * Certain port types won't support all of the * available interrupt sources. */ - if (unlikely(!irq)) + if (unlikely(irq < 0)) continue; } @@ -1080,7 +1132,7 @@ static int sci_request_irq(struct sci_port *port) out_noirq: while (--i >= 0) - free_irq(port->cfg->irqs[i], port); + free_irq(port->irqs[i], port); out_nomem: while (--j >= 0) @@ -1098,16 +1150,16 @@ static void sci_free_irq(struct sci_port *port) * IRQ first. */ for (i = 0; i < SCIx_NR_IRQS; i++) { - unsigned int irq = port->cfg->irqs[i]; + int irq = port->irqs[i]; /* * Certain port types won't support all of the available * interrupt sources. */ - if (unlikely(!irq)) + if (unlikely(irq < 0)) continue; - free_irq(port->cfg->irqs[i], port); + free_irq(port->irqs[i], port); kfree(port->irqstr[i]); if (SCIx_IRQ_IS_MUXED(port)) { @@ -1117,67 +1169,6 @@ static void sci_free_irq(struct sci_port *port) } } -static const char *sci_gpio_names[SCIx_NR_FNS] = { - "sck", "rxd", "txd", "cts", "rts", -}; - -static const char *sci_gpio_str(unsigned int index) -{ - return sci_gpio_names[index]; -} - -static void sci_init_gpios(struct sci_port *port) -{ - struct uart_port *up = &port->port; - int i; - - if (!port->cfg) - return; - - for (i = 0; i < SCIx_NR_FNS; i++) { - const char *desc; - int ret; - - if (!port->cfg->gpios[i]) - continue; - - desc = sci_gpio_str(i); - - port->gpiostr[i] = kasprintf(GFP_KERNEL, "%s:%s", - dev_name(up->dev), desc); - - /* - * If we've failed the allocation, we can still continue - * on with a NULL string. - */ - if (!port->gpiostr[i]) - dev_notice(up->dev, "%s string allocation failure\n", - desc); - - ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]); - if (unlikely(ret != 0)) { - dev_notice(up->dev, "failed %s gpio request\n", desc); - - /* - * If we can't get the GPIO for whatever reason, - * no point in keeping the verbose string around. - */ - kfree(port->gpiostr[i]); - } - } -} - -static void sci_free_gpios(struct sci_port *port) -{ - int i; - - for (i = 0; i < SCIx_NR_FNS; i++) - if (port->cfg->gpios[i]) { - gpio_free(port->cfg->gpios[i]); - kfree(port->gpiostr[i]); - } -} - static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = serial_port_in(port, SCxSR); @@ -1208,7 +1199,9 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) */ reg = sci_getreg(port, SCFCR); if (reg->size) - serial_port_out(port, SCFCR, serial_port_in(port, SCFCR) | 1); + serial_port_out(port, SCFCR, + serial_port_in(port, SCFCR) | + SCFCR_LOOP); } } @@ -1259,13 +1252,13 @@ static void sci_dma_tx_complete(void *arg) } /* Locking: called with port lock held */ -static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, - size_t count) +static int sci_dma_rx_push(struct sci_port *s, size_t count) { struct uart_port *port = &s->port; + struct tty_port *tport = &port->state->port; int i, active, room; - room = tty_buffer_request_room(tty, count); + room = tty_buffer_request_room(tport, count); if (s->active_rx == s->cookie_rx[0]) { active = 0; @@ -1277,13 +1270,13 @@ static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, } if (room < count) - dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", count - room); if (!room) return room; for (i = 0; i < room; i++) - tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], + tty_insert_flip_char(tport, ((u8 *)sg_virt(&s->sg_rx[active]))[i], TTY_NORMAL); port->icount.rx += room; @@ -1295,22 +1288,22 @@ static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; struct uart_port *port = &s->port; - struct tty_struct *tty = port->state->port.tty; unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); + dev_dbg(port->dev, "%s(%d) active #%d\n", + __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, tty, s->buf_len_rx); + count = sci_dma_rx_push(s, s->buf_len_rx); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); spin_unlock_irqrestore(&port->lock, flags); if (count) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); schedule_work(&s->work_rx); } @@ -1375,8 +1368,8 @@ static void sci_submit_rx(struct sci_port *s) sci_rx_dma_release(s, true); return; } - dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, - s->cookie_rx[i], i); + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", + __func__, s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1402,9 +1395,8 @@ static void work_fn_rx(struct work_struct *work) desc = s->desc_rx[new]; if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != - DMA_SUCCESS) { + DMA_COMPLETE) { /* Handle incomplete DMA receive */ - struct tty_struct *tty = port->state->port.tty; struct dma_chan *chan = s->chan_rx; struct shdma_desc *sh_desc = container_of(desc, struct shdma_desc, async_tx); @@ -1412,15 +1404,15 @@ static void work_fn_rx(struct work_struct *work) int count; chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); - dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + dev_dbg(port->dev, "Read %zu bytes with cookie %d\n", sh_desc->partial, sh_desc->cookie); spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, tty, sh_desc->partial); + count = sci_dma_rx_push(s, sh_desc->partial); spin_unlock_irqrestore(&port->lock, flags); if (count) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); sci_submit_rx(s); @@ -1436,8 +1428,8 @@ static void work_fn_rx(struct work_struct *work) s->active_rx = s->cookie_rx[!new]; - dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, - s->cookie_rx[new], new, s->active_rx); + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", + __func__, s->cookie_rx[new], new, s->active_rx); } static void work_fn_tx(struct work_struct *work) @@ -1490,8 +1482,8 @@ static void work_fn_tx(struct work_struct *work) return; } - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, - xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", + __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx); dma_async_issue_pending(chan); } @@ -1506,9 +1498,9 @@ static void sci_start_tx(struct uart_port *port) if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { u16 new, scr = serial_port_in(port, SCSCR); if (s->chan_tx) - new = scr | 0x8000; + new = scr | SCSCR_TDRQE; else - new = scr & ~0x8000; + new = scr & ~SCSCR_TDRQE; if (new != scr) serial_port_out(port, SCSCR, new); } @@ -1535,7 +1527,7 @@ static void sci_stop_tx(struct uart_port *port) ctrl = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x8000; + ctrl &= ~SCSCR_TDRQE; ctrl &= ~SCSCR_TIE; @@ -1549,7 +1541,7 @@ static void sci_start_rx(struct uart_port *port) ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x4000; + ctrl &= ~SCSCR_RDRQE; serial_port_out(port, SCSCR, ctrl); } @@ -1561,7 +1553,7 @@ static void sci_stop_rx(struct uart_port *port) ctrl = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x4000; + ctrl &= ~SCSCR_RDRQE; ctrl &= ~port_rx_irq_mask(port); @@ -1610,8 +1602,8 @@ static bool filter(struct dma_chan *chan, void *slave) { struct sh_dmae_slave *param = slave; - dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, - param->shdma_slave.slave_id); + dev_dbg(chan->device->dev, "%s: slave ID %d\n", + __func__, param->shdma_slave.slave_id); chan->private = ¶m->shdma_slave; return true; @@ -1624,8 +1616,8 @@ static void rx_timer_fn(unsigned long arg) u16 scr = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - scr &= ~0x4000; - enable_irq(s->cfg->irqs[1]); + scr &= ~SCSCR_RDRQE; + enable_irq(s->irqs[SCIx_RXI_IRQ]); } serial_port_out(port, SCSCR, scr | SCSCR_RIE); dev_dbg(port->dev, "DMA Rx timed out\n"); @@ -1640,8 +1632,7 @@ static void sci_request_dma(struct uart_port *port) dma_cap_mask_t mask; int nent; - dev_dbg(port->dev, "%s: port %d\n", __func__, - port->line); + dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) return; @@ -1661,16 +1652,18 @@ static void sci_request_dma(struct uart_port *port) s->chan_tx = chan; sg_init_table(&s->sg_tx, 1); /* UART circular tx buffer is an aligned page. */ - BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + BUG_ON((uintptr_t)port->state->xmit.buf & ~PAGE_MASK); sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), - UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + UART_XMIT_SIZE, + (uintptr_t)port->state->xmit.buf & ~PAGE_MASK); nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); if (!nent) sci_tx_dma_release(s, false); else - dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, - sg_dma_len(&s->sg_tx), - port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", + __func__, + sg_dma_len(&s->sg_tx), port->state->xmit.buf, + &sg_dma_address(&s->sg_tx)); s->sg_len_tx = nent; @@ -1710,7 +1703,7 @@ static void sci_request_dma(struct uart_port *port) sg_init_table(sg, 1); sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, - (int)buf[i] & ~PAGE_MASK); + (uintptr_t)buf[i] & ~PAGE_MASK); sg_dma_address(sg) = dma[i]; } @@ -1778,21 +1771,11 @@ static void sci_shutdown(struct uart_port *port) sci_free_irq(s); } -static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, +static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps, unsigned long freq) { - switch (algo_id) { - case SCBRR_ALGO_1: - return ((freq + 16 * bps) / (16 * bps) - 1); - case SCBRR_ALGO_2: - return ((freq + 16 * bps) / (32 * bps) - 1); - case SCBRR_ALGO_3: - return (((freq * 2) + 16 * bps) / (16 * bps) - 1); - case SCBRR_ALGO_4: - return (((freq * 2) + 16 * bps) / (32 * bps) - 1); - case SCBRR_ALGO_5: - return (((freq * 1000 / 32) / bps) - 1); - } + if (s->sampling_rate) + return DIV_ROUND_CLOSEST(freq, s->sampling_rate * bps) - 1; /* Warn, but use a safe default */ WARN_ON(1); @@ -1800,6 +1783,42 @@ static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, return ((freq + 16 * bps) / (32 * bps) - 1); } +/* calculate sample rate, BRR, and clock select for HSCIF */ +static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, + int *brr, unsigned int *srr, + unsigned int *cks) +{ + int sr, c, br, err; + int min_err = 1000; /* 100% */ + + /* Find the combination of sample rate and clock select with the + smallest deviation from the desired baud rate. */ + for (sr = 8; sr <= 32; sr++) { + for (c = 0; c <= 3; c++) { + /* integerized formulas from HSCIF documentation */ + br = freq / (sr * (1 << (2 * c + 1)) * bps) - 1; + if (br < 0 || br > 255) + continue; + err = freq / ((br + 1) * bps * sr * + (1 << (2 * c + 1)) / 1000) - 1000; + if (min_err > err) { + min_err = err; + *brr = br; + *srr = sr - 1; + *cks = c; + } + } + } + + if (min_err == 1000) { + WARN_ON(1); + /* use defaults */ + *brr = 255; + *srr = 15; + *cks = 0; + } +} + static void sci_reset(struct uart_port *port) { struct plat_sci_reg *reg; @@ -1821,8 +1840,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, { struct sci_port *s = to_sci_port(port); struct plat_sci_reg *reg; - unsigned int baud, smr_val, max_baud, cks; + unsigned int baud, smr_val, max_baud, cks = 0; int t = -1; + unsigned int srr = 15; /* * earlyprintk comes here early on with port->uartclk set to zero. @@ -1835,8 +1855,16 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, max_baud = port->uartclk ? port->uartclk / 16 : 115200; baud = uart_get_baud_rate(port, termios, old, 0, max_baud); - if (likely(baud && port->uartclk)) - t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk); + if (likely(baud && port->uartclk)) { + if (s->cfg->type == PORT_HSCIF) { + sci_baud_calc_hscif(baud, port->uartclk, &t, &srr, + &cks); + } else { + t = sci_scbrr_calc(s, baud, port->uartclk); + for (cks = 0; t >= 256 && cks <= 3; cks++) + t >>= 2; + } + } sci_port_enable(s); @@ -1845,25 +1873,25 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, smr_val = serial_port_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) - smr_val |= 0x40; + smr_val |= SCSMR_CHR; if (termios->c_cflag & PARENB) - smr_val |= 0x20; + smr_val |= SCSMR_PE; if (termios->c_cflag & PARODD) - smr_val |= 0x30; + smr_val |= SCSMR_PE | SCSMR_ODD; if (termios->c_cflag & CSTOPB) - smr_val |= 0x08; + smr_val |= SCSMR_STOP; uart_update_timeout(port, termios->c_cflag, baud); - for (cks = 0; t >= 256 && cks <= 3; cks++) - t >>= 2; - dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n", __func__, smr_val, cks, t, s->cfg->scscr); if (t >= 0) { - serial_port_out(port, SCSMR, (smr_val & ~3) | cks); + serial_port_out(port, SCSMR, (smr_val & ~SCSMR_CKS) | cks); serial_port_out(port, SCBRR, t); + reg = sci_getreg(port, HSSRR); + if (reg->size) + serial_port_out(port, HSSRR, srr | HSCIF_SRE); udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ } else serial_port_out(port, SCSMR, smr_val); @@ -1907,8 +1935,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, if (s->chan_rx) { s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / port->fifosize / 2; - dev_dbg(port->dev, - "DMA Rx t-out %ums, tty t-out %u jiffies\n", + dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n", s->rx_timeout * 1000 / HZ, port->timeout); if (s->rx_timeout < msecs_to_jiffies(20)) s->rx_timeout = msecs_to_jiffies(20); @@ -1927,7 +1954,7 @@ static void sci_pm(struct uart_port *port, unsigned int state, struct sci_port *sci_port = to_sci_port(port); switch (state) { - case 3: + case UART_PM_STATE_OFF: sci_port_disable(sci_port); break; default: @@ -1949,6 +1976,8 @@ static const char *sci_type(struct uart_port *port) return "scifa"; case PORT_SCIFB: return "scifb"; + case PORT_HSCIF: + return "hscif"; } return NULL; @@ -1962,7 +1991,10 @@ static inline unsigned long sci_port_size(struct uart_port *port) * from platform resource data at such a time that ports begin to * behave more erratically. */ - return 64; + if (port->type == PORT_HSCIF) + return 96; + else + return 64; } static int sci_remap_port(struct uart_port *port) @@ -1987,7 +2019,7 @@ static int sci_remap_port(struct uart_port *port) * need to do any remapping, just cast the cookie * directly. */ - port->membase = (void __iomem *)port->mapbase; + port->membase = (void __iomem *)(uintptr_t)port->mapbase; } return 0; @@ -2034,10 +2066,6 @@ static void sci_config_port(struct uart_port *port, int flags) static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) { - struct sci_port *s = to_sci_port(port); - - if (ser->irq != s->cfg->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) - return -EINVAL; if (ser->baud_base < 2400) /* No paper tape reader for Mitch.. */ return -EINVAL; @@ -2070,11 +2098,13 @@ static struct uart_ops sci_uart_ops = { }; static int sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) + struct sci_port *sci_port, unsigned int index, + struct plat_sci_port *p, bool early) { struct uart_port *port = &sci_port->port; + const struct resource *res; + unsigned int sampling_rate; + unsigned int i; int ret; sci_port->cfg = p; @@ -2083,28 +2113,76 @@ static int sci_init_single(struct platform_device *dev, port->iotype = UPIO_MEM; port->line = index; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENOMEM; + + port->mapbase = res->start; + + for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) + sci_port->irqs[i] = platform_get_irq(dev, i); + + /* The SCI generates several interrupts. They can be muxed together or + * connected to different interrupt lines. In the muxed case only one + * interrupt resource is specified. In the non-muxed case three or four + * interrupt resources are specified, as the BRI interrupt is optional. + */ + if (sci_port->irqs[0] < 0) + return -ENXIO; + + if (sci_port->irqs[1] < 0) { + sci_port->irqs[1] = sci_port->irqs[0]; + sci_port->irqs[2] = sci_port->irqs[0]; + sci_port->irqs[3] = sci_port->irqs[0]; + } + + if (p->regtype == SCIx_PROBE_REGTYPE) { + ret = sci_probe_regmap(p); + if (unlikely(ret)) + return ret; + } + switch (p->type) { case PORT_SCIFB: port->fifosize = 256; + sci_port->overrun_bit = 9; + sampling_rate = 16; + break; + case PORT_HSCIF: + port->fifosize = 128; + sampling_rate = 0; + sci_port->overrun_bit = 0; break; case PORT_SCIFA: port->fifosize = 64; + sci_port->overrun_bit = 9; + sampling_rate = 16; break; case PORT_SCIF: port->fifosize = 16; + if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { + sci_port->overrun_bit = 9; + sampling_rate = 16; + } else { + sci_port->overrun_bit = 0; + sampling_rate = 32; + } break; default: port->fifosize = 1; + sci_port->overrun_bit = 5; + sampling_rate = 32; break; } - if (p->regtype == SCIx_PROBE_REGTYPE) { - ret = sci_probe_regmap(p); - if (unlikely(ret)) - return ret; - } + /* SCIFA on sh7723 and sh7724 need a custom sampling rate that doesn't + * match the SoC datasheet, this should be investigated. Let platform + * data override the sampling rate for now. + */ + sci_port->sampling_rate = p->sampling_rate ? p->sampling_rate + : sampling_rate; - if (dev) { + if (!early) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); if (IS_ERR(sci_port->iclk)) { sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); @@ -2124,8 +2202,6 @@ static int sci_init_single(struct platform_device *dev, port->dev = &dev->dev; - sci_init_gpios(sci_port); - pm_runtime_enable(&dev->dev); } @@ -2136,32 +2212,22 @@ static int sci_init_single(struct platform_device *dev, /* * Establish some sensible defaults for the error detection. */ - if (!p->error_mask) - p->error_mask = (p->type == PORT_SCI) ? + sci_port->error_mask = (p->type == PORT_SCI) ? SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; /* * Establish sensible defaults for the overrun detection, unless * the part has explicitly disabled support for it. */ - if (p->overrun_bit != SCIx_NOT_SUPPORTED) { - if (p->type == PORT_SCI) - p->overrun_bit = 5; - else if (p->scbrr_algo_id == SCBRR_ALGO_4) - p->overrun_bit = 9; - else - p->overrun_bit = 0; - /* - * Make the error mask inclusive of overrun detection, if - * supported. - */ - p->error_mask |= (1 << p->overrun_bit); - } + /* + * Make the error mask inclusive of overrun detection, if + * supported. + */ + sci_port->error_mask |= 1 << sci_port->overrun_bit; - port->mapbase = p->mapbase; port->type = p->type; - port->flags = p->flags; + port->flags = UPF_FIXED_PORT | p->flags; port->regshift = p->regshift; /* @@ -2171,7 +2237,7 @@ static int sci_init_single(struct platform_device *dev, * * For the muxed case there's nothing more to do. */ - port->irq = p->irqs[SCIx_RXI_IRQ]; + port->irq = sci_port->irqs[SCIx_RXI_IRQ]; port->irqflags = 0; port->serial_in = sci_serial_in; @@ -2186,8 +2252,6 @@ static int sci_init_single(struct platform_device *dev, static void sci_cleanup_single(struct sci_port *port) { - sci_free_gpios(port); - clk_put(port->iclk); clk_put(port->fclk); @@ -2296,14 +2360,14 @@ static char early_serial_buf[32]; static int sci_probe_earlyprintk(struct platform_device *pdev) { - struct plat_sci_port *cfg = pdev->dev.platform_data; + struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev); if (early_serial_console.data) return -EEXIST; early_serial_console.index = pdev->id; - sci_init_single(NULL, &sci_ports[pdev->id], pdev->id, cfg); + sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true); serial_console_setup(&early_serial_console, early_serial_buf); @@ -2326,8 +2390,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev) #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ -static char banner[] __initdata = - KERN_INFO "SuperH SCI(F) driver initialized\n"; +static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized"; static struct uart_driver sci_uart_driver = { .owner = THIS_MODULE, @@ -2353,6 +2416,83 @@ static int sci_remove(struct platform_device *dev) return 0; } +struct sci_port_info { + unsigned int type; + unsigned int regtype; +}; + +static const struct of_device_id of_sci_match[] = { + { + .compatible = "renesas,scif", + .data = &(const struct sci_port_info) { + .type = PORT_SCIF, + .regtype = SCIx_SH4_SCIF_REGTYPE, + }, + }, { + .compatible = "renesas,scifa", + .data = &(const struct sci_port_info) { + .type = PORT_SCIFA, + .regtype = SCIx_SCIFA_REGTYPE, + }, + }, { + .compatible = "renesas,scifb", + .data = &(const struct sci_port_info) { + .type = PORT_SCIFB, + .regtype = SCIx_SCIFB_REGTYPE, + }, + }, { + .compatible = "renesas,hscif", + .data = &(const struct sci_port_info) { + .type = PORT_HSCIF, + .regtype = SCIx_HSCIF_REGTYPE, + }, + }, { + /* Terminator */ + }, +}; +MODULE_DEVICE_TABLE(of, of_sci_match); + +static struct plat_sci_port * +sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + const struct sci_port_info *info; + struct plat_sci_port *p; + int id; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return NULL; + + match = of_match_node(of_sci_match, pdev->dev.of_node); + if (!match) + return NULL; + + info = match->data; + + p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to allocate DT config data\n"); + return NULL; + } + + /* Get the line number for the aliases node. */ + id = of_alias_get_id(np, "serial"); + if (id < 0) { + dev_err(&pdev->dev, "failed to get alias id (%d)\n", id); + return NULL; + } + + *dev_id = id; + + p->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; + p->type = info->type; + p->regtype = info->regtype; + p->scscr = SCSCR_RE | SCSCR_TE; + + return p; +} + static int sci_probe_single(struct platform_device *dev, unsigned int index, struct plat_sci_port *p, @@ -2362,15 +2502,13 @@ static int sci_probe_single(struct platform_device *dev, /* Sanity check */ if (unlikely(index >= SCI_NPORTS)) { - dev_notice(&dev->dev, "Attempting to register port " - "%d when only %d are available.\n", + dev_notice(&dev->dev, "Attempting to register port %d when only %d are available\n", index+1, SCI_NPORTS); - dev_notice(&dev->dev, "Consider bumping " - "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); + dev_notice(&dev->dev, "Consider bumping CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); return -EINVAL; } - ret = sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p, false); if (ret) return ret; @@ -2385,8 +2523,9 @@ static int sci_probe_single(struct platform_device *dev, static int sci_probe(struct platform_device *dev) { - struct plat_sci_port *p = dev->dev.platform_data; - struct sci_port *sp = &sci_ports[dev->id]; + struct plat_sci_port *p; + struct sci_port *sp; + unsigned int dev_id; int ret; /* @@ -2397,9 +2536,24 @@ static int sci_probe(struct platform_device *dev) if (is_early_platform_device(dev)) return sci_probe_earlyprintk(dev); + if (dev->dev.of_node) { + p = sci_parse_dt(dev, &dev_id); + if (p == NULL) + return -EINVAL; + } else { + p = dev->dev.platform_data; + if (p == NULL) { + dev_err(&dev->dev, "no platform data supplied\n"); + return -EINVAL; + } + + dev_id = dev->id; + } + + sp = &sci_ports[dev_id]; platform_set_drvdata(dev, sp); - ret = sci_probe_single(dev, dev->id, p, sp); + ret = sci_probe_single(dev, dev_id, p, sp); if (ret) return ret; @@ -2408,6 +2562,7 @@ static int sci_probe(struct platform_device *dev) ret = cpufreq_register_notifier(&sp->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); if (unlikely(ret < 0)) { + uart_remove_one_port(&sci_uart_driver, &sp->port); sci_cleanup_single(sp); return ret; } @@ -2451,6 +2606,7 @@ static struct platform_driver sci_driver = { .name = "sh-sci", .owner = THIS_MODULE, .pm = &sci_dev_pm_ops, + .of_match_table = of_match_ptr(of_sci_match), }, }; @@ -2458,7 +2614,7 @@ static int __init sci_init(void) { int ret; - printk(banner); + pr_info("%s\n", banner); ret = uart_register_driver(&sci_uart_driver); if (likely(ret == 0)) { @@ -2486,4 +2642,4 @@ module_exit(sci_exit); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:sh-sci"); MODULE_AUTHOR("Paul Mundt"); -MODULE_DESCRIPTION("SuperH SCI(F) serial driver"); +MODULE_DESCRIPTION("SuperH (H)SCI(F) serial driver"); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 4c22a1529aa..d5db81a0a43 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -9,14 +9,12 @@ #define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) #define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -#define SCxSR_ERRORS(port) (to_sci_port(port)->cfg->error_mask) +#define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask) #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) || \ defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) || \ defined(CONFIG_ARCH_R8A7740) diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c index 5da5cb96276..9b4d71cff00 100644 --- a/drivers/tty/serial/sirfsoc_uart.c +++ b/drivers/tty/serial/sirfsoc_uart.c @@ -20,9 +20,12 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/io.h> +#include <linux/of_gpio.h> +#include <linux/dmaengine.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> #include <asm/irq.h> #include <asm/mach/irq.h> -#include <linux/pinctrl/consumer.h> #include "sirfsoc_uart.h" @@ -32,6 +35,9 @@ static unsigned int sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); static struct uart_driver sirfsoc_uart_drv; +static void sirfsoc_uart_tx_dma_complete_callback(void *param); +static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port); +static void sirfsoc_uart_rx_dma_complete_callback(void *param); static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { {4000000, 2359296}, {3500000, 1310721}, @@ -75,6 +81,27 @@ static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = { .line = 2, }, }, + [3] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 3, + }, + }, + [4] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 4, + }, + }, + [5] = { + .port = { + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF, + .line = 5, + }, + }, }; static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) @@ -85,21 +112,28 @@ static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) { unsigned long reg; - reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS); - if (reg & SIRFUART_FIFOEMPTY_MASK(port)) - return TIOCSER_TEMT; - else - return 0; + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; + reg = rd_regl(port, ureg->sirfsoc_tx_fifo_status); + + return (reg & ufifo_st->ff_empty(port->line)) ? TIOCSER_TEMT : 0; } static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - if (!(sirfport->ms_enabled)) { + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) goto cts_asserted; - } else if (sirfport->hw_flow_ctrl) { - if (!(rd_regl(port, SIRFUART_AFC_CTRL) & - SIRFUART_CTS_IN_STATUS)) + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) & + SIRFUART_AFC_CTS_STATUS)) + goto cts_asserted; + else + goto cts_deasserted; + } else { + if (!gpio_get_value(sirfport->cts_gpio)) goto cts_asserted; else goto cts_deasserted; @@ -113,94 +147,278 @@ cts_asserted: static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; unsigned int assert = mctrl & TIOCM_RTS; unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; unsigned int current_val; - if (sirfport->hw_flow_ctrl) { - current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF; + + if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled) + return; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF; val |= current_val; - wr_regl(port, SIRFUART_AFC_CTRL, val); + wr_regl(port, ureg->sirfsoc_afc_ctrl, val); + } else { + if (!val) + gpio_set_value(sirfport->rts_gpio, 1); + else + gpio_set_value(sirfport->rts_gpio, 0); } } static void sirfsoc_uart_stop_tx(struct uart_port *port) { - unsigned int regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + if (sirfport->tx_dma_chan) { + if (sirfport->tx_dma_state == TX_DMA_RUNNING) { + dmaengine_pause(sirfport->tx_dma_chan); + sirfport->tx_dma_state = TX_DMA_PAUSE; + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + } + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + } +} + +static void sirfsoc_uart_tx_with_dma(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct circ_buf *xmit = &port->state->xmit; + unsigned long tran_size; + unsigned long tran_start; + unsigned long pio_tx_size; + + tran_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + tran_start = (unsigned long)(xmit->buf + xmit->tail); + if (uart_circ_empty(xmit) || uart_tx_stopped(port) || + !tran_size) + return; + if (sirfport->tx_dma_state == TX_DMA_PAUSE) { + dmaengine_resume(sirfport->tx_dma_chan); + return; + } + if (sirfport->tx_dma_state == TX_DMA_RUNNING) + return; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~(uint_en->sirfsoc_txfifo_empty_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_txfifo_empty_en); + /* + * DMA requires buffer address and buffer length are both aligned with + * 4 bytes, so we use PIO for + * 1. if address is not aligned with 4bytes, use PIO for the first 1~3 + * bytes, and move to DMA for the left part aligned with 4bytes + * 2. if buffer length is not aligned with 4bytes, use DMA for aligned + * part first, move to PIO for the left 1~3 bytes + */ + if (tran_size < 4 || BYTES_TO_ALIGN(tran_start)) { + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)| + SIRFUART_IO_MODE); + if (BYTES_TO_ALIGN(tran_start)) { + pio_tx_size = sirfsoc_uart_pio_tx_chars(sirfport, + BYTES_TO_ALIGN(tran_start)); + tran_size -= pio_tx_size; + } + if (tran_size < 4) + sirfsoc_uart_pio_tx_chars(sirfport, tran_size); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)| + uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_txfifo_empty_en); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + } else { + /* tx transfer mode switch into dma mode */ + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl)& + ~SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + tran_size &= ~(0x3); + + sirfport->tx_dma_addr = dma_map_single(port->dev, + xmit->buf + xmit->tail, + tran_size, DMA_TO_DEVICE); + sirfport->tx_dma_desc = dmaengine_prep_slave_single( + sirfport->tx_dma_chan, sirfport->tx_dma_addr, + tran_size, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); + if (!sirfport->tx_dma_desc) { + dev_err(port->dev, "DMA prep slave single fail\n"); + return; + } + sirfport->tx_dma_desc->callback = + sirfsoc_uart_tx_dma_complete_callback; + sirfport->tx_dma_desc->callback_param = (void *)sirfport; + sirfport->transfer_size = tran_size; + + dmaengine_submit(sirfport->tx_dma_desc); + dma_async_issue_pending(sirfport->tx_dma_chan); + sirfport->tx_dma_state = TX_DMA_RUNNING; + } } -void sirfsoc_uart_start_tx(struct uart_port *port) +static void sirfsoc_uart_start_tx(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long regv; - sirfsoc_uart_pio_tx_chars(sirfport, 1); - wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (sirfport->tx_dma_chan) + sirfsoc_uart_tx_with_dma(sirfport); + else { + sirfsoc_uart_pio_tx_chars(sirfport, 1); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)| + uint_en->sirfsoc_txfifo_empty_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_txfifo_empty_en); + } } static void sirfsoc_uart_stop_rx(struct uart_port *port) { - unsigned long regv; - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + if (sirfport->rx_dma_chan) { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(SIRFUART_RX_DMA_INT_EN(port, uint_en) | + uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + SIRFUART_RX_DMA_INT_EN(port, uint_en)| + uint_en->sirfsoc_rx_done_en); + dmaengine_terminate_all(sirfport->rx_dma_chan); + } else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~(SIRFUART_RX_IO_INT_EN(port, uint_en))); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + SIRFUART_RX_IO_INT_EN(port, uint_en)); + } } static void sirfsoc_uart_disable_ms(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long reg; - sirfport->ms_enabled = 0; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (!sirfport->hw_flow_ctrl) return; - reg = rd_regl(port, SIRFUART_AFC_CTRL); - wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); + sirfport->ms_enabled = false; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + wr_regl(port, ureg->sirfsoc_afc_ctrl, + rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg)& + ~uint_en->sirfsoc_cts_en); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_cts_en); + } else + disable_irq(gpio_to_irq(sirfport->cts_gpio)); +} + +static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; + struct uart_port *port = &sirfport->port; + spin_lock(&port->lock); + if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled) + uart_handle_cts_change(port, + !gpio_get_value(sirfport->cts_gpio)); + spin_unlock(&port->lock); + return IRQ_HANDLED; } static void sirfsoc_uart_enable_ms(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long reg; - unsigned long flg; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + if (!sirfport->hw_flow_ctrl) return; - flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN; - reg = rd_regl(port, SIRFUART_AFC_CTRL); - wr_regl(port, SIRFUART_AFC_CTRL, reg | flg); - reg = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); - uart_handle_cts_change(port, - !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS)); - sirfport->ms_enabled = 1; + sirfport->ms_enabled = true; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + wr_regl(port, ureg->sirfsoc_afc_ctrl, + rd_regl(port, ureg->sirfsoc_afc_ctrl) | + SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) + | uint_en->sirfsoc_cts_en); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_cts_en); + } else + enable_irq(gpio_to_irq(sirfport->cts_gpio)); } static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state) { - unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL); - if (break_state) - ulcon |= SIRFUART_SET_BREAK; - else - ulcon &= ~SIRFUART_SET_BREAK; - wr_regl(port, SIRFUART_LINE_CTRL, ulcon); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + unsigned long ulcon = rd_regl(port, ureg->sirfsoc_line_ctrl); + if (break_state) + ulcon |= SIRFUART_SET_BREAK; + else + ulcon &= ~SIRFUART_SET_BREAK; + wr_regl(port, ureg->sirfsoc_line_ctrl, ulcon); + } } static unsigned int sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; unsigned int ch, rx_count = 0; struct tty_struct *tty; - tty = tty_port_tty_get(&port->state->port); if (!tty) return -ENODEV; - - while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) & - SIRFUART_FIFOEMPTY_MASK(port))) { - ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ; + while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) & + ufifo_st->ff_empty(port->line))) { + ch = rd_regl(port, ureg->sirfsoc_rx_fifo_data) | + SIRFUART_DUMMY_READ; if (unlikely(uart_handle_sysrq_char(port, ch))) continue; uart_insert_char(port, 0, 0, ch, TTY_NORMAL); @@ -209,9 +427,8 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) break; } + sirfport->rx_io_count += rx_count; port->icount.rx += rx_count; - tty_flip_buffer_push(tty); - tty_kref_put(tty); return rx_count; } @@ -220,13 +437,16 @@ static unsigned int sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) { struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; struct circ_buf *xmit = &port->state->xmit; unsigned int num_tx = 0; while (!uart_circ_empty(xmit) && - !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) & - SIRFUART_FIFOFULL_MASK(port)) && + !(rd_regl(port, ureg->sirfsoc_tx_fifo_status) & + ufifo_st->ff_full(port->line)) && count--) { - wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]); + wr_regl(port, ureg->sirfsoc_tx_fifo_data, + xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; num_tx++; @@ -236,6 +456,160 @@ sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) return num_tx; } +static void sirfsoc_uart_tx_dma_complete_callback(void *param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + xmit->tail = (xmit->tail + sirfport->transfer_size) & + (UART_XMIT_SIZE - 1); + port->icount.tx += sirfport->transfer_size; + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (sirfport->tx_dma_addr) + dma_unmap_single(port->dev, sirfport->tx_dma_addr, + sirfport->transfer_size, DMA_TO_DEVICE); + sirfport->tx_dma_state = TX_DMA_IDLE; + sirfsoc_uart_tx_with_dma(sirfport); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void sirfsoc_uart_insert_rx_buf_to_tty( + struct sirfsoc_uart_port *sirfport, int count) +{ + struct uart_port *port = &sirfport->port; + struct tty_port *tport = &port->state->port; + int inserted; + + inserted = tty_insert_flip_string(tport, + sirfport->rx_dma_items[sirfport->rx_completed].xmit.buf, count); + port->icount.rx += inserted; +} + +static void sirfsoc_rx_submit_one_dma_desc(struct uart_port *port, int index) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + + sirfport->rx_dma_items[index].xmit.tail = + sirfport->rx_dma_items[index].xmit.head = 0; + sirfport->rx_dma_items[index].desc = + dmaengine_prep_slave_single(sirfport->rx_dma_chan, + sirfport->rx_dma_items[index].dma_addr, SIRFSOC_RX_DMA_BUF_SIZE, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!sirfport->rx_dma_items[index].desc) { + dev_err(port->dev, "DMA slave single fail\n"); + return; + } + sirfport->rx_dma_items[index].desc->callback = + sirfsoc_uart_rx_dma_complete_callback; + sirfport->rx_dma_items[index].desc->callback_param = sirfport; + sirfport->rx_dma_items[index].cookie = + dmaengine_submit(sirfport->rx_dma_items[index].desc); + dma_async_issue_pending(sirfport->rx_dma_chan); +} + +static void sirfsoc_rx_tmo_process_tl(unsigned long param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + unsigned int count; + unsigned long flags; + struct dma_tx_state tx_state; + + spin_lock_irqsave(&port->lock, flags); + while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, + SIRFSOC_RX_DMA_BUF_SIZE); + sirfport->rx_completed++; + sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + } + count = CIRC_CNT(sirfport->rx_dma_items[sirfport->rx_issued].xmit.head, + sirfport->rx_dma_items[sirfport->rx_issued].xmit.tail, + SIRFSOC_RX_DMA_BUF_SIZE); + if (count > 0) + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, count); + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | + SIRFUART_IO_MODE); + sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); + if (sirfport->rx_io_count == 4) { + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_done); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_done_en); + sirfsoc_uart_start_next_rx_dma(port); + } else { + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_done); + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + (uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + uint_en->sirfsoc_rx_done_en); + } + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(&port->state->port); +} + +static void sirfsoc_uart_handle_rx_tmo(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct dma_tx_state tx_state; + dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_issued].cookie, &tx_state); + dmaengine_terminate_all(sirfport->rx_dma_chan); + sirfport->rx_dma_items[sirfport->rx_issued].xmit.head = + SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_timeout_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_timeout_en); + tasklet_schedule(&sirfport->rx_tmo_process_tasklet); +} + +static void sirfsoc_uart_handle_rx_done(struct sirfsoc_uart_port *sirfport) +{ + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + + sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count); + if (sirfport->rx_io_count == 4) { + sirfport->rx_io_count = 0; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) & + ~(uint_en->sirfsoc_rx_done_en)); + else + wr_regl(port, SIRFUART_INT_EN_CLR, + uint_en->sirfsoc_rx_done_en); + wr_regl(port, ureg->sirfsoc_int_st_reg, + uint_st->sirfsoc_rx_timeout); + sirfsoc_uart_start_next_rx_dma(port); + } +} + static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) { unsigned long intr_status; @@ -243,75 +617,201 @@ static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) unsigned long flag = TTY_NORMAL; struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; + struct sirfsoc_int_status *uint_st = &sirfport->uart_reg->uart_int_st; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; struct uart_state *state = port->state; struct circ_buf *xmit = &port->state->xmit; - intr_status = rd_regl(port, SIRFUART_INT_STATUS); - wr_regl(port, SIRFUART_INT_STATUS, intr_status); - intr_status &= rd_regl(port, SIRFUART_INT_EN); - if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) { - if (intr_status & SIRFUART_RXD_BREAK) { + spin_lock(&port->lock); + intr_status = rd_regl(port, ureg->sirfsoc_int_st_reg); + wr_regl(port, ureg->sirfsoc_int_st_reg, intr_status); + intr_status &= rd_regl(port, ureg->sirfsoc_int_en_reg); + if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT(port, uint_st)))) { + if (intr_status & uint_st->sirfsoc_rxd_brk) { + port->icount.brk++; if (uart_handle_break(port)) goto recv_char; - uart_insert_char(port, intr_status, - SIRFUART_RX_OFLOW, 0, TTY_BREAK); - return IRQ_HANDLED; } - if (intr_status & SIRFUART_RX_OFLOW) + if (intr_status & uint_st->sirfsoc_rx_oflow) port->icount.overrun++; - if (intr_status & SIRFUART_FRM_ERR) { + if (intr_status & uint_st->sirfsoc_frm_err) { port->icount.frame++; flag = TTY_FRAME; } - if (intr_status & SIRFUART_PARITY_ERR) + if (intr_status & uint_st->sirfsoc_parity_err) flag = TTY_PARITY; - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); intr_status &= port->read_status_mask; uart_insert_char(port, intr_status, - SIRFUART_RX_OFLOW_INT, 0, flag); + uint_en->sirfsoc_rx_oflow_en, 0, flag); } recv_char: - if (intr_status & SIRFUART_CTS_INT_EN) { - cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) & - SIRFUART_CTS_IN_STATUS); - if (cts_status != 0) { - uart_handle_cts_change(port, 1); - } else { - uart_handle_cts_change(port, 0); - wake_up_interruptible(&state->port.delta_msr_wait); - } + if ((sirfport->uart_reg->uart_type == SIRF_REAL_UART) && + (intr_status & SIRFUART_CTS_INT_ST(uint_st)) && + !sirfport->tx_dma_state) { + cts_status = rd_regl(port, ureg->sirfsoc_afc_ctrl) & + SIRFUART_AFC_CTS_STATUS; + if (cts_status != 0) + cts_status = 0; + else + cts_status = 1; + uart_handle_cts_change(port, cts_status); + wake_up_interruptible(&state->port.delta_msr_wait); } - if (intr_status & SIRFUART_RX_IO_INT_EN) - sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT); - if (intr_status & SIRFUART_TX_INT_EN) { - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - return IRQ_HANDLED; - } else { - sirfsoc_uart_pio_tx_chars(sirfport, + if (sirfport->rx_dma_chan) { + if (intr_status & uint_st->sirfsoc_rx_timeout) + sirfsoc_uart_handle_rx_tmo(sirfport); + if (intr_status & uint_st->sirfsoc_rx_done) + sirfsoc_uart_handle_rx_done(sirfport); + } else { + if (intr_status & SIRFUART_RX_IO_INT_ST(uint_st)) + sirfsoc_uart_pio_rx_chars(port, + SIRFSOC_UART_IO_RX_MAX_CNT); + } + spin_unlock(&port->lock); + tty_flip_buffer_push(&state->port); + spin_lock(&port->lock); + if (intr_status & uint_st->sirfsoc_txfifo_empty) { + if (sirfport->tx_dma_chan) + sirfsoc_uart_tx_with_dma(sirfport); + else { + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + spin_unlock(&port->lock); + return IRQ_HANDLED; + } else { + sirfsoc_uart_pio_tx_chars(sirfport, SIRFSOC_UART_IO_TX_REASONABLE_CNT); - if ((uart_circ_empty(xmit)) && - (rd_regl(port, SIRFUART_TX_FIFO_STATUS) & - SIRFUART_FIFOEMPTY_MASK(port))) - sirfsoc_uart_stop_tx(port); + if ((uart_circ_empty(xmit)) && + (rd_regl(port, ureg->sirfsoc_tx_fifo_status) & + ufifo_st->ff_empty(port->line))) + sirfsoc_uart_stop_tx(port); + } } } + spin_unlock(&port->lock); + return IRQ_HANDLED; } +static void sirfsoc_uart_rx_dma_complete_tl(unsigned long param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + struct uart_port *port = &sirfport->port; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + unsigned long flags; + struct dma_tx_state tx_state; + spin_lock_irqsave(&port->lock, flags); + while (DMA_COMPLETE == dmaengine_tx_status(sirfport->rx_dma_chan, + sirfport->rx_dma_items[sirfport->rx_completed].cookie, &tx_state)) { + sirfsoc_uart_insert_rx_buf_to_tty(sirfport, + SIRFSOC_RX_DMA_BUF_SIZE); + if (rd_regl(port, ureg->sirfsoc_int_en_reg) & + uint_en->sirfsoc_rx_timeout_en) + sirfsoc_rx_submit_one_dma_desc(port, + sirfport->rx_completed++); + else + sirfport->rx_completed++; + sirfport->rx_completed %= SIRFSOC_RX_LOOP_BUF_CNT; + } + spin_unlock_irqrestore(&port->lock, flags); + tty_flip_buffer_push(&port->state->port); +} + +static void sirfsoc_uart_rx_dma_complete_callback(void *param) +{ + struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)param; + unsigned long flags; + + spin_lock_irqsave(&sirfport->port.lock, flags); + sirfport->rx_issued++; + sirfport->rx_issued %= SIRFSOC_RX_LOOP_BUF_CNT; + tasklet_schedule(&sirfport->rx_dma_complete_tasklet); + spin_unlock_irqrestore(&sirfport->port.lock, flags); +} + +/* submit rx dma task into dmaengine */ +static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port) +{ + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + int i; + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) & + ~SIRFUART_IO_MODE); + for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) + sirfsoc_rx_submit_one_dma_desc(port, i); + sirfport->rx_completed = sirfport->rx_issued = 0; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + SIRFUART_RX_DMA_INT_EN(port, uint_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + SIRFUART_RX_DMA_INT_EN(port, uint_en)); +} + static void sirfsoc_uart_start_rx(struct uart_port *port) { - unsigned long regv; - regv = rd_regl(port, SIRFUART_INT_EN); - wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; + + sirfport->rx_io_count = 0; + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START); + if (sirfport->rx_dma_chan) + sirfsoc_uart_start_next_rx_dma(port); + else { + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, + rd_regl(port, ureg->sirfsoc_int_en_reg) | + SIRFUART_RX_IO_INT_EN(port, uint_en)); + else + wr_regl(port, ureg->sirfsoc_int_en_reg, + SIRFUART_RX_IO_INT_EN(port, uint_en)); + } +} + +static unsigned int +sirfsoc_usp_calc_sample_div(unsigned long set_rate, + unsigned long ioclk_rate, unsigned long *sample_reg) +{ + unsigned long min_delta = ~0UL; + unsigned short sample_div; + unsigned long ioclk_div = 0; + unsigned long temp_delta; + + for (sample_div = SIRF_MIN_SAMPLE_DIV; + sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { + temp_delta = ioclk_rate - + (ioclk_rate + (set_rate * sample_div) / 2) + / (set_rate * sample_div) * set_rate * sample_div; + + temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; + if (temp_delta < min_delta) { + ioclk_div = (2 * ioclk_rate / + (set_rate * sample_div) + 1) / 2 - 1; + if (ioclk_div > SIRF_IOCLK_DIV_MAX) + continue; + min_delta = temp_delta; + *sample_reg = sample_div; + if (!temp_delta) + break; + } + } + return ioclk_div; } static unsigned int -sirfsoc_calc_sample_div(unsigned long baud_rate, - unsigned long ioclk_rate, unsigned long *setted_baud) +sirfsoc_uart_calc_sample_div(unsigned long baud_rate, + unsigned long ioclk_rate, unsigned long *set_baud) { unsigned long min_delta = ~0UL; unsigned short sample_div; @@ -334,7 +834,7 @@ sirfsoc_calc_sample_div(unsigned long baud_rate, regv = regv & (~SIRF_SAMPLE_DIV_MASK); regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT); min_delta = temp_delta; - *setted_baud = baud_tmp; + *set_baud = baud_tmp; } } return regv; @@ -345,65 +845,93 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, struct ktermios *old) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - unsigned long ioclk_rate; + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en; unsigned long config_reg = 0; unsigned long baud_rate; - unsigned long setted_baud; + unsigned long set_baud; unsigned long flags; unsigned long ic; unsigned int clk_div_reg = 0; - unsigned long temp_reg_val; + unsigned long txfifo_op_reg, ioclk_rate; unsigned long rx_time_out; int threshold_div; - int temp; + u32 data_bit_len, stop_bit_len, len_val; + unsigned long sample_div_reg = 0xf; + ioclk_rate = port->uartclk; - ioclk_rate = 150000000; switch (termios->c_cflag & CSIZE) { default: case CS8: + data_bit_len = 8; config_reg |= SIRFUART_DATA_BIT_LEN_8; break; case CS7: + data_bit_len = 7; config_reg |= SIRFUART_DATA_BIT_LEN_7; break; case CS6: + data_bit_len = 6; config_reg |= SIRFUART_DATA_BIT_LEN_6; break; case CS5: + data_bit_len = 5; config_reg |= SIRFUART_DATA_BIT_LEN_5; break; } - if (termios->c_cflag & CSTOPB) + if (termios->c_cflag & CSTOPB) { config_reg |= SIRFUART_STOP_BIT_LEN_2; - baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); + stop_bit_len = 2; + } else + stop_bit_len = 1; + spin_lock_irqsave(&port->lock, flags); - port->read_status_mask = SIRFUART_RX_OFLOW_INT; + port->read_status_mask = uint_en->sirfsoc_rx_oflow_en; port->ignore_status_mask = 0; - /* read flags */ - if (termios->c_iflag & INPCK) - port->read_status_mask |= - SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SIRFUART_RXD_BREAK_INT; - /* ignore flags */ - if (termios->c_iflag & IGNPAR) + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (termios->c_iflag & INPCK) + port->read_status_mask |= uint_en->sirfsoc_frm_err_en | + uint_en->sirfsoc_parity_err_en; + } else { + if (termios->c_iflag & INPCK) + port->read_status_mask |= uint_en->sirfsoc_frm_err_en; + } + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= uint_en->sirfsoc_rxd_brk_en; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_frm_err_en | + uint_en->sirfsoc_parity_err_en; + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + config_reg |= SIRFUART_STICK_BIT_MARK; + else + config_reg |= SIRFUART_STICK_BIT_SPACE; + } else if (termios->c_cflag & PARODD) { + config_reg |= SIRFUART_STICK_BIT_ODD; + } else { + config_reg |= SIRFUART_STICK_BIT_EVEN; + } + } + } else { + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_frm_err_en; + if (termios->c_cflag & PARENB) + dev_warn(port->dev, + "USP-UART not support parity err\n"); + } + if (termios->c_iflag & IGNBRK) { port->ignore_status_mask |= - SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; + uint_en->sirfsoc_rxd_brk_en; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= + uint_en->sirfsoc_rx_oflow_en; + } if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= SIRFUART_DUMMY_READ; - /* enable parity if PARENB is set*/ - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - config_reg |= SIRFUART_STICK_BIT_MARK; - else - config_reg |= SIRFUART_STICK_BIT_SPACE; - } else if (termios->c_cflag & PARODD) { - config_reg |= SIRFUART_STICK_BIT_ODD; - } else { - config_reg |= SIRFUART_STICK_BIT_EVEN; - } - } /* Hardware Flow Control Settings */ if (UART_ENABLE_MS(port, termios->c_cflag)) { if (!sirfport->ms_enabled) @@ -412,72 +940,107 @@ static void sirfsoc_uart_set_termios(struct uart_port *port, if (sirfport->ms_enabled) sirfsoc_uart_disable_ms(port); } - - /* common rate: fast calculation */ - for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) - if (baud_rate == baudrate_to_regv[ic].baud_rate) - clk_div_reg = baudrate_to_regv[ic].reg_val; - setted_baud = baud_rate; - /* arbitary rate setting */ - if (unlikely(clk_div_reg == 0)) - clk_div_reg = sirfsoc_calc_sample_div(baud_rate, ioclk_rate, - &setted_baud); - wr_regl(port, SIRFUART_DIVISOR, clk_div_reg); - + baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); + if (ioclk_rate == 150000000) { + for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) + if (baud_rate == baudrate_to_regv[ic].baud_rate) + clk_div_reg = baudrate_to_regv[ic].reg_val; + } + set_baud = baud_rate; + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + if (unlikely(clk_div_reg == 0)) + clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate, + ioclk_rate, &set_baud); + wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg); + } else { + clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate, + ioclk_rate, &sample_div_reg); + sample_div_reg--; + set_baud = ((ioclk_rate / (clk_div_reg+1) - 1) / + (sample_div_reg + 1)); + /* setting usp mode 2 */ + len_val = ((1 << SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET) | + (1 << SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET)); + len_val |= ((clk_div_reg & SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK) + << SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_mode2, len_val); + } if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, setted_baud, setted_baud); - - /* set receive timeout */ - rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000); - rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out; - config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out); - temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - wr_regl(port, SIRFUART_TX_FIFO_OP, - temp_reg_val & ~SIRFUART_TX_FIFO_START); - wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO); - wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO); - wr_regl(port, SIRFUART_LINE_CTRL, config_reg); - + tty_termios_encode_baud_rate(termios, set_baud, set_baud); + /* set receive timeout && data bits len */ + rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000); + rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out); + txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_STOP); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, + (txfifo_op_reg & ~SIRFUART_FIFO_START)); + if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) { + config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out); + wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg); + } else { + /*tx frame ctrl*/ + len_val = (data_bit_len - 1) << SIRFSOC_USP_TX_DATA_LEN_OFFSET; + len_val |= (data_bit_len + 1 + stop_bit_len - 1) << + SIRFSOC_USP_TX_FRAME_LEN_OFFSET; + len_val |= ((data_bit_len - 1) << + SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET); + len_val |= (((clk_div_reg & 0xc00) >> 10) << + SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_tx_frame_ctrl, len_val); + /*rx frame ctrl*/ + len_val = (data_bit_len - 1) << SIRFSOC_USP_RX_DATA_LEN_OFFSET; + len_val |= (data_bit_len + 1 + stop_bit_len - 1) << + SIRFSOC_USP_RX_FRAME_LEN_OFFSET; + len_val |= (data_bit_len - 1) << + SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET; + len_val |= (((clk_div_reg & 0xf000) >> 12) << + SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET); + wr_regl(port, ureg->sirfsoc_rx_frame_ctrl, len_val); + /*async param*/ + wr_regl(port, ureg->sirfsoc_async_param_reg, + (SIRFUART_RECV_TIMEOUT(port, rx_time_out)) | + (sample_div_reg & SIRFSOC_USP_ASYNC_DIV2_MASK) << + SIRFSOC_USP_ASYNC_DIV2_OFFSET); + } + if (sirfport->tx_dma_chan) + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_DMA_MODE); + else + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE); + if (sirfport->rx_dma_chan) + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE); + else + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE); /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ - if (baud_rate < 1000000) + if (set_baud < 1000000) threshold_div = 1; else threshold_div = 2; - temp = port->line == 1 ? 16 : 64; - wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div); - wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div); - temp_reg_val |= SIRFUART_TX_FIFO_START; - wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val); - uart_update_timeout(port, termios->c_cflag, baud_rate); + wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, + SIRFUART_FIFO_THD(port) / threshold_div); + wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, + SIRFUART_FIFO_THD(port) / threshold_div); + txfifo_op_reg |= SIRFUART_FIFO_START; + wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg); + uart_update_timeout(port, termios->c_cflag, set_baud); sirfsoc_uart_start_rx(port); - wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN); + wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN); spin_unlock_irqrestore(&port->lock, flags); } -static void startup_uart_controller(struct uart_port *port) +static void sirfsoc_uart_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) { - unsigned long temp_regv; - int temp; - temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL); - wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO); - temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL); - wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO); - wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0); - wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0); - wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN); - wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET); - wr_regl(port, SIRFUART_TX_FIFO_OP, 0); - wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); - wr_regl(port, SIRFUART_RX_FIFO_OP, 0); - temp = port->line == 1 ? 16 : 64; - wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp); - wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp); + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + if (!state) + clk_prepare_enable(sirfport->clk); + else + clk_disable_unprepare(sirfport->clk); } static int sirfsoc_uart_startup(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; unsigned int index = port->line; int ret; set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN); @@ -491,8 +1054,58 @@ static int sirfsoc_uart_startup(struct uart_port *port) index, port->irq); goto irq_err; } - startup_uart_controller(port); + + /* initial hardware settings */ + wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_tx_dma_io_ctrl) | + SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, + rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) | + SIRFUART_IO_MODE); + wr_regl(port, ureg->sirfsoc_tx_dma_io_len, 0); + wr_regl(port, ureg->sirfsoc_rx_dma_io_len, 0); + wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_RX_EN | SIRFUART_TX_EN); + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_mode1, + SIRFSOC_USP_ENDIAN_CTRL_LSBF | + SIRFSOC_USP_EN); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_tx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET); + wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0); + wr_regl(port, ureg->sirfsoc_tx_fifo_ctrl, SIRFUART_FIFO_THD(port)); + wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port)); + if (sirfport->rx_dma_chan) + wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk, + SIRFUART_RX_FIFO_CHK_SC(port->line, 0x4) | + SIRFUART_RX_FIFO_CHK_LC(port->line, 0xe) | + SIRFUART_RX_FIFO_CHK_HC(port->line, 0x1b)); + if (sirfport->tx_dma_chan) { + sirfport->tx_dma_state = TX_DMA_IDLE; + wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk, + SIRFUART_TX_FIFO_CHK_SC(port->line, 0x1b) | + SIRFUART_TX_FIFO_CHK_LC(port->line, 0xe) | + SIRFUART_TX_FIFO_CHK_HC(port->line, 0x4)); + } + sirfport->ms_enabled = false; + if (sirfport->uart_reg->uart_type == SIRF_USP_UART && + sirfport->hw_flow_ctrl) { + set_irq_flags(gpio_to_irq(sirfport->cts_gpio), + IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(gpio_to_irq(sirfport->cts_gpio), + sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport); + if (ret != 0) { + dev_err(port->dev, "UART-USP:request gpio irq fail\n"); + goto init_rx_err; + } + } + enable_irq(port->irq); + + return 0; +init_rx_err: + free_irq(port->irq, sirfport); irq_err: return ret; } @@ -500,12 +1113,22 @@ irq_err: static void sirfsoc_uart_shutdown(struct uart_port *port) { struct sirfsoc_uart_port *sirfport = to_sirfport(port); - wr_regl(port, SIRFUART_INT_EN, 0); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + if (!sirfport->is_marco) + wr_regl(port, ureg->sirfsoc_int_en_reg, 0); + else + wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL); + free_irq(port->irq, sirfport); - if (sirfport->ms_enabled) { + if (sirfport->ms_enabled) sirfsoc_uart_disable_ms(port); - sirfport->ms_enabled = 0; + if (sirfport->uart_reg->uart_type == SIRF_USP_UART && + sirfport->hw_flow_ctrl) { + gpio_set_value(sirfport->rts_gpio, 1); + free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport); } + if (sirfport->tx_dma_chan) + sirfport->tx_dma_state = TX_DMA_IDLE; } static const char *sirfsoc_uart_type(struct uart_port *port) @@ -515,9 +1138,11 @@ static const char *sirfsoc_uart_type(struct uart_port *port) static int sirfsoc_uart_request_port(struct uart_port *port) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_uart_param *uart_param = &sirfport->uart_reg->uart_param; void *ret; ret = request_mem_region(port->mapbase, - SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME); + SIRFUART_MAP_SIZE, uart_param->port_name); return ret ? 0 : -EBUSY; } @@ -546,6 +1171,7 @@ static struct uart_ops sirfsoc_uart_ops = { .startup = sirfsoc_uart_startup, .shutdown = sirfsoc_uart_shutdown, .set_termios = sirfsoc_uart_set_termios, + .pm = sirfsoc_uart_pm, .type = sirfsoc_uart_type, .release_port = sirfsoc_uart_release_port, .request_port = sirfsoc_uart_request_port, @@ -553,32 +1179,45 @@ static struct uart_ops sirfsoc_uart_ops = { }; #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE -static int __init sirfsoc_uart_console_setup(struct console *co, char *options) +static int __init +sirfsoc_uart_console_setup(struct console *co, char *options) { unsigned int baud = 115200; unsigned int bits = 8; unsigned int parity = 'n'; unsigned int flow = 'n'; struct uart_port *port = &sirfsoc_uart_ports[co->index].port; - + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; if (co->index < 0 || co->index >= SIRFSOC_UART_NR) return -EINVAL; if (!port->mapbase) return -ENODEV; + /* enable usp in mode1 register */ + if (sirfport->uart_reg->uart_type == SIRF_USP_UART) + wr_regl(port, ureg->sirfsoc_mode1, SIRFSOC_USP_EN | + SIRFSOC_USP_ENDIAN_CTRL_LSBF); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); port->cons = co; + + /* default console tx/rx transfer using io mode */ + sirfport->rx_dma_chan = NULL; + sirfport->tx_dma_chan = NULL; return uart_set_options(port, co, baud, parity, bits, flow); } static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) { + struct sirfsoc_uart_port *sirfport = to_sirfport(port); + struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg; + struct sirfsoc_fifo_status *ufifo_st = &sirfport->uart_reg->fifo_status; while (rd_regl(port, - SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port)) + ureg->sirfsoc_tx_fifo_status) & ufifo_st->ff_full(port->line)) cpu_relax(); - wr_regb(port, SIRFUART_TX_FIFO_DATA, ch); + wr_regl(port, ureg->sirfsoc_tx_fifo_data, ch); } static void sirfsoc_uart_console_write(struct console *co, const char *s, @@ -620,27 +1259,89 @@ static struct uart_driver sirfsoc_uart_drv = { #endif }; -int sirfsoc_uart_probe(struct platform_device *pdev) +static struct of_device_id sirfsoc_uart_ids[] = { + { .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,}, + { .compatible = "sirf,marco-uart", .data = &sirfsoc_uart}, + { .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp}, + {} +}; +MODULE_DEVICE_TABLE(of, sirfsoc_uart_ids); + +static int sirfsoc_uart_probe(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport; struct uart_port *port; struct resource *res; int ret; - + int i, j; + struct dma_slave_config slv_cfg = { + .src_maxburst = 2, + }; + struct dma_slave_config tx_slv_cfg = { + .dst_maxburst = 2, + }; + const struct of_device_id *match; + + match = of_match_node(sirfsoc_uart_ids, pdev->dev.of_node); if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) { dev_err(&pdev->dev, "Unable to find cell-index in uart node.\n"); ret = -EFAULT; goto err; } - + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) + pdev->id += ((struct sirfsoc_uart_register *) + match->data)->uart_param.register_uart_nr; sirfport = &sirfsoc_uart_ports[pdev->id]; port = &sirfport->port; port->dev = &pdev->dev; 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")) + sirfport->uart_reg->uart_type = SIRF_REAL_UART; + if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-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); + 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); + else + sirfport->rts_gpio = -1; - if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL)) - sirfport->hw_flow_ctrl = 1; + if ((!gpio_is_valid(sirfport->cts_gpio) || + !gpio_is_valid(sirfport->rts_gpio))) { + ret = -EINVAL; + dev_err(&pdev->dev, + "Usp flow control must have cts and rts gpio"); + goto err; + } + ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio, + "usp-cts-gpio"); + if (ret) { + dev_err(&pdev->dev, "Unable request cts gpio"); + goto err; + } + gpio_direction_input(sirfport->cts_gpio); + ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio, + "usp-rts-gpio"); + if (ret) { + dev_err(&pdev->dev, "Unable request rts gpio"); + goto err; + } + gpio_direction_output(sirfport->rts_gpio, 1); + } +usp_no_flow_control: + if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart")) + sirfport->is_marco = true; if (of_property_read_u32(pdev->dev.of_node, "fifosize", @@ -657,6 +1358,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev) ret = -EFAULT; goto err; } + tasklet_init(&sirfport->rx_dma_complete_tasklet, + sirfsoc_uart_rx_dma_complete_tl, (unsigned long)sirfport); + tasklet_init(&sirfport->rx_tmo_process_tasklet, + sirfsoc_rx_tmo_process_tl, (unsigned long)sirfport); port->mapbase = res->start; port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!port->membase) { @@ -672,12 +1377,12 @@ int sirfsoc_uart_probe(struct platform_device *pdev) } port->irq = res->start; - if (sirfport->hw_flow_ctrl) { - sirfport->p = pinctrl_get_select_default(&pdev->dev); - ret = IS_ERR(sirfport->p); - if (ret) - goto err; + sirfport->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(sirfport->clk)) { + ret = PTR_ERR(sirfport->clk); + goto err; } + port->uartclk = clk_get_rate(sirfport->clk); port->ops = &sirfsoc_uart_ops; spin_lock_init(&port->lock); @@ -689,12 +1394,34 @@ int sirfsoc_uart_probe(struct platform_device *pdev) goto port_err; } - return 0; + sirfport->rx_dma_chan = dma_request_slave_channel(port->dev, "rx"); + for (i = 0; sirfport->rx_dma_chan && i < SIRFSOC_RX_LOOP_BUF_CNT; i++) { + sirfport->rx_dma_items[i].xmit.buf = + dma_alloc_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + &sirfport->rx_dma_items[i].dma_addr, GFP_KERNEL); + if (!sirfport->rx_dma_items[i].xmit.buf) { + dev_err(port->dev, "Uart alloc bufa failed\n"); + ret = -ENOMEM; + goto alloc_coherent_err; + } + sirfport->rx_dma_items[i].xmit.head = + sirfport->rx_dma_items[i].xmit.tail = 0; + } + if (sirfport->rx_dma_chan) + dmaengine_slave_config(sirfport->rx_dma_chan, &slv_cfg); + sirfport->tx_dma_chan = dma_request_slave_channel(port->dev, "tx"); + if (sirfport->tx_dma_chan) + dmaengine_slave_config(sirfport->tx_dma_chan, &tx_slv_cfg); + return 0; +alloc_coherent_err: + for (j = 0; j < i; j++) + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items[j].xmit.buf, + sirfport->rx_dma_items[j].dma_addr); + dma_release_channel(sirfport->rx_dma_chan); port_err: - platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) - pinctrl_put(sirfport->p); + clk_put(sirfport->clk); err: return ret; } @@ -703,45 +1430,55 @@ static int sirfsoc_uart_remove(struct platform_device *pdev) { struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); struct uart_port *port = &sirfport->port; - platform_set_drvdata(pdev, NULL); - if (sirfport->hw_flow_ctrl) - pinctrl_put(sirfport->p); + clk_put(sirfport->clk); uart_remove_one_port(&sirfsoc_uart_drv, port); + if (sirfport->rx_dma_chan) { + int i; + dmaengine_terminate_all(sirfport->rx_dma_chan); + dma_release_channel(sirfport->rx_dma_chan); + for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++) + dma_free_coherent(port->dev, SIRFSOC_RX_DMA_BUF_SIZE, + sirfport->rx_dma_items[i].xmit.buf, + sirfport->rx_dma_items[i].dma_addr); + } + if (sirfport->tx_dma_chan) { + dmaengine_terminate_all(sirfport->tx_dma_chan); + dma_release_channel(sirfport->tx_dma_chan); + } return 0; } +#ifdef CONFIG_PM_SLEEP static int -sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state) +sirfsoc_uart_suspend(struct device *pdev) { - struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); struct uart_port *port = &sirfport->port; uart_suspend_port(&sirfsoc_uart_drv, port); return 0; } -static int sirfsoc_uart_resume(struct platform_device *pdev) +static int sirfsoc_uart_resume(struct device *pdev) { - struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); + struct sirfsoc_uart_port *sirfport = dev_get_drvdata(pdev); struct uart_port *port = &sirfport->port; uart_resume_port(&sirfsoc_uart_drv, port); return 0; } +#endif -static struct of_device_id sirfsoc_uart_ids[] = { - { .compatible = "sirf,prima2-uart", }, - {} +static const struct dev_pm_ops sirfsoc_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sirfsoc_uart_suspend, sirfsoc_uart_resume) }; -MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); static struct platform_driver sirfsoc_uart_driver = { .probe = sirfsoc_uart_probe, .remove = sirfsoc_uart_remove, - .suspend = sirfsoc_uart_suspend, - .resume = sirfsoc_uart_resume, .driver = { .name = SIRFUART_PORT_NAME, .owner = THIS_MODULE, .of_match_table = sirfsoc_uart_ids, + .pm = &sirfsoc_uart_pm_ops, }, }; diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index 6e207fdc2fe..69a62ebd3af 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -6,31 +6,260 @@ * Licensed under GPLv2 or later. */ #include <linux/bitops.h> +struct sirfsoc_uart_param { + const char *uart_name; + const char *port_name; + u32 uart_nr; + u32 register_uart_nr; +}; + +struct sirfsoc_register { + /* hardware uart specific */ + u32 sirfsoc_line_ctrl; + u32 sirfsoc_divisor; + /* uart - usp common */ + u32 sirfsoc_tx_rx_en; + u32 sirfsoc_int_en_reg; + u32 sirfsoc_int_st_reg; + u32 sirfsoc_tx_dma_io_ctrl; + u32 sirfsoc_tx_dma_io_len; + u32 sirfsoc_tx_fifo_ctrl; + u32 sirfsoc_tx_fifo_level_chk; + u32 sirfsoc_tx_fifo_op; + u32 sirfsoc_tx_fifo_status; + u32 sirfsoc_tx_fifo_data; + u32 sirfsoc_rx_dma_io_ctrl; + u32 sirfsoc_rx_dma_io_len; + u32 sirfsoc_rx_fifo_ctrl; + u32 sirfsoc_rx_fifo_level_chk; + u32 sirfsoc_rx_fifo_op; + u32 sirfsoc_rx_fifo_status; + u32 sirfsoc_rx_fifo_data; + u32 sirfsoc_afc_ctrl; + u32 sirfsoc_swh_dma_io; + /* hardware usp specific */ + u32 sirfsoc_mode1; + u32 sirfsoc_mode2; + u32 sirfsoc_tx_frame_ctrl; + u32 sirfsoc_rx_frame_ctrl; + u32 sirfsoc_async_param_reg; +}; + +typedef u32 (*fifo_full_mask)(int line); +typedef u32 (*fifo_empty_mask)(int line); + +struct sirfsoc_fifo_status { + fifo_full_mask ff_full; + fifo_empty_mask ff_empty; +}; + +struct sirfsoc_int_en { + u32 sirfsoc_rx_done_en; + u32 sirfsoc_tx_done_en; + u32 sirfsoc_rx_oflow_en; + u32 sirfsoc_tx_allout_en; + u32 sirfsoc_rx_io_dma_en; + u32 sirfsoc_tx_io_dma_en; + u32 sirfsoc_rxfifo_full_en; + u32 sirfsoc_txfifo_empty_en; + u32 sirfsoc_rxfifo_thd_en; + u32 sirfsoc_txfifo_thd_en; + u32 sirfsoc_frm_err_en; + u32 sirfsoc_rxd_brk_en; + u32 sirfsoc_rx_timeout_en; + u32 sirfsoc_parity_err_en; + u32 sirfsoc_cts_en; + u32 sirfsoc_rts_en; +}; + +struct sirfsoc_int_status { + u32 sirfsoc_rx_done; + u32 sirfsoc_tx_done; + u32 sirfsoc_rx_oflow; + u32 sirfsoc_tx_allout; + u32 sirfsoc_rx_io_dma; + u32 sirfsoc_tx_io_dma; + u32 sirfsoc_rxfifo_full; + u32 sirfsoc_txfifo_empty; + u32 sirfsoc_rxfifo_thd; + u32 sirfsoc_txfifo_thd; + u32 sirfsoc_frm_err; + u32 sirfsoc_rxd_brk; + u32 sirfsoc_rx_timeout; + u32 sirfsoc_parity_err; + u32 sirfsoc_cts; + u32 sirfsoc_rts; +}; + +enum sirfsoc_uart_type { + SIRF_REAL_UART, + SIRF_USP_UART, +}; -/* UART Register Offset Define */ -#define SIRFUART_LINE_CTRL 0x0040 -#define SIRFUART_TX_RX_EN 0x004c -#define SIRFUART_DIVISOR 0x0050 -#define SIRFUART_INT_EN 0x0054 -#define SIRFUART_INT_STATUS 0x0058 -#define SIRFUART_TX_DMA_IO_CTRL 0x0100 -#define SIRFUART_TX_DMA_IO_LEN 0x0104 -#define SIRFUART_TX_FIFO_CTRL 0x0108 -#define SIRFUART_TX_FIFO_LEVEL_CHK 0x010C -#define SIRFUART_TX_FIFO_OP 0x0110 -#define SIRFUART_TX_FIFO_STATUS 0x0114 -#define SIRFUART_TX_FIFO_DATA 0x0118 -#define SIRFUART_RX_DMA_IO_CTRL 0x0120 -#define SIRFUART_RX_DMA_IO_LEN 0x0124 -#define SIRFUART_RX_FIFO_CTRL 0x0128 -#define SIRFUART_RX_FIFO_LEVEL_CHK 0x012C -#define SIRFUART_RX_FIFO_OP 0x0130 -#define SIRFUART_RX_FIFO_STATUS 0x0134 -#define SIRFUART_RX_FIFO_DATA 0x0138 -#define SIRFUART_AFC_CTRL 0x0140 -#define SIRFUART_SWH_DMA_IO 0x0148 - -/* UART Line Control Register */ +struct sirfsoc_uart_register { + struct sirfsoc_register uart_reg; + struct sirfsoc_int_en uart_int_en; + struct sirfsoc_int_status uart_int_st; + struct sirfsoc_fifo_status fifo_status; + struct sirfsoc_uart_param uart_param; + enum sirfsoc_uart_type uart_type; +}; + +u32 usp_ff_full(int line) +{ + return 0x80; +} +u32 usp_ff_empty(int line) +{ + return 0x100; +} +u32 uart_ff_full(int line) +{ + return (line == 1) ? (0x20) : (0x80); +} +u32 uart_ff_empty(int line) +{ + return (line == 1) ? (0x40) : (0x100); +} +struct sirfsoc_uart_register sirfsoc_usp = { + .uart_reg = { + .sirfsoc_mode1 = 0x0000, + .sirfsoc_mode2 = 0x0004, + .sirfsoc_tx_frame_ctrl = 0x0008, + .sirfsoc_rx_frame_ctrl = 0x000c, + .sirfsoc_tx_rx_en = 0x0010, + .sirfsoc_int_en_reg = 0x0014, + .sirfsoc_int_st_reg = 0x0018, + .sirfsoc_async_param_reg = 0x0024, + .sirfsoc_tx_dma_io_ctrl = 0x0100, + .sirfsoc_tx_dma_io_len = 0x0104, + .sirfsoc_tx_fifo_ctrl = 0x0108, + .sirfsoc_tx_fifo_level_chk = 0x010c, + .sirfsoc_tx_fifo_op = 0x0110, + .sirfsoc_tx_fifo_status = 0x0114, + .sirfsoc_tx_fifo_data = 0x0118, + .sirfsoc_rx_dma_io_ctrl = 0x0120, + .sirfsoc_rx_dma_io_len = 0x0124, + .sirfsoc_rx_fifo_ctrl = 0x0128, + .sirfsoc_rx_fifo_level_chk = 0x012c, + .sirfsoc_rx_fifo_op = 0x0130, + .sirfsoc_rx_fifo_status = 0x0134, + .sirfsoc_rx_fifo_data = 0x0138, + }, + .uart_int_en = { + .sirfsoc_rx_done_en = BIT(0), + .sirfsoc_tx_done_en = BIT(1), + .sirfsoc_rx_oflow_en = BIT(2), + .sirfsoc_tx_allout_en = BIT(3), + .sirfsoc_rx_io_dma_en = BIT(4), + .sirfsoc_tx_io_dma_en = BIT(5), + .sirfsoc_rxfifo_full_en = BIT(6), + .sirfsoc_txfifo_empty_en = BIT(7), + .sirfsoc_rxfifo_thd_en = BIT(8), + .sirfsoc_txfifo_thd_en = BIT(9), + .sirfsoc_frm_err_en = BIT(10), + .sirfsoc_rx_timeout_en = BIT(11), + .sirfsoc_rxd_brk_en = BIT(15), + }, + .uart_int_st = { + .sirfsoc_rx_done = BIT(0), + .sirfsoc_tx_done = BIT(1), + .sirfsoc_rx_oflow = BIT(2), + .sirfsoc_tx_allout = BIT(3), + .sirfsoc_rx_io_dma = BIT(4), + .sirfsoc_tx_io_dma = BIT(5), + .sirfsoc_rxfifo_full = BIT(6), + .sirfsoc_txfifo_empty = BIT(7), + .sirfsoc_rxfifo_thd = BIT(8), + .sirfsoc_txfifo_thd = BIT(9), + .sirfsoc_frm_err = BIT(10), + .sirfsoc_rx_timeout = BIT(11), + .sirfsoc_rxd_brk = BIT(15), + }, + .fifo_status = { + .ff_full = usp_ff_full, + .ff_empty = usp_ff_empty, + }, + .uart_param = { + .uart_name = "ttySiRF", + .port_name = "sirfsoc-uart", + .uart_nr = 2, + .register_uart_nr = 3, + }, +}; + +struct sirfsoc_uart_register sirfsoc_uart = { + .uart_reg = { + .sirfsoc_line_ctrl = 0x0040, + .sirfsoc_tx_rx_en = 0x004c, + .sirfsoc_divisor = 0x0050, + .sirfsoc_int_en_reg = 0x0054, + .sirfsoc_int_st_reg = 0x0058, + .sirfsoc_tx_dma_io_ctrl = 0x0100, + .sirfsoc_tx_dma_io_len = 0x0104, + .sirfsoc_tx_fifo_ctrl = 0x0108, + .sirfsoc_tx_fifo_level_chk = 0x010c, + .sirfsoc_tx_fifo_op = 0x0110, + .sirfsoc_tx_fifo_status = 0x0114, + .sirfsoc_tx_fifo_data = 0x0118, + .sirfsoc_rx_dma_io_ctrl = 0x0120, + .sirfsoc_rx_dma_io_len = 0x0124, + .sirfsoc_rx_fifo_ctrl = 0x0128, + .sirfsoc_rx_fifo_level_chk = 0x012c, + .sirfsoc_rx_fifo_op = 0x0130, + .sirfsoc_rx_fifo_status = 0x0134, + .sirfsoc_rx_fifo_data = 0x0138, + .sirfsoc_afc_ctrl = 0x0140, + .sirfsoc_swh_dma_io = 0x0148, + }, + .uart_int_en = { + .sirfsoc_rx_done_en = BIT(0), + .sirfsoc_tx_done_en = BIT(1), + .sirfsoc_rx_oflow_en = BIT(2), + .sirfsoc_tx_allout_en = BIT(3), + .sirfsoc_rx_io_dma_en = BIT(4), + .sirfsoc_tx_io_dma_en = BIT(5), + .sirfsoc_rxfifo_full_en = BIT(6), + .sirfsoc_txfifo_empty_en = BIT(7), + .sirfsoc_rxfifo_thd_en = BIT(8), + .sirfsoc_txfifo_thd_en = BIT(9), + .sirfsoc_frm_err_en = BIT(10), + .sirfsoc_rxd_brk_en = BIT(11), + .sirfsoc_rx_timeout_en = BIT(12), + .sirfsoc_parity_err_en = BIT(13), + .sirfsoc_cts_en = BIT(14), + .sirfsoc_rts_en = BIT(15), + }, + .uart_int_st = { + .sirfsoc_rx_done = BIT(0), + .sirfsoc_tx_done = BIT(1), + .sirfsoc_rx_oflow = BIT(2), + .sirfsoc_tx_allout = BIT(3), + .sirfsoc_rx_io_dma = BIT(4), + .sirfsoc_tx_io_dma = BIT(5), + .sirfsoc_rxfifo_full = BIT(6), + .sirfsoc_txfifo_empty = BIT(7), + .sirfsoc_rxfifo_thd = BIT(8), + .sirfsoc_txfifo_thd = BIT(9), + .sirfsoc_frm_err = BIT(10), + .sirfsoc_rxd_brk = BIT(11), + .sirfsoc_rx_timeout = BIT(12), + .sirfsoc_parity_err = BIT(13), + .sirfsoc_cts = BIT(14), + .sirfsoc_rts = BIT(15), + }, + .fifo_status = { + .ff_full = uart_ff_full, + .ff_empty = uart_ff_empty, + }, + .uart_param = { + .uart_name = "ttySiRF", + .port_name = "sirfsoc_uart", + .uart_nr = 3, + .register_uart_nr = 0, + }, +}; +/* uart io ctrl */ #define SIRFUART_DATA_BIT_LEN_MASK 0x3 #define SIRFUART_DATA_BIT_LEN_5 BIT(0) #define SIRFUART_DATA_BIT_LEN_6 1 @@ -50,98 +279,26 @@ #define SIRFUART_LOOP_BACK BIT(7) #define SIRFUART_PARITY_MASK (7 << 3) #define SIRFUART_DUMMY_READ BIT(16) - -#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) -#define SIRFUART_RECV_TIMEOUT_MASK (0xFFFF << 16) -#define SIRFUART_RECV_TIMEOUT(x) (((x) & 0xFFFF) << 16) - -/* UART Auto Flow Control */ -#define SIRFUART_AFC_RX_THD_MASK 0x000000FF +#define SIRFUART_AFC_CTRL_RX_THD 0x70 #define SIRFUART_AFC_RX_EN BIT(8) #define SIRFUART_AFC_TX_EN BIT(9) -#define SIRFUART_CTS_CTRL BIT(10) -#define SIRFUART_RTS_CTRL BIT(11) -#define SIRFUART_CTS_IN_STATUS BIT(12) -#define SIRFUART_RTS_OUT_STATUS BIT(13) - -/* UART Interrupt Enable Register */ -#define SIRFUART_RX_DONE_INT BIT(0) -#define SIRFUART_TX_DONE_INT BIT(1) -#define SIRFUART_RX_OFLOW_INT BIT(2) -#define SIRFUART_TX_ALLOUT_INT BIT(3) -#define SIRFUART_RX_IO_DMA_INT BIT(4) -#define SIRFUART_TX_IO_DMA_INT BIT(5) -#define SIRFUART_RXFIFO_FULL_INT BIT(6) -#define SIRFUART_TXFIFO_EMPTY_INT BIT(7) -#define SIRFUART_RXFIFO_THD_INT BIT(8) -#define SIRFUART_TXFIFO_THD_INT BIT(9) -#define SIRFUART_FRM_ERR_INT BIT(10) -#define SIRFUART_RXD_BREAK_INT BIT(11) -#define SIRFUART_RX_TIMEOUT_INT BIT(12) -#define SIRFUART_PARITY_ERR_INT BIT(13) -#define SIRFUART_CTS_INT_EN BIT(14) -#define SIRFUART_RTS_INT_EN BIT(15) - -/* UART Interrupt Status Register */ -#define SIRFUART_RX_DONE BIT(0) -#define SIRFUART_TX_DONE BIT(1) -#define SIRFUART_RX_OFLOW BIT(2) -#define SIRFUART_TX_ALL_EMPTY BIT(3) -#define SIRFUART_DMA_IO_RX_DONE BIT(4) -#define SIRFUART_DMA_IO_TX_DONE BIT(5) -#define SIRFUART_RXFIFO_FULL BIT(6) -#define SIRFUART_TXFIFO_EMPTY BIT(7) -#define SIRFUART_RXFIFO_THD_REACH BIT(8) -#define SIRFUART_TXFIFO_THD_REACH BIT(9) -#define SIRFUART_FRM_ERR BIT(10) -#define SIRFUART_RXD_BREAK BIT(11) -#define SIRFUART_RX_TIMEOUT BIT(12) -#define SIRFUART_PARITY_ERR BIT(13) -#define SIRFUART_CTS_CHANGE BIT(14) -#define SIRFUART_RTS_CHANGE BIT(15) -#define SIRFUART_PLUG_IN BIT(16) - -#define SIRFUART_ERR_INT_STAT \ - (SIRFUART_RX_OFLOW | \ - SIRFUART_FRM_ERR | \ - SIRFUART_RXD_BREAK | \ - SIRFUART_PARITY_ERR) -#define SIRFUART_ERR_INT_EN \ - (SIRFUART_RX_OFLOW_INT | \ - SIRFUART_FRM_ERR_INT | \ - SIRFUART_RXD_BREAK_INT | \ - SIRFUART_PARITY_ERR_INT) -#define SIRFUART_TX_INT_EN SIRFUART_TXFIFO_EMPTY_INT -#define SIRFUART_RX_IO_INT_EN \ - (SIRFUART_RX_TIMEOUT_INT | \ - SIRFUART_RXFIFO_THD_INT | \ - SIRFUART_RXFIFO_FULL_INT | \ - SIRFUART_ERR_INT_EN) - +#define SIRFUART_AFC_CTS_CTRL BIT(10) +#define SIRFUART_AFC_RTS_CTRL BIT(11) +#define SIRFUART_AFC_CTS_STATUS BIT(12) +#define SIRFUART_AFC_RTS_STATUS BIT(13) /* UART FIFO Register */ -#define SIRFUART_TX_FIFO_STOP 0x0 -#define SIRFUART_TX_FIFO_RESET 0x1 -#define SIRFUART_TX_FIFO_START 0x2 -#define SIRFUART_RX_FIFO_STOP 0x0 -#define SIRFUART_RX_FIFO_RESET 0x1 -#define SIRFUART_RX_FIFO_START 0x2 -#define SIRFUART_TX_MODE_DMA 0 -#define SIRFUART_TX_MODE_IO 1 -#define SIRFUART_RX_MODE_DMA 0 -#define SIRFUART_RX_MODE_IO 1 - -#define SIRFUART_RX_EN 0x1 -#define SIRFUART_TX_EN 0x2 +#define SIRFUART_FIFO_STOP 0x0 +#define SIRFUART_FIFO_RESET BIT(0) +#define SIRFUART_FIFO_START BIT(1) -/* Generic Definitions */ -#define SIRFSOC_UART_NAME "ttySiRF" -#define SIRFSOC_UART_MAJOR 0 -#define SIRFSOC_UART_MINOR 0 -#define SIRFUART_PORT_NAME "sirfsoc-uart" -#define SIRFUART_MAP_SIZE 0x200 -#define SIRFSOC_UART_NR 3 -#define SIRFSOC_PORT_TYPE 0xa5 +#define SIRFUART_RX_EN BIT(0) +#define SIRFUART_TX_EN BIT(1) + +#define SIRFUART_IO_MODE BIT(0) +#define SIRFUART_DMA_MODE 0x0 +/* Macro Specific*/ +#define SIRFUART_INT_EN_CLR 0x0060 /* Baud Rate Calculation */ #define SIRF_MIN_SAMPLE_DIV 0xf #define SIRF_MAX_SAMPLE_DIV 0x3f @@ -151,28 +308,138 @@ #define SIRF_SAMPLE_DIV_MASK 0x3f0000 #define SIRF_BAUD_RATE_SUPPORT_NR 18 +/* USP SPEC */ +#define SIRFSOC_USP_ENDIAN_CTRL_LSBF BIT(4) +#define SIRFSOC_USP_EN BIT(5) +#define SIRFSOC_USP_MODE2_RXD_DELAY_OFFSET 0 +#define SIRFSOC_USP_MODE2_TXD_DELAY_OFFSET 8 +#define SIRFSOC_USP_MODE2_CLK_DIVISOR_MASK 0x3ff +#define SIRFSOC_USP_MODE2_CLK_DIVISOR_OFFSET 21 +#define SIRFSOC_USP_TX_DATA_LEN_OFFSET 0 +#define SIRFSOC_USP_TX_SYNC_LEN_OFFSET 8 +#define SIRFSOC_USP_TX_FRAME_LEN_OFFSET 16 +#define SIRFSOC_USP_TX_SHIFTER_LEN_OFFSET 24 +#define SIRFSOC_USP_TX_CLK_DIVISOR_OFFSET 30 +#define SIRFSOC_USP_RX_DATA_LEN_OFFSET 0 +#define SIRFSOC_USP_RX_FRAME_LEN_OFFSET 8 +#define SIRFSOC_USP_RX_SHIFTER_LEN_OFFSET 16 +#define SIRFSOC_USP_RX_CLK_DIVISOR_OFFSET 24 +#define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f +#define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16 + +/* USP-UART Common */ +#define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) +#define SIRFUART_RECV_TIMEOUT_VALUE(x) \ + (((x) > 0xFFFF) ? 0xFFFF : ((x) & 0xFFFF)) +#define SIRFUART_RECV_TIMEOUT(port, x) \ + (((port)->line > 2) ? (x & 0xFFFF) : ((x) & 0xFFFF) << 16) + +#define SIRFUART_FIFO_THD(port) ((port->line) == 1 ? 16 : 64) +#define SIRFUART_ERR_INT_STAT(port, unit_st) \ + (uint_st->sirfsoc_rx_oflow | \ + uint_st->sirfsoc_frm_err | \ + uint_st->sirfsoc_rxd_brk | \ + ((port->line > 2) ? 0 : uint_st->sirfsoc_parity_err)) +#define SIRFUART_RX_IO_INT_EN(port, uint_en) \ + (uint_en->sirfsoc_rx_timeout_en |\ + uint_en->sirfsoc_rxfifo_thd_en |\ + uint_en->sirfsoc_rxfifo_full_en |\ + uint_en->sirfsoc_frm_err_en |\ + uint_en->sirfsoc_rx_oflow_en |\ + uint_en->sirfsoc_rxd_brk_en |\ + ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) +#define SIRFUART_RX_IO_INT_ST(uint_st) \ + (uint_st->sirfsoc_rx_timeout |\ + uint_st->sirfsoc_rxfifo_thd |\ + uint_st->sirfsoc_rxfifo_full) +#define SIRFUART_CTS_INT_ST(uint_st) (uint_st->sirfsoc_cts) +#define SIRFUART_RX_DMA_INT_EN(port, uint_en) \ + (uint_en->sirfsoc_rx_timeout_en |\ + uint_en->sirfsoc_frm_err_en |\ + uint_en->sirfsoc_rx_oflow_en |\ + uint_en->sirfsoc_rxd_brk_en |\ + ((port->line > 2) ? 0 : uint_en->sirfsoc_parity_err_en)) +/* Generic Definitions */ +#define SIRFSOC_UART_NAME "ttySiRF" +#define SIRFSOC_UART_MAJOR 0 +#define SIRFSOC_UART_MINOR 0 +#define SIRFUART_PORT_NAME "sirfsoc-uart" +#define SIRFUART_MAP_SIZE 0x200 +#define SIRFSOC_UART_NR 6 +#define SIRFSOC_PORT_TYPE 0xa5 + +/* Uart Common Use Macro*/ +#define SIRFSOC_RX_DMA_BUF_SIZE 256 +#define BYTES_TO_ALIGN(dma_addr) ((unsigned long)(dma_addr) & 0x3) +#define LOOP_DMA_BUFA_FILL 1 +#define LOOP_DMA_BUFB_FILL 2 +#define TX_TRAN_PIO 1 +#define TX_TRAN_DMA 2 +/* Uart Fifo Level Chk */ +#define SIRFUART_TX_FIFO_SC_OFFSET 0 +#define SIRFUART_TX_FIFO_LC_OFFSET 10 +#define SIRFUART_TX_FIFO_HC_OFFSET 20 +#define SIRFUART_TX_FIFO_CHK_SC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_SC_OFFSET) +#define SIRFUART_TX_FIFO_CHK_LC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_LC_OFFSET) +#define SIRFUART_TX_FIFO_CHK_HC(line, value) ((((line) == 1) ? (value & 0x3) :\ + (value & 0x1f)) << SIRFUART_TX_FIFO_HC_OFFSET) + +#define SIRFUART_RX_FIFO_CHK_SC SIRFUART_TX_FIFO_CHK_SC +#define SIRFUART_RX_FIFO_CHK_LC SIRFUART_TX_FIFO_CHK_LC +#define SIRFUART_RX_FIFO_CHK_HC SIRFUART_TX_FIFO_CHK_HC +/* Indicate how many buffers used */ +#define SIRFSOC_RX_LOOP_BUF_CNT 2 + /* For Fast Baud Rate Calculation */ struct sirfsoc_baudrate_to_regv { unsigned int baud_rate; unsigned int reg_val; }; +enum sirfsoc_tx_state { + TX_DMA_IDLE, + TX_DMA_RUNNING, + TX_DMA_PAUSE, +}; + +struct sirfsoc_loop_buffer { + struct circ_buf xmit; + dma_cookie_t cookie; + struct dma_async_tx_descriptor *desc; + dma_addr_t dma_addr; +}; + struct sirfsoc_uart_port { - unsigned char hw_flow_ctrl; - unsigned char ms_enabled; + bool hw_flow_ctrl; + bool ms_enabled; struct uart_port port; - struct pinctrl *p; -}; + struct clk *clk; + /* for SiRFmarco, there are SET/CLR for UART_INT_EN */ + bool is_marco; + struct sirfsoc_uart_register *uart_reg; + struct dma_chan *rx_dma_chan; + struct dma_chan *tx_dma_chan; + dma_addr_t tx_dma_addr; + struct dma_async_tx_descriptor *tx_dma_desc; + struct tasklet_struct rx_dma_complete_tasklet; + struct tasklet_struct rx_tmo_process_tasklet; + unsigned int rx_io_count; + unsigned long transfer_size; + enum sirfsoc_tx_state tx_dma_state; + unsigned int cts_gpio; + unsigned int rts_gpio; -/* Hardware Flow Control */ -#define SIRFUART_AFC_CTRL_RX_THD 0x70 + struct sirfsoc_loop_buffer rx_dma_items[SIRFSOC_RX_LOOP_BUF_CNT]; + int rx_completed; + int rx_issued; +}; /* Register Access Control */ #define portaddr(port, reg) ((port)->membase + (reg)) -#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) -#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) /* UART Port Mask */ diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index 1c6de9f5869..f51ffdc696f 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -457,8 +457,8 @@ static int sn_debug_printf(const char *fmt, ...) static void sn_receive_chars(struct sn_cons_port *port, unsigned long flags) { + struct tty_port *tport = NULL; int ch; - struct tty_struct *tty; if (!port) { printk(KERN_ERR "sn_receive_chars - port NULL so can't receive\n"); @@ -472,11 +472,7 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags) if (port->sc_port.state) { /* The serial_core stuffs are initialized, use them */ - tty = port->sc_port.state->port.tty; - } - else { - /* Not registered yet - can't pass to tty layer. */ - tty = NULL; + tport = &port->sc_port.state->port; } while (port->sc_ops->sal_input_pending()) { @@ -516,15 +512,15 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags) #endif /* CONFIG_MAGIC_SYSRQ */ /* record the character to pass up to the tty layer */ - if (tty) { - if(tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0) + if (tport) { + if (tty_insert_flip_char(tport, ch, TTY_NORMAL) == 0) break; } port->sc_port.icount.rx++; } - if (tty) - tty_flip_buffer_push(tty); + if (tport) + tty_flip_buffer_push(tport); } /** diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c new file mode 100644 index 00000000000..f48b1cc07ee --- /dev/null +++ b/drivers/tty/serial/st-asc.c @@ -0,0 +1,932 @@ +/* + * st-asc.c: ST Asynchronous serial controller (ASC) driver + * + * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#if defined(CONFIG_SERIAL_ST_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/serial_core.h> +#include <linux/clk.h> + +#define DRIVER_NAME "st-asc" +#define ASC_SERIAL_NAME "ttyAS" +#define ASC_FIFO_SIZE 16 +#define ASC_MAX_PORTS 8 + +struct asc_port { + struct uart_port port; + struct clk *clk; + unsigned int hw_flow_control:1; + unsigned int force_m1:1; +}; + +static struct asc_port asc_ports[ASC_MAX_PORTS]; +static struct uart_driver asc_uart_driver; + +/*---- UART Register definitions ------------------------------*/ + +/* Register offsets */ + +#define ASC_BAUDRATE 0x00 +#define ASC_TXBUF 0x04 +#define ASC_RXBUF 0x08 +#define ASC_CTL 0x0C +#define ASC_INTEN 0x10 +#define ASC_STA 0x14 +#define ASC_GUARDTIME 0x18 +#define ASC_TIMEOUT 0x1C +#define ASC_TXRESET 0x20 +#define ASC_RXRESET 0x24 +#define ASC_RETRIES 0x28 + +/* ASC_RXBUF */ +#define ASC_RXBUF_PE 0x100 +#define ASC_RXBUF_FE 0x200 +/** + * Some of status comes from higher bits of the character and some come from + * the status register. Combining both of them in to single status using dummy + * bits. + */ +#define ASC_RXBUF_DUMMY_RX 0x10000 +#define ASC_RXBUF_DUMMY_BE 0x20000 +#define ASC_RXBUF_DUMMY_OE 0x40000 + +/* ASC_CTL */ + +#define ASC_CTL_MODE_MSK 0x0007 +#define ASC_CTL_MODE_8BIT 0x0001 +#define ASC_CTL_MODE_7BIT_PAR 0x0003 +#define ASC_CTL_MODE_9BIT 0x0004 +#define ASC_CTL_MODE_8BIT_WKUP 0x0005 +#define ASC_CTL_MODE_8BIT_PAR 0x0007 +#define ASC_CTL_STOP_MSK 0x0018 +#define ASC_CTL_STOP_HALFBIT 0x0000 +#define ASC_CTL_STOP_1BIT 0x0008 +#define ASC_CTL_STOP_1_HALFBIT 0x0010 +#define ASC_CTL_STOP_2BIT 0x0018 +#define ASC_CTL_PARITYODD 0x0020 +#define ASC_CTL_LOOPBACK 0x0040 +#define ASC_CTL_RUN 0x0080 +#define ASC_CTL_RXENABLE 0x0100 +#define ASC_CTL_SCENABLE 0x0200 +#define ASC_CTL_FIFOENABLE 0x0400 +#define ASC_CTL_CTSENABLE 0x0800 +#define ASC_CTL_BAUDMODE 0x1000 + +/* ASC_GUARDTIME */ + +#define ASC_GUARDTIME_MSK 0x00FF + +/* ASC_INTEN */ + +#define ASC_INTEN_RBE 0x0001 +#define ASC_INTEN_TE 0x0002 +#define ASC_INTEN_THE 0x0004 +#define ASC_INTEN_PE 0x0008 +#define ASC_INTEN_FE 0x0010 +#define ASC_INTEN_OE 0x0020 +#define ASC_INTEN_TNE 0x0040 +#define ASC_INTEN_TOI 0x0080 +#define ASC_INTEN_RHF 0x0100 + +/* ASC_RETRIES */ + +#define ASC_RETRIES_MSK 0x00FF + +/* ASC_RXBUF */ + +#define ASC_RXBUF_MSK 0x03FF + +/* ASC_STA */ + +#define ASC_STA_RBF 0x0001 +#define ASC_STA_TE 0x0002 +#define ASC_STA_THE 0x0004 +#define ASC_STA_PE 0x0008 +#define ASC_STA_FE 0x0010 +#define ASC_STA_OE 0x0020 +#define ASC_STA_TNE 0x0040 +#define ASC_STA_TOI 0x0080 +#define ASC_STA_RHF 0x0100 +#define ASC_STA_TF 0x0200 +#define ASC_STA_NKD 0x0400 + +/* ASC_TIMEOUT */ + +#define ASC_TIMEOUT_MSK 0x00FF + +/* ASC_TXBUF */ + +#define ASC_TXBUF_MSK 0x01FF + +/*---- Inline function definitions ---------------------------*/ + +static inline struct asc_port *to_asc_port(struct uart_port *port) +{ + return container_of(port, struct asc_port, port); +} + +static inline u32 asc_in(struct uart_port *port, u32 offset) +{ + return readl(port->membase + offset); +} + +static inline void asc_out(struct uart_port *port, u32 offset, u32 value) +{ + writel(value, port->membase + offset); +} + +/* + * Some simple utility functions to enable and disable interrupts. + * Note that these need to be called with interrupts disabled. + */ +static inline void asc_disable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline void asc_disable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline u32 asc_txfifo_is_empty(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_TE; +} + +static inline u32 asc_txfifo_is_half_empty(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_THE; +} + +static inline const char *asc_port_name(struct uart_port *port) +{ + return to_platform_device(port->dev)->name; +} + +/*----------------------------------------------------------------------*/ + +/* + * This section contains code to support the use of the ASC as a + * generic serial port. + */ + +static inline unsigned asc_hw_txroom(struct uart_port *port) +{ + u32 status = asc_in(port, ASC_STA); + + if (status & ASC_STA_THE) + return port->fifosize / 2; + else if (!(status & ASC_STA_TF)) + return 1; + + return 0; +} + +/* + * Start transmitting chars. + * This is called from both interrupt and task level. + * Either way interrupts are disabled. + */ +static void asc_transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int txroom; + unsigned char c; + + txroom = asc_hw_txroom(port); + + if ((txroom != 0) && port->x_char) { + c = port->x_char; + port->x_char = 0; + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom = asc_hw_txroom(port); + } + + if (uart_tx_stopped(port)) { + /* + * We should try and stop the hardware here, but I + * don't think the ASC has any way to do that. + */ + asc_disable_tx_interrupts(port); + return; + } + + if (uart_circ_empty(xmit)) { + asc_disable_tx_interrupts(port); + return; + } + + if (txroom == 0) + return; + + do { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom--; + } while ((txroom > 0) && (!uart_circ_empty(xmit))); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + asc_disable_tx_interrupts(port); +} + +static void asc_receive_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + unsigned long status; + unsigned long c = 0; + char flag; + + if (port->irq_wake) + pm_wakeup_event(tport->tty->dev, 0); + + while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) { + c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX; + flag = TTY_NORMAL; + port->icount.rx++; + + if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) || + status & ASC_STA_OE) { + + if (c & ASC_RXBUF_FE) { + if (c == (ASC_RXBUF_FE | ASC_RXBUF_DUMMY_RX)) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + c |= ASC_RXBUF_DUMMY_BE; + } else { + port->icount.frame++; + } + } else if (c & ASC_RXBUF_PE) { + port->icount.parity++; + } + /* + * Reading any data from the RX FIFO clears the + * overflow error condition. + */ + if (status & ASC_STA_OE) { + port->icount.overrun++; + c |= ASC_RXBUF_DUMMY_OE; + } + + c &= port->read_status_mask; + + if (c & ASC_RXBUF_DUMMY_BE) + flag = TTY_BREAK; + else if (c & ASC_RXBUF_PE) + flag = TTY_PARITY; + else if (c & ASC_RXBUF_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, c & 0xff)) + continue; + + uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag); + } + + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tport); +} + +static irqreturn_t asc_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + u32 status; + + spin_lock(&port->lock); + + status = asc_in(port, ASC_STA); + + if (status & ASC_STA_RBF) { + /* Receive FIFO not empty */ + asc_receive_chars(port); + } + + if ((status & ASC_STA_THE) && + (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) { + /* Transmitter FIFO at least half empty */ + asc_transmit_chars(port); + } + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * UART Functions + */ + +static unsigned int asc_tx_empty(struct uart_port *port) +{ + return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0; +} + +static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* + * This routine is used for seting signals of: DTR, DCD, CTS/RTS + * We use ASC's hardware for CTS/RTS, so don't need any for that. + * Some boards have DTR and DCD implemented using PIO pins, + * code to do this should be hooked in here. + */ +} + +static unsigned int asc_get_mctrl(struct uart_port *port) +{ + /* + * This routine is used for geting signals of: DTR, DCD, DSR, RI, + * and CTS/RTS + */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* There are probably characters waiting to be transmitted. */ +static void asc_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (!uart_circ_empty(xmit)) + asc_enable_tx_interrupts(port); +} + +/* Transmit stop */ +static void asc_stop_tx(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); +} + +/* Receive stop */ +static void asc_stop_rx(struct uart_port *port) +{ + asc_disable_rx_interrupts(port); +} + +/* Force modem status interrupts on */ +static void asc_enable_ms(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +/* Handle breaks - ignored by us */ +static void asc_break_ctl(struct uart_port *port, int break_state) +{ + /* Nothing here yet .. */ +} + +/* + * Enable port for reception. + */ +static int asc_startup(struct uart_port *port) +{ + if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND, + asc_port_name(port), port)) { + dev_err(port->dev, "cannot allocate irq.\n"); + return -ENODEV; + } + + asc_transmit_chars(port); + asc_enable_rx_interrupts(port); + + return 0; +} + +static void asc_shutdown(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); + asc_disable_rx_interrupts(port); + free_irq(port->irq, port); +} + +static void asc_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned long flags = 0; + u32 ctl; + + switch (state) { + case UART_PM_STATE_ON: + clk_prepare_enable(ascport->clk); + break; + case UART_PM_STATE_OFF: + /* + * Disable the ASC baud rate generator, which is as close as + * we can come to turning it off. Note this is not called with + * the port spinlock held. + */ + spin_lock_irqsave(&port->lock, flags); + ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; + asc_out(port, ASC_CTL, ctl); + spin_unlock_irqrestore(&port->lock, flags); + clk_disable_unprepare(ascport->clk); + break; + } +} + +static void asc_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned int baud; + u32 ctrl_val; + tcflag_t cflag; + unsigned long flags; + + /* Update termios to reflect hardware capabilities */ + termios->c_cflag &= ~(CMSPAR | + (ascport->hw_flow_control ? 0 : CRTSCTS)); + + port->uartclk = clk_get_rate(ascport->clk); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + cflag = termios->c_cflag; + + spin_lock_irqsave(&port->lock, flags); + + /* read control register */ + ctrl_val = asc_in(port, ASC_CTL); + + /* stop serial port and reset value */ + asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN)); + ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE; + + /* reset fifo rx & tx */ + asc_out(port, ASC_TXRESET, 1); + asc_out(port, ASC_RXRESET, 1); + + /* set character length */ + if ((cflag & CSIZE) == CS7) { + ctrl_val |= ASC_CTL_MODE_7BIT_PAR; + } else { + ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR : + ASC_CTL_MODE_8BIT; + } + + /* set stop bit */ + ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT; + + /* odd parity */ + if (cflag & PARODD) + ctrl_val |= ASC_CTL_PARITYODD; + + /* hardware flow control */ + if ((cflag & CRTSCTS)) + ctrl_val |= ASC_CTL_CTSENABLE; + + if ((baud < 19200) && !ascport->force_m1) { + asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud))); + } else { + /* + * MODE 1: recommended for high bit rates (above 19.2K) + * + * baudrate * 16 * 2^16 + * ASCBaudRate = ------------------------ + * inputclock + * + * However to keep the maths inside 32bits we divide top and + * bottom by 64. The +1 is to avoid a divide by zero if the + * input clock rate is something unexpected. + */ + u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1); + asc_out(port, ASC_BAUDRATE, counter); + ctrl_val |= ASC_CTL_BAUDMODE; + } + + uart_update_timeout(port, cflag, baud); + + ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE; + if (termios->c_iflag & INPCK) + ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) + ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE; + + /* + * Characters to ignore + */ + ascport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & IGNBRK) { + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if (!(termios->c_cflag & CREAD)) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX; + + /* Set the timeout */ + asc_out(port, ASC_TIMEOUT, 20); + + /* write final value and enable port */ + asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *asc_type(struct uart_port *port) +{ + return (port->type == PORT_ASC) ? DRIVER_NAME : NULL; +} + +static void asc_release_port(struct uart_port *port) +{ +} + +static int asc_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set + * Set type field if successful + */ +static void asc_config_port(struct uart_port *port, int flags) +{ + if ((flags & UART_CONFIG_TYPE)) + port->type = PORT_ASC; +} + +static int +asc_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* No user changeable parameters */ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context (i.e. kgdb). + */ + +static int asc_get_poll_char(struct uart_port *port) +{ + if (!(asc_in(port, ASC_STA) & ASC_STA_RBF)) + return NO_POLL_CHAR; + + return asc_in(port, ASC_RXBUF); +} + +static void asc_put_poll_char(struct uart_port *port, unsigned char c) +{ + while (!asc_txfifo_is_half_empty(port)) + cpu_relax(); + asc_out(port, ASC_TXBUF, c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +/*---------------------------------------------------------------------*/ + +static struct uart_ops asc_uart_ops = { + .tx_empty = asc_tx_empty, + .set_mctrl = asc_set_mctrl, + .get_mctrl = asc_get_mctrl, + .start_tx = asc_start_tx, + .stop_tx = asc_stop_tx, + .stop_rx = asc_stop_rx, + .enable_ms = asc_enable_ms, + .break_ctl = asc_break_ctl, + .startup = asc_startup, + .shutdown = asc_shutdown, + .set_termios = asc_set_termios, + .type = asc_type, + .release_port = asc_release_port, + .request_port = asc_request_port, + .config_port = asc_config_port, + .verify_port = asc_verify_port, + .pm = asc_pm, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = asc_get_poll_char, + .poll_put_char = asc_put_poll_char, +#endif /* CONFIG_CONSOLE_POLL */ +}; + +static int asc_init_port(struct asc_port *ascport, + struct platform_device *pdev) +{ + struct uart_port *port = &ascport->port; + struct resource *res; + + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &asc_uart_ops; + port->fifosize = ASC_FIFO_SIZE; + port->dev = &pdev->dev; + port->irq = platform_get_irq(pdev, 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + port->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + port->mapbase = res->start; + + spin_lock_init(&port->lock); + + ascport->clk = devm_clk_get(&pdev->dev, NULL); + + if (WARN_ON(IS_ERR(ascport->clk))) + return -EINVAL; + /* ensure that clk rate is correct by enabling the clk */ + clk_prepare_enable(ascport->clk); + ascport->port.uartclk = clk_get_rate(ascport->clk); + WARN_ON(ascport->port.uartclk == 0); + clk_disable_unprepare(ascport->clk); + + return 0; +} + +static struct asc_port *asc_of_get_asc_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, ASC_SERIAL_NAME); + + if (id < 0) + id = 0; + + if (WARN_ON(id >= ASC_MAX_PORTS)) + return NULL; + + asc_ports[id].hw_flow_control = of_property_read_bool(np, + "st,hw-flow-control"); + asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1"); + asc_ports[id].port.line = id; + return &asc_ports[id]; +} + +#ifdef CONFIG_OF +static struct of_device_id asc_match[] = { + { .compatible = "st,asc", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, asc_match); +#endif + +static int asc_serial_probe(struct platform_device *pdev) +{ + int ret; + struct asc_port *ascport; + + ascport = asc_of_get_asc_port(pdev); + if (!ascport) + return -ENODEV; + + ret = asc_init_port(ascport, pdev); + if (ret) + return ret; + + ret = uart_add_one_port(&asc_uart_driver, &ascport->port); + if (ret) + return ret; + + platform_set_drvdata(pdev, &ascport->port); + + return 0; +} + +static int asc_serial_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_remove_one_port(&asc_uart_driver, port); +} + +#ifdef CONFIG_PM_SLEEP +static int asc_serial_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_suspend_port(&asc_uart_driver, port); +} + +static int asc_serial_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_resume_port(&asc_uart_driver, port); +} + +#endif /* CONFIG_PM_SLEEP */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE +static void asc_console_putchar(struct uart_port *port, int ch) +{ + unsigned int timeout = 1000000; + + /* Wait for upto 1 second in case flow control is stopping us. */ + while (--timeout && !asc_txfifo_is_half_empty(port)) + udelay(1); + + asc_out(port, ASC_TXBUF, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ + +static void asc_console_write(struct console *co, const char *s, unsigned count) +{ + struct uart_port *port = &asc_ports[co->index].port; + unsigned long flags; + unsigned long timeout = 1000000; + int locked = 1; + u32 intenable; + + local_irq_save(flags); + if (port->sysrq) + locked = 0; /* asc_interrupt has already claimed the lock */ + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + + /* + * Disable interrupts so we don't get the IRQ line bouncing + * up and down while interrupts are disabled. + */ + intenable = asc_in(port, ASC_INTEN); + asc_out(port, ASC_INTEN, 0); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ + + uart_console_write(port, s, count, asc_console_putchar); + + while (--timeout && !asc_txfifo_is_empty(port)) + udelay(1); + + asc_out(port, ASC_INTEN, intenable); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int asc_console_setup(struct console *co, char *options) +{ + struct asc_port *ascport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= ASC_MAX_PORTS) + return -ENODEV; + + ascport = &asc_ports[co->index]; + + /* + * This driver does not support early console initialization + * (use ARM early printk support instead), so we only expect + * this to be called during the uart port registration when the + * driver gets probed and the port should be mapped at that point. + */ + BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&ascport->port, co, baud, parity, bits, flow); +} + +static struct console asc_console = { + .name = ASC_SERIAL_NAME, + .device = uart_console_device, + .write = asc_console_write, + .setup = asc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &asc_uart_driver, +}; + +#define ASC_SERIAL_CONSOLE (&asc_console) + +#else +#define ASC_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */ + +static struct uart_driver asc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ASC_SERIAL_NAME, + .major = 0, + .minor = 0, + .nr = ASC_MAX_PORTS, + .cons = ASC_SERIAL_CONSOLE, +}; + +static const struct dev_pm_ops asc_serial_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume) +}; + +static struct platform_driver asc_serial_driver = { + .probe = asc_serial_probe, + .remove = asc_serial_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &asc_serial_pm_ops, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(asc_match), + }, +}; + +static int __init asc_init(void) +{ + int ret; + static char banner[] __initdata = + KERN_INFO "STMicroelectronics ASC driver initialized\n"; + + printk(banner); + + ret = uart_register_driver(&asc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&asc_serial_driver); + if (ret) + uart_unregister_driver(&asc_uart_driver); + + return ret; +} + +static void __exit asc_exit(void) +{ + platform_driver_unregister(&asc_serial_driver); + uart_unregister_driver(&asc_uart_driver); +} + +module_init(asc_init); +module_exit(asc_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("STMicroelectronics (R&D) Limited"); +MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index b9bf9c53f7f..dc697cee248 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -72,7 +72,7 @@ static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) } } -static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) +static int receive_chars_getchar(struct uart_port *port) { int saw_console_brk = 0; int limit = 10000; @@ -99,7 +99,7 @@ static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) uart_handle_dcd_change(port, 1); } - if (tty == NULL) { + if (port->state == NULL) { uart_handle_sysrq_char(port, c); continue; } @@ -109,13 +109,13 @@ static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) if (uart_handle_sysrq_char(port, c)) continue; - tty_insert_flip_char(tty, c, TTY_NORMAL); + tty_insert_flip_char(&port->state->port, c, TTY_NORMAL); } return saw_console_brk; } -static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) +static int receive_chars_read(struct uart_port *port) { int saw_console_brk = 0; int limit = 10000; @@ -152,12 +152,13 @@ static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) for (i = 0; i < bytes_read; i++) uart_handle_sysrq_char(port, con_read_page[i]); - if (tty == NULL) + if (port->state == NULL) continue; port->icount.rx += bytes_read; - tty_insert_flip_string(tty, con_read_page, bytes_read); + tty_insert_flip_string(&port->state->port, con_read_page, + bytes_read); } return saw_console_brk; @@ -165,7 +166,7 @@ static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) struct sunhv_ops { void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); - int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); + int (*receive_chars)(struct uart_port *port); }; static struct sunhv_ops bychar_ops = { @@ -180,17 +181,17 @@ static struct sunhv_ops bywrite_ops = { static struct sunhv_ops *sunhv_ops = &bychar_ops; -static struct tty_struct *receive_chars(struct uart_port *port) +static struct tty_port *receive_chars(struct uart_port *port) { - struct tty_struct *tty = NULL; + struct tty_port *tport = NULL; if (port->state != NULL) /* Unopened serial console */ - tty = port->state->port.tty; + tport = &port->state->port; - if (sunhv_ops->receive_chars(port, tty)) + if (sunhv_ops->receive_chars(port)) sun_do_break(); - return tty; + return tport; } static void transmit_chars(struct uart_port *port) @@ -213,16 +214,16 @@ static void transmit_chars(struct uart_port *port) static irqreturn_t sunhv_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty; + struct tty_port *tport; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - tty = receive_chars(port); + tport = receive_chars(port); transmit_chars(port); spin_unlock_irqrestore(&port->lock, flags); - if (tty) - tty_flip_buffer_push(tty); + if (tport) + tty_flip_buffer_push(tport); return IRQ_HANDLED; } @@ -432,13 +433,10 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign unsigned long flags; int locked = 1; - local_irq_save(flags); - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else - spin_lock(&port->lock); + if (port->sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); while (n > 0) { unsigned long ra = __pa(con_write_page); @@ -469,8 +467,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign } if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static inline void sunhv_console_putchar(struct uart_port *port, char c) @@ -491,7 +488,10 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig unsigned long flags; int i, locked = 1; - local_irq_save(flags); + if (port->sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); if (port->sysrq) { locked = 0; } else if (oops_in_progress) { @@ -506,8 +506,7 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig } if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static struct console sunhv_console = { @@ -576,7 +575,7 @@ static int hv_probe(struct platform_device *op) if (err) goto out_remove_port; - dev_set_drvdata(&op->dev, port); + platform_set_drvdata(op, port); return 0; @@ -600,7 +599,7 @@ out_free_port: static int hv_remove(struct platform_device *dev) { - struct uart_port *port = dev_get_drvdata(&dev->dev); + struct uart_port *port = platform_get_drvdata(dev); free_irq(port->irq, port); @@ -611,8 +610,6 @@ static int hv_remove(struct platform_device *dev) kfree(port); sunhv_port = NULL; - dev_set_drvdata(&dev->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index bd8b3b63410..2f57df9a71d 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -107,11 +107,11 @@ static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up) udelay(1); } -static struct tty_struct * +static struct tty_port * receive_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat) { - struct tty_struct *tty = NULL; + struct tty_port *port = NULL; unsigned char buf[32]; int saw_console_brk = 0; int free_fifo = 0; @@ -119,7 +119,7 @@ receive_chars(struct uart_sunsab_port *up, int i; if (up->port.state != NULL) /* Unopened serial console */ - tty = up->port.state->port.tty; + port = &up->port.state->port; /* Read number of BYTES (Character + Status) available. */ if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { @@ -136,7 +136,7 @@ receive_chars(struct uart_sunsab_port *up, if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { sunsab_cec_wait(up); writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr); - return tty; + return port; } if (stat->sreg.isr0 & SAB82532_ISR0_RFO) @@ -160,11 +160,6 @@ receive_chars(struct uart_sunsab_port *up, for (i = 0; i < count; i++) { unsigned char ch = buf[i], flag; - if (tty == NULL) { - uart_handle_sysrq_char(&up->port, ch); - continue; - } - flag = TTY_NORMAL; up->port.icount.rx++; @@ -208,20 +203,20 @@ receive_chars(struct uart_sunsab_port *up, flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0) - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(port, ch, flag); if (stat->sreg.isr0 & SAB82532_ISR0_RFO) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); } if (saw_console_brk) sun_do_break(); - return tty; + return port; } static void sunsab_stop_tx(struct uart_port *); @@ -304,7 +299,7 @@ static void check_status(struct uart_sunsab_port *up, static irqreturn_t sunsab_interrupt(int irq, void *dev_id) { struct uart_sunsab_port *up = dev_id; - struct tty_struct *tty; + struct tty_port *port = NULL; union sab82532_irq_status status; unsigned long flags; unsigned char gis; @@ -318,12 +313,11 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) if (gis & 2) status.sreg.isr1 = readb(&up->regs->r.isr1); - tty = NULL; if (status.stat) { if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || (status.sreg.isr1 & SAB82532_ISR1_BRK)) - tty = receive_chars(up, &status); + port = receive_chars(up, &status); if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || (status.sreg.isr1 & SAB82532_ISR1_CSC)) check_status(up, &status); @@ -333,8 +327,8 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) spin_unlock_irqrestore(&up->port.lock, flags); - if (tty) - tty_flip_buffer_push(tty); + if (port) + tty_flip_buffer_push(port); return IRQ_HANDLED; } @@ -433,6 +427,9 @@ static void sunsab_start_tx(struct uart_port *port) struct circ_buf *xmit = &up->port.state->xmit; int i; + if (uart_circ_empty(xmit)) + return; + up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); writeb(up->interrupt_mask1, &up->regs->w.imr1); @@ -725,7 +722,7 @@ static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cfla if (iflag & INPCK) up->port.read_status_mask |= (SAB82532_ISR0_PERR | SAB82532_ISR0_FERR); - if (iflag & (BRKINT | PARMRK)) + if (iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8); /* @@ -850,20 +847,16 @@ static void sunsab_console_write(struct console *con, const char *s, unsigned n) unsigned long flags; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); uart_console_write(&up->port, s, n, sunsab_console_putchar); sunsab_tec_wait(up); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } static int sunsab_console_setup(struct console *con, char *options) @@ -900,7 +893,7 @@ static int sunsab_console_setup(struct console *con, char *options) case B115200: baud = 115200; break; case B230400: baud = 230400; break; case B460800: baud = 460800; break; - }; + } /* * Temporary fix. @@ -1043,7 +1036,7 @@ static int sab_probe(struct platform_device *op) if (err) goto out3; - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); inst++; @@ -1065,7 +1058,7 @@ out: static int sab_remove(struct platform_device *op) { - struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsab_port *up = platform_get_drvdata(op); uart_remove_one_port(&sunsab_reg, &up[1].port); uart_remove_one_port(&sunsab_reg, &up[0].port); @@ -1076,8 +1069,6 @@ static int sab_remove(struct platform_device *op) up[0].port.membase, sizeof(union sab82532_async_regs)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 220da3f9724..5326ae195e5 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -315,10 +315,10 @@ static void sunsu_enable_ms(struct uart_port *port) spin_unlock_irqrestore(&up->port.lock, flags); } -static struct tty_struct * +static void receive_chars(struct uart_sunsu_port *up, unsigned char *status) { - struct tty_struct *tty = up->port.state->port.tty; + struct tty_port *port = &up->port.state->port; unsigned char ch, flag; int max_count = 256; int saw_console_brk = 0; @@ -376,22 +376,20 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) if (uart_handle_sysrq_char(&up->port, ch)) goto ignore_char; if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(port, ch, flag); if (*status & UART_LSR_OE) /* * Overrun is special, since it's reported * immediately, and doesn't affect the current * character. */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); ignore_char: *status = serial_inp(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); if (saw_console_brk) sun_do_break(); - - return tty; } static void transmit_chars(struct uart_sunsu_port *up) @@ -460,20 +458,16 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) spin_lock_irqsave(&up->port.lock, flags); do { - struct tty_struct *tty; - status = serial_inp(up, UART_LSR); - tty = NULL; if (status & UART_LSR_DR) - tty = receive_chars(up, &status); + receive_chars(up, &status); check_modem_status(up); if (status & UART_LSR_THRE) transmit_chars(up); spin_unlock_irqrestore(&up->port.lock, flags); - if (tty) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&up->port.state->port); spin_lock_irqsave(&up->port.lock, flags); @@ -528,7 +522,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break) serio_interrupt(&up->serio, ch, 0); #endif break; - }; + } } } while (serial_in(up, UART_LSR) & UART_LSR_DR); } @@ -840,7 +834,7 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag, up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (iflag & INPCK) up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (iflag & (BRKINT | PARMRK)) + if (iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= UART_LSR_BI; /* @@ -974,6 +968,7 @@ static struct uart_ops sunsu_pops = { #define UART_NR 4 static struct uart_sunsu_port sunsu_ports[UART_NR]; +static int nr_inst; /* Number of already registered ports */ #ifdef CONFIG_SERIO @@ -1300,13 +1295,10 @@ static void sunsu_console_write(struct console *co, const char *s, unsigned int ier; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); /* * First save the UER then disable the interrupts @@ -1324,8 +1316,7 @@ static void sunsu_console_write(struct console *co, const char *s, serial_out(up, UART_IER, ier); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } /* @@ -1343,13 +1334,8 @@ static int __init sunsu_console_setup(struct console *co, char *options) printk("Console: ttyS%d (SU)\n", (sunsu_reg.minor - 64) + co->index); - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; + if (co->index > nr_inst) + return -ENODEV; port = &sunsu_ports[co->index].port; /* @@ -1414,7 +1400,6 @@ static enum su_type su_get_type(struct device_node *dp) static int su_probe(struct platform_device *op) { - static int inst; struct device_node *dp = op->dev.of_node; struct uart_sunsu_port *up; struct resource *rp; @@ -1424,16 +1409,16 @@ static int su_probe(struct platform_device *op) type = su_get_type(dp); if (type == SU_PORT_PORT) { - if (inst >= UART_NR) + if (nr_inst >= UART_NR) return -EINVAL; - up = &sunsu_ports[inst]; + up = &sunsu_ports[nr_inst]; } else { up = kzalloc(sizeof(*up), GFP_KERNEL); if (!up) return -ENOMEM; } - up->port.line = inst; + up->port.line = nr_inst; spin_lock_init(&up->port.lock); @@ -1465,7 +1450,9 @@ static int su_probe(struct platform_device *op) kfree(up); return err; } - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); + + nr_inst++; return 0; } @@ -1492,9 +1479,9 @@ static int su_probe(struct platform_device *op) if (err) goto out_unmap; - dev_set_drvdata(&op->dev, up); + platform_set_drvdata(op, up); - inst++; + nr_inst++; return 0; @@ -1505,7 +1492,7 @@ out_unmap: static int su_remove(struct platform_device *op) { - struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); + struct uart_sunsu_port *up = platform_get_drvdata(op); bool kbdms = false; if (up->su_type == SU_PORT_MS || @@ -1525,8 +1512,6 @@ static int su_remove(struct platform_device *op) if (kbdms) kfree(up); - dev_set_drvdata(&op->dev, NULL); - return 0; } @@ -1601,6 +1586,7 @@ static int __init sunsu_init(void) static void __exit sunsu_exit(void) { + platform_driver_unregister(&su_driver); if (sunsu_reg.nr) sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr); } diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index aef4fab957c..02df3940b95 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -319,21 +319,19 @@ static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, serio_interrupt(&up->serio, ch, 0); #endif break; - }; + } } } -static struct tty_struct * +static struct tty_port * sunzilog_receive_chars(struct uart_sunzilog_port *up, struct zilog_channel __iomem *channel) { - struct tty_struct *tty; + struct tty_port *port = NULL; unsigned char ch, r1, flag; - tty = NULL; - if (up->port.state != NULL && /* Unopened serial console */ - up->port.state->port.tty != NULL) /* Keyboard || mouse */ - tty = up->port.state->port.tty; + if (up->port.state != NULL) /* Unopened serial console */ + port = &up->port.state->port; for (;;) { @@ -366,11 +364,6 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, continue; } - if (tty == NULL) { - uart_handle_sysrq_char(&up->port, ch); - continue; - } - /* A real serial line, record the character and status. */ flag = TTY_NORMAL; up->port.icount.rx++; @@ -395,18 +388,18 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, else if (r1 & CRC_ERR) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_handle_sysrq_char(&up->port, ch) || !port) continue; if (up->port.ignore_status_mask == 0xff || (r1 & up->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(port, ch, flag); } if (r1 & Rx_OVR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(port, 0, TTY_OVERRUN); } - return tty; + return port; } static void sunzilog_status_handle(struct uart_sunzilog_port *up, @@ -539,21 +532,21 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) while (up) { struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - struct tty_struct *tty; + struct tty_port *port; unsigned char r3; spin_lock(&up->port.lock); r3 = read_zsreg(channel, R3); /* Channel A */ - tty = NULL; + port = NULL; if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { writeb(RES_H_IUS, &channel->control); ZSDELAY(); ZS_WSYNC(channel); if (r3 & CHARxIP) - tty = sunzilog_receive_chars(up, channel); + port = sunzilog_receive_chars(up, channel); if (r3 & CHAEXT) sunzilog_status_handle(up, channel); if (r3 & CHATxIP) @@ -561,22 +554,22 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) } spin_unlock(&up->port.lock); - if (tty) - tty_flip_buffer_push(tty); + if (port) + tty_flip_buffer_push(port); /* Channel B */ up = up->next; channel = ZILOG_CHANNEL_FROM_PORT(&up->port); spin_lock(&up->port.lock); - tty = NULL; + port = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { writeb(RES_H_IUS, &channel->control); ZSDELAY(); ZS_WSYNC(channel); if (r3 & CHBRxIP) - tty = sunzilog_receive_chars(up, channel); + port = sunzilog_receive_chars(up, channel); if (r3 & CHBEXT) sunzilog_status_handle(up, channel); if (r3 & CHBTxIP) @@ -584,8 +577,8 @@ static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) } spin_unlock(&up->port.lock); - if (tty) - tty_flip_buffer_push(tty); + if (port) + tty_flip_buffer_push(port); up = up->next; } @@ -710,6 +703,8 @@ static void sunzilog_start_tx(struct uart_port *port) } else { struct circ_buf *xmit = &port->state->xmit; + if (uart_circ_empty(xmit)) + return; writeb(xmit->buf[xmit->tail], &channel->data); ZSDELAY(); ZS_WSYNC(channel); @@ -904,7 +899,7 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, up->curregs[R5] |= Tx8; up->parity_mask = 0xff; break; - }; + } up->curregs[R4] &= ~0x0c; if (cflag & CSTOPB) up->curregs[R4] |= SB2; @@ -922,7 +917,7 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, up->port.read_status_mask = Rx_OVR; if (iflag & INPCK) up->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) + if (iflag & (IGNBRK | BRKINT | PARMRK)) up->port.read_status_mask |= BRK_ABRT; up->port.ignore_status_mask = 0; @@ -1202,20 +1197,16 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count) unsigned long flags; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); uart_console_write(&up->port, s, count, sunzilog_putchar); udelay(2); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } static int __init sunzilog_console_setup(struct console *con, char *options) @@ -1246,7 +1237,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) default: case B9600: baud = 9600; break; case B19200: baud = 19200; break; case B38400: baud = 38400; break; - }; + } brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); @@ -1502,7 +1493,7 @@ static int zs_probe(struct platform_device *op) kbm_inst++; } - dev_set_drvdata(&op->dev, &up[0]); + platform_set_drvdata(op, &up[0]); return 0; } @@ -1519,7 +1510,7 @@ static void zs_remove_one(struct uart_sunzilog_port *up) static int zs_remove(struct platform_device *op) { - struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); + struct uart_sunzilog_port *up = platform_get_drvdata(op); struct zilog_layout __iomem *regs; zs_remove_one(&up[0]); @@ -1528,8 +1519,6 @@ static int zs_remove(struct platform_device *op) regs = sunzilog_chip_regs[up[0].port.line / 2]; of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/tty/serial/tilegx.c b/drivers/tty/serial/tilegx.c new file mode 100644 index 00000000000..613ccf09dc2 --- /dev/null +++ b/drivers/tty/serial/tilegx.c @@ -0,0 +1,708 @@ +/* + * Copyright 2013 Tilera Corporation. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * TILEGx UART driver. + */ + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#include <gxio/common.h> +#include <gxio/iorpc_globals.h> +#include <gxio/iorpc_uart.h> +#include <gxio/kiorpc.h> + +#include <hv/drv_uart_intf.h> + +/* + * Use device name ttyS, major 4, minor 64-65. + * This is the usual serial port name, 8250 conventional range. + */ +#define TILEGX_UART_MAJOR TTY_MAJOR +#define TILEGX_UART_MINOR 64 +#define TILEGX_UART_NAME "ttyS" +#define DRIVER_NAME_STRING "TILEGx_Serial" +#define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */ + +struct tile_uart_port { + /* UART port. */ + struct uart_port uart; + + /* GXIO device context. */ + gxio_uart_context_t context; + + /* UART access mutex. */ + struct mutex mutex; + + /* CPU receiving interrupts. */ + int irq_cpu; +}; + +static struct tile_uart_port tile_uart_ports[TILEGX_UART_NR]; +static struct uart_driver tilegx_uart_driver; + + +/* + * Read UART rx fifo, and insert the chars into tty buffer. + */ +static void receive_chars(struct tile_uart_port *tile_uart, + struct tty_struct *tty) +{ + int i; + char c; + UART_FIFO_COUNT_t count; + gxio_uart_context_t *context = &tile_uart->context; + struct tty_port *port = tty->port; + + count.word = gxio_uart_read(context, UART_FIFO_COUNT); + for (i = 0; i < count.rfifo_count; i++) { + c = (char)gxio_uart_read(context, UART_RECEIVE_DATA); + tty_insert_flip_char(port, c, TTY_NORMAL); + } +} + + +/* + * Drain the Rx FIFO, called by interrupt handler. + */ +static void handle_receive(struct tile_uart_port *tile_uart) +{ + struct tty_port *port = &tile_uart->uart.state->port; + struct tty_struct *tty = tty_port_tty_get(port); + gxio_uart_context_t *context = &tile_uart->context; + + if (!tty) + return; + + /* First read UART rx fifo. */ + receive_chars(tile_uart, tty); + + /* Reset RFIFO_WE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__RFIFO_WE_MASK); + + /* Final read, if any chars comes between the first read and + * the interrupt reset. + */ + receive_chars(tile_uart, tty); + + spin_unlock(&tile_uart->uart.lock); + tty_flip_buffer_push(port); + spin_lock(&tile_uart->uart.lock); + tty_kref_put(tty); +} + + +/* + * Push one char to UART Write FIFO. + * Return 0 on success, -1 if write filo is full. + */ +static int tilegx_putchar(gxio_uart_context_t *context, char c) +{ + UART_FLAG_t flag; + flag.word = gxio_uart_read(context, UART_FLAG); + if (flag.wfifo_full) + return -1; + + gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); + return 0; +} + + +/* + * Send chars to UART Write FIFO; called by interrupt handler. + */ +static void handle_transmit(struct tile_uart_port *tile_uart) +{ + unsigned char ch; + struct uart_port *port; + struct circ_buf *xmit; + gxio_uart_context_t *context = &tile_uart->context; + + /* First reset WFIFO_RE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK); + + port = &tile_uart->uart; + xmit = &port->state->xmit; + if (port->x_char) { + if (tilegx_putchar(context, port->x_char)) + return; + port->x_char = 0; + port->icount.tx++; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + while (!uart_circ_empty(xmit)) { + ch = xmit->buf[xmit->tail]; + if (tilegx_putchar(context, ch)) + break; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + /* Reset WFIFO_RE interrupt. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + + +/* + * UART Interrupt handler. + */ +static irqreturn_t tilegx_interrupt(int irq, void *dev_id) +{ + unsigned long flags; + UART_INTERRUPT_STATUS_t intr_stat; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + struct uart_port *port = dev_id; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&port->lock, flags); + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + intr_stat.word = gxio_uart_read(context, UART_INTERRUPT_STATUS); + + if (intr_stat.rfifo_we) { + handle_receive(tile_uart); + ret = IRQ_HANDLED; + } + if (intr_stat.wfifo_re) { + handle_transmit(tile_uart); + ret = IRQ_HANDLED; + } + + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + + +/* + * Return TIOCSER_TEMT when transmitter FIFO is empty. + */ +static u_int tilegx_tx_empty(struct uart_port *port) +{ + int ret; + UART_FLAG_t flag; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return 0; + context = &tile_uart->context; + + flag.word = gxio_uart_read(context, UART_FLAG); + ret = (flag.wfifo_empty) ? TIOCSER_TEMT : 0; + mutex_unlock(&tile_uart->mutex); + + return ret; +} + + +/* + * Set state of the modem control output lines. + */ +static void tilegx_set_mctrl(struct uart_port *port, u_int mctrl) +{ + /* N/A */ +} + + +/* + * Get state of the modem control input lines. + */ +static u_int tilegx_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + + +/* + * Stop transmitting. + */ +static void tilegx_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + + +/* + * Start transmitting. + */ +static void tilegx_start_tx(struct uart_port *port) +{ + unsigned char ch; + struct circ_buf *xmit; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + context = &tile_uart->context; + xmit = &port->state->xmit; + if (port->x_char) { + if (tilegx_putchar(context, port->x_char)) + return; + port->x_char = 0; + port->icount.tx++; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mutex_unlock(&tile_uart->mutex); + return; + } + + while (!uart_circ_empty(xmit)) { + ch = xmit->buf[xmit->tail]; + if (tilegx_putchar(context, ch)) + break; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Stop receiving - port is in process of being closed. + */ +static void tilegx_stop_rx(struct uart_port *port) +{ + int err; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int cpu; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + + context = &tile_uart->context; + cpu = tile_uart->irq_cpu; + err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, -1); + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Enable modem status interrupts. + */ +static void tilegx_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +/* + * Control the transmission of a break signal. + */ +static void tilegx_break_ctl(struct uart_port *port, int break_state) +{ + /* N/A */ +} + + +/* + * Perform initialization and enable port for reception. + */ +static int tilegx_startup(struct uart_port *port) +{ + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int ret = 0; + int cpu = raw_smp_processor_id(); /* pick an arbitrary cpu */ + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (mutex_lock_interruptible(&tile_uart->mutex)) + return -EBUSY; + context = &tile_uart->context; + + /* Now open the hypervisor device if we haven't already. */ + if (context->fd < 0) { + UART_INTERRUPT_MASK_t intr_mask; + + /* Initialize UART device. */ + ret = gxio_uart_init(context, port->line); + if (ret) { + ret = -ENXIO; + goto err; + } + + /* Create our IRQs. */ + port->irq = irq_alloc_hwirq(-1); + if (!port->irq) + goto err_uart_dest; + tile_irq_activate(port->irq, TILE_IRQ_PERCPU); + + /* Register our IRQs. */ + ret = request_irq(port->irq, tilegx_interrupt, 0, + tilegx_uart_driver.driver_name, port); + if (ret) + goto err_dest_irq; + + /* Request that the hardware start sending us interrupts. */ + tile_uart->irq_cpu = cpu; + ret = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, port->irq); + if (ret) + goto err_free_irq; + + /* Enable UART Tx/Rx Interrupt. */ + intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); + intr_mask.wfifo_re = 0; + intr_mask.rfifo_we = 0; + gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); + + /* Reset the Tx/Rx interrupt in case it's set. */ + gxio_uart_write(context, UART_INTERRUPT_STATUS, + UART_INTERRUPT_MASK__WFIFO_RE_MASK | + UART_INTERRUPT_MASK__RFIFO_WE_MASK); + } + + mutex_unlock(&tile_uart->mutex); + return ret; + +err_free_irq: + free_irq(port->irq, port); +err_dest_irq: + irq_free_hwirq(port->irq); +err_uart_dest: + gxio_uart_destroy(context); + ret = -ENXIO; +err: + mutex_unlock(&tile_uart->mutex); + return ret; +} + + +/* + * Release kernel resources if it is the last close, disable the port, + * free IRQ and close the port. + */ +static void tilegx_shutdown(struct uart_port *port) +{ + int err; + UART_INTERRUPT_MASK_t intr_mask; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + int cpu; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (mutex_lock_interruptible(&tile_uart->mutex)) + return; + context = &tile_uart->context; + + /* Disable UART Tx/Rx Interrupt. */ + intr_mask.word = gxio_uart_read(context, UART_INTERRUPT_MASK); + intr_mask.wfifo_re = 1; + intr_mask.rfifo_we = 1; + gxio_uart_write(context, UART_INTERRUPT_MASK, intr_mask.word); + + /* Request that the hardware stop sending us interrupts. */ + cpu = tile_uart->irq_cpu; + err = gxio_uart_cfg_interrupt(context, cpu_x(cpu), cpu_y(cpu), + KERNEL_PL, -1); + + if (port->irq > 0) { + free_irq(port->irq, port); + irq_free_hwirq(port->irq); + port->irq = 0; + } + + gxio_uart_destroy(context); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Flush the buffer. + */ +static void tilegx_flush_buffer(struct uart_port *port) +{ + /* N/A */ +} + + +/* + * Change the port parameters. + */ +static void tilegx_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + int err; + UART_DIVISOR_t divisor; + UART_TYPE_t type; + unsigned int baud; + struct tile_uart_port *tile_uart; + gxio_uart_context_t *context; + + tile_uart = container_of(port, struct tile_uart_port, uart); + if (!mutex_trylock(&tile_uart->mutex)) + return; + context = &tile_uart->context; + + /* Open the hypervisor device if we haven't already. */ + if (context->fd < 0) { + err = gxio_uart_init(context, port->line); + if (err) { + mutex_unlock(&tile_uart->mutex); + return; + } + } + + divisor.word = gxio_uart_read(context, UART_DIVISOR); + type.word = gxio_uart_read(context, UART_TYPE); + + /* Divisor. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + divisor.divisor = uart_get_divisor(port, baud); + + /* Byte size. */ + if ((termios->c_cflag & CSIZE) == CS7) + type.dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS; + else + type.dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS; + + /* Parity. */ + if (termios->c_cflag & PARENB) { + /* Mark or Space parity. */ + if (termios->c_cflag & CMSPAR) + if (termios->c_cflag & PARODD) + type.ptype = UART_TYPE__PTYPE_VAL_MARK; + else + type.ptype = UART_TYPE__PTYPE_VAL_SPACE; + else if (termios->c_cflag & PARODD) + type.ptype = UART_TYPE__PTYPE_VAL_ODD; + else + type.ptype = UART_TYPE__PTYPE_VAL_EVEN; + } else + type.ptype = UART_TYPE__PTYPE_VAL_NONE; + + /* Stop bits. */ + if (termios->c_cflag & CSTOPB) + type.sbits = UART_TYPE__SBITS_VAL_TWO_SBITS; + else + type.sbits = UART_TYPE__SBITS_VAL_ONE_SBITS; + + /* Set the uart paramters. */ + gxio_uart_write(context, UART_DIVISOR, divisor.word); + gxio_uart_write(context, UART_TYPE, type.word); + + mutex_unlock(&tile_uart->mutex); +} + + +/* + * Return string describing the specified port. + */ +static const char *tilegx_type(struct uart_port *port) +{ + return port->type == PORT_TILEGX ? DRIVER_NAME_STRING : NULL; +} + + +/* + * Release the resources being used by 'port'. + */ +static void tilegx_release_port(struct uart_port *port) +{ + /* Nothing to release. */ +} + + +/* + * Request the resources being used by 'port'. + */ +static int tilegx_request_port(struct uart_port *port) +{ + /* Always present. */ + return 0; +} + + +/* + * Configure/autoconfigure the port. + */ +static void tilegx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_TILEGX; +} + + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int tilegx_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_TILEGX)) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL + +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int tilegx_poll_get_char(struct uart_port *port) +{ + UART_FIFO_COUNT_t count; + gxio_uart_context_t *context; + struct tile_uart_port *tile_uart; + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + count.word = gxio_uart_read(context, UART_FIFO_COUNT); + if (count.rfifo_count == 0) + return NO_POLL_CHAR; + return (char)gxio_uart_read(context, UART_RECEIVE_DATA); +} + +static void tilegx_poll_put_char(struct uart_port *port, unsigned char c) +{ + gxio_uart_context_t *context; + struct tile_uart_port *tile_uart; + + tile_uart = container_of(port, struct tile_uart_port, uart); + context = &tile_uart->context; + gxio_uart_write(context, UART_TRANSMIT_DATA, (unsigned long)c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + + +static const struct uart_ops tilegx_ops = { + .tx_empty = tilegx_tx_empty, + .set_mctrl = tilegx_set_mctrl, + .get_mctrl = tilegx_get_mctrl, + .stop_tx = tilegx_stop_tx, + .start_tx = tilegx_start_tx, + .stop_rx = tilegx_stop_rx, + .enable_ms = tilegx_enable_ms, + .break_ctl = tilegx_break_ctl, + .startup = tilegx_startup, + .shutdown = tilegx_shutdown, + .flush_buffer = tilegx_flush_buffer, + .set_termios = tilegx_set_termios, + .type = tilegx_type, + .release_port = tilegx_release_port, + .request_port = tilegx_request_port, + .config_port = tilegx_config_port, + .verify_port = tilegx_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = tilegx_poll_get_char, + .poll_put_char = tilegx_poll_put_char, +#endif +}; + + +static void tilegx_init_ports(void) +{ + int i; + struct uart_port *port; + + for (i = 0; i < TILEGX_UART_NR; i++) { + port = &tile_uart_ports[i].uart; + port->ops = &tilegx_ops; + port->line = i; + port->type = PORT_TILEGX; + port->uartclk = TILEGX_UART_REF_CLK; + port->flags = UPF_BOOT_AUTOCONF; + + tile_uart_ports[i].context.fd = -1; + mutex_init(&tile_uart_ports[i].mutex); + } +} + + +static struct uart_driver tilegx_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME_STRING, + .dev_name = TILEGX_UART_NAME, + .major = TILEGX_UART_MAJOR, + .minor = TILEGX_UART_MINOR, + .nr = TILEGX_UART_NR, +}; + + +static int __init tilegx_init(void) +{ + int i; + int ret; + struct tty_driver *tty_drv; + + ret = uart_register_driver(&tilegx_uart_driver); + if (ret) + return ret; + tty_drv = tilegx_uart_driver.tty_driver; + tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty_drv->init_termios.c_ispeed = 115200; + tty_drv->init_termios.c_ospeed = 115200; + + tilegx_init_ports(); + + for (i = 0; i < TILEGX_UART_NR; i++) { + struct uart_port *port = &tile_uart_ports[i].uart; + ret = uart_add_one_port(&tilegx_uart_driver, port); + } + + return 0; +} + + +static void __exit tilegx_exit(void) +{ + int i; + struct uart_port *port; + + for (i = 0; i < TILEGX_UART_NR; i++) { + port = &tile_uart_ports[i].uart; + uart_remove_one_port(&tilegx_uart_driver, port); + } + + uart_unregister_driver(&tilegx_uart_driver); +} + + +module_init(tilegx_init); +module_exit(tilegx_exit); + +MODULE_AUTHOR("Tilera Corporation"); +MODULE_DESCRIPTION("TILEGx serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 5be0d68fece..f87097acd8a 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -91,16 +91,16 @@ static void timbuart_flush_buffer(struct uart_port *port) static void timbuart_rx_chars(struct uart_port *port) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); port->icount.rx++; - tty_insert_flip_char(tty, ch, TTY_NORMAL); + tty_insert_flip_char(tport, ch, TTY_NORMAL); } spin_unlock(&port->lock); - tty_flip_buffer_push(port->state->port.tty); + tty_flip_buffer_push(tport); spin_lock(&port->lock); dev_dbg(port->dev, "%s - total read %d bytes\n", @@ -162,7 +162,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) dev_dbg(port->dev, "%s - leaving\n", __func__); } -void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) +static void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) { if (isr & RXFLAGS) { /* Some RX status is set */ @@ -184,7 +184,7 @@ void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) dev_dbg(port->dev, "%s - leaving\n", __func__); } -void timbuart_tasklet(unsigned long arg) +static void timbuart_tasklet(unsigned long arg) { struct timbuart_port *uart = (struct timbuart_port *)arg; u32 isr, ier = 0; diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 89eee43c4e2..dce27f34937 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -19,7 +19,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/init.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> @@ -34,7 +34,7 @@ * Register definitions * * For register details see datasheet: - * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf + * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf */ #define ULITE_RX 0x00 @@ -57,6 +57,54 @@ #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +struct uartlite_reg_ops { + u32 (*in)(void __iomem *addr); + void (*out)(u32 val, void __iomem *addr); +}; + +static u32 uartlite_inbe32(void __iomem *addr) +{ + return ioread32be(addr); +} + +static void uartlite_outbe32(u32 val, void __iomem *addr) +{ + iowrite32be(val, addr); +} + +static struct uartlite_reg_ops uartlite_be = { + .in = uartlite_inbe32, + .out = uartlite_outbe32, +}; + +static u32 uartlite_inle32(void __iomem *addr) +{ + return ioread32(addr); +} + +static void uartlite_outle32(u32 val, void __iomem *addr) +{ + iowrite32(val, addr); +} + +static 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; + + 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; + + reg_ops->out(val, port->membase + offset); +} static struct uart_port ulite_ports[ULITE_NR_UARTS]; @@ -66,7 +114,7 @@ static struct uart_port ulite_ports[ULITE_NR_UARTS]; static int ulite_receive(struct uart_port *port, int stat) { - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; unsigned char ch = 0; char flag = TTY_NORMAL; @@ -77,7 +125,7 @@ static int ulite_receive(struct uart_port *port, int stat) /* stats */ if (stat & ULITE_STATUS_RXVALID) { port->icount.rx++; - ch = ioread32be(port->membase + ULITE_RX); + ch = uart_in32(ULITE_RX, port); if (stat & ULITE_STATUS_PARITY) port->icount.parity++; @@ -103,13 +151,13 @@ static int ulite_receive(struct uart_port *port, int stat) stat &= ~port->ignore_status_mask; if (stat & ULITE_STATUS_RXVALID) - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tport, ch, flag); if (stat & ULITE_STATUS_FRAME) - tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_insert_flip_char(tport, 0, TTY_FRAME); if (stat & ULITE_STATUS_OVERRUN) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); return 1; } @@ -122,7 +170,7 @@ static int ulite_transmit(struct uart_port *port, int stat) return 0; if (port->x_char) { - iowrite32be(port->x_char, port->membase + ULITE_TX); + uart_out32(port->x_char, ULITE_TX, port); port->x_char = 0; port->icount.tx++; return 1; @@ -131,7 +179,7 @@ static int ulite_transmit(struct uart_port *port, int stat) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return 0; - iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); + uart_out32(xmit->buf[xmit->tail], ULITE_TX, port); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); port->icount.tx++; @@ -148,7 +196,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) int busy, n = 0; do { - int stat = ioread32be(port->membase + ULITE_STATUS); + int stat = uart_in32(ULITE_STATUS, port); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); n++; @@ -156,7 +204,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) /* work done? */ if (n > 1) { - tty_flip_buffer_push(port->state->port.tty); + tty_flip_buffer_push(&port->state->port); return IRQ_HANDLED; } else { return IRQ_NONE; @@ -169,7 +217,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&port->lock, flags); - ret = ioread32be(port->membase + ULITE_STATUS); + ret = uart_in32(ULITE_STATUS, port); spin_unlock_irqrestore(&port->lock, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; @@ -192,7 +240,7 @@ static void ulite_stop_tx(struct uart_port *port) static void ulite_start_tx(struct uart_port *port) { - ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); + ulite_transmit(port, uart_in32(ULITE_STATUS, port)); } static void ulite_stop_rx(struct uart_port *port) @@ -220,17 +268,17 @@ static int ulite_startup(struct uart_port *port) if (ret) return ret; - iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, - port->membase + ULITE_CONTROL); - iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + uart_out32(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + ULITE_CONTROL, port); + uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port); return 0; } static void ulite_shutdown(struct uart_port *port) { - iowrite32be(0, port->membase + ULITE_CONTROL); - ioread32be(port->membase + ULITE_CONTROL); /* dummy */ + uart_out32(0, ULITE_CONTROL, port); + uart_in32(ULITE_CONTROL, port); /* dummy */ free_irq(port->irq, port); } @@ -281,6 +329,8 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + int ret; + pr_debug("ulite console: port=%p; port->mapbase=%llx\n", port, (unsigned long long) port->mapbase); @@ -296,6 +346,14 @@ static int ulite_request_port(struct uart_port *port) return -EBUSY; } + port->private_data = &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; + return 0; } @@ -314,20 +372,19 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) #ifdef CONFIG_CONSOLE_POLL static int ulite_get_poll_char(struct uart_port *port) { - if (!(ioread32be(port->membase + ULITE_STATUS) - & ULITE_STATUS_RXVALID)) + if (!(uart_in32(ULITE_STATUS, port) & ULITE_STATUS_RXVALID)) return NO_POLL_CHAR; - return ioread32be(port->membase + ULITE_RX); + return uart_in32(ULITE_RX, port); } static void ulite_put_poll_char(struct uart_port *port, unsigned char ch) { - while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL) + while (uart_in32(ULITE_STATUS, port) & ULITE_STATUS_TXFULL) cpu_relax(); /* write char to device */ - iowrite32be(ch, port->membase + ULITE_TX); + uart_out32(ch, ULITE_TX, port); } #endif @@ -361,14 +418,23 @@ static struct uart_ops ulite_ops = { #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE static void ulite_console_wait_tx(struct uart_port *port) { - int i; u8 val; - - /* Spin waiting for TX fifo to have space available */ - for (i = 0; i < 100000; i++) { - val = ioread32be(port->membase + ULITE_STATUS); + unsigned long timeout; + + /* + * Spin waiting for TX fifo to have space available. + * When using the Microblaze Debug Module this can take up to 1s + */ + timeout = jiffies + msecs_to_jiffies(1000); + while (1) { + val = uart_in32(ULITE_STATUS, port); if ((val & ULITE_STATUS_TXFULL) == 0) break; + if (time_after(jiffies, timeout)) { + dev_warn(port->dev, + "timeout waiting for TX buffer empty\n"); + break; + } cpu_relax(); } } @@ -376,7 +442,7 @@ static void ulite_console_wait_tx(struct uart_port *port) static void ulite_console_putchar(struct uart_port *port, int ch) { ulite_console_wait_tx(port); - iowrite32be(ch, port->membase + ULITE_TX); + uart_out32(ch, ULITE_TX, port); } static void ulite_console_write(struct console *co, const char *s, @@ -393,8 +459,8 @@ static void ulite_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - iowrite32be(0, port->membase + ULITE_CONTROL); + ier = uart_in32(ULITE_STATUS, port) & ULITE_STATUS_IE; + uart_out32(0, ULITE_CONTROL, port); uart_console_write(port, s, count, ulite_console_putchar); @@ -402,7 +468,7 @@ static void ulite_console_write(struct console *co, const char *s, /* restore interrupt state */ if (ier) - iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + uart_out32(ULITE_CONTROL_IE, ULITE_CONTROL, port); if (locked) spin_unlock_irqrestore(&port->lock, flags); @@ -615,7 +681,7 @@ static struct platform_driver ulite_platform_driver = { * Module setup/teardown */ -int __init ulite_init(void) +static int __init ulite_init(void) { int ret; @@ -634,11 +700,11 @@ int __init ulite_init(void) err_plat: uart_unregister_driver(&ulite_uart_driver); err_uart: - printk(KERN_ERR "registering uartlite driver failed: err=%i", ret); + pr_err("registering uartlite driver failed: err=%i", ret); return ret; } -void __exit ulite_exit(void) +static void __exit ulite_exit(void) { platform_driver_unregister(&ulite_platform_driver); uart_unregister_driver(&ulite_uart_driver); diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index f99b0c965f8..1c52074c38d 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -25,6 +25,8 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/dma-mapping.h> @@ -269,7 +271,7 @@ static unsigned int qe_uart_tx_empty(struct uart_port *port) return 1; bdp++; - }; + } } /* @@ -469,7 +471,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) int i; unsigned char ch, *cp; struct uart_port *port = &qe_port->port; - struct tty_struct *tty = port->state->port.tty; + struct tty_port *tport = &port->state->port; struct qe_bd *bdp; u16 status; unsigned int flg; @@ -491,7 +493,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) /* If we don't have enough room in RX buffer for the entire BD, * then we try later, which will be the next RX interrupt. */ - if (tty_buffer_request_room(tty, i) < i) { + if (tty_buffer_request_room(tport, i) < i) { dev_dbg(port->dev, "ucc-uart: no room in RX buffer\n"); return; } @@ -512,7 +514,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) continue; error_return: - tty_insert_flip_char(tty, ch, flg); + tty_insert_flip_char(tport, ch, flg); } @@ -530,7 +532,7 @@ error_return: qe_port->rx_cur = bdp; /* Activate BH processing */ - tty_flip_buffer_push(tty); + tty_flip_buffer_push(tport); return; @@ -560,7 +562,7 @@ handle_error: /* Overrun does not affect the current character ! */ if (status & BD_SC_OV) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); #ifdef SUPPORT_SYSRQ port->sysrq = 0; #endif @@ -934,7 +936,7 @@ static void qe_uart_set_termios(struct uart_port *port, port->read_status_mask = BD_SC_EMPTY | BD_SC_OV; if (termios->c_iflag & INPCK) port->read_status_mask |= BD_SC_FR | BD_SC_PR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= BD_SC_BR; /* @@ -1451,7 +1453,7 @@ static int ucc_uart_probe(struct platform_device *ofdev) goto out_np; } - dev_set_drvdata(&ofdev->dev, qe_port); + platform_set_drvdata(ofdev, qe_port); dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", qe_port->ucc_num + 1, qe_port->port.line); @@ -1471,13 +1473,12 @@ out_free: static int ucc_uart_remove(struct platform_device *ofdev) { - struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); + struct uart_qe_port *qe_port = platform_get_drvdata(ofdev); dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); uart_remove_one_port(&ucc_uart_driver, &qe_port->port); - dev_set_drvdata(&ofdev->dev, NULL); kfree(qe_port); return 0; @@ -1518,9 +1519,11 @@ static int __init ucc_uart_init(void) } ret = platform_driver_register(&ucc_uart_of_driver); - if (ret) + if (ret) { printk(KERN_ERR "ucc-uart: could not register platform driver\n"); + uart_unregister_driver(&ucc_uart_driver); + } return ret; } diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index 62ee0166bc6..db0c8a4ab03 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -313,12 +313,10 @@ static void siu_break_ctl(struct uart_port *port, int ctl) static inline void receive_chars(struct uart_port *port, uint8_t *status) { - struct tty_struct *tty; uint8_t lsr, ch; char flag; int max_count = RX_MAX_COUNT; - tty = port->state->port.tty; lsr = *status; do { @@ -365,7 +363,7 @@ static inline void receive_chars(struct uart_port *port, uint8_t *status) lsr = siu_read(port, UART_LSR); } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); *status = lsr; } @@ -561,7 +559,7 @@ static void siu_set_termios(struct uart_port *port, struct ktermios *new, port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR; if (c_iflag & INPCK) port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (c_iflag & (BRKINT | PARMRK)) + if (c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= UART_LSR_BI; port->ignore_status_mask = 0; @@ -707,7 +705,7 @@ static int siu_init_ports(struct platform_device *pdev) { struct uart_port *port; struct resource *res; - int *type = pdev->dev.platform_data; + int *type = dev_get_platdata(&pdev->dev); int i; if (!type) diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index d5ed9f61300..15ad6fcda88 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c @@ -35,6 +35,7 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/of.h> +#include <linux/err.h> /* * UART Register offsets @@ -136,22 +137,14 @@ static void vt8500_enable_ms(struct uart_port *port) static void handle_rx(struct uart_port *port) { - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - if (!tty) { - /* Discard data: no tty available */ - int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8; - u16 ch; - while (count--) - ch = readw(port->membase + VT8500_RXFIFO); - return; - } + struct tty_port *tport = &port->state->port; /* * Handle overrun */ if ((vt8500_read(port, VT8500_URISR) & RXOVER)) { port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_insert_flip_char(tport, 0, TTY_OVERRUN); } /* and now the main RX loop */ @@ -174,11 +167,12 @@ static void handle_rx(struct uart_port *port) port->icount.rx++; if (!uart_handle_sysrq_char(port, c)) - tty_insert_flip_char(tty, c, flag); + tty_insert_flip_char(tport, c, flag); } - tty_flip_buffer_push(tty); - tty_kref_put(tty); + spin_unlock(&port->lock); + tty_flip_buffer_push(tport); + spin_lock(&port->lock); } static void handle_tx(struct uart_port *port) @@ -567,12 +561,13 @@ static int vt8500_serial_probe(struct platform_device *pdev) if (!mmres || !irqres) return -ENODEV; - if (np) + if (np) { port = of_alias_get_id(np, "serial"); - if (port > VT8500_MAX_PORTS) + if (port >= VT8500_MAX_PORTS) port = -1; - else + } else { port = -1; + } if (port < 0) { /* calculate the port id */ @@ -580,7 +575,7 @@ static int vt8500_serial_probe(struct platform_device *pdev) sizeof(vt8500_ports_in_use)); } - if (port > VT8500_MAX_PORTS) + if (port >= VT8500_MAX_PORTS) return -ENODEV; /* reserve the port id */ @@ -589,10 +584,27 @@ static int vt8500_serial_probe(struct platform_device *pdev) return -EBUSY; } - vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); + vt8500_port = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_port), + GFP_KERNEL); if (!vt8500_port) return -ENOMEM; + vt8500_port->uart.membase = devm_ioremap_resource(&pdev->dev, mmres); + if (IS_ERR(vt8500_port->uart.membase)) + return PTR_ERR(vt8500_port->uart.membase); + + vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(vt8500_port->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return -EINVAL; + } + + ret = clk_prepare_enable(vt8500_port->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + return ret; + } + vt8500_port->uart.type = PORT_VT8500; vt8500_port->uart.iotype = UPIO_MEM; vt8500_port->uart.mapbase = mmres->start; @@ -603,24 +615,11 @@ static int vt8500_serial_probe(struct platform_device *pdev) vt8500_port->uart.dev = &pdev->dev; vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); - if (!IS_ERR(vt8500_port->clk)) { - vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); - } else { - /* use the default of 24Mhz if not specified and warn */ - pr_warn("%s: serial clock source not specified\n", __func__); - vt8500_port->uart.uartclk = 24000000; - } + vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); snprintf(vt8500_port->name, sizeof(vt8500_port->name), "VT8500 UART%d", pdev->id); - vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres)); - if (!vt8500_port->uart.membase) { - ret = -ENOMEM; - goto err; - } - vt8500_uart_ports[port] = vt8500_port; uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); @@ -628,19 +627,14 @@ static int vt8500_serial_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vt8500_port); return 0; - -err: - kfree(vt8500_port); - return ret; } static int vt8500_serial_remove(struct platform_device *pdev) { struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); + clk_disable_unprepare(vt8500_port->clk); uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); - kfree(vt8500_port); return 0; } @@ -656,7 +650,7 @@ static struct platform_driver vt8500_platform_driver = { .driver = { .name = "vt8500_serial", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(wmt_dt_ids), + .of_match_table = wmt_dt_ids, }, }; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 9ab910370c5..8809775e2ba 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1,7 +1,7 @@ /* - * Xilinx PS UART driver + * Cadence UART driver (found in Xilinx Zynq) * - * 2011 (c) Xilinx Inc. + * 2011 - 2014 (C) Xilinx Inc. * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public @@ -9,101 +9,105 @@ * either version 2 of the License, or (at your option) any * later version. * + * This driver has originally been pushed by Xilinx using a Zynq-branding. This + * still shows in the naming of this file, the kconfig symbols and some symbols + * in the code. */ +#if defined(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/platform_device.h> #include <linux/serial.h> +#include <linux/console.h> #include <linux/serial_core.h> +#include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/console.h> +#include <linux/clk.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> #include <linux/module.h> -#define XUARTPS_TTY_NAME "ttyPS" -#define XUARTPS_NAME "xuartps" -#define XUARTPS_MAJOR 0 /* use dynamic node allocation */ -#define XUARTPS_MINOR 0 /* works best with devtmpfs */ -#define XUARTPS_NR_PORTS 2 -#define XUARTPS_FIFO_SIZE 16 /* FIFO size */ -#define XUARTPS_REGISTER_SPACE 0xFFF +#define CDNS_UART_TTY_NAME "ttyPS" +#define CDNS_UART_NAME "xuartps" +#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */ +#define CDNS_UART_MINOR 0 /* works best with devtmpfs */ +#define CDNS_UART_NR_PORTS 2 +#define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ +#define CDNS_UART_REGISTER_SPACE 0xFFF + +#define cdns_uart_readl(offset) ioread32(port->membase + offset) +#define cdns_uart_writel(val, offset) iowrite32(val, port->membase + offset) + +/* Rx Trigger level */ +static int rx_trigger_level = 56; +module_param(rx_trigger_level, uint, S_IRUGO); +MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes"); + +/* Rx Timeout */ +static int rx_timeout = 10; +module_param(rx_timeout, uint, S_IRUGO); +MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); + +/* Register offsets for the UART. */ +#define CDNS_UART_CR_OFFSET 0x00 /* Control Register */ +#define CDNS_UART_MR_OFFSET 0x04 /* Mode Register */ +#define CDNS_UART_IER_OFFSET 0x08 /* Interrupt Enable */ +#define CDNS_UART_IDR_OFFSET 0x0C /* Interrupt Disable */ +#define CDNS_UART_IMR_OFFSET 0x10 /* Interrupt Mask */ +#define CDNS_UART_ISR_OFFSET 0x14 /* Interrupt Status */ +#define CDNS_UART_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator */ +#define CDNS_UART_RXTOUT_OFFSET 0x1C /* RX Timeout */ +#define CDNS_UART_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level */ +#define CDNS_UART_MODEMCR_OFFSET 0x24 /* Modem Control */ +#define CDNS_UART_MODEMSR_OFFSET 0x28 /* Modem Status */ +#define CDNS_UART_SR_OFFSET 0x2C /* Channel Status */ +#define CDNS_UART_FIFO_OFFSET 0x30 /* FIFO */ +#define CDNS_UART_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider */ +#define CDNS_UART_FLOWDEL_OFFSET 0x38 /* Flow Delay */ +#define CDNS_UART_IRRX_PWIDTH_OFFSET 0x3C /* IR Min Received Pulse Width */ +#define CDNS_UART_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse Width */ +#define CDNS_UART_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level */ + +/* Control Register Bit Definitions */ +#define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */ +#define CDNS_UART_CR_STARTBRK 0x00000080 /* Set TX break */ +#define CDNS_UART_CR_TX_DIS 0x00000020 /* TX disabled. */ +#define CDNS_UART_CR_TX_EN 0x00000010 /* TX enabled */ +#define CDNS_UART_CR_RX_DIS 0x00000008 /* RX disabled. */ +#define CDNS_UART_CR_RX_EN 0x00000004 /* RX enabled */ +#define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */ +#define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */ +#define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ -#define xuartps_readl(offset) ioread32(port->membase + offset) -#define xuartps_writel(val, offset) iowrite32(val, port->membase + offset) - -/********************************Register Map********************************/ -/** UART - * - * Register offsets for the UART. - * - */ -#define XUARTPS_CR_OFFSET 0x00 /* Control Register [8:0] */ -#define XUARTPS_MR_OFFSET 0x04 /* Mode Register [10:0] */ -#define XUARTPS_IER_OFFSET 0x08 /* Interrupt Enable [10:0] */ -#define XUARTPS_IDR_OFFSET 0x0C /* Interrupt Disable [10:0] */ -#define XUARTPS_IMR_OFFSET 0x10 /* Interrupt Mask [10:0] */ -#define XUARTPS_ISR_OFFSET 0x14 /* Interrupt Status [10:0]*/ -#define XUARTPS_BAUDGEN_OFFSET 0x18 /* Baud Rate Generator [15:0] */ -#define XUARTPS_RXTOUT_OFFSET 0x1C /* RX Timeout [7:0] */ -#define XUARTPS_RXWM_OFFSET 0x20 /* RX FIFO Trigger Level [5:0] */ -#define XUARTPS_MODEMCR_OFFSET 0x24 /* Modem Control [5:0] */ -#define XUARTPS_MODEMSR_OFFSET 0x28 /* Modem Status [8:0] */ -#define XUARTPS_SR_OFFSET 0x2C /* Channel Status [11:0] */ -#define XUARTPS_FIFO_OFFSET 0x30 /* FIFO [15:0] or [7:0] */ -#define XUARTPS_BAUDDIV_OFFSET 0x34 /* Baud Rate Divider [7:0] */ -#define XUARTPS_FLOWDEL_OFFSET 0x38 /* Flow Delay [15:0] */ -#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse - Width [15:0] */ -#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse - Width [7:0] */ -#define XUARTPS_TXWM_OFFSET 0x44 /* TX FIFO Trigger Level [5:0] */ - -/** Control Register - * - * The Control register (CR) controls the major functions of the device. - * - * Control Register Bit Definitions - */ -#define XUARTPS_CR_STOPBRK 0x00000100 /* Stop TX break */ -#define XUARTPS_CR_STARTBRK 0x00000080 /* Set TX break */ -#define XUARTPS_CR_TX_DIS 0x00000020 /* TX disabled. */ -#define XUARTPS_CR_TX_EN 0x00000010 /* TX enabled */ -#define XUARTPS_CR_RX_DIS 0x00000008 /* RX disabled. */ -#define XUARTPS_CR_RX_EN 0x00000004 /* RX enabled */ -#define XUARTPS_CR_TXRST 0x00000002 /* TX logic reset */ -#define XUARTPS_CR_RXRST 0x00000001 /* RX logic reset */ -#define XUARTPS_CR_RST_TO 0x00000040 /* Restart Timeout Counter */ - -/** Mode Register - * +/* + * Mode Register: * The mode register (MR) defines the mode of transfer as well as the data * format. If this register is modified during transmission or reception, * data validity cannot be guaranteed. - * - * Mode Register Bit Definitions - * */ -#define XUARTPS_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ -#define XUARTPS_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ -#define XUARTPS_MR_CHMODE_NORM 0x00000000 /* Normal mode */ +#define CDNS_UART_MR_CLKSEL 0x00000001 /* Pre-scalar selection */ +#define CDNS_UART_MR_CHMODE_L_LOOP 0x00000200 /* Local loop back mode */ +#define CDNS_UART_MR_CHMODE_NORM 0x00000000 /* Normal mode */ -#define XUARTPS_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ -#define XUARTPS_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ +#define CDNS_UART_MR_STOPMODE_2_BIT 0x00000080 /* 2 stop bits */ +#define CDNS_UART_MR_STOPMODE_1_BIT 0x00000000 /* 1 stop bit */ -#define XUARTPS_MR_PARITY_NONE 0x00000020 /* No parity mode */ -#define XUARTPS_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ -#define XUARTPS_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ -#define XUARTPS_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ -#define XUARTPS_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ +#define CDNS_UART_MR_PARITY_NONE 0x00000020 /* No parity mode */ +#define CDNS_UART_MR_PARITY_MARK 0x00000018 /* Mark parity mode */ +#define CDNS_UART_MR_PARITY_SPACE 0x00000010 /* Space parity mode */ +#define CDNS_UART_MR_PARITY_ODD 0x00000008 /* Odd parity mode */ +#define CDNS_UART_MR_PARITY_EVEN 0x00000000 /* Even parity mode */ -#define XUARTPS_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ -#define XUARTPS_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ -#define XUARTPS_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ +#define CDNS_UART_MR_CHARLEN_6_BIT 0x00000006 /* 6 bits data */ +#define CDNS_UART_MR_CHARLEN_7_BIT 0x00000004 /* 7 bits data */ +#define CDNS_UART_MR_CHARLEN_8_BIT 0x00000000 /* 8 bits data */ -/** Interrupt Registers - * +/* + * Interrupt Registers: * Interrupt control logic uses the interrupt enable register (IER) and the * interrupt disable register (IDR) to set the value of the bits in the * interrupt mask register (IMR). The IMR determines whether to pass an @@ -111,98 +115,159 @@ * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an * interrupt. IMR and ISR are read only, and IER and IDR are write only. * Reading either IER or IDR returns 0x00. - * * All four registers have the same bit definitions. */ -#define XUARTPS_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ -#define XUARTPS_IXR_PARITY 0x00000080 /* Parity error interrupt */ -#define XUARTPS_IXR_FRAMING 0x00000040 /* Framing error interrupt */ -#define XUARTPS_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ -#define XUARTPS_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ -#define XUARTPS_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ -#define XUARTPS_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ -#define XUARTPS_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ -#define XUARTPS_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ -#define XUARTPS_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ -#define XUARTPS_IXR_MASK 0x00001FFF /* Valid bit mask */ - -/** Channel Status Register - * +#define CDNS_UART_IXR_TOUT 0x00000100 /* RX Timeout error interrupt */ +#define CDNS_UART_IXR_PARITY 0x00000080 /* Parity error interrupt */ +#define CDNS_UART_IXR_FRAMING 0x00000040 /* Framing error interrupt */ +#define CDNS_UART_IXR_OVERRUN 0x00000020 /* Overrun error interrupt */ +#define CDNS_UART_IXR_TXFULL 0x00000010 /* TX FIFO Full interrupt */ +#define CDNS_UART_IXR_TXEMPTY 0x00000008 /* TX FIFO empty interrupt */ +#define CDNS_UART_ISR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt */ +#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ +#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ +#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ +#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ + +/* Goes in read_status_mask for break detection as the HW doesn't do it*/ +#define CDNS_UART_IXR_BRK 0x80000000 + +/* + * Channel Status Register: * The channel status register (CSR) is provided to enable the control logic * to monitor the status of bits in the channel interrupt status register, * even if these are masked out by the interrupt mask register. */ -#define XUARTPS_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ -#define XUARTPS_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ -#define XUARTPS_SR_TXFULL 0x00000010 /* TX FIFO full */ -#define XUARTPS_SR_RXTRIG 0x00000001 /* Rx Trigger */ +#define CDNS_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ +#define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ +#define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */ + +/* baud dividers min/max values */ +#define CDNS_UART_BDIV_MIN 4 +#define CDNS_UART_BDIV_MAX 255 +#define CDNS_UART_CD_MAX 65535 + +/** + * struct cdns_uart - device data + * @port: Pointer to the UART port + * @uartclk: Reference clock + * @pclk: APB clock + * @baud: Current baud rate + * @clk_rate_change_nb: Notifier block for clock changes + */ +struct cdns_uart { + struct uart_port *port; + struct clk *uartclk; + struct clk *pclk; + unsigned int baud; + struct notifier_block clk_rate_change_nb; +}; +#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ + clk_rate_change_nb); /** - * xuartps_isr - Interrupt handler + * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * - * Returns IRQHANDLED - **/ -static irqreturn_t xuartps_isr(int irq, void *dev_id) + * Return: IRQHANDLED + */ +static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; - struct tty_struct *tty; unsigned long flags; unsigned int isrstatus, numbytes; unsigned int data; char status = TTY_NORMAL; - /* Get the tty which could be NULL so don't assume it's valid */ - tty = tty_port_tty_get(&port->state->port); - spin_lock_irqsave(&port->lock, flags); /* Read the interrupt status register to determine which * interrupt(s) is/are active. */ - isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET); + isrstatus = cdns_uart_readl(CDNS_UART_ISR_OFFSET); + + /* + * There is no hardware break detection, so we interpret framing + * error with all-zeros data as a break sequence. Most of the time, + * there's another non-zero byte at the end of the sequence. + */ + if (isrstatus & CDNS_UART_IXR_FRAMING) { + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) { + if (!cdns_uart_readl(CDNS_UART_FIFO_OFFSET)) { + port->read_status_mask |= CDNS_UART_IXR_BRK; + isrstatus &= ~CDNS_UART_IXR_FRAMING; + } + } + cdns_uart_writel(CDNS_UART_IXR_FRAMING, CDNS_UART_ISR_OFFSET); + } /* drop byte with parity error if IGNPAR specified */ - if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY) - isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT); + if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY) + isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT); isrstatus &= port->read_status_mask; isrstatus &= ~port->ignore_status_mask; - if ((isrstatus & XUARTPS_IXR_TOUT) || - (isrstatus & XUARTPS_IXR_RXTRIG)) { + if ((isrstatus & CDNS_UART_IXR_TOUT) || + (isrstatus & CDNS_UART_IXR_RXTRIG)) { /* Receive Timeout Interrupt */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - data = xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + data = cdns_uart_readl(CDNS_UART_FIFO_OFFSET); + + /* Non-NULL byte after BREAK is garbage (99%) */ + if (data && (port->read_status_mask & + CDNS_UART_IXR_BRK)) { + port->read_status_mask &= ~CDNS_UART_IXR_BRK; + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + +#ifdef SUPPORT_SYSRQ + /* + * uart_handle_sysrq_char() doesn't work if + * spinlocked, for some reason + */ + if (port->sysrq) { + spin_unlock(&port->lock); + if (uart_handle_sysrq_char(port, + (unsigned char)data)) { + spin_lock(&port->lock); + continue; + } + spin_lock(&port->lock); + } +#endif + port->icount.rx++; - if (isrstatus & XUARTPS_IXR_PARITY) { + if (isrstatus & CDNS_UART_IXR_PARITY) { port->icount.parity++; status = TTY_PARITY; - } else if (isrstatus & XUARTPS_IXR_FRAMING) { + } else if (isrstatus & CDNS_UART_IXR_FRAMING) { port->icount.frame++; status = TTY_FRAME; - } else if (isrstatus & XUARTPS_IXR_OVERRUN) + } else if (isrstatus & CDNS_UART_IXR_OVERRUN) { port->icount.overrun++; + } - if (tty) - uart_insert_char(port, isrstatus, - XUARTPS_IXR_OVERRUN, data, - status); + uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN, + data, status); } spin_unlock(&port->lock); - if (tty) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&port->state->port); spin_lock(&port->lock); } /* Dispatch an appropriate handler */ - if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) { + if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY) { if (uart_circ_empty(&port->state->xmit)) { - xuartps_writel(XUARTPS_IXR_TXEMPTY, - XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, + CDNS_UART_IDR_OFFSET); } else { numbytes = port->fifosize; /* Break if no more data available in the UART buffer */ @@ -210,12 +275,12 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer - * and write it to the xuartps's TX_FIFO + * and write it to the cdns_uart's TX_FIFO * register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit. - tail], XUARTPS_FIFO_OFFSET); + tail], CDNS_UART_FIFO_OFFSET); port->icount.tx++; @@ -223,7 +288,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) * the buffer if it reaches limit. */ port->state->xmit.tail = - (port->state->xmit.tail + 1) & \ + (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } @@ -233,107 +298,235 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id) } } - xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET); + cdns_uart_writel(isrstatus, CDNS_UART_ISR_OFFSET); /* be sure to release the lock and tty before leaving */ spin_unlock_irqrestore(&port->lock, flags); - tty_kref_put(tty); return IRQ_HANDLED; } /** - * xuartps_set_baud_rate - Calculate and set the baud rate - * @port: Handle to the uart port structure - * @baud: Baud rate to set + * cdns_uart_calc_baud_divs - Calculate baud rate divisors + * @clk: UART module input clock + * @baud: Desired baud rate + * @rbdiv: BDIV value (return value) + * @rcd: CD value (return value) + * @div8: Value for clk_sel bit in mod (return value) + * Return: baud rate, requested baud when possible, or actual baud when there + * was too much error, zero if no valid divisors are found. * - * Returns baud rate, requested baud when possible, or actual baud when there - * was too much error - **/ -static unsigned int xuartps_set_baud_rate(struct uart_port *port, - unsigned int baud) + * Formula to obtain baud rate is + * baud_tx/rx rate = clk/CD * (BDIV + 1) + * input_clk = (Uart User Defined Clock or Apb Clock) + * depends on UCLKEN in MR Reg + * clk = input_clk or input_clk/8; + * depends on CLKS in MR reg + * CD and BDIV depends on values in + * baud rate generate register + * baud rate clock divisor register + */ +static unsigned int cdns_uart_calc_baud_divs(unsigned int clk, + unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8) { - unsigned int sel_clk; - unsigned int calc_baud = 0; - unsigned int brgr_val, brdiv_val; + u32 cd, bdiv; + unsigned int calc_baud; + unsigned int bestbaud = 0; unsigned int bauderror; + unsigned int besterror = ~0; - /* Formula to obtain baud rate is - * baud_tx/rx rate = sel_clk/CD * (BDIV + 1) - * input_clk = (Uart User Defined Clock or Apb Clock) - * depends on UCLKEN in MR Reg - * sel_clk = input_clk or input_clk/8; - * depends on CLKS in MR reg - * CD and BDIV depends on values in - * baud rate generate register - * baud rate clock divisor register - */ - sel_clk = port->uartclk; - if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL) - sel_clk = sel_clk / 8; - - /* Find the best values for baud generation */ - for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) { + if (baud < clk / ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX)) { + *div8 = 1; + clk /= 8; + } else { + *div8 = 0; + } - brgr_val = sel_clk / (baud * (brdiv_val + 1)); - if (brgr_val < 2 || brgr_val > 65535) + for (bdiv = CDNS_UART_BDIV_MIN; bdiv <= CDNS_UART_BDIV_MAX; bdiv++) { + cd = DIV_ROUND_CLOSEST(clk, baud * (bdiv + 1)); + if (cd < 1 || cd > CDNS_UART_CD_MAX) continue; - calc_baud = sel_clk / (brgr_val * (brdiv_val + 1)); + calc_baud = clk / (cd * (bdiv + 1)); if (baud > calc_baud) bauderror = baud - calc_baud; else bauderror = calc_baud - baud; - /* use the values when percent error is acceptable */ - if (((bauderror * 100) / baud) < 3) { - calc_baud = baud; - break; + if (besterror > bauderror) { + *rbdiv = bdiv; + *rcd = cd; + bestbaud = calc_baud; + besterror = bauderror; } } + /* use the values when percent error is acceptable */ + if (((besterror * 100) / baud) < 3) + bestbaud = baud; + + return bestbaud; +} - /* Set the values for the new baud rate */ - xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET); - xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET); +/** + * cdns_uart_set_baud_rate - Calculate and set the baud rate + * @port: Handle to the uart port structure + * @baud: Baud rate to set + * Return: baud rate, requested baud when possible, or actual baud when there + * was too much error, zero if no valid divisors are found. + */ +static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, + unsigned int baud) +{ + unsigned int calc_baud; + u32 cd = 0, bdiv = 0; + u32 mreg; + int div8; + struct cdns_uart *cdns_uart = port->private_data; + + calc_baud = cdns_uart_calc_baud_divs(port->uartclk, baud, &bdiv, &cd, + &div8); + + /* Write new divisors to hardware */ + mreg = cdns_uart_readl(CDNS_UART_MR_OFFSET); + if (div8) + mreg |= CDNS_UART_MR_CLKSEL; + else + mreg &= ~CDNS_UART_MR_CLKSEL; + cdns_uart_writel(mreg, CDNS_UART_MR_OFFSET); + cdns_uart_writel(cd, CDNS_UART_BAUDGEN_OFFSET); + cdns_uart_writel(bdiv, CDNS_UART_BAUDDIV_OFFSET); + cdns_uart->baud = baud; return calc_baud; } -/*----------------------Uart Operations---------------------------*/ +#ifdef CONFIG_COMMON_CLK +/** + * cdns_uart_clk_notitifer_cb - Clock notifier callback + * @nb: Notifier block + * @event: Notify event + * @data: Notifier data + * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. + */ +static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + u32 ctrl_reg; + struct uart_port *port; + int locked = 0; + struct clk_notifier_data *ndata = data; + unsigned long flags = 0; + struct cdns_uart *cdns_uart = to_cdns_uart(nb); + + port = cdns_uart->port; + if (port->suspended) + return NOTIFY_OK; + + switch (event) { + case PRE_RATE_CHANGE: + { + u32 bdiv, cd; + int div8; + + /* + * Find out if current baud-rate can be achieved with new clock + * frequency. + */ + if (!cdns_uart_calc_baud_divs(ndata->new_rate, cdns_uart->baud, + &bdiv, &cd, &div8)) { + dev_warn(port->dev, "clock rate change rejected\n"); + return NOTIFY_BAD; + } + + spin_lock_irqsave(&cdns_uart->port->lock, flags); + + /* Disable the TX and RX to set baud rate */ + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + + return NOTIFY_OK; + } + case POST_RATE_CHANGE: + /* + * Set clk dividers to generate correct baud with new clock + * frequency. + */ + + spin_lock_irqsave(&cdns_uart->port->lock, flags); + + locked = 1; + port->uartclk = ndata->new_rate; + + cdns_uart->baud = cdns_uart_set_baud_rate(cdns_uart->port, + cdns_uart->baud); + /* fall through */ + case ABORT_RATE_CHANGE: + if (!locked) + spin_lock_irqsave(&cdns_uart->port->lock, flags); + + /* Set TX/RX Reset */ + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) + cpu_relax(); + + /* + * Clear the RX disable and TX disable bits and then set the TX + * enable bit and RX enable bit to enable the transmitter and + * receiver. + */ + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + + spin_unlock_irqrestore(&cdns_uart->port->lock, flags); + + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} +#endif /** - * xuartps_start_tx - Start transmitting bytes + * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure - * - **/ -static void xuartps_start_tx(struct uart_port *port) + */ +static void cdns_uart_start_tx(struct uart_port *port) { unsigned int status, numbytes = port->fifosize; if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) return; - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Set the TX enable bit and clear the TX disable bit to enable the * transmitter. */ - xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN, - XUARTPS_CR_OFFSET); - - while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET) - & XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) { + cdns_uart_writel((status & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); + while (numbytes-- && ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_TXFULL)) != CDNS_UART_SR_TXFULL) { /* Break if no more data available in the UART buffer */ if (uart_circ_empty(&port->state->xmit)) break; /* Get the data from the UART circular buffer and - * write it to the xuartps's TX_FIFO register. + * write it to the cdns_uart's TX_FIFO register. */ - xuartps_writel( + cdns_uart_writel( port->state->xmit.buf[port->state->xmit.tail], - XUARTPS_FIFO_OFFSET); + CDNS_UART_FIFO_OFFSET); port->icount.tx++; /* Adjust the tail of the UART buffer and wrap @@ -342,173 +535,173 @@ static void xuartps_start_tx(struct uart_port *port) port->state->xmit.tail = (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); } - + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_ISR_OFFSET); /* Enable the TX Empty interrupt */ - xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY, CDNS_UART_IER_OFFSET); if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } /** - * xuartps_stop_tx - Stop TX + * cdns_uart_stop_tx - Stop TX * @port: Handle to the uart port structure - * - **/ -static void xuartps_stop_tx(struct uart_port *port) + */ +static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_TX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_TX_DIS; /* Disable the transmitter */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_stop_rx - Stop RX + * cdns_uart_stop_rx - Stop RX * @port: Handle to the uart port structure - * - **/ -static void xuartps_stop_rx(struct uart_port *port) + */ +static void cdns_uart_stop_rx(struct uart_port *port) { unsigned int regval; - regval = xuartps_readl(XUARTPS_CR_OFFSET); - regval |= XUARTPS_CR_RX_DIS; + regval = cdns_uart_readl(CDNS_UART_CR_OFFSET); + regval |= CDNS_UART_CR_RX_DIS; /* Disable the receiver */ - xuartps_writel(regval, XUARTPS_CR_OFFSET); + cdns_uart_writel(regval, CDNS_UART_CR_OFFSET); } /** - * xuartps_tx_empty - Check whether TX is empty + * cdns_uart_tx_empty - Check whether TX is empty * @port: Handle to the uart port structure * - * Returns TIOCSER_TEMT on success, 0 otherwise - **/ -static unsigned int xuartps_tx_empty(struct uart_port *port) + * Return: TIOCSER_TEMT on success, 0 otherwise + */ +static unsigned int cdns_uart_tx_empty(struct uart_port *port) { unsigned int status; - status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY; + status = cdns_uart_readl(CDNS_UART_ISR_OFFSET) & CDNS_UART_IXR_TXEMPTY; return status ? TIOCSER_TEMT : 0; } /** - * xuartps_break_ctl - Based on the input ctl we have to start or stop + * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure * @ctl: Value based on which start or stop decision is taken - * - **/ -static void xuartps_break_ctl(struct uart_port *port, int ctl) + */ +static void cdns_uart_break_ctl(struct uart_port *port, int ctl) { unsigned int status; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); if (ctl == -1) - xuartps_writel(XUARTPS_CR_STARTBRK | status, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_STARTBRK | status, + CDNS_UART_CR_OFFSET); else { - if ((status & XUARTPS_CR_STOPBRK) == 0) - xuartps_writel(XUARTPS_CR_STOPBRK | status, - XUARTPS_CR_OFFSET); + if ((status & CDNS_UART_CR_STOPBRK) == 0) + cdns_uart_writel(CDNS_UART_CR_STOPBRK | status, + CDNS_UART_CR_OFFSET); } spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_set_termios - termios operations, handling data length, parity, + * cdns_uart_set_termios - termios operations, handling data length, parity, * stop bits, flow control, baud rate * @port: Handle to the uart port structure * @termios: Handle to the input termios structure * @old: Values of the previously saved termios structure - * - **/ -static void xuartps_set_termios(struct uart_port *port, + */ +static void cdns_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned int cval = 0; - unsigned int baud; + unsigned int baud, minbaud, maxbaud; unsigned long flags; unsigned int ctrl_reg, mode_reg; spin_lock_irqsave(&port->lock, flags); /* Empty the receive FIFO 1st before making changes */ - while ((xuartps_readl(XUARTPS_SR_OFFSET) & - XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) { - xuartps_readl(XUARTPS_FIFO_OFFSET); + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) { + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); } /* Disable the TX and RX to set baud rate */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - /* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */ - baud = uart_get_baud_rate(port, termios, old, 0, 10000000); - baud = xuartps_set_baud_rate(port, baud); + /* + * Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk + * min and max baud should be calculated here based on port->uartclk. + * this way we get a valid baud and can safely call set_baud() + */ + minbaud = port->uartclk / + ((CDNS_UART_BDIV_MAX + 1) * CDNS_UART_CD_MAX * 8); + maxbaud = port->uartclk / (CDNS_UART_BDIV_MIN + 1); + baud = uart_get_baud_rate(port, termios, old, minbaud, maxbaud); + baud = cdns_uart_set_baud_rate(port, baud); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); - /* - * Update the per-port timeout. - */ + /* Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* Set TX/RX Reset */ - xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) | - (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST), - XUARTPS_CR_OFFSET); - - ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - /* Clear the RX disable and TX disable bits and then set the TX enable + /* + * Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ - xuartps_writel( - (ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) - | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN), - XUARTPS_CR_OFFSET); + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); - xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); - port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT; + port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; port->ignore_status_mask = 0; if (termios->c_iflag & INPCK) - port->read_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING; + port->read_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING; if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; /* ignore all characters if CREAD is not set */ if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= XUARTPS_IXR_RXTRIG | - XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN; + port->ignore_status_mask |= CDNS_UART_IXR_RXTRIG | + CDNS_UART_IXR_TOUT | CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN; - mode_reg = xuartps_readl(XUARTPS_MR_OFFSET); + mode_reg = cdns_uart_readl(CDNS_UART_MR_OFFSET); /* Handling Data Size */ switch (termios->c_cflag & CSIZE) { case CS6: - cval |= XUARTPS_MR_CHARLEN_6_BIT; + cval |= CDNS_UART_MR_CHARLEN_6_BIT; break; case CS7: - cval |= XUARTPS_MR_CHARLEN_7_BIT; + cval |= CDNS_UART_MR_CHARLEN_7_BIT; break; default: case CS8: - cval |= XUARTPS_MR_CHARLEN_8_BIT; + cval |= CDNS_UART_MR_CHARLEN_8_BIT; termios->c_cflag &= ~CSIZE; termios->c_cflag |= CS8; break; @@ -516,125 +709,135 @@ static void xuartps_set_termios(struct uart_port *port, /* Handling Parity and Stop Bits length */ if (termios->c_cflag & CSTOPB) - cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */ + cval |= CDNS_UART_MR_STOPMODE_2_BIT; /* 2 STOP bits */ else - cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */ + cval |= CDNS_UART_MR_STOPMODE_1_BIT; /* 1 STOP bit */ if (termios->c_cflag & PARENB) { /* Mark or Space parity */ if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_MARK; + cval |= CDNS_UART_MR_PARITY_MARK; else - cval |= XUARTPS_MR_PARITY_SPACE; - } else if (termios->c_cflag & PARODD) - cval |= XUARTPS_MR_PARITY_ODD; + cval |= CDNS_UART_MR_PARITY_SPACE; + } else { + if (termios->c_cflag & PARODD) + cval |= CDNS_UART_MR_PARITY_ODD; else - cval |= XUARTPS_MR_PARITY_EVEN; - } else - cval |= XUARTPS_MR_PARITY_NONE; - xuartps_writel(cval , XUARTPS_MR_OFFSET); + cval |= CDNS_UART_MR_PARITY_EVEN; + } + } else { + cval |= CDNS_UART_MR_PARITY_NONE; + } + cval |= mode_reg & 1; + cdns_uart_writel(cval, CDNS_UART_MR_OFFSET); spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_startup - Called when an application opens a xuartps port + * cdns_uart_startup - Called when an application opens a cdns_uart port * @port: Handle to the uart port structure * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_startup(struct uart_port *port) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_startup(struct uart_port *port) { unsigned int retval = 0, status = 0; - retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME, + retval = request_irq(port->irq, cdns_uart_isr, 0, CDNS_UART_NAME, (void *)port); if (retval) return retval; /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); /* Set the Control Register with TX/RX Enable, TX/RX Reset, * no break chars. */ - xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST, + CDNS_UART_CR_OFFSET); - status = xuartps_readl(XUARTPS_CR_OFFSET); + status = cdns_uart_readl(CDNS_UART_CR_OFFSET); /* Clear the RX disable and TX disable bits and then set the TX enable * bit and RX enable bit to enable the transmitter and receiver. */ - xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS)) - | (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN | - XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET); + cdns_uart_writel((status & ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS)) + | (CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN | + CDNS_UART_CR_STOPBRK), CDNS_UART_CR_OFFSET); /* Set the Mode Register with normal mode,8 data bits,1 stop bit, * no parity. */ - xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT - | XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT, - XUARTPS_MR_OFFSET); + cdns_uart_writel(CDNS_UART_MR_CHMODE_NORM | CDNS_UART_MR_STOPMODE_1_BIT + | CDNS_UART_MR_PARITY_NONE | CDNS_UART_MR_CHARLEN_8_BIT, + CDNS_UART_MR_OFFSET); - /* Set the RX FIFO Trigger level to 14 assuming FIFO size as 16 */ - xuartps_writel(14, XUARTPS_RXWM_OFFSET); + /* + * Set the RX FIFO Trigger level to use most of the FIFO, but it + * can be tuned with a module parameter + */ + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); - /* Receive Timeout register is enabled with value of 10 */ - xuartps_writel(10, XUARTPS_RXTOUT_OFFSET); + /* + * Receive Timeout register is enabled but it + * can be tuned with a module parameter + */ + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); + /* Clear out any pending interrupts before enabling them */ + cdns_uart_writel(cdns_uart_readl(CDNS_UART_ISR_OFFSET), + CDNS_UART_ISR_OFFSET); /* Set the Interrupt Registers with desired interrupts */ - xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET); - xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY | - XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN | - XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_OFFSET); + cdns_uart_writel(CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_PARITY | + CDNS_UART_IXR_FRAMING | CDNS_UART_IXR_OVERRUN | + CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT, + CDNS_UART_IER_OFFSET); return retval; } /** - * xuartps_shutdown - Called when an application closes a xuartps port + * cdns_uart_shutdown - Called when an application closes a cdns_uart port * @port: Handle to the uart port structure - * - **/ -static void xuartps_shutdown(struct uart_port *port) + */ +static void cdns_uart_shutdown(struct uart_port *port) { int status; /* Disable interrupts */ - status = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(status, XUARTPS_IDR_OFFSET); + status = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(status, CDNS_UART_IDR_OFFSET); /* Disable the TX and RX */ - xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS, - XUARTPS_CR_OFFSET); + cdns_uart_writel(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS, + CDNS_UART_CR_OFFSET); free_irq(port->irq, port); } /** - * xuartps_type - Set UART type to xuartps port + * cdns_uart_type - Set UART type to cdns_uart port * @port: Handle to the uart port structure * - * Returns string on success, NULL otherwise - **/ -static const char *xuartps_type(struct uart_port *port) + * Return: string on success, NULL otherwise + */ +static const char *cdns_uart_type(struct uart_port *port) { - return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL; + return port->type == PORT_XUARTPS ? CDNS_UART_NAME : NULL; } /** - * xuartps_verify_port - Verify the port params + * cdns_uart_verify_port - Verify the port params * @port: Handle to the uart port structure * @ser: Handle to the structure whose members are compared * - * Returns 0 if success otherwise -EINVAL - **/ -static int xuartps_verify_port(struct uart_port *port, + * Return: 0 on success, negative errno otherwise. + */ +static int cdns_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS) @@ -651,135 +854,170 @@ static int xuartps_verify_port(struct uart_port *port, } /** - * xuartps_request_port - Claim the memory region attached to xuartps port, - * called when the driver adds a xuartps port via + * cdns_uart_request_port - Claim the memory region attached to cdns_uart port, + * called when the driver adds a cdns_uart port via * uart_add_one_port() * @port: Handle to the uart port structure * - * Returns 0, -ENOMEM if request fails - **/ -static int xuartps_request_port(struct uart_port *port) + * Return: 0 on success, negative errno otherwise. + */ +static int cdns_uart_request_port(struct uart_port *port) { - if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE, - XUARTPS_NAME)) { + if (!request_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE, + CDNS_UART_NAME)) { return -ENOMEM; } - port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE); + port->membase = ioremap(port->mapbase, CDNS_UART_REGISTER_SPACE); if (!port->membase) { dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); return -ENOMEM; } return 0; } /** - * xuartps_release_port - Release the memory region attached to a xuartps - * port, called when the driver removes a xuartps - * port via uart_remove_one_port(). + * cdns_uart_release_port - Release UART port * @port: Handle to the uart port structure * - **/ -static void xuartps_release_port(struct uart_port *port) + * Release the memory region attached to a cdns_uart port. Called when the + * driver removes a cdns_uart port via uart_remove_one_port(). + */ +static void cdns_uart_release_port(struct uart_port *port) { - release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE); + release_mem_region(port->mapbase, CDNS_UART_REGISTER_SPACE); iounmap(port->membase); port->membase = NULL; } /** - * xuartps_config_port - Configure xuartps, called when the driver adds a - * xuartps port + * cdns_uart_config_port - Configure UART port * @port: Handle to the uart port structure * @flags: If any - * - **/ -static void xuartps_config_port(struct uart_port *port, int flags) + */ +static void cdns_uart_config_port(struct uart_port *port, int flags) { - if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0) + if (flags & UART_CONFIG_TYPE && cdns_uart_request_port(port) == 0) port->type = PORT_XUARTPS; } /** - * xuartps_get_mctrl - Get the modem control state - * + * cdns_uart_get_mctrl - Get the modem control state * @port: Handle to the uart port structure * - * Returns the modem control state - * - **/ -static unsigned int xuartps_get_mctrl(struct uart_port *port) + * Return: the modem control state + */ +static unsigned int cdns_uart_get_mctrl(struct uart_port *port) { return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { /* N/A */ } -static void xuartps_enable_ms(struct uart_port *port) +static void cdns_uart_enable_ms(struct uart_port *port) { /* N/A */ } -/** The UART operations structure - */ -static struct uart_ops xuartps_ops = { - .set_mctrl = xuartps_set_mctrl, - .get_mctrl = xuartps_get_mctrl, - .enable_ms = xuartps_enable_ms, - - .start_tx = xuartps_start_tx, /* Start transmitting */ - .stop_tx = xuartps_stop_tx, /* Stop transmission */ - .stop_rx = xuartps_stop_rx, /* Stop reception */ - .tx_empty = xuartps_tx_empty, /* Transmitter busy? */ - .break_ctl = xuartps_break_ctl, /* Start/stop - * transmitting break - */ - .set_termios = xuartps_set_termios, /* Set termios */ - .startup = xuartps_startup, /* App opens xuartps */ - .shutdown = xuartps_shutdown, /* App closes xuartps */ - .type = xuartps_type, /* Set UART type */ - .verify_port = xuartps_verify_port, /* Verification of port - * params - */ - .request_port = xuartps_request_port, /* Claim resources - * associated with a - * xuartps port - */ - .release_port = xuartps_release_port, /* Release resources - * associated with a - * xuartps port - */ - .config_port = xuartps_config_port, /* Configure when driver - * adds a xuartps port - */ +#ifdef CONFIG_CONSOLE_POLL +static int cdns_uart_poll_get_char(struct uart_port *port) +{ + u32 imr; + int c; + + /* Disable all interrupts */ + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); + + /* Check if FIFO is empty */ + if (cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_RXEMPTY) + c = NO_POLL_CHAR; + else /* Read a character */ + c = (unsigned char) cdns_uart_readl(CDNS_UART_FIFO_OFFSET); + + /* Enable interrupts */ + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); + + return c; +} + +static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) +{ + u32 imr; + + /* Disable all interrupts */ + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); + + /* Wait until FIFO is empty */ + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) + cpu_relax(); + + /* Write a character */ + cdns_uart_writel(c, CDNS_UART_FIFO_OFFSET); + + /* Wait until FIFO is empty */ + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY)) + cpu_relax(); + + /* Enable interrupts */ + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); + + return; +} +#endif + +static struct uart_ops cdns_uart_ops = { + .set_mctrl = cdns_uart_set_mctrl, + .get_mctrl = cdns_uart_get_mctrl, + .enable_ms = cdns_uart_enable_ms, + .start_tx = cdns_uart_start_tx, + .stop_tx = cdns_uart_stop_tx, + .stop_rx = cdns_uart_stop_rx, + .tx_empty = cdns_uart_tx_empty, + .break_ctl = cdns_uart_break_ctl, + .set_termios = cdns_uart_set_termios, + .startup = cdns_uart_startup, + .shutdown = cdns_uart_shutdown, + .type = cdns_uart_type, + .verify_port = cdns_uart_verify_port, + .request_port = cdns_uart_request_port, + .release_port = cdns_uart_release_port, + .config_port = cdns_uart_config_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = cdns_uart_poll_get_char, + .poll_put_char = cdns_uart_poll_put_char, +#endif }; -static struct uart_port xuartps_port[2]; +static struct uart_port cdns_uart_port[2]; /** - * xuartps_get_port - Configure the port from the platform device resource - * info + * cdns_uart_get_port - Configure the port from platform device resource info + * @id: Port id * - * Returns a pointer to a uart_port or NULL for failure - **/ -static struct uart_port *xuartps_get_port(void) + * Return: a pointer to a uart_port or NULL for failure + */ +static struct uart_port *cdns_uart_get_port(int id) { struct uart_port *port; - int id; - /* Find the next unused port */ - for (id = 0; id < XUARTPS_NR_PORTS; id++) - if (xuartps_port[id].mapbase == 0) - break; + /* Try the given port id if failed use default method */ + if (cdns_uart_port[id].mapbase != 0) { + /* Find the next unused port */ + for (id = 0; id < CDNS_UART_NR_PORTS; id++) + if (cdns_uart_port[id].mapbase == 0) + break; + } - if (id >= XUARTPS_NR_PORTS) + if (id >= CDNS_UART_NR_PORTS) return NULL; - port = &xuartps_port[id]; + port = &cdns_uart_port[id]; /* At this point, we've got an empty uart_port struct, initialize it */ spin_lock_init(&port->lock); @@ -789,52 +1027,48 @@ static struct uart_port *xuartps_get_port(void) port->type = PORT_UNKNOWN; port->iotype = UPIO_MEM32; port->flags = UPF_BOOT_AUTOCONF; - port->ops = &xuartps_ops; - port->fifosize = XUARTPS_FIFO_SIZE; + port->ops = &cdns_uart_ops; + port->fifosize = CDNS_UART_FIFO_SIZE; port->line = id; port->dev = NULL; return port; } -/*-----------------------Console driver operations--------------------------*/ - #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** - * xuartps_console_wait_tx - Wait for the TX to be full + * cdns_uart_console_wait_tx - Wait for the TX to be full * @port: Handle to the uart port structure - * - **/ -static void xuartps_console_wait_tx(struct uart_port *port) + */ +static void cdns_uart_console_wait_tx(struct uart_port *port) { - while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY) - != XUARTPS_SR_TXEMPTY) + while ((cdns_uart_readl(CDNS_UART_SR_OFFSET) & CDNS_UART_SR_TXEMPTY) + != CDNS_UART_SR_TXEMPTY) barrier(); } /** - * xuartps_console_putchar - write the character to the FIFO buffer + * cdns_uart_console_putchar - write the character to the FIFO buffer * @port: Handle to the uart port structure * @ch: Character to be written - * - **/ -static void xuartps_console_putchar(struct uart_port *port, int ch) + */ +static void cdns_uart_console_putchar(struct uart_port *port, int ch) { - xuartps_console_wait_tx(port); - xuartps_writel(ch, XUARTPS_FIFO_OFFSET); + cdns_uart_console_wait_tx(port); + cdns_uart_writel(ch, CDNS_UART_FIFO_OFFSET); } /** - * xuartps_console_write - perform write operation - * @port: Handle to the uart port structure + * cdns_uart_console_write - perform write operation + * @co: Console handle * @s: Pointer to character array * @count: No of characters - **/ -static void xuartps_console_write(struct console *co, const char *s, + */ +static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; unsigned long flags; - unsigned int imr; + unsigned int imr, ctrl; int locked = 1; if (oops_in_progress) @@ -843,39 +1077,45 @@ static void xuartps_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - imr = xuartps_readl(XUARTPS_IMR_OFFSET); - xuartps_writel(imr, XUARTPS_IDR_OFFSET); + imr = cdns_uart_readl(CDNS_UART_IMR_OFFSET); + cdns_uart_writel(imr, CDNS_UART_IDR_OFFSET); - uart_console_write(port, s, count, xuartps_console_putchar); - xuartps_console_wait_tx(port); - - /* restore interrupt state, it seems like there may be a h/w bug - * in that the interrupt enable register should not need to be - * written based on the data sheet + /* + * Make sure that the tx part is enabled. Set the TX enable bit and + * clear the TX disable bit to enable the transmitter. */ - xuartps_writel(~imr, XUARTPS_IDR_OFFSET); - xuartps_writel(imr, XUARTPS_IER_OFFSET); + ctrl = cdns_uart_readl(CDNS_UART_CR_OFFSET); + cdns_uart_writel((ctrl & ~CDNS_UART_CR_TX_DIS) | CDNS_UART_CR_TX_EN, + CDNS_UART_CR_OFFSET); + + uart_console_write(port, s, count, cdns_uart_console_putchar); + cdns_uart_console_wait_tx(port); + + cdns_uart_writel(ctrl, CDNS_UART_CR_OFFSET); + + /* restore interrupt state */ + cdns_uart_writel(imr, CDNS_UART_IER_OFFSET); if (locked) spin_unlock_irqrestore(&port->lock, flags); } /** - * xuartps_console_setup - Initialize the uart to default config + * cdns_uart_console_setup - Initialize the uart to default config * @co: Console handle * @options: Initial settings of uart * - * Returns 0, -ENODEV if no device - **/ -static int __init xuartps_console_setup(struct console *co, char *options) + * Return: 0 on success, negative errno otherwise. + */ +static int __init cdns_uart_console_setup(struct console *co, char *options) { - struct uart_port *port = &xuartps_port[co->index]; + struct uart_port *port = &cdns_uart_port[co->index]; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; - if (co->index < 0 || co->index >= XUARTPS_NR_PORTS) + if (co->index < 0 || co->index >= CDNS_UART_NR_PORTS) return -EINVAL; if (!port->mapbase) { @@ -889,87 +1129,241 @@ static int __init xuartps_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static struct uart_driver xuartps_uart_driver; +static struct uart_driver cdns_uart_uart_driver; -static struct console xuartps_console = { - .name = XUARTPS_TTY_NAME, - .write = xuartps_console_write, +static struct console cdns_uart_console = { + .name = CDNS_UART_TTY_NAME, + .write = cdns_uart_console_write, .device = uart_console_device, - .setup = xuartps_console_setup, + .setup = cdns_uart_console_setup, .flags = CON_PRINTBUFFER, .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */ - .data = &xuartps_uart_driver, + .data = &cdns_uart_uart_driver, }; /** - * xuartps_console_init - Initialization call + * cdns_uart_console_init - Initialization call * - * Returns 0 on success, negative error otherwise - **/ -static int __init xuartps_console_init(void) + * Return: 0 on success, negative errno otherwise + */ +static int __init cdns_uart_console_init(void) { - register_console(&xuartps_console); + register_console(&cdns_uart_console); return 0; } -console_initcall(xuartps_console_init); +console_initcall(cdns_uart_console_init); #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ -/** Structure Definitions - */ -static struct uart_driver xuartps_uart_driver = { - .owner = THIS_MODULE, /* Owner */ - .driver_name = XUARTPS_NAME, /* Driver name */ - .dev_name = XUARTPS_TTY_NAME, /* Node name */ - .major = XUARTPS_MAJOR, /* Major number */ - .minor = XUARTPS_MINOR, /* Minor number */ - .nr = XUARTPS_NR_PORTS, /* Number of UART ports */ +static struct uart_driver cdns_uart_uart_driver = { + .owner = THIS_MODULE, + .driver_name = CDNS_UART_NAME, + .dev_name = CDNS_UART_TTY_NAME, + .major = CDNS_UART_MAJOR, + .minor = CDNS_UART_MINOR, + .nr = CDNS_UART_NR_PORTS, #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE - .cons = &xuartps_console, /* Console */ + .cons = &cdns_uart_console, #endif }; -/* --------------------------------------------------------------------- - * Platform bus binding +#ifdef CONFIG_PM_SLEEP +/** + * cdns_uart_suspend - suspend event + * @device: Pointer to the device structure + * + * Return: 0 */ +static int cdns_uart_suspend(struct device *device) +{ + struct uart_port *port = dev_get_drvdata(device); + struct tty_struct *tty; + struct device *tty_dev; + int may_wake = 0; + + /* Get the tty which could be NULL so don't assume it's valid */ + tty = tty_port_tty_get(&port->state->port); + if (tty) { + tty_dev = tty->dev; + may_wake = device_may_wakeup(tty_dev); + tty_kref_put(tty); + } + + /* + * Call the API provided in serial_core.c file which handles + * the suspend. + */ + uart_suspend_port(&cdns_uart_uart_driver, port); + if (console_suspend_enabled && !may_wake) { + struct cdns_uart *cdns_uart = port->private_data; + + clk_disable(cdns_uart->uartclk); + clk_disable(cdns_uart->pclk); + } else { + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + /* Empty the receive FIFO 1st before making changes */ + while (!(cdns_uart_readl(CDNS_UART_SR_OFFSET) & + CDNS_UART_SR_RXEMPTY)) + cdns_uart_readl(CDNS_UART_FIFO_OFFSET); + /* set RX trigger level to 1 */ + cdns_uart_writel(1, CDNS_UART_RXWM_OFFSET); + /* disable RX timeout interrups */ + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IDR_OFFSET); + spin_unlock_irqrestore(&port->lock, flags); + } + + return 0; +} + /** - * xuartps_probe - Platform driver probe + * cdns_uart_resume - Resume after a previous suspend + * @device: Pointer to the device structure + * + * Return: 0 + */ +static int cdns_uart_resume(struct device *device) +{ + struct uart_port *port = dev_get_drvdata(device); + unsigned long flags = 0; + u32 ctrl_reg; + struct tty_struct *tty; + struct device *tty_dev; + int may_wake = 0; + + /* Get the tty which could be NULL so don't assume it's valid */ + tty = tty_port_tty_get(&port->state->port); + if (tty) { + tty_dev = tty->dev; + may_wake = device_may_wakeup(tty_dev); + tty_kref_put(tty); + } + + if (console_suspend_enabled && !may_wake) { + struct cdns_uart *cdns_uart = port->private_data; + + clk_enable(cdns_uart->pclk); + clk_enable(cdns_uart->uartclk); + + spin_lock_irqsave(&port->lock, flags); + + /* Set TX/RX Reset */ + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + while (cdns_uart_readl(CDNS_UART_CR_OFFSET) & + (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) + cpu_relax(); + + /* restore rx timeout value */ + cdns_uart_writel(rx_timeout, CDNS_UART_RXTOUT_OFFSET); + /* Enable Tx/Rx */ + ctrl_reg = cdns_uart_readl(CDNS_UART_CR_OFFSET); + ctrl_reg &= ~(CDNS_UART_CR_TX_DIS | CDNS_UART_CR_RX_DIS); + ctrl_reg |= CDNS_UART_CR_TX_EN | CDNS_UART_CR_RX_EN; + cdns_uart_writel(ctrl_reg, CDNS_UART_CR_OFFSET); + + spin_unlock_irqrestore(&port->lock, flags); + } else { + spin_lock_irqsave(&port->lock, flags); + /* restore original rx trigger level */ + cdns_uart_writel(rx_trigger_level, CDNS_UART_RXWM_OFFSET); + /* enable RX timeout interrupt */ + cdns_uart_writel(CDNS_UART_IXR_TOUT, CDNS_UART_IER_OFFSET); + spin_unlock_irqrestore(&port->lock, flags); + } + + return uart_resume_port(&cdns_uart_uart_driver, port); +} +#endif /* ! CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend, + cdns_uart_resume); + +/** + * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_probe(struct platform_device *pdev) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_probe(struct platform_device *pdev) { - int rc; + int rc, id; struct uart_port *port; struct resource *res, *res2; - int clk = 0; + struct cdns_uart *cdns_uart_data; - const unsigned int *prop; + cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data), + GFP_KERNEL); + if (!cdns_uart_data) + return -ENOMEM; - prop = of_get_property(pdev->dev.of_node, "clock", NULL); - if (prop) - clk = be32_to_cpup(prop); - if (!clk) { - dev_err(&pdev->dev, "no clock specified\n"); - return -ENODEV; + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(cdns_uart_data->pclk)) { + cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk"); + if (!IS_ERR(cdns_uart_data->pclk)) + dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n"); + } + if (IS_ERR(cdns_uart_data->pclk)) { + dev_err(&pdev->dev, "pclk clock not found.\n"); + return PTR_ERR(cdns_uart_data->pclk); + } + + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); + if (IS_ERR(cdns_uart_data->uartclk)) { + cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (!IS_ERR(cdns_uart_data->uartclk)) + dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n"); + } + if (IS_ERR(cdns_uart_data->uartclk)) { + dev_err(&pdev->dev, "uart_clk clock not found.\n"); + return PTR_ERR(cdns_uart_data->uartclk); + } + + rc = clk_prepare_enable(cdns_uart_data->pclk); + if (rc) { + dev_err(&pdev->dev, "Unable to enable pclk clock.\n"); + return rc; + } + rc = clk_prepare_enable(cdns_uart_data->uartclk); + if (rc) { + dev_err(&pdev->dev, "Unable to enable device clock.\n"); + goto err_out_clk_dis_pclk; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + if (!res) { + rc = -ENODEV; + goto err_out_clk_disable; + } res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) - return -ENODEV; + if (!res2) { + rc = -ENODEV; + goto err_out_clk_disable; + } + +#ifdef CONFIG_COMMON_CLK + cdns_uart_data->clk_rate_change_nb.notifier_call = + cdns_uart_clk_notifier_cb; + if (clk_notifier_register(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb)) + dev_warn(&pdev->dev, "Unable to register clock notifier.\n"); +#endif + /* Look for a serialN alias */ + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) + id = 0; /* Initialize the port structure */ - port = xuartps_get_port(); + port = cdns_uart_get_port(id); if (!port) { dev_err(&pdev->dev, "Cannot get uart_port structure\n"); - return -ENODEV; + rc = -ENODEV; + goto err_out_notif_unreg; } else { /* Register the port. * This function also registers this device with the tty layer @@ -978,130 +1372,104 @@ static int xuartps_probe(struct platform_device *pdev) port->mapbase = res->start; port->irq = res2->start; port->dev = &pdev->dev; - port->uartclk = clk; - dev_set_drvdata(&pdev->dev, port); - rc = uart_add_one_port(&xuartps_uart_driver, port); + port->uartclk = clk_get_rate(cdns_uart_data->uartclk); + port->private_data = cdns_uart_data; + cdns_uart_data->port = port; + platform_set_drvdata(pdev, port); + rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { dev_err(&pdev->dev, "uart_add_one_port() failed; err=%i\n", rc); - dev_set_drvdata(&pdev->dev, NULL); - return rc; + goto err_out_notif_unreg; } return 0; } -} -/** - * xuartps_remove - called when the platform driver is unregistered - * @pdev: Pointer to the platform device structure - * - * Returns 0 on success, negative error otherwise - **/ -static int xuartps_remove(struct platform_device *pdev) -{ - struct uart_port *port = dev_get_drvdata(&pdev->dev); - int rc = 0; - - /* Remove the xuartps port from the serial core */ - if (port) { - rc = uart_remove_one_port(&xuartps_uart_driver, port); - dev_set_drvdata(&pdev->dev, NULL); - port->mapbase = 0; - } +err_out_notif_unreg: +#ifdef CONFIG_COMMON_CLK + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); +#endif +err_out_clk_disable: + clk_disable_unprepare(cdns_uart_data->uartclk); +err_out_clk_dis_pclk: + clk_disable_unprepare(cdns_uart_data->pclk); + return rc; } /** - * xuartps_suspend - suspend event + * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure - * @state: State of the device * - * Returns 0 - **/ -static int xuartps_suspend(struct platform_device *pdev, pm_message_t state) + * Return: 0 on success, negative errno otherwise + */ +static int cdns_uart_remove(struct platform_device *pdev) { - /* Call the API provided in serial_core.c file which handles - * the suspend. - */ - uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; -} + struct uart_port *port = platform_get_drvdata(pdev); + struct cdns_uart *cdns_uart_data = port->private_data; + int rc; -/** - * xuartps_resume - Resume after a previous suspend - * @pdev: Pointer to the platform device structure - * - * Returns 0 - **/ -static int xuartps_resume(struct platform_device *pdev) -{ - uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]); - return 0; + /* Remove the cdns_uart port from the serial core */ +#ifdef CONFIG_COMMON_CLK + clk_notifier_unregister(cdns_uart_data->uartclk, + &cdns_uart_data->clk_rate_change_nb); +#endif + rc = uart_remove_one_port(&cdns_uart_uart_driver, port); + port->mapbase = 0; + clk_disable_unprepare(cdns_uart_data->uartclk); + clk_disable_unprepare(cdns_uart_data->pclk); + return rc; } /* Match table for of_platform binding */ -static struct of_device_id xuartps_of_match[] = { +static struct of_device_id cdns_uart_of_match[] = { { .compatible = "xlnx,xuartps", }, + { .compatible = "cdns,uart-r1p8", }, {} }; -MODULE_DEVICE_TABLE(of, xuartps_of_match); +MODULE_DEVICE_TABLE(of, cdns_uart_of_match); -static struct platform_driver xuartps_platform_driver = { - .probe = xuartps_probe, /* Probe method */ - .remove = __exit_p(xuartps_remove), /* Detach method */ - .suspend = xuartps_suspend, /* Suspend */ - .resume = xuartps_resume, /* Resume after a suspend */ +static struct platform_driver cdns_uart_platform_driver = { + .probe = cdns_uart_probe, + .remove = cdns_uart_remove, .driver = { .owner = THIS_MODULE, - .name = XUARTPS_NAME, /* Driver name */ - .of_match_table = xuartps_of_match, + .name = CDNS_UART_NAME, + .of_match_table = cdns_uart_of_match, + .pm = &cdns_uart_dev_pm_ops, }, }; -/* --------------------------------------------------------------------- - * Module Init and Exit - */ -/** - * xuartps_init - Initial driver registration call - * - * Returns whether the registration was successful or not - **/ -static int __init xuartps_init(void) +static int __init cdns_uart_init(void) { int retval = 0; - /* Register the xuartps driver with the serial core */ - retval = uart_register_driver(&xuartps_uart_driver); + /* Register the cdns_uart driver with the serial core */ + retval = uart_register_driver(&cdns_uart_uart_driver); if (retval) return retval; /* Register the platform driver */ - retval = platform_driver_register(&xuartps_platform_driver); + retval = platform_driver_register(&cdns_uart_platform_driver); if (retval) - uart_unregister_driver(&xuartps_uart_driver); + uart_unregister_driver(&cdns_uart_uart_driver); return retval; } -/** - * xuartps_exit - Driver unregistration call - **/ -static void __exit xuartps_exit(void) +static void __exit cdns_uart_exit(void) { - /* The order of unregistration is important. Unregister the - * UART driver before the platform driver crashes the system. - */ - /* Unregister the platform driver */ - platform_driver_unregister(&xuartps_platform_driver); + platform_driver_unregister(&cdns_uart_platform_driver); - /* Unregister the xuartps driver */ - uart_unregister_driver(&xuartps_uart_driver); + /* Unregister the cdns_uart driver */ + uart_unregister_driver(&cdns_uart_uart_driver); } -module_init(xuartps_init); -module_exit(xuartps_exit); +module_init(cdns_uart_init); +module_exit(cdns_uart_exit); -MODULE_DESCRIPTION("Driver for PS UART"); +MODULE_DESCRIPTION("Driver for Cadence UART"); MODULE_AUTHOR("Xilinx Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 92c00b24d0d..2b65bb7ffb8 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -603,7 +603,7 @@ static void zs_receive_chars(struct zs_port *zport) uart_insert_char(uport, status, Rx_OVR, ch, flag); } - tty_flip_buffer_push(uport->state->port.tty); + tty_flip_buffer_push(&uport->state->port); } static void zs_raw_transmit_chars(struct zs_port *zport) @@ -923,7 +923,7 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, uport->read_status_mask = Rx_OVR; if (termios->c_iflag & INPCK) uport->read_status_mask |= FRM_ERR | PAR_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) + if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) uport->read_status_mask |= Rx_BRK; uport->ignore_status_mask = 0; diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 9e071f6985f..d48e040cd8c 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -291,8 +291,7 @@ struct mgsl_struct { bool lcr_mem_requested; u32 misc_ctrl_value; - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; + char *flag_buf; bool drop_rts_on_tx_done; bool loopmode_insert_requested; @@ -578,22 +577,22 @@ struct mgsl_struct { #define SICR_RXC_ACTIVE BIT15 #define SICR_RXC_INACTIVE BIT14 -#define SICR_RXC (BIT15+BIT14) +#define SICR_RXC (BIT15|BIT14) #define SICR_TXC_ACTIVE BIT13 #define SICR_TXC_INACTIVE BIT12 -#define SICR_TXC (BIT13+BIT12) +#define SICR_TXC (BIT13|BIT12) #define SICR_RI_ACTIVE BIT11 #define SICR_RI_INACTIVE BIT10 -#define SICR_RI (BIT11+BIT10) +#define SICR_RI (BIT11|BIT10) #define SICR_DSR_ACTIVE BIT9 #define SICR_DSR_INACTIVE BIT8 -#define SICR_DSR (BIT9+BIT8) +#define SICR_DSR (BIT9|BIT8) #define SICR_DCD_ACTIVE BIT7 #define SICR_DCD_INACTIVE BIT6 -#define SICR_DCD (BIT7+BIT6) +#define SICR_DCD (BIT7|BIT6) #define SICR_CTS_ACTIVE BIT5 #define SICR_CTS_INACTIVE BIT4 -#define SICR_CTS (BIT5+BIT4) +#define SICR_CTS (BIT5|BIT4) #define SICR_RCC_UNDERFLOW BIT3 #define SICR_DPLL_NO_SYNC BIT2 #define SICR_BRG1_ZERO BIT1 @@ -1059,9 +1058,6 @@ static void mgsl_bh_handler(struct work_struct *work) container_of(work, struct mgsl_struct, task); int action; - if (!info) - return; - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) entry\n", __FILE__,__LINE__,info->device_name); @@ -1165,7 +1161,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info ) { u16 status = usc_InReg( info, RCSR ); - if ( debug_level >= DEBUG_LEVEL_ISR ) + if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s(%d):mgsl_isr_receive_status status=%04X\n", __FILE__,__LINE__,status); @@ -1185,7 +1181,7 @@ static void mgsl_isr_receive_status( struct mgsl_struct *info ) (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); } - if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { + if (status & (RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED)) { if (status & RXSTATUS_EXITED_HUNT) info->icount.exithunt++; if (status & RXSTATUS_IDLE_RECEIVED) @@ -1440,7 +1436,6 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) u16 status; int work = 0; unsigned char DataByte; - struct tty_struct *tty = info->port.tty; struct mgsl_icount *icount = &info->icount; if ( debug_level >= DEBUG_LEVEL_ISR ) @@ -1468,21 +1463,21 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) /* get the status of the received byte */ status = usc_InReg(info, RCSR); - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) + if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR | + RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) ) usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); icount->rx++; flag = 0; - if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + - RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { - printk("rxerr=%04X\n",status); + if ( status & (RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR | + RXSTATUS_OVERRUN | RXSTATUS_BREAK_RECEIVED) ) { + printk("rxerr=%04X\n",status); /* update error statistics */ if ( status & RXSTATUS_BREAK_RECEIVED ) { - status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); + status &= ~(RXSTATUS_FRAMING_ERROR | RXSTATUS_PARITY_ERROR); icount->brk++; - } else if (status & RXSTATUS_PARITY_ERROR) + } else if (status & RXSTATUS_PARITY_ERROR) icount->parity++; else if (status & RXSTATUS_FRAMING_ERROR) icount->frame++; @@ -1493,7 +1488,7 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) icount->overrun++; } - /* discard char if tty control flags say so */ + /* discard char if tty control flags say so */ if (status & info->ignore_status_mask) continue; @@ -1502,19 +1497,19 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) if (status & RXSTATUS_BREAK_RECEIVED) { flag = TTY_BREAK; if (info->port.flags & ASYNC_SAK) - do_SAK(tty); + do_SAK(info->port.tty); } else if (status & RXSTATUS_PARITY_ERROR) flag = TTY_PARITY; else if (status & RXSTATUS_FRAMING_ERROR) flag = TTY_FRAME; } /* end of if (error) */ - tty_insert_flip_char(tty, DataByte, flag); + tty_insert_flip_char(&info->port, DataByte, flag); if (status & RXSTATUS_OVERRUN) { /* Overrun is special, since it's * reported immediately, and doesn't * affect the current character */ - work += tty_insert_flip_char(tty, 0, TTY_OVERRUN); + work += tty_insert_flip_char(&info->port, 0, TTY_OVERRUN); } } @@ -1525,7 +1520,7 @@ static void mgsl_isr_receive_data( struct mgsl_struct *info ) } if(work) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&info->port); } /* mgsl_isr_misc() @@ -1550,8 +1545,8 @@ static void mgsl_isr_misc( struct mgsl_struct *info ) usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); usc_DmaCmd(info, DmaCmd_ResetRxChannel); usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); - usc_DisableInterrupts(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS); + usc_DisableInterrupts(info, RECEIVE_DATA | RECEIVE_STATUS); /* schedule BH handler to restart receiver */ info->pending_bh |= BH_RECEIVE; @@ -1600,7 +1595,7 @@ static void mgsl_isr_receive_dma( struct mgsl_struct *info ) u16 status; /* clear interrupt pending and IUS bit for Rx DMA IRQ */ - usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); + usc_OutDmaReg( info, CDIR, BIT9 | BIT1 ); /* Read the receive DMA status to identify interrupt type. */ /* This also clears the status bits. */ @@ -1644,7 +1639,7 @@ static void mgsl_isr_transmit_dma( struct mgsl_struct *info ) u16 status; /* clear interrupt pending and IUS bit for Tx DMA IRQ */ - usc_OutDmaReg(info, CDIR, BIT8+BIT0 ); + usc_OutDmaReg(info, CDIR, BIT8 | BIT0 ); /* Read the transmit DMA status to identify interrupt type. */ /* This also clears the status bits. */ @@ -1837,8 +1832,8 @@ static void shutdown(struct mgsl_struct * info) usc_DisableMasterIrqBit(info); usc_stop_receiver(info); usc_stop_transmitter(info); - usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + - TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); + usc_DisableInterrupts(info,RECEIVE_DATA | RECEIVE_STATUS | + TRANSMIT_DATA | TRANSMIT_STATUS | IO_PIN | MISC ); usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); /* Disable DMAEN (Port 7, Bit 14) */ @@ -1852,7 +1847,7 @@ static void shutdown(struct mgsl_struct * info) usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); usc_set_serial_signals(info); } @@ -1891,7 +1886,7 @@ static void mgsl_program_hw(struct mgsl_struct *info) info->ri_chkcount = 0; info->dsr_chkcount = 0; - usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); + usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); usc_EnableInterrupts(info, IO_PIN); usc_get_serial_signals(info); @@ -1917,12 +1912,12 @@ static void mgsl_change_params(struct mgsl_struct *info) cflag = info->port.tty->termios.c_cflag; - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ + /* if B0 rate (hangup) specified then negate RTS and DTR */ + /* otherwise assert RTS and DTR */ if (cflag & CBAUD) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); /* byte size and parity */ @@ -2778,7 +2773,7 @@ static int mgsl_wait_event(struct mgsl_struct * info, int __user * mask_ptr) if (!waitqueue_active(&info->event_wait_q)) { /* disable enable exit hunt mode/idle rcvd IRQs */ usc_OutReg(info, RICR, usc_InReg(info,RICR) & - ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); + ~(RXSTATUS_EXITED_HUNT | RXSTATUS_IDLE_RECEIVED)); } spin_unlock_irqrestore(&info->irq_spinlock,flags); } @@ -3046,7 +3041,7 @@ static void mgsl_set_termios(struct tty_struct *tty, struct ktermios *old_termio /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && !(tty->termios.c_cflag & CBAUD)) { - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->irq_spinlock,flags); usc_set_serial_signals(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -3097,7 +3092,7 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgsl_close(%s) entry, count=%d\n", __FILE__,__LINE__, info->device_name, info->port.count); - if (tty_port_close_start(&info->port, tty, filp) == 0) + if (tty_port_close_start(&info->port, tty, filp) == 0) goto cleanup; mutex_lock(&info->port.mutex); @@ -3245,9 +3240,9 @@ static void dtr_rts(struct tty_port *port, int on) spin_lock_irqsave(&info->irq_spinlock,flags); if (on) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); usc_set_serial_signals(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); } @@ -3313,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3409,14 +3404,14 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; } - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); if (info->netcount) { @@ -3898,7 +3893,13 @@ static int mgsl_alloc_intermediate_rxbuffer_memory(struct mgsl_struct *info) info->intermediate_rxbuffer = kmalloc(info->max_frame_size, GFP_KERNEL | GFP_DMA); if ( info->intermediate_rxbuffer == NULL ) return -ENOMEM; - + /* unused flag buffer to satisfy receive_buf calling interface */ + info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL); + if (!info->flag_buf) { + kfree(info->intermediate_rxbuffer); + info->intermediate_rxbuffer = NULL; + return -ENOMEM; + } return 0; } /* end of mgsl_alloc_intermediate_rxbuffer_memory() */ @@ -3917,6 +3918,8 @@ static void mgsl_free_intermediate_rxbuffer_memory(struct mgsl_struct *info) { kfree(info->intermediate_rxbuffer); info->intermediate_rxbuffer = NULL; + kfree(info->flag_buf); + info->flag_buf = NULL; } /* end of mgsl_free_intermediate_rxbuffer_memory() */ @@ -4294,7 +4297,7 @@ static struct mgsl_struct* mgsl_allocate_device(void) spin_lock_init(&info->irq_spinlock); spin_lock_init(&info->netlock); memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); - info->idle_mode = HDLC_TXIDLE_FLAGS; + info->idle_mode = HDLC_TXIDLE_FLAGS; info->num_tx_dma_buffers = 1; info->num_tx_holding_buffers = 0; } @@ -4719,7 +4722,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) RegValue |= BIT15; else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) - RegValue |= BIT15 + BIT14; + RegValue |= BIT15 | BIT14; } if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) @@ -4760,11 +4763,11 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.encoding ) { case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 | BIT13; break; case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 | BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break; } if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) @@ -4835,15 +4838,15 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.encoding ) { case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; - case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 | BIT13; break; case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; - case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; - case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 | BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 | BIT14 | BIT13; break; } if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_16_CCITT ) - RegValue |= BIT9 + BIT8; + RegValue |= BIT9 | BIT8; else if ( (info->params.crc_type & HDLC_CRC_MASK) == HDLC_CRC_32_CCITT ) RegValue |= ( BIT12 | BIT10 | BIT9 | BIT8); @@ -4954,7 +4957,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) RegValue = 0x0000; - if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { + if ( info->params.flags & (HDLC_FLAG_RXC_DPLL | HDLC_FLAG_TXC_DPLL) ) { u32 XtalSpeed; u32 DpllDivisor; u16 Tc; @@ -5016,7 +5019,7 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) case HDLC_ENCODING_BIPHASE_MARK: case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; case HDLC_ENCODING_BIPHASE_LEVEL: - case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 | BIT8; break; } } @@ -5053,8 +5056,8 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) /* enable Master Interrupt Enable bit (MIE) */ usc_EnableMasterIrqBit( info ); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + - TRANSMIT_STATUS + TRANSMIT_DATA + MISC); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS | RECEIVE_DATA | + TRANSMIT_STATUS | TRANSMIT_DATA | MISC); /* arm RCC underflow interrupt */ usc_OutReg(info, SICR, (u16)(usc_InReg(info,SICR) | BIT3)); @@ -5172,14 +5175,14 @@ static void usc_set_sdlc_mode( struct mgsl_struct *info ) switch ( info->params.preamble_length ) { case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; - case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; + case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 | BIT10; break; } switch ( info->params.preamble ) { - case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; + case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 | BIT12; break; case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; - case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; + case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 | BIT8; break; } usc_OutReg( info, CCR, RegValue ); @@ -5218,7 +5221,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable) { if (enable) { /* blank external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7 | BIT6)); /* Clock mode Control Register (CMCR) * @@ -5257,7 +5260,7 @@ static void usc_enable_loopback(struct mgsl_struct *info, int enable) outw( 0x0300, info->io_base + CCAR ); } else { /* enable external TXD output */ - usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7 | BIT6)); /* clear Internal Data loopback mode */ info->loopback_bits = 0; @@ -5444,13 +5447,13 @@ static void usc_process_rxoverrun_sync( struct mgsl_struct *info ) usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableInterrupts( info, RECEIVE_STATUS ); /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 ); usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); usc_DmaCmd( info, DmaCmd_InitRxChannel ); if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) @@ -5485,8 +5488,8 @@ static void usc_stop_receiver( struct mgsl_struct *info ) usc_DmaCmd( info, DmaCmd_ResetRxChannel ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); - usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); + usc_DisableInterrupts( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); @@ -5533,13 +5536,13 @@ static void usc_start_receiver( struct mgsl_struct *info ) usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); - usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA | RECEIVE_STATUS ); usc_EnableInterrupts( info, RECEIVE_STATUS ); /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ - usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, RDIAR, BIT3 | BIT2 ); usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); usc_DmaCmd( info, DmaCmd_InitRxChannel ); if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) @@ -5548,7 +5551,7 @@ static void usc_start_receiver( struct mgsl_struct *info ) usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); } else { usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); - usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_ClearIrqPendingBits(info, RECEIVE_DATA | RECEIVE_STATUS); usc_EnableInterrupts(info, RECEIVE_DATA); usc_RTCmd( info, RTCmd_PurgeRxFifo ); @@ -5922,7 +5925,7 @@ static void usc_set_async_mode( struct mgsl_struct *info ) RegValue = 0; if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; + RegValue |= BIT4 | BIT3 | BIT2; if ( info->params.parity != ASYNC_PARITY_NONE ) { RegValue |= BIT5; @@ -5979,7 +5982,7 @@ static void usc_set_async_mode( struct mgsl_struct *info ) RegValue = 0; if ( info->params.data_bits != 8 ) - RegValue |= BIT4+BIT3+BIT2; + RegValue |= BIT4 | BIT3 | BIT2; if ( info->params.parity != ASYNC_PARITY_NONE ) { RegValue |= BIT5; @@ -6126,7 +6129,7 @@ static void usc_loopback_frame( struct mgsl_struct *info ) /* WAIT FOR RECEIVE COMPLETE */ for (i=0 ; i<1000 ; i++) - if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) + if (usc_InReg( info, RCSR ) & (BIT8 | BIT4 | BIT3 | BIT1)) break; /* clear Internal Data loopback mode */ @@ -6233,8 +6236,8 @@ static void usc_get_serial_signals( struct mgsl_struct *info ) { u16 status; - /* clear all serial signals except DTR and RTS */ - info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + /* clear all serial signals except RTS and DTR */ + info->serial_signals &= SerialSignal_RTS | SerialSignal_DTR; /* Read the Misc Interrupt status Register (MISR) to get */ /* the V24 status signals. */ @@ -6259,7 +6262,7 @@ static void usc_get_serial_signals( struct mgsl_struct *info ) /* usc_set_serial_signals() * - * Set the state of DTR and RTS based on contents of + * Set the state of RTS and DTR based on contents of * serial_signals member of device extension. * * Arguments: info pointer to device instance data @@ -6576,8 +6579,8 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info) status = info->rx_buffer_list[EndIndex].status; - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN | + RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) { if ( status & RXSTATUS_SHORT_FRAME ) info->icount.rxshort++; else if ( status & RXSTATUS_ABORT ) @@ -6759,8 +6762,8 @@ static bool mgsl_get_raw_rx_frame(struct mgsl_struct *info) status = info->rx_buffer_list[CurrentIndex].status; - if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + - RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & (RXSTATUS_SHORT_FRAME | RXSTATUS_OVERRUN | + RXSTATUS_CRC_ERROR | RXSTATUS_ABORT) ) { if ( status & RXSTATUS_SHORT_FRAME ) info->icount.rxshort++; else if ( status & RXSTATUS_ABORT ) @@ -6896,7 +6899,7 @@ static void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, /* set CMR:13 to start transmit when * next GoAhead (abort) is received */ - info->cmr_value |= BIT13; + info->cmr_value |= BIT13; } /* begin loading the frame in the next available tx dma @@ -7275,7 +7278,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) spin_unlock_irqrestore(&info->irq_spinlock,flags); - + /******************************/ /* WAIT FOR TRANSMIT COMPLETE */ /******************************/ @@ -7289,7 +7292,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) status = usc_InReg( info, TCSR ); spin_unlock_irqrestore(&info->irq_spinlock,flags); - while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { + while ( !(status & (BIT6 | BIT5 | BIT4 | BIT2 | BIT1)) ) { if (time_after(jiffies, EndTime)) { rc = false; break; @@ -7304,7 +7307,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) if ( rc ){ /* CHECK FOR TRANSMIT ERRORS */ - if ( status & (BIT5 + BIT1) ) + if ( status & (BIT5 | BIT1) ) rc = false; } @@ -7330,7 +7333,7 @@ static bool mgsl_dma_test( struct mgsl_struct *info ) /* CHECK FOR RECEIVE ERRORS */ status = info->rx_buffer_list[0].status; - if ( status & (BIT8 + BIT3 + BIT1) ) { + if ( status & (BIT8 | BIT3 | BIT1) ) { /* receive error has occurred */ rc = false; } else { @@ -7602,7 +7605,7 @@ static void usc_loopmode_send_done( struct mgsl_struct * info ) { info->loopmode_send_done_requested = false; /* clear CMR:13 to 0 to start echoing RxData to TxData */ - info->cmr_value &= ~BIT13; + info->cmr_value &= ~BIT13; usc_OutReg(info, CMR, info->cmr_value); } @@ -7773,8 +7776,8 @@ static int hdlcdev_open(struct net_device *dev) return rc; } - /* assert DTR and RTS, apply hardware settings */ - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + /* assert RTS and DTR, apply hardware settings */ + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; mgsl_program_hw(info); /* enable network layer transmit */ @@ -7863,6 +7866,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + memset(&new_line, 0, sizeof(new_line)); switch (flags){ case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index aba1e59f4a8..c359a91f734 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -317,8 +317,7 @@ struct slgt_info { unsigned char *tx_buf; int tx_count; - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; + char *flag_buf; bool drop_rts_on_tx_done; struct _input_signal_events input_signal_events; @@ -675,15 +674,15 @@ static int open(struct tty_struct *tty, struct file *filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; } mutex_lock(&info->port.mutex); - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); if (info->netcount) { @@ -786,7 +785,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && !(tty->termios.c_cflag & CBAUD)) { - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); @@ -1561,8 +1560,8 @@ static int hdlcdev_open(struct net_device *dev) return rc; } - /* assert DTR and RTS, apply hardware settings */ - info->signals |= SerialSignal_RTS + SerialSignal_DTR; + /* assert RTS and DTR, apply hardware settings */ + info->signals |= SerialSignal_RTS | SerialSignal_DTR; program_hw(info); /* enable network layer transmit */ @@ -1855,7 +1854,6 @@ static void hdlcdev_exit(struct slgt_info *info) */ static void rx_async(struct slgt_info *info) { - struct tty_struct *tty = info->port.tty; struct mgsl_icount *icount = &info->icount; unsigned int start, end; unsigned char *p; @@ -1894,10 +1892,8 @@ static void rx_async(struct slgt_info *info) else if (status & BIT0) stat = TTY_FRAME; } - if (tty) { - tty_insert_flip_char(tty, ch, stat); - chars++; - } + tty_insert_flip_char(&info->port, ch, stat); + chars++; } if (i < count) { @@ -1918,8 +1914,8 @@ static void rx_async(struct slgt_info *info) break; } - if (tty && chars) - tty_flip_buffer_push(tty); + if (chars) + tty_flip_buffer_push(&info->port); } /* @@ -1961,8 +1957,6 @@ static void bh_handler(struct work_struct *work) struct slgt_info *info = container_of(work, struct slgt_info, task); int action; - if (!info) - return; info->bh_running = true; while((action = bh_action(info))) { @@ -2183,7 +2177,7 @@ static void isr_serial(struct slgt_info *info) if (info->port.tty) { if (!(status & info->ignore_status_mask)) { if (info->read_status_mask & MASK_BREAK) { - tty_insert_flip_char(info->port.tty, 0, TTY_BREAK); + tty_insert_flip_char(&info->port, 0, TTY_BREAK); if (info->port.flags & ASYNC_SAK) do_SAK(info->port.tty); } @@ -2494,7 +2488,7 @@ static void shutdown(struct slgt_info *info) slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { - info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); } @@ -2554,12 +2548,12 @@ static void change_params(struct slgt_info *info) cflag = info->port.tty->termios.c_cflag; - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ + /* if B0 rate (hangup) specified then negate RTS and DTR */ + /* otherwise assert RTS and DTR */ if (cflag & CBAUD) - info->signals |= SerialSignal_RTS + SerialSignal_DTR; + info->signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); /* byte size and parity */ @@ -3262,9 +3256,9 @@ static void dtr_rts(struct tty_port *port, int on) spin_lock_irqsave(&info->lock,flags); if (on) - info->signals |= SerialSignal_RTS + SerialSignal_DTR; + info->signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } @@ -3314,7 +3308,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if ((tty->termios.c_cflag & CBAUD)) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3355,11 +3349,24 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, return retval; } +/* + * allocate buffers used for calling line discipline receive_buf + * directly in synchronous mode + * note: add 5 bytes to max frame size to allow appending + * 32-bit CRC and status byte when configured to do so + */ static int alloc_tmp_rbuf(struct slgt_info *info) { info->tmp_rbuf = kmalloc(info->max_frame_size + 5, GFP_KERNEL); if (info->tmp_rbuf == NULL) return -ENOMEM; + /* unused flag buffer to satisfy receive_buf calling interface */ + info->flag_buf = kzalloc(info->max_frame_size + 5, GFP_KERNEL); + if (!info->flag_buf) { + kfree(info->tmp_rbuf); + info->tmp_rbuf = NULL; + return -ENOMEM; + } return 0; } @@ -3367,6 +3374,8 @@ static void free_tmp_rbuf(struct slgt_info *info) { kfree(info->tmp_rbuf); info->tmp_rbuf = NULL; + kfree(info->flag_buf); + info->flag_buf = NULL; } /* @@ -4110,7 +4119,7 @@ static void reset_port(struct slgt_info *info) tx_stop(info); rx_stop(info); - info->signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); slgt_irq_off(info, IRQ_ALL | IRQ_MASTER); @@ -4537,8 +4546,8 @@ static void get_signals(struct slgt_info *info) { unsigned short status = rd_reg16(info, SSR); - /* clear all serial signals except DTR and RTS */ - info->signals &= SerialSignal_DTR + SerialSignal_RTS; + /* clear all serial signals except RTS and DTR */ + info->signals &= SerialSignal_RTS | SerialSignal_DTR; if (status & BIT3) info->signals |= SerialSignal_DSR; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index fd43fb6f7ce..53ba8537de8 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -262,8 +262,7 @@ typedef struct _synclinkmp_info { bool sca_statctrl_requested; u32 misc_ctrl_value; - char flag_buf[MAX_ASYNC_BUFFER_SIZE]; - char char_buf[MAX_ASYNC_BUFFER_SIZE]; + char *flag_buf; bool drop_rts_on_tx_done; struct _input_signal_events input_signal_events; @@ -755,14 +754,14 @@ static int open(struct tty_struct *tty, struct file *filp) /* If port is closing, signal caller to try again */ if (tty_hung_up_p(filp) || info->port.flags & ASYNC_CLOSING){ - if (info->port.flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->port.close_wait); + wait_event_interruptible_tty(tty, info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); goto cleanup; } - info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); if (info->netcount) { @@ -883,7 +882,7 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && !(tty->termios.c_cflag & CBAUD)) { - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); @@ -1677,8 +1676,8 @@ static int hdlcdev_open(struct net_device *dev) return rc; } - /* assert DTR and RTS, apply hardware settings */ - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + /* assert RTS and DTR, apply hardware settings */ + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; program_hw(info); /* enable network layer transmit */ @@ -1767,6 +1766,7 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) HDLC_FLAG_TXC_TXCPIN | HDLC_FLAG_TXC_DPLL | HDLC_FLAG_TXC_BRG | HDLC_FLAG_TXC_RXCPIN); + memset(&new_line, 0, sizeof(new_line)); switch (flags){ case (HDLC_FLAG_RXC_RXCPIN | HDLC_FLAG_TXC_TXCPIN): new_line.clock_type = CLOCK_EXT; break; case (HDLC_FLAG_RXC_BRG | HDLC_FLAG_TXC_BRG): new_line.clock_type = CLOCK_INT; break; @@ -2008,9 +2008,6 @@ static void bh_handler(struct work_struct *work) SLMP_INFO *info = container_of(work, SLMP_INFO, task); int action; - if (!info) - return; - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):%s bh_handler() entry\n", __FILE__,__LINE__,info->device_name); @@ -2132,13 +2129,11 @@ static void isr_rxint(SLMP_INFO * info) /* process break detection if tty control * is not set to ignore it */ - if ( tty ) { - if (!(status & info->ignore_status_mask1)) { - if (info->read_status_mask1 & BRKD) { - tty_insert_flip_char(tty, 0, TTY_BREAK); - if (info->port.flags & ASYNC_SAK) - do_SAK(tty); - } + if (!(status & info->ignore_status_mask1)) { + if (info->read_status_mask1 & BRKD) { + tty_insert_flip_char(&info->port, 0, TTY_BREAK); + if (tty && (info->port.flags & ASYNC_SAK)) + do_SAK(tty); } } } @@ -2170,7 +2165,6 @@ static void isr_rxrdy(SLMP_INFO * info) { u16 status; unsigned char DataByte; - struct tty_struct *tty = info->port.tty; struct mgsl_icount *icount = &info->icount; if ( debug_level >= DEBUG_LEVEL_ISR ) @@ -2203,26 +2197,22 @@ static void isr_rxrdy(SLMP_INFO * info) status &= info->read_status_mask2; - if ( tty ) { - if (status & PE) - flag = TTY_PARITY; - else if (status & FRME) - flag = TTY_FRAME; - if (status & OVRN) { - /* Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - over = true; - } + if (status & PE) + flag = TTY_PARITY; + else if (status & FRME) + flag = TTY_FRAME; + if (status & OVRN) { + /* Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + over = true; } } /* end of if (error) */ - if ( tty ) { - tty_insert_flip_char(tty, DataByte, flag); - if (over) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } + tty_insert_flip_char(&info->port, DataByte, flag); + if (over) + tty_insert_flip_char(&info->port, 0, TTY_OVERRUN); } if ( debug_level >= DEBUG_LEVEL_ISR ) { @@ -2232,8 +2222,7 @@ static void isr_rxrdy(SLMP_INFO * info) icount->frame,icount->overrun); } - if ( tty ) - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&info->port); } static void isr_txeom(SLMP_INFO * info, unsigned char status) @@ -2718,7 +2707,7 @@ static void shutdown(SLMP_INFO * info) reset_port(info); if (!info->port.tty || info->port.tty->termios.c_cflag & HUPCL) { - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); } @@ -2780,12 +2769,12 @@ static void change_params(SLMP_INFO *info) cflag = info->port.tty->termios.c_cflag; - /* if B0 rate (hangup) specified then negate DTR and RTS */ - /* otherwise assert DTR and RTS */ + /* if B0 rate (hangup) specified then negate RTS and DTR */ + /* otherwise assert RTS and DTR */ if (cflag & CBAUD) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); /* byte size and parity */ @@ -3224,12 +3213,12 @@ static int tiocmget(struct tty_struct *tty) get_signals(info); spin_unlock_irqrestore(&info->lock,flags); - result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + - ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + - ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + - ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + - ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + - ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); + result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS : 0) | + ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR : 0) | + ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR : 0) | + ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG : 0) | + ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR : 0) | + ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS : 0); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s tiocmget() value=%08X\n", @@ -3284,9 +3273,9 @@ static void dtr_rts(struct tty_port *port, int on) spin_lock_irqsave(&info->lock,flags); if (on) - info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + info->serial_signals |= SerialSignal_RTS | SerialSignal_DTR; else - info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } @@ -3341,7 +3330,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, port->blocked_open++; while (1) { - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); @@ -3490,7 +3479,7 @@ static int alloc_buf_list(SLMP_INFO *info) for ( i = 0; i < info->rx_buf_count; i++ ) { /* calculate and store physical address of this buffer entry */ info->rx_buf_list_ex[i].phys_entry = - info->buffer_list_phys + (i * sizeof(SCABUFSIZE)); + info->buffer_list_phys + (i * SCABUFSIZE); /* calculate and store physical address of */ /* next entry in cirular list of entries */ @@ -3553,6 +3542,13 @@ static int alloc_tmp_rx_buf(SLMP_INFO *info) info->tmp_rx_buf = kmalloc(info->max_frame_size, GFP_KERNEL); if (info->tmp_rx_buf == NULL) return -ENOMEM; + /* unused flag buffer to satisfy receive_buf calling interface */ + info->flag_buf = kzalloc(info->max_frame_size, GFP_KERNEL); + if (!info->flag_buf) { + kfree(info->tmp_rx_buf); + info->tmp_rx_buf = NULL; + return -ENOMEM; + } return 0; } @@ -3560,6 +3556,8 @@ static void free_tmp_rx_buf(SLMP_INFO *info) { kfree(info->tmp_rx_buf); info->tmp_rx_buf = NULL; + kfree(info->flag_buf); + info->flag_buf = NULL; } static int claim_resources(SLMP_INFO *info) @@ -4357,7 +4355,7 @@ static void reset_port(SLMP_INFO *info) tx_stop(info); rx_stop(info); - info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + info->serial_signals &= ~(SerialSignal_RTS | SerialSignal_DTR); set_signals(info); /* disable all port interrupts */ @@ -4753,8 +4751,8 @@ static void get_signals(SLMP_INFO *info) u16 gpstatus = read_status_reg(info); u16 testbit; - /* clear all serial signals except DTR and RTS */ - info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + /* clear all serial signals except RTS and DTR */ + info->serial_signals &= SerialSignal_RTS | SerialSignal_DTR; /* set serial signal bits to reflect MISR */ @@ -4773,7 +4771,7 @@ static void get_signals(SLMP_INFO *info) info->serial_signals |= SerialSignal_DSR; } -/* Set the state of DTR and RTS based on contents of +/* Set the state of RTS and DTR based on contents of * serial_signals member of device context. */ static void set_signals(SLMP_INFO *info) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index b3c4a250ff8..454b65898e2 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -15,6 +15,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/sched.h> +#include <linux/sched/rt.h> #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/fs.h> @@ -41,14 +42,22 @@ #include <linux/slab.h> #include <linux/input.h> #include <linux/uaccess.h> +#include <linux/moduleparam.h> +#include <linux/jiffies.h> +#include <linux/syscalls.h> +#include <linux/of.h> +#include <linux/rcupdate.h> #include <asm/ptrace.h> #include <asm/irq_regs.h> /* Whether we react on sysrq keys or just ignore them */ -static int __read_mostly sysrq_enabled = SYSRQ_DEFAULT_ENABLE; +static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; +unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; +int sysrq_reset_downtime_ms __weak; + static bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; @@ -80,7 +89,7 @@ static void sysrq_handle_loglevel(int key) int i; i = key - '0'; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk("Loglevel set to %d\n", i); console_loglevel = i; } @@ -99,7 +108,7 @@ static void sysrq_handle_SAK(int key) } static struct sysrq_key_op sysrq_SAK_op = { .handler = sysrq_handle_SAK, - .help_msg = "saK", + .help_msg = "sak(k)", .action_msg = "SAK", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -115,7 +124,7 @@ static void sysrq_handle_unraw(int key) static struct sysrq_key_op sysrq_unraw_op = { .handler = sysrq_handle_unraw, - .help_msg = "unRaw", + .help_msg = "unraw(r)", .action_msg = "Keyboard mode set to system default", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; @@ -133,7 +142,7 @@ static void sysrq_handle_crash(int key) } static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, - .help_msg = "Crash", + .help_msg = "crash(c)", .action_msg = "Trigger a crash", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -146,7 +155,7 @@ static void sysrq_handle_reboot(int key) } static struct sysrq_key_op sysrq_reboot_op = { .handler = sysrq_handle_reboot, - .help_msg = "reBoot", + .help_msg = "reboot(b)", .action_msg = "Resetting", .enable_mask = SYSRQ_ENABLE_BOOT, }; @@ -157,7 +166,7 @@ static void sysrq_handle_sync(int key) } static struct sysrq_key_op sysrq_sync_op = { .handler = sysrq_handle_sync, - .help_msg = "Sync", + .help_msg = "sync(s)", .action_msg = "Emergency Sync", .enable_mask = SYSRQ_ENABLE_SYNC, }; @@ -169,7 +178,7 @@ static void sysrq_handle_show_timers(int key) static struct sysrq_key_op sysrq_show_timers_op = { .handler = sysrq_handle_show_timers, - .help_msg = "show-all-timers(Q)", + .help_msg = "show-all-timers(q)", .action_msg = "Show clockevent devices & pending hrtimers (no others)", }; @@ -179,7 +188,7 @@ static void sysrq_handle_mountro(int key) } static struct sysrq_key_op sysrq_mountro_op = { .handler = sysrq_handle_mountro, - .help_msg = "Unmount", + .help_msg = "unmount(u)", .action_msg = "Emergency Remount R/O", .enable_mask = SYSRQ_ENABLE_REMOUNT, }; @@ -192,7 +201,7 @@ static void sysrq_handle_showlocks(int key) static struct sysrq_key_op sysrq_showlocks_op = { .handler = sysrq_handle_showlocks, - .help_msg = "show-all-locks(D)", + .help_msg = "show-all-locks(d)", .action_msg = "Show Locks Held", }; #else @@ -243,7 +252,7 @@ static void sysrq_handle_showallcpus(int key) static struct sysrq_key_op sysrq_showallcpus_op = { .handler = sysrq_handle_showallcpus, - .help_msg = "show-backtrace-all-active-cpus(L)", + .help_msg = "show-backtrace-all-active-cpus(l)", .action_msg = "Show backtrace of all active CPUs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -258,7 +267,7 @@ static void sysrq_handle_showregs(int key) } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, - .help_msg = "show-registers(P)", + .help_msg = "show-registers(p)", .action_msg = "Show Regs", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -269,7 +278,7 @@ static void sysrq_handle_showstate(int key) } static struct sysrq_key_op sysrq_showstate_op = { .handler = sysrq_handle_showstate, - .help_msg = "show-task-states(T)", + .help_msg = "show-task-states(t)", .action_msg = "Show State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -280,7 +289,7 @@ static void sysrq_handle_showstate_blocked(int key) } static struct sysrq_key_op sysrq_showstate_blocked_op = { .handler = sysrq_handle_showstate_blocked, - .help_msg = "show-blocked-tasks(W)", + .help_msg = "show-blocked-tasks(w)", .action_msg = "Show Blocked State", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -294,7 +303,7 @@ static void sysrq_ftrace_dump(int key) } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, - .help_msg = "dump-ftrace-buffer(Z)", + .help_msg = "dump-ftrace-buffer(z)", .action_msg = "Dump ftrace buffer", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -308,7 +317,7 @@ static void sysrq_handle_showmem(int key) } static struct sysrq_key_op sysrq_showmem_op = { .handler = sysrq_handle_showmem, - .help_msg = "show-memory-usage(M)", + .help_msg = "show-memory-usage(m)", .action_msg = "Show Memory", .enable_mask = SYSRQ_ENABLE_DUMP, }; @@ -335,11 +344,11 @@ static void send_sig_all(int sig) static void sysrq_handle_term(int key) { send_sig_all(SIGTERM); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_term_op = { .handler = sysrq_handle_term, - .help_msg = "terminate-all-tasks(E)", + .help_msg = "terminate-all-tasks(e)", .action_msg = "Terminate All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -358,7 +367,7 @@ static void sysrq_handle_moom(int key) } static struct sysrq_key_op sysrq_moom_op = { .handler = sysrq_handle_moom, - .help_msg = "memory-full-oom-kill(F)", + .help_msg = "memory-full-oom-kill(f)", .action_msg = "Manual OOM execution", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -370,7 +379,7 @@ static void sysrq_handle_thaw(int key) } static struct sysrq_key_op sysrq_thaw_op = { .handler = sysrq_handle_thaw, - .help_msg = "thaw-filesystems(J)", + .help_msg = "thaw-filesystems(j)", .action_msg = "Emergency Thaw of all frozen filesystems", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -379,11 +388,11 @@ static struct sysrq_key_op sysrq_thaw_op = { static void sysrq_handle_kill(int key) { send_sig_all(SIGKILL); - console_loglevel = 8; + console_loglevel = CONSOLE_LOGLEVEL_DEBUG; } static struct sysrq_key_op sysrq_kill_op = { .handler = sysrq_handle_kill, - .help_msg = "kill-all-tasks(I)", + .help_msg = "kill-all-tasks(i)", .action_msg = "Kill All Tasks", .enable_mask = SYSRQ_ENABLE_SIGNAL, }; @@ -394,7 +403,7 @@ static void sysrq_handle_unrt(int key) } static struct sysrq_key_op sysrq_unrt_op = { .handler = sysrq_handle_unrt, - .help_msg = "nice-all-RT-tasks(N)", + .help_msg = "nice-all-RT-tasks(n)", .action_msg = "Nice All RT Tasks", .enable_mask = SYSRQ_ENABLE_RTNICE, }; @@ -502,9 +511,9 @@ void __handle_sysrq(int key, bool check_mask) struct sysrq_key_op *op_p; int orig_log_level; int i; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + rcu_sysrq_start(); + rcu_read_lock(); /* * Raise the apparent loglevel to maximum so that the sysrq header * is shown to provide the user with positive feedback. We do not @@ -512,7 +521,7 @@ void __handle_sysrq(int key, bool check_mask) * routing in the consumers of /proc/kmsg. */ orig_log_level = console_loglevel; - console_loglevel = 7; + console_loglevel = CONSOLE_LOGLEVEL_DEFAULT; printk(KERN_INFO "SysRq : "); op_p = __sysrq_get_key_op(key); @@ -546,7 +555,8 @@ void __handle_sysrq(int key, bool check_mask) printk("\n"); console_loglevel = orig_log_level; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + rcu_read_unlock(); + rcu_sysrq_end(); } void handle_sysrq(int key) @@ -577,8 +587,137 @@ struct sysrq_state { bool active; bool need_reinject; bool reinjecting; + + /* reset sequence handling */ + bool reset_canceled; + bool reset_requested; + unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; + int reset_seq_len; + int reset_seq_cnt; + int reset_seq_version; + struct timer_list keyreset_timer; }; +#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ +static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX]; +static unsigned int sysrq_reset_seq_len; +static unsigned int sysrq_reset_seq_version = 1; + +static void sysrq_parse_reset_sequence(struct sysrq_state *state) +{ + int i; + unsigned short key; + + state->reset_seq_cnt = 0; + + for (i = 0; i < sysrq_reset_seq_len; i++) { + key = sysrq_reset_seq[i]; + + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + __set_bit(key, state->reset_keybit); + state->reset_seq_len++; + + if (test_bit(key, state->key_down)) + state->reset_seq_cnt++; + } + + /* Disable reset until old keys are not released */ + state->reset_canceled = state->reset_seq_cnt != 0; + + state->reset_seq_version = sysrq_reset_seq_version; +} + +static void sysrq_do_reset(unsigned long _state) +{ + struct sysrq_state *state = (struct sysrq_state *) _state; + + state->reset_requested = true; + + sys_sync(); + kernel_restart(NULL); +} + +static void sysrq_handle_reset_request(struct sysrq_state *state) +{ + if (state->reset_requested) + __handle_sysrq(sysrq_xlate[KEY_B], false); + + if (sysrq_reset_downtime_ms) + mod_timer(&state->keyreset_timer, + jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); + else + sysrq_do_reset((unsigned long)state); +} + +static void sysrq_detect_reset_sequence(struct sysrq_state *state, + unsigned int code, int value) +{ + if (!test_bit(code, state->reset_keybit)) { + /* + * Pressing any key _not_ in reset sequence cancels + * the reset sequence. Also cancelling the timer in + * case additional keys were pressed after a reset + * has been requested. + */ + if (value && state->reset_seq_cnt) { + state->reset_canceled = true; + del_timer(&state->keyreset_timer); + } + } else if (value == 0) { + /* + * Key release - all keys in the reset sequence need + * to be pressed and held for the reset timeout + * to hold. + */ + del_timer(&state->keyreset_timer); + + if (--state->reset_seq_cnt == 0) + state->reset_canceled = false; + } else if (value == 1) { + /* key press, not autorepeat */ + if (++state->reset_seq_cnt == state->reset_seq_len && + !state->reset_canceled) { + sysrq_handle_reset_request(state); + } + } +} + +#ifdef CONFIG_OF +static void sysrq_of_get_keyreset_config(void) +{ + u32 key; + struct device_node *np; + struct property *prop; + const __be32 *p; + + np = of_find_node_by_path("/chosen/linux,sysrq-reset-seq"); + if (!np) { + pr_debug("No sysrq node found"); + return; + } + + /* Reset in case a __weak definition was present */ + sysrq_reset_seq_len = 0; + + of_property_for_each_u32(np, "keyset", prop, p, key) { + if (key == KEY_RESERVED || key > KEY_MAX || + sysrq_reset_seq_len == SYSRQ_KEY_RESET_MAX) + break; + + sysrq_reset_seq[sysrq_reset_seq_len++] = (unsigned short)key; + } + + /* Get reset timeout if any. */ + of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms); +} +#else +static void sysrq_of_get_keyreset_config(void) +{ +} +#endif + static void sysrq_reinject_alt_sysrq(struct work_struct *work) { struct sysrq_state *sysrq = @@ -605,11 +744,102 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work) } } +static bool sysrq_handle_keypress(struct sysrq_state *sysrq, + unsigned int code, int value) +{ + bool was_active = sysrq->active; + bool suppress; + + switch (code) { + + case KEY_LEFTALT: + case KEY_RIGHTALT: + if (!value) { + /* One of ALTs is being released */ + if (sysrq->active && code == sysrq->alt_use) + sysrq->active = false; + + sysrq->alt = KEY_RESERVED; + + } else if (value != 2) { + sysrq->alt = code; + sysrq->need_reinject = false; + } + break; + + case KEY_SYSRQ: + if (value == 1 && sysrq->alt != KEY_RESERVED) { + sysrq->active = true; + sysrq->alt_use = sysrq->alt; + /* + * If nothing else will be pressed we'll need + * to re-inject Alt-SysRq keysroke. + */ + sysrq->need_reinject = true; + } + + /* + * Pretend that sysrq was never pressed at all. This + * is needed to properly handle KGDB which will try + * to release all keys after exiting debugger. If we + * do not clear key bit it KGDB will end up sending + * release events for Alt and SysRq, potentially + * triggering print screen function. + */ + if (sysrq->active) + clear_bit(KEY_SYSRQ, sysrq->handle.dev->key); + + break; + + default: + if (sysrq->active && value && value != 2) { + sysrq->need_reinject = false; + __handle_sysrq(sysrq_xlate[code], true); + } + break; + } + + suppress = sysrq->active; + + if (!sysrq->active) { + + /* + * See if reset sequence has changed since the last time. + */ + if (sysrq->reset_seq_version != sysrq_reset_seq_version) + sysrq_parse_reset_sequence(sysrq); + + /* + * If we are not suppressing key presses keep track of + * keyboard state so we can release keys that have been + * pressed before entering SysRq mode. + */ + if (value) + set_bit(code, sysrq->key_down); + else + clear_bit(code, sysrq->key_down); + + if (was_active) + schedule_work(&sysrq->reinject_work); + + /* Check for reset sequence */ + sysrq_detect_reset_sequence(sysrq, code, value); + + } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { + /* + * Pass on release events for keys that was pressed before + * entering SysRq mode. + */ + suppress = false; + } + + return suppress; +} + static bool sysrq_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct sysrq_state *sysrq = handle->private; - bool was_active = sysrq->active; bool suppress; /* @@ -626,79 +856,7 @@ static bool sysrq_filter(struct input_handle *handle, break; case EV_KEY: - switch (code) { - - case KEY_LEFTALT: - case KEY_RIGHTALT: - if (!value) { - /* One of ALTs is being released */ - if (sysrq->active && code == sysrq->alt_use) - sysrq->active = false; - - sysrq->alt = KEY_RESERVED; - - } else if (value != 2) { - sysrq->alt = code; - sysrq->need_reinject = false; - } - break; - - case KEY_SYSRQ: - if (value == 1 && sysrq->alt != KEY_RESERVED) { - sysrq->active = true; - sysrq->alt_use = sysrq->alt; - /* - * If nothing else will be pressed we'll need - * to re-inject Alt-SysRq keysroke. - */ - sysrq->need_reinject = true; - } - - /* - * Pretend that sysrq was never pressed at all. This - * is needed to properly handle KGDB which will try - * to release all keys after exiting debugger. If we - * do not clear key bit it KGDB will end up sending - * release events for Alt and SysRq, potentially - * triggering print screen function. - */ - if (sysrq->active) - clear_bit(KEY_SYSRQ, handle->dev->key); - - break; - - default: - if (sysrq->active && value && value != 2) { - sysrq->need_reinject = false; - __handle_sysrq(sysrq_xlate[code], true); - } - break; - } - - suppress = sysrq->active; - - if (!sysrq->active) { - /* - * If we are not suppressing key presses keep track of - * keyboard state so we can release keys that have been - * pressed before entering SysRq mode. - */ - if (value) - set_bit(code, sysrq->key_down); - else - clear_bit(code, sysrq->key_down); - - if (was_active) - schedule_work(&sysrq->reinject_work); - - } else if (value == 0 && - test_and_clear_bit(code, sysrq->key_down)) { - /* - * Pass on release events for keys that was pressed before - * entering SysRq mode. - */ - suppress = false; - } + suppress = sysrq_handle_keypress(sysrq, code, value); break; default: @@ -726,6 +884,8 @@ static int sysrq_connect(struct input_handler *handler, sysrq->handle.handler = handler; sysrq->handle.name = "sysrq"; sysrq->handle.private = sysrq; + setup_timer(&sysrq->keyreset_timer, + sysrq_do_reset, (unsigned long)sysrq); error = input_register_handle(&sysrq->handle); if (error) { @@ -755,6 +915,7 @@ static void sysrq_disconnect(struct input_handle *handle) input_close_device(handle); cancel_work_sync(&sysrq->reinject_work); + del_timer_sync(&sysrq->keyreset_timer); input_unregister_handle(handle); kfree(sysrq); } @@ -786,7 +947,24 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { + unsigned short key; int error; + int i; + + /* First check if a __weak interface was instantiated. */ + for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { + key = platform_sysrq_reset_seq[i]; + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + sysrq_reset_seq[sysrq_reset_seq_len++] = key; + } + + /* + * DT configuration takes precedence over anything that would + * have been defined via the __weak interface. + */ + sysrq_of_get_keyreset_config(); error = input_register_handler(&sysrq_handler); if (error) @@ -803,6 +981,38 @@ static inline void sysrq_unregister_handler(void) } } +static int sysrq_reset_seq_param_set(const char *buffer, + const struct kernel_param *kp) +{ + unsigned long val; + int error; + + error = kstrtoul(buffer, 0, &val); + if (error < 0) + return error; + + if (val > KEY_MAX) + return -EINVAL; + + *((unsigned short *)kp->arg) = val; + sysrq_reset_seq_version++; + + return 0; +} + +static struct kernel_param_ops param_ops_sysrq_reset_seq = { + .get = param_get_ushort, + .set = sysrq_reset_seq_param_set, +}; + +#define param_check_sysrq_reset_seq(name, p) \ + __param_check(name, p, unsigned short) + +module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, + &sysrq_reset_seq_len, 0644); + +module_param_named(sysrq_downtime_ms, sysrq_reset_downtime_ms, int, 0644); + #else static inline void sysrq_register_handler(void) @@ -835,16 +1045,23 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, struct sysrq_key_op *remove_op_p) { int retval; - unsigned long flags; - spin_lock_irqsave(&sysrq_key_table_lock, flags); + spin_lock(&sysrq_key_table_lock); if (__sysrq_get_key_op(key) == remove_op_p) { __sysrq_put_key_op(key, insert_op_p); retval = 0; } else { retval = -1; } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); + spin_unlock(&sysrq_key_table_lock); + + /* + * A concurrent __handle_sysrq either got the old op or the new op. + * Wait for it to go away before returning, so the code for an old + * op is not freed (eg. on module unload) while it is in use. + */ + synchronize_rcu(); + return retval; } diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index 6953dc82850..90ca082935f 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -60,24 +60,23 @@ static void tty_audit_buf_put(struct tty_audit_buf *buf) tty_audit_buf_free(buf); } -static void tty_audit_log(const char *description, struct task_struct *tsk, - kuid_t loginuid, unsigned sessionid, int major, - int minor, unsigned char *data, size_t size) +static void tty_audit_log(const char *description, int major, int minor, + unsigned char *data, size_t size) { struct audit_buffer *ab; + struct task_struct *tsk = current; + pid_t pid = task_pid_nr(tsk); + uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); + uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); + unsigned int sessionid = audit_get_sessionid(tsk); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); if (ab) { char name[sizeof(tsk->comm)]; - kuid_t uid = task_uid(tsk); - - audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " - "major=%d minor=%d comm=", description, - tsk->pid, - from_kuid(&init_user_ns, uid), - from_kuid(&init_user_ns, loginuid), - sessionid, - major, minor); + + audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" + " minor=%d comm=", description, pid, uid, + loginuid, sessionid, major, minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); @@ -90,11 +89,9 @@ static void tty_audit_log(const char *description, struct task_struct *tsk, * tty_audit_buf_push - Push buffered data out * * Generate an audit message from the contents of @buf, which is owned by - * @tsk with @loginuid. @buf->mutex must be locked. + * the current task. @buf->mutex must be locked. */ -static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, - unsigned int sessionid, - struct tty_audit_buf *buf) +static void tty_audit_buf_push(struct tty_audit_buf *buf) { if (buf->valid == 0) return; @@ -102,25 +99,11 @@ static void tty_audit_buf_push(struct task_struct *tsk, kuid_t loginuid, buf->valid = 0; return; } - tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor, - buf->data, buf->valid); + tty_audit_log("tty", buf->major, buf->minor, buf->data, buf->valid); buf->valid = 0; } /** - * tty_audit_buf_push_current - Push buffered data out - * - * Generate an audit message from the contents of @buf, which is owned by - * the current task. @buf->mutex must be locked. - */ -static void tty_audit_buf_push_current(struct tty_audit_buf *buf) -{ - kuid_t auid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - tty_audit_buf_push(current, auid, sessionid, buf); -} - -/** * tty_audit_exit - Handle a task exit * * Make sure all buffered data is written out and deallocate the buffer. @@ -130,15 +113,13 @@ void tty_audit_exit(void) { struct tty_audit_buf *buf; - spin_lock_irq(¤t->sighand->siglock); buf = current->signal->tty_audit_buf; current->signal->tty_audit_buf = NULL; - spin_unlock_irq(¤t->sighand->siglock); if (!buf) return; mutex_lock(&buf->mutex); - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -151,9 +132,8 @@ void tty_audit_exit(void) */ void tty_audit_fork(struct signal_struct *sig) { - spin_lock_irq(¤t->sighand->siglock); sig->audit_tty = current->signal->audit_tty; - spin_unlock_irq(¤t->sighand->siglock); + sig->audit_tty_log_passwd = current->signal->audit_tty_log_passwd; } /** @@ -163,20 +143,21 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) { struct tty_audit_buf *buf; int major, minor, should_audit; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); should_audit = current->signal->audit_tty; buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); major = tty->driver->major; minor = tty->driver->minor_start + tty->index; if (buf) { mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } @@ -187,24 +168,20 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) auid = audit_get_loginuid(current); sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major, - minor, &ch, 1); + tty_audit_log("ioctl=TIOCSTI", major, minor, &ch, 1); } } /** - * tty_audit_push_task - Flush task's pending audit data - * @tsk: task pointer - * @loginuid: sender login uid - * @sessionid: sender session id + * tty_audit_push_current - Flush current's pending audit data * - * Called with a ref on @tsk held. Try to lock sighand and get a - * reference to the tty audit buffer if available. + * Try to lock sighand and get a reference to the tty audit buffer if available. * Flush the buffer or return an appropriate error code. */ -int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) +int tty_audit_push_current(void) { struct tty_audit_buf *buf = ERR_PTR(-EPERM); + struct task_struct *tsk = current; unsigned long flags; if (!lock_task_sighand(tsk, &flags)) @@ -225,7 +202,7 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) return PTR_ERR(buf); mutex_lock(&buf->mutex); - tty_audit_buf_push(tsk, loginuid, sessionid, buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -243,10 +220,11 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, unsigned icanon) { struct tty_audit_buf *buf, *buf2; + unsigned long flags; buf = NULL; buf2 = NULL; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) goto out; buf = current->signal->tty_audit_buf; @@ -254,7 +232,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); goto out; } - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, @@ -264,7 +242,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, return NULL; } - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (!current->signal->audit_tty) goto out; buf = current->signal->tty_audit_buf; @@ -276,7 +254,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, atomic_inc(&buf->count); /* Fall through */ out: - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf2) tty_audit_buf_free(buf2); return buf; @@ -292,10 +270,18 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, { struct tty_audit_buf *buf; int major, minor; + int audit_log_tty_passwd; + unsigned long flags; if (unlikely(size == 0)) return; + spin_lock_irqsave(¤t->sighand->siglock, flags); + audit_log_tty_passwd = current->signal->audit_tty_log_passwd; + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + if (!audit_log_tty_passwd && icanon && !L_ECHO(tty)) + return; + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) return; @@ -309,7 +295,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor || buf->icanon != icanon) { - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); buf->major = major; buf->minor = minor; buf->icanon = icanon; @@ -325,7 +311,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, data += run; size -= run; if (buf->valid == N_TTY_BUF_SIZE) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); } while (size != 0); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); @@ -339,16 +325,17 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, void tty_audit_push(struct tty_struct *tty) { struct tty_audit_buf *buf; + unsigned long flags; - spin_lock_irq(¤t->sighand->siglock); + spin_lock_irqsave(¤t->sighand->siglock, flags); if (likely(!current->signal->audit_tty)) { - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); return; } buf = current->signal->tty_audit_buf; if (buf) atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); if (buf) { int major, minor; @@ -357,7 +344,7 @@ void tty_audit_push(struct tty_struct *tty) minor = tty->driver->minor_start + tty->index; mutex_lock(&buf->mutex); if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); + tty_audit_buf_push(buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 45d916198f7..143deb62467 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -11,11 +11,99 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/wait.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/ratelimit.h> + + +#define MIN_TTYB_SIZE 256 +#define TTYB_ALIGN_MASK 255 + +/* + * Byte threshold to limit memory consumption for flip buffers. + * The actual memory limit is > 2x this amount. + */ +#define TTYB_DEFAULT_MEM_LIMIT 65536 + +/* + * We default to dicing tty buffer allocations to this many characters + * in order to avoid multiple page allocations. We know the size of + * tty_buffer itself but it must also be taken into account that the + * the buffer is 256 byte aligned. See tty_buffer_find for the allocation + * logic this must match + */ + +#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) + + +/** + * tty_buffer_lock_exclusive - gain exclusive access to buffer + * tty_buffer_unlock_exclusive - release exclusive access + * + * @port - tty_port owning the flip buffer + * + * Guarantees safe use of the line discipline's receive_buf() method by + * excluding the buffer work and any pending flush from using the flip + * buffer. Data can continue to be added concurrently to the flip buffer + * from the driver side. + * + * On release, the buffer work is restarted if there is data in the + * flip buffer + */ + +void tty_buffer_lock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + + atomic_inc(&buf->priority); + mutex_lock(&buf->lock); +} +EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive); + +void tty_buffer_unlock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + int restart; + + restart = buf->head->commit != buf->head->read; + + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); + if (restart) + queue_work(system_unbound_wq, &buf->work); +} +EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); + +/** + * tty_buffer_space_avail - return unused buffer space + * @port - tty_port owning the flip buffer + * + * Returns the # of bytes which can be written by the driver without + * reaching the buffer limit. + * + * Note: this does not guarantee that memory is available to write + * the returned # of bytes (use tty_prepare_flip_string_xxx() to + * pre-allocate if memory guarantee is required). + */ + +int tty_buffer_space_avail(struct tty_port *port) +{ + int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used); + return max(space, 0); +} +EXPORT_SYMBOL_GPL(tty_buffer_space_avail); + +static void tty_buffer_reset(struct tty_buffer *p, size_t size) +{ + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; + p->flags = 0; +} /** * tty_buffer_free_all - free buffers used by a tty @@ -23,25 +111,28 @@ * * Remove all the buffers pending on a tty whether queued with data * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none */ void tty_buffer_free_all(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; + struct tty_buffer *p, *next; + struct llist_node *llist; - while ((thead = buf->head) != NULL) { - buf->head = thead->next; - kfree(thead); - } - while ((thead = buf->free) != NULL) { - buf->free = thead->next; - kfree(thead); + while ((p = buf->head) != NULL) { + buf->head = p->next; + if (p->size > 0) + kfree(p); } - buf->tail = NULL; - buf->memory_used = 0; + llist = llist_del_all(&buf->free); + llist_for_each_entry_safe(p, next, llist, free) + kfree(p); + + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; + + atomic_set(&buf->mem_used, 0); } /** @@ -50,29 +141,39 @@ void tty_buffer_free_all(struct tty_port *port) * @size: desired size (characters) * * Allocate a new tty buffer to hold the desired number of characters. + * We round our buffers off in 256 character chunks to get better + * allocation behaviour. * Return NULL if out of memory or the allocation would exceed the * per device queue - * - * Locking: Caller must hold tty->buf.lock */ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { + struct llist_node *free; struct tty_buffer *p; - if (port->buf.memory_used + size > 65536) + /* Round the buffer size out */ + size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); + + if (size <= MIN_TTYB_SIZE) { + free = llist_del_first(&port->buf.free); + if (free) { + p = llist_entry(free, struct tty_buffer, free); + goto found; + } + } + + /* Should possibly check if this fails for the largest buffer we + have queued and recycle that ? */ + if (atomic_read(&port->buf.mem_used) > port->buf.mem_limit) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - port->buf.memory_used += size; + +found: + tty_buffer_reset(p, size); + atomic_add(size, &port->buf.mem_used); return p; } @@ -83,8 +184,6 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) * * Free a tty buffer, or add it to the free list according to our * internal strategy - * - * Locking: Caller must hold tty->buf.lock */ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) @@ -92,206 +191,121 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ - buf->memory_used -= b->size; - WARN_ON(buf->memory_used < 0); + WARN_ON(atomic_sub_return(b->size, &buf->mem_used) < 0); - if (b->size >= 512) + if (b->size > MIN_TTYB_SIZE) kfree(b); - else { - b->next = buf->free; - buf->free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_port *port) -{ - struct tty_bufhead *buf = &port->buf; - struct tty_buffer *thead; - - while ((thead = buf->head) != NULL) { - buf->head = thead->next; - tty_buffer_free(port, thead); - } - buf->tail = NULL; + else if (b->size > 0) + llist_add(&b->free, &buf->free); } /** * tty_buffer_flush - flush full tty buffers * @tty: tty to flush * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function + * flush all the buffers containing receive data. * - * Locking: none + * Locking: takes buffer lock to ensure single-threaded flip buffer + * 'consumer' */ void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - unsigned long flags; - - spin_lock_irqsave(&buf->lock, flags); - - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTYP_FLUSHING, &port->iflags)) { - set_bit(TTYP_FLUSHPENDING, &port->iflags); - spin_unlock_irqrestore(&buf->lock, flags); - wait_event(tty->read_wait, - test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); - return; - } else - __tty_buffer_flush(port); - spin_unlock_irqrestore(&buf->lock, flags); -} + struct tty_buffer *next; -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ + atomic_inc(&buf->priority); -static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) -{ - struct tty_buffer **tbh = &port->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - port->buf.memory_used += t->size; - return t; - } - tbh = &((*tbh)->next); + mutex_lock(&buf->lock); + while ((next = buf->head->next) != NULL) { + tty_buffer_free(port, buf->head); + buf->head = next; } - /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(port, size); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ + buf->head->read = buf->head->commit; + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); } + /** - * __tty_buffer_request_room - grow tty buffer if needed + * tty_buffer_request_room - grow tty buffer if needed * @tty: tty structure * @size: size desired + * @flags: buffer flags if new buffer allocated (default = 0) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * Locking: Caller must hold port->buf.lock + * + * Will change over to a new buffer if the current buffer is encoded as + * TTY_NORMAL (so has no flags buffer) and the new buffer requires + * a flags buffer. */ -static int __tty_buffer_request_room(struct tty_port *port, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size, + int flags) { struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; - int left; - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to - remove this conditional if its worth it. This would be invisible - to the callers */ + int left, change; + b = buf->tail; - if (b != NULL) - left = b->size - b->used; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; else - left = 0; + left = b->size - b->used; - if (left < size) { + change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL); + if (change || left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(port, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - buf->head = n; + if ((n = tty_buffer_alloc(port, size)) != NULL) { + n->flags = flags; buf->tail = n; - } else + b->commit = b->used; + /* paired w/ barrier in flush_to_ldisc(); ensures the + * latest commit value can be read before the head is + * advanced to the next buffer + */ + smp_wmb(); + b->next = n; + } else if (change) + size = 0; + else size = left; } - return size; } - -/** - * tty_buffer_request_room - grow tty buffer if needed - * @tty: tty structure - * @size: size desired - * - * Make at least size bytes of linear space available for the tty - * buffer. If we fail return the size we managed to find. - * - * Locking: Takes port->buf.lock - */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) +int tty_buffer_request_room(struct tty_port *port, size_t size) { - struct tty_port *port = tty->port; - unsigned long flags; - int length; - - spin_lock_irqsave(&port->buf.lock, flags); - length = __tty_buffer_request_room(port, size); - spin_unlock_irqrestore(&port->buf.lock, flags); - return length; + return __tty_buffer_request_room(port, size, 0); } EXPORT_SYMBOL_GPL(tty_buffer_request_room); /** * tty_insert_flip_string_fixed_flag - Add characters to the tty buffer - * @tty: tty structure + * @port: tty port * @chars: characters * @flag: flag value for each character * @size: size * * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. - * - * Locking: Called functions may take port->buf.lock */ -int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, +int tty_insert_flip_string_fixed_flag(struct tty_port *port, const unsigned char *chars, char flag, size_t size) { - struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space; - unsigned long flags; - struct tty_buffer *tb; - - spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty->port, goal); - tb = buf->tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { - spin_unlock_irqrestore(&buf->lock, flags); + int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0; + int space = __tty_buffer_request_room(port, goal, flags); + struct tty_buffer *tb = port->buf.tail; + if (unlikely(space == 0)) break; - } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, flag, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + if (~tb->flags & TTYB_NORMAL) + memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; - spin_unlock_irqrestore(&buf->lock, flags); copied += space; chars += space; /* There is a small chance that we need to split the data over @@ -303,7 +317,7 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); /** * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure + * @port: tty port * @chars: characters * @flags: flag bytes * @size: size @@ -311,33 +325,21 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * Queue a series of bytes to the tty buffering. For each character * the flags array indicates the status of the character. Returns the * number added. - * - * Locking: Called functions may take port->buf.lock */ -int tty_insert_flip_string_flags(struct tty_struct *tty, +int tty_insert_flip_string_flags(struct tty_port *port, const unsigned char *chars, const char *flags, size_t size) { - struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space; - unsigned long __flags; - struct tty_buffer *tb; - - spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty->port, goal); - tb = buf->tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { - spin_unlock_irqrestore(&buf->lock, __flags); + int space = tty_buffer_request_room(port, goal); + struct tty_buffer *tb = port->buf.tail; + if (unlikely(space == 0)) break; - } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; - spin_unlock_irqrestore(&buf->lock, __flags); copied += space; chars += space; flags += space; @@ -350,34 +352,25 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); /** * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from + * @port: tty port to push from * * Takes any pending buffers and transfers their ownership to the * ldisc side of the queue. It then schedules those characters for * processing by the line discipline. - * Note that this function can only be used when the low_latency flag - * is unset. Otherwise the workqueue won't be flushed. - * - * Locking: Takes port->buf.lock */ -void tty_schedule_flip(struct tty_struct *tty) +void tty_schedule_flip(struct tty_port *port) { - struct tty_bufhead *buf = &tty->port->buf; - unsigned long flags; - WARN_ON(tty->low_latency); - - spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); + struct tty_bufhead *buf = &port->buf; + + buf->tail->commit = buf->tail->used; schedule_work(&buf->work); } EXPORT_SYMBOL(tty_schedule_flip); /** * tty_prepare_flip_string - make room for characters - * @tty: tty + * @port: tty port * @chars: return pointer for character write area * @size: desired size * @@ -386,71 +379,44 @@ EXPORT_SYMBOL(tty_schedule_flip); * accounted for as ready for normal characters. This is used for drivers * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking port->buf.lock */ -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, +int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, size_t size) { - struct tty_bufhead *buf = &tty->port->buf; - int space; - unsigned long flags; - struct tty_buffer *tb; - - spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty->port, size); - - tb = buf->tail; + int space = __tty_buffer_request_room(port, size, TTYB_NORMAL); if (likely(space)) { - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + struct tty_buffer *tb = port->buf.tail; + *chars = char_buf_ptr(tb, tb->used); + if (~tb->flags & TTYB_NORMAL) + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); tb->used += space; } - spin_unlock_irqrestore(&buf->lock, flags); return space; } EXPORT_SYMBOL_GPL(tty_prepare_flip_string); -/** - * tty_prepare_flip_string_flags - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking port->buf.lock - */ -int tty_prepare_flip_string_flags(struct tty_struct *tty, - unsigned char **chars, char **flags, size_t size) +static int +receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { - struct tty_bufhead *buf = &tty->port->buf; - int space; - unsigned long __flags; - struct tty_buffer *tb; + struct tty_ldisc *disc = tty->ldisc; + unsigned char *p = char_buf_ptr(head, head->read); + char *f = NULL; - spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty->port, size); + if (~head->flags & TTYB_NORMAL) + f = flag_buf_ptr(head, head->read); - tb = buf->tail; - if (likely(space)) { - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; - tb->used += space; + if (disc->ops->receive_buf2) + count = disc->ops->receive_buf2(tty, p, f, count); + else { + count = min_t(int, count, tty->receive_room); + if (count) + disc->ops->receive_buf(tty, p, f, count); } - spin_unlock_irqrestore(&buf->lock, __flags); - return space; + head->read += count; + return count; } -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - - /** * flush_to_ldisc @@ -459,9 +425,10 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); * This routine is called out of the software interrupt to flush data * from the buffer chain to the line discipline. * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. + * The receive_buf method is single threaded for each tty instance. + * + * Locking: takes buffer lock to ensure single-threaded flip buffer + * 'consumer' */ static void flush_to_ldisc(struct work_struct *work) @@ -469,62 +436,48 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_port *port = container_of(work, struct tty_port, buf.work); struct tty_bufhead *buf = &port->buf; struct tty_struct *tty; - unsigned long flags; struct tty_ldisc *disc; tty = port->itty; - if (WARN_RATELIMIT(tty == NULL, "tty is NULL\n")) + if (tty == NULL) return; disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ + if (disc == NULL) return; - spin_lock_irqsave(&buf->lock, flags); - - if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - struct tty_buffer *head; - while ((head = buf->head) != NULL) { - int count; - char *char_buf; - unsigned char *flag_buf; - - count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - buf->head = head->next; - tty_buffer_free(port, head); - continue; - } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) - break; - if (!tty->receive_room) + mutex_lock(&buf->lock); + + while (1) { + struct tty_buffer *head = buf->head; + struct tty_buffer *next; + int count; + + /* Ldisc or user is trying to gain exclusive access */ + if (atomic_read(&buf->priority)) + break; + + next = head->next; + /* paired w/ barrier in __tty_buffer_request_room(); + * ensures commit value read is not stale if the head + * is advancing to the next buffer + */ + smp_rmb(); + count = head->commit - head->read; + if (!count) { + if (next == NULL) break; - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&buf->lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&buf->lock, flags); + buf->head = next; + tty_buffer_free(port, head); + continue; } - clear_bit(TTYP_FLUSHING, &port->iflags); - } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); + count = receive_buf(tty, head, count); + if (!count) + break; } - spin_unlock_irqrestore(&buf->lock, flags); + + mutex_unlock(&buf->lock); tty_ldisc_deref(disc); } @@ -539,37 +492,23 @@ static void flush_to_ldisc(struct work_struct *work) */ void tty_flush_to_ldisc(struct tty_struct *tty) { - if (!tty->low_latency) - flush_work(&tty->port->buf.work); + flush_work(&tty->port->buf.work); } /** * tty_flip_buffer_push - terminal - * @tty: tty to push + * @port: tty port to push * - * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. + * Queue a push of the terminal flip buffers to the line discipline. + * Can be called from IRQ/atomic context. * * In the event of the queue being busy for flipping the work will be * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. */ -void tty_flip_buffer_push(struct tty_struct *tty) +void tty_flip_buffer_push(struct tty_port *port) { - struct tty_bufhead *buf = &tty->port->buf; - unsigned long flags; - - spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; - spin_unlock_irqrestore(&buf->lock, flags); - - if (tty->low_latency) - flush_to_ldisc(&buf->work); - else - schedule_work(&buf->work); + tty_schedule_flip(port); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -579,19 +518,36 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * * Set up the initial state of the buffer management for a tty device. * Must be called before the other tty buffer functions are used. - * - * Locking: none */ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - spin_lock_init(&buf->lock); - buf->head = NULL; - buf->tail = NULL; - buf->free = NULL; - buf->memory_used = 0; + mutex_init(&buf->lock); + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; + init_llist_head(&buf->free); + atomic_set(&buf->mem_used, 0); + atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); + buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT; } +/** + * tty_buffer_set_limit - change the tty buffer memory limit + * @port: tty port to change + * + * Change the tty buffer memory limit. + * Must be called before the other tty buffer functions are used. + */ + +int tty_buffer_set_limit(struct tty_port *port, int limit) +{ + if (limit < MIN_TTYB_SIZE) + return -EINVAL; + port->buf.mem_limit = limit; + return 0; +} +EXPORT_SYMBOL_GPL(tty_buffer_set_limit); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index da9fde85075..34110719fe0 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -533,10 +533,64 @@ void tty_wakeup(struct tty_struct *tty) EXPORT_SYMBOL_GPL(tty_wakeup); /** + * tty_signal_session_leader - sends SIGHUP to session leader + * @tty controlling tty + * @exit_session if non-zero, signal all foreground group processes + * + * Send SIGHUP and SIGCONT to the session leader and its process group. + * Optionally, signal all processes in the foreground process group. + * + * Returns the number of processes in the session with this tty + * as their controlling terminal. This value is used to drop + * tty references for those processes. + */ +static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) +{ + struct task_struct *p; + int refs = 0; + struct pid *tty_pgrp = NULL; + + read_lock(&tasklist_lock); + if (tty->session) { + do_each_pid_task(tty->session, PIDTYPE_SID, p) { + spin_lock_irq(&p->sighand->siglock); + if (p->signal->tty == tty) { + p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } + if (!p->signal->leader) { + spin_unlock_irq(&p->sighand->siglock); + continue; + } + __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); + __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); + put_pid(p->signal->tty_old_pgrp); /* A noop */ + spin_lock(&tty->ctrl_lock); + tty_pgrp = get_pid(tty->pgrp); + if (tty->pgrp) + p->signal->tty_old_pgrp = get_pid(tty->pgrp); + spin_unlock(&tty->ctrl_lock); + spin_unlock_irq(&p->sighand->siglock); + } while_each_pid_task(tty->session, PIDTYPE_SID, p); + } + read_unlock(&tasklist_lock); + + if (tty_pgrp) { + if (exit_session) + kill_pgrp(tty_pgrp, SIGHUP, exit_session); + put_pid(tty_pgrp); + } + + return refs; +} + +/** * __tty_hangup - actual handler for hangup events * @work: tty device * - * This can be called by the "eventd" kernel thread. That is process + * This can be called by a "kworker" kernel thread. That is process * synchronous but doesn't hold any locks, so we need to make sure we * have the appropriate locks for what we're doing. * @@ -549,20 +603,18 @@ EXPORT_SYMBOL_GPL(tty_wakeup); * BTM * redirect lock for undoing redirection * file list lock for manipulating list of ttys - * tty_ldisc_lock from called functions - * termios_mutex resetting termios data + * tty_ldiscs_lock from called functions + * termios_rwsem resetting termios data * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ -static void __tty_hangup(struct tty_struct *tty) +static void __tty_hangup(struct tty_struct *tty, int exit_session) { struct file *cons_filp = NULL; struct file *filp, *f = NULL; - struct task_struct *p; struct tty_file_private *priv; int closecount = 0, n; - unsigned long flags; - int refs = 0; + int refs; if (!tty) return; @@ -577,6 +629,11 @@ static void __tty_hangup(struct tty_struct *tty) tty_lock(tty); + if (test_bit(TTY_HUPPED, &tty->flags)) { + tty_unlock(tty); + return; + } + /* some functions below drop BTM, so we need this bit */ set_bit(TTY_HUPPING, &tty->flags); @@ -599,52 +656,26 @@ static void __tty_hangup(struct tty_struct *tty) } spin_unlock(&tty_files_lock); + refs = tty_signal_session_leader(tty, exit_session); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * it drops BTM and thus races with reopen * we protect the race by TTY_HUPPING */ tty_ldisc_hangup(tty); - read_lock(&tasklist_lock); - if (tty->session) { - do_each_pid_task(tty->session, PIDTYPE_SID, p) { - spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) { - p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ - refs++; - } - if (!p->signal->leader) { - spin_unlock_irq(&p->sighand->siglock); - continue; - } - __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->pgrp) - p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - spin_unlock_irq(&p->sighand->siglock); - } while_each_pid_task(tty->session, PIDTYPE_SID, p); - } - read_unlock(&tasklist_lock); - - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); - clear_bit(TTY_PUSH, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); put_pid(tty->session); put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - /* Account for the p->signal references we killed */ - while (refs--) - tty_kref_put(tty); + spin_unlock_irq(&tty->ctrl_lock); /* * If one of the devices matches a console pointer, we @@ -666,7 +697,6 @@ static void __tty_hangup(struct tty_struct *tty) */ set_bit(TTY_HUPPED, &tty->flags); clear_bit(TTY_HUPPING, &tty->flags); - tty_ldisc_enable(tty); tty_unlock(tty); @@ -679,7 +709,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_struct *tty = container_of(work, struct tty_struct, hangup_work); - __tty_hangup(tty); + __tty_hangup(tty, 0); } /** @@ -717,7 +747,7 @@ void tty_vhangup(struct tty_struct *tty) printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); #endif - __tty_hangup(tty); + __tty_hangup(tty, 0); } EXPORT_SYMBOL(tty_vhangup); @@ -741,6 +771,27 @@ void tty_vhangup_self(void) } /** + * tty_vhangup_session - hangup session leader exit + * @tty: tty to hangup + * + * The session leader is exiting and hanging up its controlling terminal. + * Every process in the foreground process group is signalled SIGHUP. + * + * We do this synchronously so that when the syscall returns the process + * is complete. That guarantee is necessary for security reasons. + */ + +static void tty_vhangup_session(struct tty_struct *tty) +{ +#ifdef TTY_DEBUG_HANGUP + char buf[64]; + + printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf)); +#endif + __tty_hangup(tty, 1); +} + +/** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty * @@ -797,18 +848,19 @@ void disassociate_ctty(int on_exit) tty = get_current_tty(); if (tty) { - struct pid *tty_pgrp = get_pid(tty->pgrp); - if (on_exit) { - if (tty->driver->type != TTY_DRIVER_TYPE_PTY) - tty_vhangup(tty); + if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) { + tty_vhangup_session(tty); + } else { + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); + if (!on_exit) + kill_pgrp(tty_pgrp, SIGCONT, on_exit); + put_pid(tty_pgrp); + } } tty_kref_put(tty); - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); - } + } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -826,9 +878,8 @@ void disassociate_ctty(int on_exit) spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); current->signal->tty_old_pgrp = NULL; - spin_unlock_irq(¤t->sighand->siglock); - tty = get_current_tty(); + tty = tty_kref_get(current->signal->tty); if (tty) { unsigned long flags; spin_lock_irqsave(&tty->ctrl_lock, flags); @@ -845,6 +896,7 @@ void disassociate_ctty(int on_exit) #endif } + spin_unlock_irq(¤t->sighand->siglock); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); session_clear_tty(task_session(current)); @@ -941,6 +993,14 @@ void start_tty(struct tty_struct *tty) EXPORT_SYMBOL(start_tty); +/* We limit tty time update visibility to every 8 seconds or so. */ +static void tty_update_time(struct timespec *time) +{ + unsigned long sec = get_seconds() & ~7; + if ((long)(sec - time->tv_sec) > 0) + time->tv_sec = sec; +} + /** * tty_read - read method for tty device files * @file: pointer to tty file @@ -960,7 +1020,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int i; - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; @@ -977,8 +1037,10 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, else i = -EIO; tty_ldisc_deref(ld); + if (i > 0) - inode->i_atime = current_fs_time(inode->i_sb); + tty_update_time(&inode->i_atime); + return i; } @@ -1080,8 +1142,7 @@ static inline ssize_t do_tty_write( cond_resched(); } if (written) { - struct inode *inode = file->f_path.dentry->d_inode; - inode->i_mtime = current_fs_time(inode->i_sb); + tty_update_time(&file_inode(file)->i_mtime); ret = written; } out: @@ -1136,12 +1197,11 @@ void tty_write_message(struct tty_struct *tty, char *msg) static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct inode *inode = file->f_path.dentry->d_inode; struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; ssize_t ret; - if (tty_paranoia_check(tty, inode, "tty_write")) + if (tty_paranoia_check(tty, file_inode(file), "tty_write")) return -EIO; if (!tty || !tty->ops->write || (test_bit(TTY_IO_ERROR, &tty->flags))) @@ -1211,12 +1271,13 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) * * Locking: None */ -static void tty_line_name(struct tty_driver *driver, int index, char *p) +static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p) { if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE) - strcpy(p, driver->name); + return sprintf(p, "%s", driver->name); else - sprintf(p, "%s%d", driver->name, index + driver->name_base); + return sprintf(p, "%s%d", driver->name, + index + driver->name_base); } /** @@ -1333,8 +1394,7 @@ static int tty_reopen(struct tty_struct *tty) struct tty_driver *driver = tty->driver; if (test_bit(TTY_CLOSING, &tty->flags) || - test_bit(TTY_HUPPING, &tty->flags) || - test_bit(TTY_LDISC_CHANGING, &tty->flags)) + test_bit(TTY_HUPPING, &tty->flags)) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && @@ -1350,9 +1410,7 @@ static int tty_reopen(struct tty_struct *tty) } tty->count++; - mutex_lock(&tty->ldisc_mutex); - WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); - mutex_unlock(&tty->ldisc_mutex); + WARN_ON(!tty->ldisc); return 0; } @@ -1469,6 +1527,17 @@ void tty_free_termios(struct tty_struct *tty) } EXPORT_SYMBOL(tty_free_termios); +/** + * tty_flush_works - flush all works of a tty + * @tty: tty device to flush works for + * + * Sync flush all works belonging to @tty. + */ +static void tty_flush_works(struct tty_struct *tty) +{ + flush_work(&tty->SAK_work); + flush_work(&tty->hangup_work); +} /** * release_one_tty - release tty structure memory @@ -1554,6 +1623,9 @@ static void release_tty(struct tty_struct *tty, int idx) tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); tty->port->itty = NULL; + if (tty->link) + tty->link->port->itty = NULL; + cancel_work_sync(&tty->port->buf.work); if (tty->link) tty_kref_put(tty->link); @@ -1783,12 +1855,21 @@ int tty_release(struct inode *inode, struct file *filp) return 0; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); + printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf)); #endif /* * Ask the line discipline code to release its structures */ tty_ldisc_release(tty, o_tty); + + /* Wait for pending work before tty destruction commmences */ + tty_flush_works(tty); + if (o_tty) + tty_flush_works(o_tty); + +#ifdef TTY_DEBUG_HANGUP + printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf)); +#endif /* * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. The tty_unlock_pair @@ -2006,6 +2087,7 @@ retry_open: filp->f_op = &tty_fops; goto retry_open; } + clear_bit(TTY_HUPPED, &tty->flags); tty_unlock(tty); @@ -2051,7 +2133,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) struct tty_ldisc *ld; int ret = 0; - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll")) + if (tty_paranoia_check(tty, file_inode(filp), "tty_poll")) return 0; ld = tty_ldisc_ref_wait(tty); @@ -2064,21 +2146,28 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) static int __tty_fasync(int fd, struct file *filp, int on) { struct tty_struct *tty = file_tty(filp); + struct tty_ldisc *ldisc; unsigned long flags; int retval = 0; - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync")) + if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync")) goto out; retval = fasync_helper(fd, filp, on, &tty->fasync); if (retval <= 0) goto out; + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + if (ldisc->ops->fasync) + ldisc->ops->fasync(tty, on); + tty_ldisc_deref(ldisc); + } + if (on) { enum pid_type type; struct pid *pid; - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = 1; + spin_lock_irqsave(&tty->ctrl_lock, flags); if (tty->pgrp) { pid = tty->pgrp; @@ -2091,13 +2180,7 @@ static int __tty_fasync(int fd, struct file *filp, int on) spin_unlock_irqrestore(&tty->ctrl_lock, flags); retval = __f_setown(filp, pid, type, 0); put_pid(pid); - if (retval) - goto out; - } else { - if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; } - retval = 0; out: return retval; } @@ -2125,7 +2208,7 @@ static int tty_fasync(int fd, struct file *filp, int on) * FIXME: does not honour flow control ?? * * Locking: - * Called functions take tty_ldisc_lock + * Called functions take tty_ldiscs_lock * current->signal->tty check is safe without locks * * FIXME: may race normal receive processing @@ -2154,7 +2237,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_mutex is taken to ensure the winsize data + * Locking: tty->winsize_mutex is taken to ensure the winsize data * is consistent. */ @@ -2162,9 +2245,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) { int err; - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return err ? -EFAULT: 0; } @@ -2185,7 +2268,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) unsigned long flags; /* Lock the tty */ - mutex_lock(&tty->termios_mutex); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can @@ -2200,9 +2283,10 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; done: - mutex_unlock(&tty->termios_mutex); + mutex_unlock(&tty->winsize_mutex); return 0; } +EXPORT_SYMBOL(tty_do_resize); /** * tiocswinsz - implement window size set ioctl @@ -2640,9 +2724,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) void __user *p = (void __user *)arg; int retval; struct tty_ldisc *ld; - struct inode *inode = file->f_dentry->d_inode; - if (tty_paranoia_check(tty, inode, "tty_ioctl")) + if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl")) return -EINVAL; real_tty = tty_pair_get_tty(tty); @@ -2783,12 +2866,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static long tty_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct inode *inode = file->f_dentry->d_inode; struct tty_struct *tty = file_tty(file); struct tty_ldisc *ld; int retval = -ENOIOCTLCMD; - if (tty_paranoia_check(tty, inode, "tty_ioctl")) + if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl")) return -EINVAL; if (tty->ops->compat_ioctl) { @@ -2906,9 +2988,9 @@ void do_SAK(struct tty_struct *tty) EXPORT_SYMBOL(do_SAK); -static int dev_match_devt(struct device *dev, void *data) +static int dev_match_devt(struct device *dev, const void *data) { - dev_t *devt = data; + const dev_t *devt = data; return dev->devt == *devt; } @@ -2940,8 +3022,10 @@ void initialize_tty_struct(struct tty_struct *tty, tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); - mutex_init(&tty->termios_mutex); - mutex_init(&tty->ldisc_mutex); + mutex_init(&tty->throttle_mutex); + init_rwsem(&tty->termios_rwsem); + mutex_init(&tty->winsize_mutex); + init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); @@ -3462,9 +3546,19 @@ static ssize_t show_cons_active(struct device *dev, if (i >= ARRAY_SIZE(cs)) break; } - while (i--) - count += sprintf(buf + count, "%s%d%c", - cs[i]->name, cs[i]->index, i ? ' ':'\n'); + while (i--) { + int index = cs[i]->index; + struct tty_driver *drv = cs[i]->device(cs[i], &index); + + /* don't resolve tty0 as some programs depend on it */ + if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR)) + count += tty_line_name(drv, index, buf + count); + else + count += sprintf(buf + count, "%s%d", + cs[i]->name, cs[i]->index); + + count += sprintf(buf + count, "%c", i ? ' ':'\n'); + } console_unlock(); return count; diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 8481b29d5b3..6fd60fece6b 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -94,19 +94,20 @@ EXPORT_SYMBOL(tty_driver_flush_buffer); * @tty: terminal * * Indicate that a tty should stop transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. */ void tty_throttle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); /* check TTY_THROTTLED first so it indicates our state */ if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); - mutex_unlock(&tty->termios_mutex); + tty->flow_change = 0; + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_throttle); @@ -115,7 +116,7 @@ EXPORT_SYMBOL(tty_throttle); * @tty: terminal * * Indicate that a tty may continue transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. * @@ -125,15 +126,78 @@ EXPORT_SYMBOL(tty_throttle); void tty_unthrottle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); - mutex_unlock(&tty->termios_mutex); + tty->flow_change = 0; + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_unthrottle); /** + * tty_throttle_safe - flow control + * @tty: terminal + * + * Similar to tty_throttle() but will only attempt throttle + * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + * throttle due to race conditions when throttling is conditional + * on factors evaluated prior to throttling. + * + * Returns 0 if tty is throttled (or was already throttled) + */ + +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->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +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->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + +/** * tty_wait_until_sent - wait for I/O to finish * @tty: tty we are waiting for * @timeout: how long we will wait @@ -404,7 +468,7 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); * @obad: output baud rate * * Update the current termios data for the tty with the new speed - * settings. The caller must hold the termios_mutex for the tty in + * settings. The caller must hold the termios_rwsem for the tty in * question. */ @@ -415,34 +479,6 @@ void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) EXPORT_SYMBOL_GPL(tty_encode_baud_rate); /** - * tty_get_baud_rate - get tty bit rates - * @tty: tty to query - * - * Returns the baud rate as an integer for this terminal. The - * termios lock must be held by the caller and the terminal bit - * flags may be updated. - * - * Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ - speed_t baud = tty_termios_baud_rate(&tty->termios); - - if (baud == 38400 && tty->alt_speed) { - if (!tty->warned) { - printk(KERN_WARNING "Use of setserial/setrocket to " - "set SPD_* flags is deprecated\n"); - tty->warned = 1; - } - baud = tty->alt_speed; - } - - return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - -/** * tty_termios_copy_hw - copy hardware settings * @new: New termios * @old: Old termios @@ -492,7 +528,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_mutex + * Locking: termios_rwsem */ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) @@ -508,7 +544,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old_termios = tty->termios; tty->termios = *new_termios; unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); @@ -550,7 +586,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } EXPORT_SYMBOL_GPL(tty_set_termios); @@ -565,7 +601,7 @@ EXPORT_SYMBOL_GPL(tty_set_termios); * functions before using tty_set_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_mutex locks + * Called functions take ldisc and termios_rwsem locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -577,9 +613,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) if (retval) return retval; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp_termios = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); if (opt & TERMIOS_TERMIO) { if (user_termio_to_kernel_termios(&tmp_termios, @@ -617,7 +653,7 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) if (opt & TERMIOS_WAIT) { tty_wait_until_sent(tty, 0); if (signal_pending(current)) - return -EINTR; + return -ERESTARTSYS; } tty_set_termios(tty, &tmp_termios); @@ -631,16 +667,16 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios_locked; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static int get_termio(struct tty_struct *tty, struct termio __user *termio) @@ -684,13 +720,13 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) if (opt & TERMIOS_WAIT) { tty_wait_until_sent(tty, 0); if (signal_pending(current)) - return -EINTR; + return -ERESTARTSYS; } - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (tty->ops->set_termiox) tty->ops->set_termiox(tty, &tnew); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } @@ -725,13 +761,13 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) { struct sgttyb tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.sg_ispeed = tty->termios.c_ispeed; tmp.sg_ospeed = tty->termios.c_ospeed; tmp.sg_erase = tty->termios.c_cc[VERASE]; tmp.sg_kill = tty->termios.c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -770,7 +806,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_mutex + * Locking: termios_rwsem */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -786,7 +822,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); termios = tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; @@ -796,7 +832,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) tty_termios_encode_baud_rate(&termios, termios.c_ispeed, termios.c_ospeed); #endif - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); tty_set_termios(tty, &termios); return 0; } @@ -807,14 +843,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) { struct tchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_intrc = tty->termios.c_cc[VINTR]; tmp.t_quitc = tty->termios.c_cc[VQUIT]; tmp.t_startc = tty->termios.c_cc[VSTART]; tmp.t_stopc = tty->termios.c_cc[VSTOP]; tmp.t_eofc = tty->termios.c_cc[VEOF]; tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -824,14 +860,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) if (copy_from_user(&tmp, tchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VINTR] = tmp.t_intrc; tty->termios.c_cc[VQUIT] = tmp.t_quitc; tty->termios.c_cc[VSTART] = tmp.t_startc; tty->termios.c_cc[VSTOP] = tmp.t_stopc; tty->termios.c_cc[VEOF] = tmp.t_eofc; tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -841,7 +877,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) { struct ltchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_suspc = tty->termios.c_cc[VSUSP]; /* what is dsuspc anyway? */ tmp.t_dsuspc = tty->termios.c_cc[VSUSP]; @@ -850,7 +886,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tmp.t_flushc = tty->termios.c_cc[VEOL2]; tmp.t_werasc = tty->termios.c_cc[VWERASE]; tmp.t_lnextc = tty->termios.c_cc[VLNEXT]; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -861,7 +897,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) if (copy_from_user(&tmp, ltchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */ tty->termios.c_cc[VEOL2] = tmp.t_dsuspc; @@ -870,7 +906,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tty->termios.c_cc[VEOL2] = tmp.t_flushc; tty->termios.c_cc[VWERASE] = tmp.t_werasc; tty->termios.c_cc[VLNEXT] = tmp.t_lnextc; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -910,7 +946,7 @@ static int send_prio_char(struct tty_struct *tty, char ch) * @arg: enable/disable CLOCAL * * Perform a change to the CLOCAL state and call into the driver - * layer to make it visible. All done with the termios mutex + * layer to make it visible. All done with the termios rwsem */ static int tty_change_softcar(struct tty_struct *tty, int arg) @@ -919,7 +955,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) int bit = arg ? CLOCAL : 0; struct ktermios old; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old = tty->termios; tty->termios.c_cflag &= ~CLOCAL; tty->termios.c_cflag |= bit; @@ -927,7 +963,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) tty->ops->set_termios(tty, &old); if ((tty->termios.c_cflag & CLOCAL) != bit) ret = -EINVAL; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return ret; } @@ -1030,9 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return 0; #else case TIOCGLCKTRMIOS: @@ -1047,9 +1083,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios_1(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return ret; #endif #ifdef TCGETX @@ -1057,9 +1093,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, struct termiox ktermx; if (real_tty->termiox == NULL) return -EINVAL; - mutex_lock(&real_tty->termios_mutex); + down_read(&real_tty->termios_rwsem); memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); - mutex_unlock(&real_tty->termios_mutex); + up_read(&real_tty->termios_rwsem); if (copy_to_user(p, &ktermx, sizeof(struct termiox))) ret = -EFAULT; return ret; @@ -1086,33 +1122,47 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, } EXPORT_SYMBOL_GPL(tty_mode_ioctl); -int tty_perform_flush(struct tty_struct *tty, unsigned long arg) + +/* Caller guarantees ldisc reference is held */ +static int __tty_perform_flush(struct tty_struct *tty, unsigned long arg) { - struct tty_ldisc *ld; - int retval = tty_check_change(tty); - if (retval) - return retval; + struct tty_ldisc *ld = tty->ldisc; - ld = tty_ldisc_ref_wait(tty); switch (arg) { case TCIFLUSH: - if (ld && ld->ops->flush_buffer) + if (ld && ld->ops->flush_buffer) { ld->ops->flush_buffer(tty); + tty_unthrottle(tty); + } break; case TCIOFLUSH: - if (ld && ld->ops->flush_buffer) + if (ld && ld->ops->flush_buffer) { ld->ops->flush_buffer(tty); + tty_unthrottle(tty); + } /* fall through */ case TCOFLUSH: tty_driver_flush_buffer(tty); break; default: - tty_ldisc_deref(ld); return -EINVAL; } - tty_ldisc_deref(ld); return 0; } + +int tty_perform_flush(struct tty_struct *tty, unsigned long arg) +{ + struct tty_ldisc *ld; + int retval = tty_check_change(tty); + if (retval) + return retval; + + ld = tty_ldisc_ref_wait(tty); + retval = __tty_perform_flush(tty, arg); + if (ld) + tty_ldisc_deref(ld); + return retval; +} EXPORT_SYMBOL_GPL(tty_perform_flush); int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, @@ -1151,7 +1201,10 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } return 0; case TCFLSH: - return tty_perform_flush(tty, arg); + retval = tty_check_change(tty); + if (retval) + return retval; + return __tty_perform_flush(tty, arg); default: /* Try the mode commands */ return tty_mode_ioctl(tty, file, cmd, arg); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index c5782294e53..2d822aa259b 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -11,7 +11,6 @@ #include <linux/slab.h> #include <linux/poll.h> #include <linux/proc_fs.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/wait.h> @@ -20,53 +19,34 @@ #include <linux/uaccess.h> #include <linux/ratelimit.h> +#undef LDISC_DEBUG_HANGUP + +#ifdef LDISC_DEBUG_HANGUP +#define tty_ldisc_debug(tty, f, args...) ({ \ + char __b[64]; \ + printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ +}) +#else +#define tty_ldisc_debug(tty, f, args...) +#endif + +/* lockdep nested classes for tty->ldisc_sem */ +enum { + LDISC_SEM_NORMAL, + LDISC_SEM_OTHER, +}; + + /* * This guards the refcounted line discipline lists. The lock * must be taken with irqs off because there are hangup path * callers who will do ldisc lookups and cannot sleep. */ -static DEFINE_RAW_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); +static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; -static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) -{ - if (ld) - atomic_inc(&ld->users); - return ld; -} - -static void put_ldisc(struct tty_ldisc *ld) -{ - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - /* - * If this is the last user, free the ldisc, and - * release the ldisc ops. - * - * We really want an "atomic_dec_and_raw_lock_irqsave()", - * but we don't have it, so this does it by hand. - */ - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - if (atomic_dec_and_test(&ld->users)) { - struct tty_ldisc_ops *ldo = ld->ops; - - ldo->refcount--; - module_put(ldo->owner); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - kfree(ld); - return; - } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - wake_up(&ld->wq_idle); -} - /** * tty_register_ldisc - install a line discipline * @disc: ldisc number @@ -77,7 +57,7 @@ static void put_ldisc(struct tty_ldisc *ld) * from this point onwards. * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) @@ -88,11 +68,11 @@ int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); tty_ldiscs[disc] = new_ldisc; new_ldisc->num = disc; new_ldisc->refcount = 0; - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -107,7 +87,7 @@ EXPORT_SYMBOL(tty_register_ldisc); * currently in use. * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ int tty_unregister_ldisc(int disc) @@ -118,12 +98,12 @@ int tty_unregister_ldisc(int disc) if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else tty_ldiscs[disc] = NULL; - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -134,7 +114,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) unsigned long flags; struct tty_ldisc_ops *ldops, *ret; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ret = ERR_PTR(-EINVAL); ldops = tty_ldiscs[disc]; if (ldops) { @@ -144,7 +124,7 @@ static struct tty_ldisc_ops *get_ldops(int disc) ret = ldops; } } - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); return ret; } @@ -152,10 +132,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) { unsigned long flags; - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); + raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); ldops->refcount--; module_put(ldops->owner); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); + raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); } /** @@ -168,10 +148,10 @@ static void put_ldops(struct tty_ldisc_ops *ldops) * available * * Locking: - * takes tty_ldisc_lock to guard against ldisc races + * takes tty_ldiscs_lock to guard against ldisc races */ -static struct tty_ldisc *tty_ldisc_get(int disc) +static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) { struct tty_ldisc *ld; struct tty_ldisc_ops *ldops; @@ -198,12 +178,25 @@ static struct tty_ldisc *tty_ldisc_get(int disc) } ld->ops = ldops; - atomic_set(&ld->users, 1); - init_waitqueue_head(&ld->wq_idle); + ld->tty = tty; return ld; } +/** + * tty_ldisc_put - release the ldisc + * + * Complement of tty_ldisc_get(). + */ +static inline void tty_ldisc_put(struct tty_ldisc *ld) +{ + if (WARN_ON_ONCE(!ld)) + return; + + put_ldops(ld->ops); + kfree(ld); +} + static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) { return (*pos < NR_LDISCS) ? pos : NULL; @@ -253,49 +246,6 @@ const struct file_operations tty_ldiscs_proc_fops = { }; /** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains. - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - tty->ldisc = ld; -} - -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - - raw_spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags)) - ld = get_ldisc(tty->ldisc); - raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ld; -} - -/** * tty_ldisc_ref_wait - wait for the tty ldisc * @tty: tty device * @@ -308,16 +258,15 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) * against a discipline change, such as an existing ldisc reference * (which we check for) * - * Locking: call functions take tty_ldisc_lock + * Note: only callable from a file_operations routine (which + * guarantees tty->ldisc != NULL when the lock is acquired). */ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) { - struct tty_ldisc *ld; - - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); - return ld; + ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); + WARN_ON(!tty->ldisc); + return tty->ldisc; } EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); @@ -328,13 +277,18 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); * Dereference the line discipline for the terminal and take a * reference to it. If the line discipline is in flux then * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock */ struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) { - return tty_ldisc_try(tty); + struct tty_ldisc *ld = NULL; + + if (ldsem_down_read_trylock(&tty->ldisc_sem)) { + ld = tty->ldisc; + if (!ld) + ldsem_up_read(&tty->ldisc_sem); + } + return ld; } EXPORT_SYMBOL_GPL(tty_ldisc_ref); @@ -344,38 +298,91 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); * * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock */ void tty_ldisc_deref(struct tty_ldisc *ld) { - put_ldisc(ld); + ldsem_up_read(&ld->tty->ldisc_sem); } EXPORT_SYMBOL_GPL(tty_ldisc_deref); -static inline void tty_ldisc_put(struct tty_ldisc *ld) + +static inline int __lockfunc +tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { - put_ldisc(ld); + return ldsem_down_write(&tty->ldisc_sem, timeout); } -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. Clear the LDISC - * changing flag to indicate any ldisc change is now over. - * - * Note: nobody should set the TTY_LDISC bit except via this function. - * Clearing directly is allowed. - */ +static inline int __lockfunc +tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) +{ + return ldsem_down_write_nested(&tty->ldisc_sem, + LDISC_SEM_OTHER, timeout); +} -void tty_ldisc_enable(struct tty_struct *tty) +static inline void tty_ldisc_unlock(struct tty_struct *tty) { - set_bit(TTY_LDISC, &tty->flags); - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - wake_up(&tty_ldisc_wait); + return ldsem_up_write(&tty->ldisc_sem); +} + +static int __lockfunc +tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, + unsigned long timeout) +{ + int ret; + + if (tty < tty2) { + ret = tty_ldisc_lock(tty, timeout); + if (ret) { + ret = tty_ldisc_lock_nested(tty2, timeout); + if (!ret) + tty_ldisc_unlock(tty); + } + } else { + /* if this is possible, it has lots of implications */ + WARN_ON_ONCE(tty == tty2); + if (tty2 && tty != tty2) { + ret = tty_ldisc_lock(tty2, timeout); + if (ret) { + ret = tty_ldisc_lock_nested(tty, timeout); + if (!ret) + tty_ldisc_unlock(tty2); + } + } else + ret = tty_ldisc_lock(tty, timeout); + } + + if (!ret) + return -EBUSY; + + set_bit(TTY_LDISC_HALTED, &tty->flags); + if (tty2) + set_bit(TTY_LDISC_HALTED, &tty2->flags); + return 0; +} + +static void __lockfunc +tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) +{ + tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); +} + +static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + tty_ldisc_unlock(tty); + if (tty2) + tty_ldisc_unlock(tty2); +} + +static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, + struct tty_struct *tty2) +{ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + if (tty2) + clear_bit(TTY_LDISC_HALTED, &tty2->flags); + + tty_ldisc_unlock_pair(tty, tty2); } /** @@ -407,14 +414,14 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * they are not on hot paths so a little discipline won't do * any harm. * - * Locking: takes termios_mutex + * Locking: takes termios_rwsem */ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_line = num; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } /** @@ -475,17 +482,17 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) int r; /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(old->ops->num); + old = tty_ldisc_get(tty, old->ops->num); WARN_ON(IS_ERR(old)); - tty_ldisc_assign(tty, old); + tty->ldisc = old; tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); /* This driver is always present */ - new_ldisc = tty_ldisc_get(N_TTY); + new_ldisc = tty_ldisc_get(tty, N_TTY); if (IS_ERR(new_ldisc)) panic("n_tty: get"); - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, N_TTY); r = tty_ldisc_open(tty, new_ldisc); if (r < 0) @@ -496,55 +503,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } /** - * tty_ldisc_halt - shut down the line discipline - * @tty: tty device - * - * Shut down the line discipline and work queue for this tty device. - * The TTY_LDISC flag being cleared ensures no further references can - * be obtained while the delayed work queue halt ensures that no more - * data is fed to the ldisc. - * - * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) - * in order to make sure any currently executing ldisc work is also - * flushed. - */ - -static int tty_ldisc_halt(struct tty_struct *tty) -{ - clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->port->buf.work); -} - -/** - * tty_ldisc_flush_works - flush all works of a tty - * @tty: tty device to flush works for - * - * Sync flush all works belonging to @tty. - */ -static void tty_ldisc_flush_works(struct tty_struct *tty) -{ - flush_work(&tty->hangup_work); - flush_work(&tty->SAK_work); - flush_work(&tty->port->buf.work); -} - -/** - * tty_ldisc_wait_idle - wait for the ldisc to become idle - * @tty: tty to wait for - * @timeout: for how long to wait at most - * - * Wait for the line discipline to become idle. The discipline must - * have been halted for this to guarantee it remains idle. - */ -static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) -{ - long ret; - ret = wait_event_timeout(tty->ldisc->wq_idle, - atomic_read(&tty->ldisc->users) == 1, timeout); - return ret > 0 ? 0 : -EBUSY; -} - -/** * tty_set_ldisc - set line discipline * @tty: the terminal to set * @ldisc: the line discipline @@ -553,161 +511,83 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) * context. The ldisc change logic has to protect itself against any * overlapping ldisc change (including on the other end of pty pairs), * the close of one side of a tty/pty pair, and eventually hangup. - * - * Locking: takes tty_ldisc_lock, termios_mutex */ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; - struct tty_ldisc *o_ldisc, *new_ldisc; - int work, o_work = 0; - struct tty_struct *o_tty; + struct tty_ldisc *old_ldisc, *new_ldisc; + struct tty_struct *o_tty = tty->link; - new_ldisc = tty_ldisc_get(ldisc); + new_ldisc = tty_ldisc_get(tty, ldisc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(tty); - /* - * We need to look at the tty locking here for pty/tty pairs - * when both sides try to change in parallel. - */ - - o_tty = tty->link; /* o_tty is the pty side or NULL */ - + retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); + if (retval) { + tty_ldisc_put(new_ldisc); + return retval; + } /* * Check the no-op case */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(tty); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); return 0; } - tty_unlock(tty); - /* - * Problem: What do we do if this blocks ? - * We could deadlock here - */ - - tty_wait_until_sent(tty, 0); - + old_ldisc = tty->ldisc; tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - - /* - * We could be midstream of another ldisc change which has - * dropped the lock during processing. If so we need to wait. - */ - - while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { - mutex_unlock(&tty->ldisc_mutex); - tty_unlock(tty); - wait_event(tty_ldisc_wait, - test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - } - - set_bit(TTY_LDISC_CHANGING, &tty->flags); - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - - tty_unlock(tty); - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - * - * We must clear the TTY_LDISC bit here to avoid a livelock - * with a userspace app continually trying to use the tty in - * parallel to the change and re-referencing the tty. - */ - work = tty_ldisc_halt(tty); - if (o_tty) - o_work = tty_ldisc_halt(o_tty); - - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate. - * We must drop the mutex here in case a hangup is also in process. - */ - - mutex_unlock(&tty->ldisc_mutex); - - tty_ldisc_flush_works(tty); - - retval = tty_ldisc_wait_idle(tty, 5 * HZ); - - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - - /* handle wait idle failure locked */ - if (retval) { - tty_ldisc_put(new_ldisc); - goto enable; - } - - if (test_bit(TTY_HUPPING, &tty->flags)) { + if (test_bit(TTY_HUPPING, &tty->flags) || + test_bit(TTY_HUPPED, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return -EIO; } - /* Shutdown the current discipline. */ - tty_ldisc_close(tty, o_ldisc); + /* Shutdown the old discipline. */ + tty_ldisc_close(tty, old_ldisc); /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, new_ldisc); + tty->ldisc = new_ldisc; tty_set_termios_ldisc(tty, ldisc); retval = tty_ldisc_open(tty, new_ldisc); if (retval < 0) { /* Back to the old one or N_TTY if we can't */ tty_ldisc_put(new_ldisc); - tty_ldisc_restore(tty, o_ldisc); + tty_ldisc_restore(tty, old_ldisc); } - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc) + if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) tty->ops->set_ldisc(tty); - tty_ldisc_put(o_ldisc); + /* At this point we hold a reference to the new ldisc and a + reference to the old ldisc, or we hold two references to + the old ldisc (if it was restored as part of error cleanup + above). In either case, releasing a single reference from + the old ldisc is correct. */ + + tty_ldisc_put(old_ldisc); -enable: /* * Allow ldisc referencing to occur again */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); + tty_ldisc_enable_pair(tty, o_tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ - if (work) - schedule_work(&tty->port->buf.work); - if (o_work) + schedule_work(&tty->port->buf.work); + if (o_tty) schedule_work(&o_tty->port->buf.work); - mutex_unlock(&tty->ldisc_mutex); + tty_unlock(tty); return retval; } @@ -721,11 +601,11 @@ enable: static void tty_reset_termios(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios = tty->driver->init_termios; tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } @@ -740,18 +620,17 @@ static void tty_reset_termios(struct tty_struct *tty) static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) { - struct tty_ldisc *ld = tty_ldisc_get(ldisc); + struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc); if (IS_ERR(ld)) return -1; tty_ldisc_close(tty, tty->ldisc); tty_ldisc_put(tty->ldisc); - tty->ldisc = NULL; /* * Switch the line discipline back */ - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; tty_set_termios_ldisc(tty, ldisc); return 0; @@ -778,14 +657,10 @@ void tty_ldisc_hangup(struct tty_struct *tty) int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; int err = 0; - /* - * FIXME! What are the locking issues here? This may me overdoing - * things... This question is especially important now that we've - * removed the irqlock. - */ + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); + ld = tty_ldisc_ref(tty); if (ld != NULL) { - /* We may have no line discipline at this point */ if (ld->ops->flush_buffer) ld->ops->flush_buffer(tty); tty_driver_flush_buffer(tty); @@ -796,54 +671,27 @@ void tty_ldisc_hangup(struct tty_struct *tty) ld->ops->hangup(tty); tty_ldisc_deref(ld); } - /* - * FIXME: Once we trust the LDISC code better we can wait here for - * ldisc completion and fix the driver call race - */ + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); wake_up_interruptible_poll(&tty->read_wait, POLLIN); + + tty_unlock(tty); + /* * Shutdown the current line discipline, and reset it to * N_TTY if need be. * * Avoid racing set_ldisc or tty_ldisc_release */ - mutex_lock(&tty->ldisc_mutex); - - /* - * this is like tty_ldisc_halt, but we need to give up - * the BTM before calling cancel_work_sync, which may - * need to wait for another function taking the BTM - */ - clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(tty); - cancel_work_sync(&tty->port->buf.work); - mutex_unlock(&tty->ldisc_mutex); -retry: + tty_ldisc_lock_pair(tty, tty->link); tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is - a FIXME */ - if (tty->ldisc) { /* Not yet closed */ - if (atomic_read(&tty->ldisc->users) != 1) { - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - mutex_unlock(&tty->ldisc_mutex); - goto retry; - } + if (tty->ldisc) { + + /* At this point we have a halted ldisc; we want to close it and + reopen a new ldisc. We could defer the reopen to the next + open but it means auditing a lot of other paths so this is + a FIXME */ if (reset == 0) { if (!tty_ldisc_reinit(tty, tty->termios.c_line)) @@ -857,11 +705,12 @@ retry: BUG_ON(tty_ldisc_reinit(tty, N_TTY)); WARN_ON(tty_ldisc_open(tty, tty->ldisc)); } - tty_ldisc_enable(tty); } - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, tty->link); if (reset) tty_reset_termios(tty); + + tty_ldisc_debug(tty, "re-opened ldisc: %p\n", tty->ldisc); } /** @@ -889,20 +738,12 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_close(tty, ld); return retval; } - tty_ldisc_enable(o_tty); } - tty_ldisc_enable(tty); return 0; } static void tty_ldisc_kill(struct tty_struct *tty) { - /* There cannot be users from userspace now. But there still might be - * drivers holding a reference via tty_ldisc_ref. Do not steal them the - * ldisc until they are done. */ - tty_ldisc_wait_idle(tty, MAX_SCHEDULE_TIMEOUT); - - mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc */ @@ -913,7 +754,6 @@ static void tty_ldisc_kill(struct tty_struct *tty) /* Ensure the next open requests the N_TTY ldisc */ tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); } /** @@ -929,28 +769,26 @@ static void tty_ldisc_kill(struct tty_struct *tty) void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) { /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. + * Shutdown this line discipline. As this is the final close, + * it does not race with the set_ldisc code path. */ + tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); + + tty_ldisc_lock_pair(tty, o_tty); tty_lock_pair(tty, o_tty); - tty_ldisc_halt(tty); - tty_ldisc_flush_works(tty); - if (o_tty) { - tty_ldisc_halt(o_tty); - tty_ldisc_flush_works(o_tty); - } - /* This will need doing differently if we need to lock */ tty_ldisc_kill(tty); - if (o_tty) tty_ldisc_kill(o_tty); tty_unlock_pair(tty, o_tty); + tty_ldisc_unlock_pair(tty, o_tty); + /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ + + tty_ldisc_debug(tty, "ldisc closed\n"); } /** @@ -963,10 +801,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) void tty_ldisc_init(struct tty_struct *tty) { - struct tty_ldisc *ld = tty_ldisc_get(N_TTY); + struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); - tty_ldisc_assign(tty, ld); + tty->ldisc = ld; } /** @@ -978,8 +816,8 @@ void tty_ldisc_init(struct tty_struct *tty) */ void tty_ldisc_deinit(struct tty_struct *tty) { - put_ldisc(tty->ldisc); - tty_ldisc_assign(tty, NULL); + tty_ldisc_put(tty->ldisc); + tty->ldisc = NULL; } void tty_ldisc_begin(void) diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c new file mode 100644 index 00000000000..0ffb0cbe282 --- /dev/null +++ b/drivers/tty/tty_ldsem.c @@ -0,0 +1,456 @@ +/* + * Ldisc rw semaphore + * + * The ldisc semaphore is semantically a rw_semaphore but which enforces + * an alternate policy, namely: + * 1) Supports lock wait timeouts + * 2) Write waiter has priority + * 3) Downgrading is not supported + * + * Implementation notes: + * 1) Upper half of semaphore count is a wait count (differs from rwsem + * in that rwsem normalizes the upper half to the wait bias) + * 2) Lacks overflow checking + * + * The generic counting was copied and modified from include/asm-generic/rwsem.h + * by Paul Mackerras <paulus@samba.org>. + * + * The scheduling policy was copied and modified from lib/rwsem.c + * Written by David Howells (dhowells@redhat.com). + * + * This implementation incorporates the write lock stealing work of + * Michel Lespinasse <walken@google.com>. + * + * Copyright (C) 2013 Peter Hurley <peter@hurleysoftware.com> + * + * This file may be redistributed under the terms of the GNU General Public + * License v2. + */ + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/atomic.h> +#include <linux/tty.h> +#include <linux/sched.h> + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __acq(l, s, t, r, c, n, i) \ + lock_acquire(&(l)->dep_map, s, t, r, c, n, i) +# define __rel(l, n, i) \ + lock_release(&(l)->dep_map, n, i) +#define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) +#define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) +#define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) +#define lockdep_release(l, n, i) __rel(l, n, i) +#else +# define lockdep_acquire(l, s, t, i) do { } while (0) +# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0) +# define lockdep_acquire_read(l, s, t, i) do { } while (0) +# define lockdep_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_LOCK_STAT +# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_) +#else +# define lock_stat(_lock, stat) do { } while (0) +#endif + + +#if BITS_PER_LONG == 64 +# define LDSEM_ACTIVE_MASK 0xffffffffL +#else +# define LDSEM_ACTIVE_MASK 0x0000ffffL +#endif + +#define LDSEM_UNLOCKED 0L +#define LDSEM_ACTIVE_BIAS 1L +#define LDSEM_WAIT_BIAS (-LDSEM_ACTIVE_MASK-1) +#define LDSEM_READ_BIAS LDSEM_ACTIVE_BIAS +#define LDSEM_WRITE_BIAS (LDSEM_WAIT_BIAS + LDSEM_ACTIVE_BIAS) + +struct ldsem_waiter { + struct list_head list; + struct task_struct *task; +}; + +static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem) +{ + return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); +} + +/* + * ldsem_cmpxchg() updates @*old with the last-known sem->count value. + * Returns 1 if count was successfully changed; @*old will have @new value. + * Returns 0 if count was not changed; @*old will have most recent sem->count + */ +static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem) +{ + long tmp = atomic_long_cmpxchg(&sem->count, *old, new); + if (tmp == *old) { + *old = new; + return 1; + } else { + *old = tmp; + return 0; + } +} + +/* + * Initialize an ldsem: + */ +void __init_ldsem(struct ld_semaphore *sem, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held semaphore: + */ + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key, 0); +#endif + sem->count = LDSEM_UNLOCKED; + sem->wait_readers = 0; + raw_spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->read_wait); + INIT_LIST_HEAD(&sem->write_wait); +} + +static void __ldsem_wake_readers(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter, *next; + struct task_struct *tsk; + long adjust, count; + + /* Try to grant read locks to all readers on the read wait list. + * Note the 'active part' of the count is incremented by + * the number of readers before waking any processes up. + */ + adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS); + count = ldsem_atomic_update(adjust, sem); + do { + if (count > 0) + break; + if (ldsem_cmpxchg(&count, count - adjust, sem)) + return; + } while (1); + + list_for_each_entry_safe(waiter, next, &sem->read_wait, list) { + tsk = waiter->task; + smp_mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + } + INIT_LIST_HEAD(&sem->read_wait); + sem->wait_readers = 0; +} + +static inline int writer_trylock(struct ld_semaphore *sem) +{ + /* only wake this writer if the active part of the count can be + * transitioned from 0 -> 1 + */ + long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem); + do { + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) + return 1; + if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem)) + return 0; + } while (1); +} + +static void __ldsem_wake_writer(struct ld_semaphore *sem) +{ + struct ldsem_waiter *waiter; + + waiter = list_entry(sem->write_wait.next, struct ldsem_waiter, list); + wake_up_process(waiter->task); +} + +/* + * handle the lock release when processes blocked on it that can now run + * - if we come here from up_xxxx(), then: + * - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed) + * - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so) + * - the spinlock must be held by the caller + * - woken process blocks are discarded from the list after having task zeroed + */ +static void __ldsem_wake(struct ld_semaphore *sem) +{ + if (!list_empty(&sem->write_wait)) + __ldsem_wake_writer(sem); + else if (!list_empty(&sem->read_wait)) + __ldsem_wake_readers(sem); +} + +static void ldsem_wake(struct ld_semaphore *sem) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&sem->wait_lock, flags); + __ldsem_wake(sem); + raw_spin_unlock_irqrestore(&sem->wait_lock, flags); +} + +/* + * wait for the read lock to be granted + */ +static struct ld_semaphore __sched * +down_read_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if there are are no waiters, + * and early-out if not */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if (count > 0) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->read_wait); + sem->wait_readers++; + + waiter.task = tsk; + get_task_struct(tsk); + + /* if there are no active locks, wake the new lock owner(s) */ + if ((count & LDSEM_ACTIVE_MASK) == 0) + __ldsem_wake(sem); + + raw_spin_unlock_irq(&sem->wait_lock); + + /* wait to be given the lock */ + for (;;) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + if (!waiter.task) + break; + if (!timeout) + break; + timeout = schedule_timeout(timeout); + } + + __set_task_state(tsk, TASK_RUNNING); + + if (!timeout) { + /* lock timed out but check if this task was just + * granted lock ownership - if so, pretend there + * was no timeout; otherwise, cleanup lock wait */ + raw_spin_lock_irq(&sem->wait_lock); + if (waiter.task) { + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + put_task_struct(waiter.task); + return NULL; + } + raw_spin_unlock_irq(&sem->wait_lock); + } + + return sem; +} + +/* + * wait for the write lock to be granted + */ +static struct ld_semaphore __sched * +down_write_failed(struct ld_semaphore *sem, long count, long timeout) +{ + struct ldsem_waiter waiter; + struct task_struct *tsk = current; + long adjust = -LDSEM_ACTIVE_BIAS; + int locked = 0; + + /* set up my own style of waitqueue */ + raw_spin_lock_irq(&sem->wait_lock); + + /* Try to reverse the lock attempt but if the count has changed + * so that reversing fails, check if the lock is now owned, + * and early-out if so */ + do { + if (ldsem_cmpxchg(&count, count + adjust, sem)) + break; + if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + } while (1); + + list_add_tail(&waiter.list, &sem->write_wait); + + waiter.task = tsk; + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + for (;;) { + if (!timeout) + break; + raw_spin_unlock_irq(&sem->wait_lock); + timeout = schedule_timeout(timeout); + raw_spin_lock_irq(&sem->wait_lock); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((locked = writer_trylock(sem))) + break; + } + + if (!locked) + ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + list_del(&waiter.list); + raw_spin_unlock_irq(&sem->wait_lock); + + __set_task_state(tsk, TASK_RUNNING); + + /* lock wait may have timed out */ + if (!locked) + return NULL; + return sem; +} + + + +static inline int __ldsem_down_read_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire_read(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_READ_BIAS, sem); + if (count <= 0) { + lock_stat(sem, contended); + if (!down_read_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + +static inline int __ldsem_down_write_nested(struct ld_semaphore *sem, + int subclass, long timeout) +{ + long count; + + lockdep_acquire(sem, subclass, 0, _RET_IP_); + + count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem); + if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { + lock_stat(sem, contended); + if (!down_write_failed(sem, count, timeout)) { + lockdep_release(sem, 1, _RET_IP_); + return 0; + } + } + lock_stat(sem, acquired); + return 1; +} + + +/* + * lock for reading -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, 0, timeout); +} + +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +int ldsem_down_read_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while (count >= 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) { + lockdep_acquire_read(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * lock for writing -- returns 1 if successful, 0 if timed out + */ +int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, 0, timeout); +} + +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +int ldsem_down_write_trylock(struct ld_semaphore *sem) +{ + long count = sem->count; + + while ((count & LDSEM_ACTIVE_MASK) == 0) { + if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) { + lockdep_acquire(sem, 0, 1, _RET_IP_); + lock_stat(sem, acquired); + return 1; + } + } + return 0; +} + +/* + * release a read lock + */ +void ldsem_up_read(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem); + if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) + ldsem_wake(sem); +} + +/* + * release a write lock + */ +void ldsem_up_write(struct ld_semaphore *sem) +{ + long count; + + lockdep_release(sem, 1, _RET_IP_); + + count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem); + if (count < 0) + ldsem_wake(sem); +} + + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, long timeout) +{ + might_sleep(); + return __ldsem_down_read_nested(sem, subclass, timeout); +} + +int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, + long timeout) +{ + might_sleep(); + return __ldsem_down_write_nested(sem, subclass, timeout); +} + +#endif diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index b7ff59d3db8..3f746c8eb0d 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -12,7 +12,6 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/wait.h> #include <linux/bitops.h> #include <linux/delay.h> @@ -132,6 +131,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf); */ void tty_port_destroy(struct tty_port *port) { + cancel_work_sync(&port->buf.work); tty_buffer_free_all(port); } EXPORT_SYMBOL(tty_port_destroy); @@ -139,6 +139,10 @@ EXPORT_SYMBOL(tty_port_destroy); static void tty_port_destructor(struct kref *kref) { struct tty_port *port = container_of(kref, struct tty_port, kref); + + /* check if last port ref was dropped before tty release */ + if (WARN_ON(port->itty)) + return; if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); tty_port_destroy(port); @@ -196,12 +200,24 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); -static void tty_port_shutdown(struct tty_port *port) +static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty) { mutex_lock(&port->mutex); - if (port->ops->shutdown && !port->console && - test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) + if (port->console) + goto out; + + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* + * Drop DTR/RTS if HUPCL is set. This causes any attached + * modem to hang up the line. + */ + if (tty && C_HUPCL(tty)) + tty_port_lower_dtr_rts(port); + + if (port->ops->shutdown) port->ops->shutdown(port); + } +out: mutex_unlock(&port->mutex); } @@ -215,24 +231,57 @@ static void tty_port_shutdown(struct tty_port *port) void tty_port_hangup(struct tty_port *port) { + struct tty_struct *tty; unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } + tty = port->tty; + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); + tty_port_shutdown(port, tty); + tty_kref_put(tty); wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->delta_msr_wait); - tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); /** + * tty_port_tty_hangup - helper to hang up a tty + * + * @port: tty port + * @check_clocal: hang only ttys with CLOCAL unset? + */ +void tty_port_tty_hangup(struct tty_port *port, bool check_clocal) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty && (!check_clocal || !C_CLOCAL(tty))) + tty_hangup(tty); + tty_kref_put(tty); +} +EXPORT_SYMBOL_GPL(tty_port_tty_hangup); + +/** + * tty_port_tty_wakeup - helper to wake up a tty + * + * @port: tty port + */ +void tty_port_tty_wakeup(struct tty_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(port); + + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } +} +EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); + +/** * tty_port_carrier_raised - carrier raised check * @port: tty port * @@ -350,7 +399,7 @@ int tty_port_block_til_ready(struct tty_port *port, while (1) { /* Indicate we are open */ - if (tty->termios.c_cflag & CBAUD) + if (C_BAUD(tty) && test_bit(ASYNCB_INITIALIZED, &port->flags)) tty_port_raise_dtr_rts(port); prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); @@ -395,6 +444,20 @@ int tty_port_block_til_ready(struct tty_port *port, } EXPORT_SYMBOL(tty_port_block_til_ready); +static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) +{ + unsigned int bps = tty_get_baud_rate(tty); + long timeout; + + if (bps > 1200) { + timeout = (HZ * 10 * port->drain_delay) / bps; + timeout = max_t(long, timeout, HZ / 10); + } else { + timeout = 2 * HZ; + } + schedule_timeout_interruptible(timeout); +} + int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { @@ -420,41 +483,25 @@ int tty_port_close_start(struct tty_port *port, if (port->count) { spin_unlock_irqrestore(&port->lock, flags); - if (port->ops->drop) - port->ops->drop(port); return 0; } set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); - /* Don't block on a stalled port, just pull the chain */ - if (tty->flow_stopped) - tty_driver_flush_buffer(tty); - if (test_bit(ASYNCB_INITIALIZED, &port->flags) && - port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, port->closing_wait); - if (port->drain_delay) { - unsigned int bps = tty_get_baud_rate(tty); - long timeout; - - if (bps > 1200) - timeout = max_t(long, - (HZ * 10 * port->drain_delay) / bps, HZ / 10); - else - timeout = 2 * HZ; - schedule_timeout_interruptible(timeout); + + if (test_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* Don't block on a stalled port, just pull the chain */ + if (tty->flow_stopped) + tty_driver_flush_buffer(tty); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent_from_close(tty, port->closing_wait); + if (port->drain_delay) + tty_port_drain_delay(port, tty); } /* Flush the ldisc buffering */ tty_ldisc_flush(tty); - /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to - hang up the line */ - if (tty->termios.c_cflag & HUPCL) - tty_port_lower_dtr_rts(port); - - /* Don't call port->drop for the last reference. Callers will want - to drop the last active reference in ->shutdown() or the tty - shutdown path */ + /* Report to caller this is the last port reference */ return 1; } EXPORT_SYMBOL(tty_port_close_start); @@ -486,7 +533,7 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty, { if (tty_port_close_start(port, tty, filp) == 0) return; - tty_port_shutdown(port); + tty_port_shutdown(port, tty); set_bit(TTY_IO_ERROR, &tty->flags); tty_port_close_end(port, tty); tty_port_tty_set(port, NULL); diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile index 14a51c9960d..17ae94cb29f 100644 --- a/drivers/tty/vt/Makefile +++ b/drivers/tty/vt/Makefile @@ -27,8 +27,6 @@ $(obj)/defkeymap.o: $(obj)/defkeymap.c ifdef GENERATE_KEYMAP $(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@.tmp - sed -e 's/^static *//' $@.tmp > $@ - rm $@.tmp + loadkeys --mktable $< > $@ endif diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 248381b3072..610b720d3b9 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -179,7 +179,6 @@ struct uni_pagedir { unsigned long sum; unsigned char *inverse_translations[4]; u16 *inverse_trans_unicode; - int readonly; }; static struct uni_pagedir *dflt; @@ -194,8 +193,7 @@ static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int q = p->inverse_translations[i]; if (!q) { - q = p->inverse_translations[i] = (unsigned char *) - kmalloc(MAX_GLYPH, GFP_KERNEL); + q = p->inverse_translations[i] = kmalloc(MAX_GLYPH, GFP_KERNEL); if (!q) return; } memset(q, 0, MAX_GLYPH); @@ -263,7 +261,7 @@ u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) int m; if (glyph < 0 || glyph >= MAX_GLYPH) return 0; - else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) + else if (!(p = *conp->vc_uni_pagedir_loc)) return glyph; else if (use_unicode) { if (!p->inverse_trans_unicode) @@ -288,7 +286,7 @@ static void update_user_maps(void) for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons_allocated(i)) continue; - p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + p = *vc_cons[i].d->vc_uni_pagedir_loc; if (p && p != q) { set_inverse_transl(vc_cons[i].d, p, USER_MAP); set_inverse_trans_unicode(vc_cons[i].d, p); @@ -419,10 +417,10 @@ void con_free_unimap(struct vc_data *vc) { struct uni_pagedir *p; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; if (!p) return; - *vc->vc_uni_pagedir_loc = 0; + *vc->vc_uni_pagedir_loc = NULL; if (--p->refcount) return; con_release_unimap(p); @@ -437,7 +435,7 @@ static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) for (i = 0; i < MAX_NR_CONSOLES; i++) { if (!vc_cons_allocated(i)) continue; - q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; + q = *vc_cons[i].d->vc_uni_pagedir_loc; if (!q || q == p || q->sum != p->sum) continue; for (j = 0; j < 32; j++) { @@ -460,7 +458,7 @@ static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) } if (j == 32) { q->refcount++; - *conp->vc_uni_pagedir_loc = (unsigned long)q; + *conp->vc_uni_pagedir_loc = q; con_release_unimap(p); kfree(p); return 1; @@ -501,10 +499,7 @@ static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui) { struct uni_pagedir *p, *q; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p && p->readonly) - return -EIO; - + p = *vc->vc_uni_pagedir_loc; if (!p || --p->refcount) { q = kzalloc(sizeof(*p), GFP_KERNEL); if (!q) { @@ -513,7 +508,7 @@ static int con_do_clear_unimap(struct vc_data *vc, struct unimapinit *ui) return -ENOMEM; } q->refcount=1; - *vc->vc_uni_pagedir_loc = (unsigned long)q; + *vc->vc_uni_pagedir_loc = q; } else { if (p == dflt) dflt = NULL; p->refcount++; @@ -537,19 +532,13 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) int err = 0, err1, i; struct uni_pagedir *p, *q; + if (!ct) + return 0; + console_lock(); /* Save original vc_unipagdir_loc in case we allocate a new one */ - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p->readonly) { - console_unlock(); - return -EIO; - } - - if (!ct) { - console_unlock(); - return 0; - } + p = *vc->vc_uni_pagedir_loc; if (p->refcount > 1) { int j, k; @@ -565,7 +554,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) * Since refcount was > 1, con_clear_unimap() allocated a * a new uni_pagedir for this vc. Re: p != q */ - q = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + q = *vc->vc_uni_pagedir_loc; /* * uni_pgdir is a 32*32*64 table with rows allocated @@ -587,7 +576,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) err1 = con_insert_unipair(q, l, p2[k]); if (err1) { p->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)p; + *vc->vc_uni_pagedir_loc = p; con_release_unimap(q); kfree(q); console_unlock(); @@ -656,12 +645,12 @@ int con_set_default_unimap(struct vc_data *vc) struct uni_pagedir *p; if (dflt) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; if (p == dflt) return 0; dflt->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)dflt; + *vc->vc_uni_pagedir_loc = dflt; if (p && !--p->refcount) { con_release_unimap(p); kfree(p); @@ -675,7 +664,7 @@ int con_set_default_unimap(struct vc_data *vc) if (err) return err; - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; q = dfont_unitable; for (i = 0; i < 256; i++) @@ -686,7 +675,7 @@ int con_set_default_unimap(struct vc_data *vc) } if (con_unify_unimap(vc, p)) { - dflt = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + dflt = *vc->vc_uni_pagedir_loc; return err; } @@ -714,9 +703,9 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc) return 0; con_free_unimap(dst_vc); - q = (struct uni_pagedir *)*src_vc->vc_uni_pagedir_loc; + q = *src_vc->vc_uni_pagedir_loc; q->refcount++; - *dst_vc->vc_uni_pagedir_loc = (long)q; + *dst_vc->vc_uni_pagedir_loc = q; return 0; } EXPORT_SYMBOL(con_copy_unimap); @@ -738,7 +727,7 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni ect = 0; if (*vc->vc_uni_pagedir_loc) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; + p = *vc->vc_uni_pagedir_loc; for (i = 0; i < 32; i++) if ((p1 = p->uni_pgdir[i])) for (j = 0; j < 32; j++) @@ -811,7 +800,7 @@ conv_uni_to_pc(struct vc_data *conp, long ucs) if (!*conp->vc_uni_pagedir_loc) return -3; - p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; + p = *conp->vc_uni_pagedir_loc; if ((p1 = p->uni_pgdir[ucs >> 11]) && (p2 = p1[(ucs >> 6) & 0x1f]) && (h = p2[ucs & 0x3f]) < MAX_GLYPH) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 681765baef6..d0e3a449770 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -132,12 +132,6 @@ static int shift_state = 0; static unsigned char ledstate = 0xff; /* undefined */ static unsigned char ledioctl; -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - /* * Notifier list for console keyboard events */ @@ -307,26 +301,17 @@ int kbd_rate(struct kbd_repeat *rep) */ static void put_queue(struct vc_data *vc, int ch) { - struct tty_struct *tty = vc->port.tty; - - if (tty) { - tty_insert_flip_char(tty, ch, 0); - tty_schedule_flip(tty); - } + tty_insert_flip_char(&vc->port, ch, 0); + tty_schedule_flip(&vc->port); } static void puts_queue(struct vc_data *vc, char *cp) { - struct tty_struct *tty = vc->port.tty; - - if (!tty) - return; - while (*cp) { - tty_insert_flip_char(tty, *cp, 0); + tty_insert_flip_char(&vc->port, *cp, 0); cp++; } - tty_schedule_flip(tty); + tty_schedule_flip(&vc->port); } static void applkey(struct vc_data *vc, int key, char mode) @@ -582,12 +567,8 @@ static void fn_inc_console(struct vc_data *vc) static void fn_send_intr(struct vc_data *vc) { - struct tty_struct *tty = vc->port.tty; - - if (!tty) - return; - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); + tty_insert_flip_char(&vc->port, 0, TTY_BREAK); + tty_schedule_flip(&vc->port); } static void fn_scroll_forw(struct vc_data *vc) @@ -1007,24 +988,11 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) static inline unsigned char getleds(void) { struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - int i; if (kbd->ledmode == LED_SHOW_IOCTL) return ledioctl; - leds = kbd->ledflagstate; - - if (kbd->ledmode == LED_SHOW_MEM) { - for (i = 0; i < 3; i++) - if (ledptrs[i].valid) { - if (*ledptrs[i].addr & ledptrs[i].mask) - leds |= (1 << i); - else - leds &= ~(1 << i); - } - } - return leds; + return kbd->ledflagstate; } static int kbd_update_leds_helper(struct input_handle *handle, void *data) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 60b7b692605..ea27804d87a 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -24,6 +24,7 @@ #include <linux/selection.h> #include <linux/tiocl.h> #include <linux/console.h> +#include <linux/tty_flip.h> /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -346,8 +347,8 @@ int paste_selection(struct tty_struct *tty) console_unlock(); ld = tty_ldisc_ref_wait(tty); + tty_buffer_lock_exclusive(&vc->port); - /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); @@ -356,13 +357,14 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); + count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL, + count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); __set_current_state(TASK_RUNNING); + tty_buffer_unlock_exclusive(&vc->port); tty_ldisc_deref(ld); return 0; } diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index fa7268a93c0..14a2b5f11bc 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -93,7 +93,7 @@ vcs_poll_data_free(struct vcs_poll_data *poll) static struct vcs_poll_data * vcs_poll_data_get(struct file *file) { - struct vcs_poll_data *poll = file->private_data; + struct vcs_poll_data *poll = file->private_data, *kill = NULL; if (poll) return poll; @@ -101,7 +101,7 @@ vcs_poll_data_get(struct file *file) poll = kzalloc(sizeof(*poll), GFP_KERNEL); if (!poll) return NULL; - poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127; + poll->cons_num = iminor(file_inode(file)) & 127; init_waitqueue_head(&poll->waitq); poll->notifier.notifier_call = vcs_notifier; if (register_vt_notifier(&poll->notifier) != 0) { @@ -122,10 +122,12 @@ vcs_poll_data_get(struct file *file) file->private_data = poll; } else { /* someone else raced ahead of us */ - vcs_poll_data_free(poll); + kill = poll; poll = file->private_data; } spin_unlock(&file->f_lock); + if (kill) + vcs_poll_data_free(kill); return poll; } @@ -182,33 +184,18 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) int size; console_lock(); - size = vcs_size(file->f_path.dentry->d_inode); + size = vcs_size(file_inode(file)); console_unlock(); if (size < 0) return size; - switch (orig) { - default: - return -EINVAL; - case 2: - offset += size; - break; - case 1: - offset += file->f_pos; - case 0: - break; - } - if (offset < 0 || offset > size) { - return -EINVAL; - } - file->f_pos = offset; - return file->f_pos; + return fixed_size_llseek(file, offset, orig, size); } static ssize_t vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); unsigned int currcons = iminor(inode); struct vc_data *vc; struct vcs_poll_data *poll; @@ -386,7 +373,7 @@ unlock_out: static ssize_t vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct inode *inode = file->f_path.dentry->d_inode; + struct inode *inode = file_inode(file); unsigned int currcons = iminor(inode); struct vc_data *vc; long pos; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d06..b33b00b386d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -539,7 +539,7 @@ static void insert_char(struct vc_data *vc, unsigned int nr) { unsigned short *p = (unsigned short *) vc->vc_pos; - scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x) * 2); + scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2); scr_memsetw(p, vc->vc_video_erase_char, nr * 2); vc->vc_need_wrap = 0; if (DO_UPDATE(vc)) @@ -638,7 +638,7 @@ static inline void save_screen(struct vc_data *vc) * Redrawing of screen */ -static void clear_buffer_attributes(struct vc_data *vc) +void clear_buffer_attributes(struct vc_data *vc) { unsigned short *p = (unsigned short *)vc->vc_origin; int count = vc->vc_screenbuf_size / 2; @@ -735,7 +735,7 @@ static void visual_init(struct vc_data *vc, int num, int init) vc->vc_num = num; vc->vc_display_fg = &master_display_fg; vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir; - vc->vc_uni_pagedir = 0; + vc->vc_uni_pagedir = NULL; vc->vc_hi_font_mask = 0; vc->vc_complement_mask = 0; vc->vc_can_do_color = 0; @@ -779,7 +779,6 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ con_set_default_unimap(vc); vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); if (!vc->vc_screenbuf) { - tty_port_destroy(&vc->port); kfree(vc); vc_cons[currcons].d = NULL; return -ENOMEM; @@ -829,7 +828,7 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, * If the caller passes a tty structure then update the termios winsize * information and perform any necessary signal handling. * - * Caller must hold the console semaphore. Takes the termios mutex and + * Caller must hold the console semaphore. Takes the termios rwsem and * ctrl_lock of the tty IFF a tty is passed. */ @@ -973,7 +972,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) * the actual work. * * Takes the console sem and the called methods then take the tty - * termios_mutex and the tty ctrl_lock in that order. + * termios_rwsem and the tty ctrl_lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -986,26 +985,25 @@ static int vt_resize(struct tty_struct *tty, struct winsize *ws) return ret; } -void vc_deallocate(unsigned int currcons) +struct vc_data *vc_deallocate(unsigned int currcons) { + struct vc_data *vc = NULL; + WARN_CONSOLE_UNLOCKED(); if (vc_cons_allocated(currcons)) { - struct vc_data *vc = vc_cons[currcons].d; - struct vt_notifier_param param = { .vc = vc }; + struct vt_notifier_param param; + param.vc = vc = vc_cons[currcons].d; atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); vcs_remove_sysfs(currcons); vc->vc_sw->con_deinit(vc); put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); kfree(vc->vc_screenbuf); - if (currcons >= MIN_NR_CONSOLES) { - tty_port_destroy(&vc->port); - kfree(vc); - } vc_cons[currcons].d = NULL; } + return vc; } /* @@ -1166,6 +1164,8 @@ static void csi_J(struct vc_data *vc, int vpar) scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char, vc->vc_screenbuf_size >> 1); set_origin(vc); + if (CON_IS_VISIBLE(vc)) + update_screen(vc); /* fall through */ case 2: /* erase whole display */ count = vc->vc_cols * vc->vc_rows; @@ -1231,6 +1231,52 @@ static void default_attr(struct vc_data *vc) vc->vc_color = vc->vc_def_color; } +struct rgb { u8 r; u8 g; u8 b; }; + +struct rgb rgb_from_256(int i) +{ + struct rgb c; + if (i < 8) { /* Standard colours. */ + c.r = i&1 ? 0xaa : 0x00; + c.g = i&2 ? 0xaa : 0x00; + c.b = i&4 ? 0xaa : 0x00; + } else if (i < 16) { + c.r = i&1 ? 0xff : 0x55; + c.g = i&2 ? 0xff : 0x55; + c.b = i&4 ? 0xff : 0x55; + } else if (i < 232) { /* 6x6x6 colour cube. */ + c.r = (i - 16) / 36 * 85 / 2; + c.g = (i - 16) / 6 % 6 * 85 / 2; + c.b = (i - 16) % 6 * 85 / 2; + } else /* Grayscale ramp. */ + c.r = c.g = c.b = i * 10 - 2312; + return c; +} + +static void rgb_foreground(struct vc_data *vc, struct rgb c) +{ + u8 hue, max = c.r; + if (c.g > max) + max = c.g; + if (c.b > max) + max = c.b; + hue = (c.r > max/2 ? 4 : 0) + | (c.g > max/2 ? 2 : 0) + | (c.b > max/2 ? 1 : 0); + if (hue == 7 && max <= 0x55) + hue = 0, vc->vc_intensity = 2; + else + vc->vc_intensity = (max > 0xaa) + 1; + vc->vc_color = (vc->vc_color & 0xf0) | hue; +} + +static void rgb_background(struct vc_data *vc, struct rgb c) +{ + /* For backgrounds, err on the dark side. */ + vc->vc_color = (vc->vc_color & 0x0f) + | (c.r&0x80) >> 1 | (c.g&0x80) >> 2 | (c.b&0x80) >> 3; +} + /* console_lock is held */ static void csi_m(struct vc_data *vc) { @@ -1302,21 +1348,55 @@ static void csi_m(struct vc_data *vc) case 27: vc->vc_reverse = 0; break; - case 38: /* ANSI X3.64-1979 (SCO-ish?) - * Enables underscore, white foreground - * with white underscore (Linux - use - * default foreground). + case 38: /* ITU T.416 + * Higher colour modes. + * They break the usual properties of SGR codes + * and thus need to be detected and ignored by + * hand. Strictly speaking, that standard also + * wants : rather than ; as separators, contrary + * to ECMA-48, but no one produces such codes + * and almost no one accepts them. */ - vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 1; + i++; + if (i > vc->vc_npar) + break; + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { /* ubiquitous */ + i++; + rgb_foreground(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) {/* extremely rare */ + struct rgb c = {r:vc->vc_par[i+1], + g:vc->vc_par[i+2], + b:vc->vc_par[i+3]}; + rgb_foreground(vc, c); + i += 3; + } + /* Subcommands 3 (CMY) and 4 (CMYK) are so insane + * there's no point in supporting them. + */ break; - case 39: /* ANSI X3.64-1979 (SCO-ish?) - * Disable underline option. - * Reset colour to default? It did this - * before... - */ + case 48: + i++; + if (i > vc->vc_npar) + break; + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { + i++; + rgb_background(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) { + struct rgb c = {r:vc->vc_par[i+1], + g:vc->vc_par[i+2], + b:vc->vc_par[i+3]}; + rgb_background(vc, c); + i += 3; + } + break; + case 39: vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 0; break; case 49: vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f); @@ -1333,13 +1413,13 @@ static void csi_m(struct vc_data *vc) update_attr(vc); } -static void respond_string(const char *p, struct tty_struct *tty) +static void respond_string(const char *p, struct tty_port *port) { while (*p) { - tty_insert_flip_char(tty, *p, 0); + tty_insert_flip_char(port, *p, 0); p++; } - tty_schedule_flip(tty); + tty_schedule_flip(port); } static void cursor_report(struct vc_data *vc, struct tty_struct *tty) @@ -1347,17 +1427,17 @@ static void cursor_report(struct vc_data *vc, struct tty_struct *tty) char buf[40]; sprintf(buf, "\033[%d;%dR", vc->vc_y + (vc->vc_decom ? vc->vc_top + 1 : 1), vc->vc_x + 1); - respond_string(buf, tty); + respond_string(buf, tty->port); } static inline void status_report(struct tty_struct *tty) { - respond_string("\033[0n", tty); /* Terminal ok */ + respond_string("\033[0n", tty->port); /* Terminal ok */ } -static inline void respond_ID(struct tty_struct * tty) +static inline void respond_ID(struct tty_struct *tty) { - respond_string(VT102ID, tty); + respond_string(VT102ID, tty->port); } void mouse_report(struct tty_struct *tty, int butt, int mrx, int mry) @@ -1366,7 +1446,7 @@ void mouse_report(struct tty_struct *tty, int butt, int mrx, int mry) sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), (char)('!' + mry)); - respond_string(buf, tty); + respond_string(buf, tty->port); } /* invoked via ioctl(TIOCLINUX) and through set_selection */ @@ -1581,9 +1661,9 @@ static void restore_cur(struct vc_data *vc) vc->vc_need_wrap = 0; } -enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, - ESpalette }; + ESpalette, ESosc }; /* console_lock is held (except via vc_init()) */ static void reset_terminal(struct vc_data *vc, int do_clear) @@ -1643,11 +1723,15 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) * Control characters can be used in the _middle_ * of an escape sequence. */ + if (vc->vc_state == ESosc && c>=8 && c<=13) /* ... except for OSC */ + return; switch (c) { case 0: return; case 7: - if (vc->vc_bell_duration) + if (vc->vc_state == ESosc) + vc->vc_state = ESnormal; + else if (vc->vc_bell_duration) kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); return; case 8: @@ -1758,7 +1842,9 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) } else if (c=='R') { /* reset palette */ reset_palette(vc); vc->vc_state = ESnormal; - } else + } else if (c>='0' && c<='9') + vc->vc_state = ESosc; + else vc->vc_state = ESnormal; return; case ESpalette: @@ -1798,9 +1884,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) vc->vc_par[vc->vc_npar] *= 10; vc->vc_par[vc->vc_npar] += c - '0'; return; - } else - vc->vc_state = ESgotpars; - case ESgotpars: + } vc->vc_state = ESnormal; switch(c) { case 'h': @@ -2014,6 +2098,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) vc->vc_translate = set_translate(vc->vc_G1_charset, vc); vc->vc_state = ESnormal; return; + case ESosc: + return; default: vc->vc_state = ESnormal; } @@ -2811,8 +2897,10 @@ static void con_shutdown(struct tty_struct *tty) console_unlock(); } +static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) +module_param_named(color, default_color, int, S_IRUGO | S_IWUSR); module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR); module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR); @@ -2834,7 +2922,7 @@ static void vc_init(struct vc_data *vc, unsigned int rows, vc->vc_palette[k++] = default_grn[j] ; vc->vc_palette[k++] = default_blu[j] ; } - vc->vc_def_color = 0x07; /* white */ + vc->vc_def_color = default_color; vc->vc_ulcolor = default_underline_color; vc->vc_itcolor = default_italic_color; vc->vc_halfcolor = 0x08; /* grey */ @@ -2987,7 +3075,7 @@ int __init vty_init(const struct file_operations *console_fops) static struct class *vtconsole_class; -static int bind_con_driver(const struct consw *csw, int first, int last, +static int do_bind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; @@ -2998,7 +3086,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last, if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3083,11 +3171,11 @@ static int bind_con_driver(const struct consw *csw, int first, int last, retval = 0; err: - console_unlock(); module_put(owner); return retval; }; + #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3105,24 +3193,8 @@ static int con_is_graphics(const struct consw *csw, int first, int last) return retval; } -/** - * unbind_con_driver - unbind a console driver - * @csw: pointer to console driver to unregister - * @first: first in range of consoles that @csw should be unbound from - * @last: last in range of consoles that @csw should be unbound from - * @deflt: should next bound console driver be default after @csw is unbound? - * - * To unbind a driver from all possible consoles, pass 0 as @first and - * %MAX_NR_CONSOLES as @last. - * - * @deflt controls whether the console that ends up replacing @csw should be - * the default console. - * - * RETURNS: - * -ENODEV if @csw isn't a registered console driver or can't be unregistered - * or 0 on success. - */ -int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) +/* unlocked version of unbind_con_driver() */ +int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; const struct consw *defcsw = NULL; @@ -3132,7 +3204,7 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered and if it is unbindable */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3145,10 +3217,8 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } retval = -ENODEV; @@ -3156,23 +3226,18 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_back = ®istered_con_driver[i]; - if (con_back->con && - !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { + if (con_back->con && con_back->con != csw) { defcsw = con_back->con; retval = 0; break; } } - if (retval) { - console_unlock(); + if (retval) goto err; - } - if (!con_is_bound(csw)) { - console_unlock(); + if (!con_is_bound(csw)) goto err; - } first = max(first, con_driver->first); last = min(last, con_driver->last); @@ -3199,15 +3264,14 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!con_is_bound(csw)) con_driver->flag &= ~CON_DRIVER_FLAG_INIT; - console_unlock(); /* ignore return value, binding should not fail */ - bind_con_driver(defcsw, first, last, deflt); + do_bind_con_driver(defcsw, first, last, deflt); err: module_put(owner); return retval; } -EXPORT_SYMBOL(unbind_con_driver); +EXPORT_SYMBOL_GPL(do_unbind_con_driver); static int vt_bind(struct con_driver *con) { @@ -3248,8 +3312,11 @@ static int vt_bind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - bind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + } first = -1; last = -1; @@ -3264,6 +3331,7 @@ static int vt_unbind(struct con_driver *con) { const struct consw *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; + int ret; if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || con_is_graphics(con->con, con->first, con->last)) @@ -3287,8 +3355,13 @@ static int vt_unbind(struct con_driver *con) if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - unbind_con_driver(csw, first, last, deflt); + if (first != -1) { + console_lock(); + ret = do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + if (ret != 0) + return ret; + } first = -1; last = -1; @@ -3492,28 +3565,18 @@ int con_debug_leave(void) } EXPORT_SYMBOL_GPL(con_debug_leave); -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) +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; + WARN_CONSOLE_UNLOCKED(); + if (!try_module_get(owner)) return -ENODEV; - console_lock(); - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; @@ -3566,14 +3629,13 @@ int register_con_driver(const struct consw *csw, int first, int last) } err: - console_unlock(); module_put(owner); return retval; } -EXPORT_SYMBOL(register_con_driver); + /** - * unregister_con_driver - unregister console driver from console layer + * do_unregister_con_driver - unregister console driver from console layer * @csw: console driver * * DESCRIPTION: All drivers that registers to the console layer must @@ -3583,21 +3645,22 @@ EXPORT_SYMBOL(register_con_driver); * * The driver must unbind first prior to unregistration. */ -int unregister_con_driver(const struct consw *csw) +int do_unregister_con_driver(const struct consw *csw) { - int i, retval = -ENODEV; - - console_lock(); + int i; /* cannot unregister a bound driver */ if (con_is_bound(csw)) - goto err; + return -EBUSY; + + if (csw == conswitchp) + return -EINVAL; for (i = 0; i < MAX_NR_CON_DRIVER; i++) { struct con_driver *con_driver = ®istered_con_driver[i]; if (con_driver->con == csw && - con_driver->flag & CON_DRIVER_FLAG_MODULE) { + con_driver->flag & CON_DRIVER_FLAG_INIT) { vtconsole_deinit_device(con_driver); device_destroy(vtconsole_class, MKDEV(0, con_driver->node)); @@ -3608,39 +3671,40 @@ int unregister_con_driver(const struct consw *csw) con_driver->flag = 0; con_driver->first = 0; con_driver->last = 0; - retval = 0; - break; + return 0; } } -err: - console_unlock(); - return retval; + + return -ENODEV; } -EXPORT_SYMBOL(unregister_con_driver); +EXPORT_SYMBOL_GPL(do_unregister_con_driver); /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * do_take_over_console is basically a register followed by unbind */ -int take_over_console(const struct consw *csw, int first, int last, int deflt) +int do_take_over_console(const struct consw *csw, int first, int last, int deflt) { int err; - err = register_con_driver(csw, first, last); - /* if we get an busy error we still want to bind the console driver + err = do_register_con_driver(csw, first, last); + /* + * If we get an busy error we still want to bind the console driver * and return success, as we may have unbound the console driver - * but not unregistered it. - */ + * but not unregistered it. + */ if (err == -EBUSY) err = 0; if (!err) - bind_con_driver(csw, first, last, deflt); + do_bind_con_driver(csw, first, last, deflt); return err; } +EXPORT_SYMBOL_GPL(do_take_over_console); + /* * give_up_console is a wrapper to unregister_con_driver. It will only @@ -3648,7 +3712,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt) */ void give_up_console(const struct consw *csw) { - unregister_con_driver(csw); + console_lock(); + do_unregister_con_driver(csw); + console_unlock(); } static int __init vtconsole_class_init(void) @@ -4204,6 +4270,5 @@ EXPORT_SYMBOL(console_blanked); EXPORT_SYMBOL(vc_cons); EXPORT_SYMBOL(global_cursor_default); #ifndef VT_SINGLE_DRIVER -EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); #endif diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 98ff1735eaf..2bd78e2ac8e 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -283,6 +283,48 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ return 0; } +/* deallocate a single console, if possible (leave 0) */ +static int vt_disallocate(unsigned int vc_num) +{ + struct vc_data *vc = NULL; + int ret = 0; + + console_lock(); + if (VT_BUSY(vc_num)) + ret = -EBUSY; + else if (vc_num) + vc = vc_deallocate(vc_num); + console_unlock(); + + if (vc && vc_num >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc->port); + kfree(vc); + } + + return ret; +} + +/* deallocate all unused consoles, but leave 0 */ +static void vt_disallocate_all(void) +{ + struct vc_data *vc[MAX_NR_CONSOLES]; + int i; + + console_lock(); + for (i = 1; i < MAX_NR_CONSOLES; i++) + if (!VT_BUSY(i)) + vc[i] = vc_deallocate(i); + else + vc[i] = NULL; + console_unlock(); + + for (i = 1; i < MAX_NR_CONSOLES; i++) { + if (vc[i] && i >= MIN_NR_CONSOLES) { + tty_port_destroy(&vc[i]->port); + kfree(vc[i]); + } + } +} /* @@ -769,24 +811,10 @@ int vt_ioctl(struct tty_struct *tty, ret = -ENXIO; break; } - if (arg == 0) { - /* deallocate all unused consoles, but leave 0 */ - console_lock(); - for (i=1; i<MAX_NR_CONSOLES; i++) - if (! VT_BUSY(i)) - vc_deallocate(i); - console_unlock(); - } else { - /* deallocate a single console, if possible */ - arg--; - if (VT_BUSY(arg)) - ret = -EBUSY; - else if (arg) { /* leave 0 */ - console_lock(); - vc_deallocate(arg); - console_unlock(); - } - } + if (arg == 0) + vt_disallocate_all(); + else + ret = vt_disallocate(--arg); break; case VT_RESIZE: |
