diff options
Diffstat (limited to 'drivers/tty')
76 files changed, 1854 insertions, 3558 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index bd7cc0527999..8816f53e004d 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -60,9 +60,13 @@ config VT_CONSOLE If unsure, say Y. +config VT_CONSOLE_SLEEP + def_bool y + depends on VT_CONSOLE && PM_SLEEP + config HW_CONSOLE bool - depends on VT && !S390 && !UML + depends on VT && !UML default y config VT_HW_CONSOLE_BINDING @@ -350,3 +354,37 @@ config TRACE_SINK If you select this option, you need to select "Trace data router for MIPI P1149.7 cJTAG standard". + +config PPC_EPAPR_HV_BYTECHAN + tristate "ePAPR hypervisor byte channel driver" + depends on PPC + help + This driver creates /dev entries for each ePAPR hypervisor byte + channel, thereby allowing applications to communicate with byte + channels as if they were serial ports. + +config PPC_EARLY_DEBUG_EHV_BC + bool "Early console (udbg) support for ePAPR hypervisors" + depends on PPC_EPAPR_HV_BYTECHAN + help + Select this option to enable early console (a.k.a. "udbg") support + via an ePAPR byte channel. You also need to choose the byte channel + handle below. + +config PPC_EARLY_DEBUG_EHV_BC_HANDLE + int "Byte channel handle for early console (udbg)" + depends on PPC_EARLY_DEBUG_EHV_BC + default 0 + help + If you want early console (udbg) output through a byte channel, + specify the handle of the byte channel to use. + + For this to work, the byte channel driver must be compiled + in-kernel, not as a module. + + Note that only one early console driver can be enabled, so don't + enable any others if you enable this one. + + 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. diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index ea89b0bd15fe..2953059530e4 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -26,5 +26,6 @@ obj-$(CONFIG_ROCKETPORT) += rocket.o 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-y += ipwireless/ diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 220579592c20..b84c83456dcc 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1529,7 +1529,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) { struct async_struct * info = tty->driver_data; unsigned long orig_jiffies, char_time; - int tty_was_locked = tty_locked(); int lsr; if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) @@ -1541,12 +1540,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) orig_jiffies = jiffies; /* - * tty_wait_until_sent is called from lots of places, - * with or without the BTM. - */ - if (!tty_was_locked) - tty_lock(); - /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. @@ -1586,8 +1579,7 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) break; } __set_current_state(TASK_RUNNING); - if (!tty_was_locked) - tty_unlock(); + #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); #endif @@ -2025,7 +2017,7 @@ static int __init amiga_serial_probe(struct platform_device *pdev) if (error) goto fail_unregister; - error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED, + error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0, "serial RX", state); if (error) goto fail_free_irq; diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index c0e8f2eeb886..c9bf779481d8 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -45,7 +45,6 @@ #undef CY_DEBUG_IO #undef CY_DEBUG_COUNT #undef CY_DEBUG_DTR -#undef CY_DEBUG_WAIT_UNTIL_SENT #undef CY_DEBUG_INTERRUPTS #undef CY_16Y_HACK #undef CY_ENABLE_MONITORING @@ -1678,16 +1677,10 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) */ if (!timeout || timeout > 2 * info->timeout) timeout = 2 * info->timeout; -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "In cy_wait_until_sent(%d) check=%d, jiff=%lu...", - timeout, char_time, jiffies); -#endif + card = info->card; if (!cy_is_Z(card)) { while (cyy_readb(info, CySRER) & CyTxRdy) { -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies); -#endif if (msleep_interruptible(jiffies_to_msecs(char_time))) break; if (timeout && time_after(jiffies, orig_jiffies + @@ -1697,9 +1690,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) } /* Run one more char cycle */ msleep_interruptible(jiffies_to_msecs(char_time * 5)); -#ifdef CY_DEBUG_WAIT_UNTIL_SENT - printk(KERN_DEBUG "Clean (jiff=%lu)...done\n", jiffies); -#endif } static void cy_flush_buffer(struct tty_struct *tty) @@ -3377,7 +3367,7 @@ static int __init cy_detect_isa(void) /* allocate IRQ */ if (request_irq(cy_isa_irq, cyy_interrupt, - IRQF_DISABLED, "Cyclom-Y", &cy_card[j])) { + 0, "Cyclom-Y", &cy_card[j])) { printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but " "could not allocate IRQ#%d.\n", (unsigned long)cy_isa_address, cy_isa_irq); diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c new file mode 100644 index 000000000000..1595dba0072c --- /dev/null +++ b/drivers/tty/ehv_bytechan.c @@ -0,0 +1,881 @@ +/* ePAPR hypervisor byte channel device driver + * + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * Author: Timur Tabi <timur@freescale.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + * This driver support three distinct interfaces, all of which are related to + * ePAPR hypervisor byte channels. + * + * 1) An early-console (udbg) driver. This provides early console output + * through a byte channel. The byte channel handle must be specified in a + * Kconfig option. + * + * 2) A normal console driver. Output is sent to the byte channel designated + * for stdout in the device tree. The console driver is for handling kernel + * printk calls. + * + * 3) A tty driver, which is used to handle user-space input and output. The + * byte channel used for the console is designated as the default tty. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <asm/epapr_hcalls.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/cdev.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/circ_buf.h> +#include <asm/udbg.h> + +/* The size of the transmit circular buffer. This must be a power of two. */ +#define BUF_SIZE 2048 + +/* Per-byte channel private data */ +struct ehv_bc_data { + struct device *dev; + struct tty_port port; + uint32_t handle; + unsigned int rx_irq; + unsigned int tx_irq; + + spinlock_t lock; /* lock for transmit buffer */ + unsigned char buf[BUF_SIZE]; /* transmit circular buffer */ + unsigned int head; /* circular buffer head */ + unsigned int tail; /* circular buffer tail */ + + int tx_irq_enabled; /* true == TX interrupt is enabled */ +}; + +/* Array of byte channel objects */ +static struct ehv_bc_data *bcs; + +/* Byte channel handle for stdout (and stdin), taken from device tree */ +static unsigned int stdout_bc; + +/* Virtual IRQ for the byte channel handle for stdin, taken from device tree */ +static unsigned int stdout_irq; + +/**************************** SUPPORT FUNCTIONS ****************************/ + +/* + * Enable the transmit interrupt + * + * Unlike a serial device, byte channels have no mechanism for disabling their + * own receive or transmit interrupts. To emulate that feature, we toggle + * the IRQ in the kernel. + * + * We cannot just blindly call enable_irq() or disable_irq(), because these + * calls are reference counted. This means that we cannot call enable_irq() + * if interrupts are already enabled. This can happen in two situations: + * + * 1. The tty layer makes two back-to-back calls to ehv_bc_tty_write() + * 2. A transmit interrupt occurs while executing ehv_bc_tx_dequeue() + * + * To work around this, we keep a flag to tell us if the IRQ is enabled or not. + */ +static void enable_tx_interrupt(struct ehv_bc_data *bc) +{ + if (!bc->tx_irq_enabled) { + enable_irq(bc->tx_irq); + bc->tx_irq_enabled = 1; + } +} + +static void disable_tx_interrupt(struct ehv_bc_data *bc) +{ + if (bc->tx_irq_enabled) { + disable_irq_nosync(bc->tx_irq); + bc->tx_irq_enabled = 0; + } +} + +/* + * find the byte channel handle to use for the console + * + * The byte channel to be used for the console is specified via a "stdout" + * property in the /chosen node. + * + * For compatible with legacy device trees, we also look for a "stdout" alias. + */ +static int find_console_handle(void) +{ + struct device_node *np, *np2; + const char *sprop = NULL; + const uint32_t *iprop; + + np = of_find_node_by_path("/chosen"); + if (np) + sprop = of_get_property(np, "stdout-path", NULL); + + if (!np || !sprop) { + of_node_put(np); + np = of_find_node_by_name(NULL, "aliases"); + if (np) + sprop = of_get_property(np, "stdout", NULL); + } + + if (!sprop) { + of_node_put(np); + return 0; + } + + /* We don't care what the aliased node is actually called. We only + * care if it's compatible with "epapr,hv-byte-channel", because that + * indicates that it's a byte channel node. We use a temporary + * variable, 'np2', because we can't release 'np' until we're done with + * 'sprop'. + */ + np2 = of_find_node_by_path(sprop); + of_node_put(np); + np = np2; + if (!np) { + pr_warning("ehv-bc: stdout node '%s' does not exist\n", sprop); + return 0; + } + + /* Is it a byte channel? */ + if (!of_device_is_compatible(np, "epapr,hv-byte-channel")) { + of_node_put(np); + return 0; + } + + stdout_irq = irq_of_parse_and_map(np, 0); + if (stdout_irq == NO_IRQ) { + pr_err("ehv-bc: no 'interrupts' property in %s node\n", sprop); + of_node_put(np); + return 0; + } + + /* + * The 'hv-handle' property contains the handle for this byte channel. + */ + iprop = of_get_property(np, "hv-handle", NULL); + if (!iprop) { + pr_err("ehv-bc: no 'hv-handle' property in %s node\n", + np->name); + of_node_put(np); + return 0; + } + stdout_bc = be32_to_cpu(*iprop); + + of_node_put(np); + return 1; +} + +/*************************** EARLY CONSOLE DRIVER ***************************/ + +#ifdef CONFIG_PPC_EARLY_DEBUG_EHV_BC + +/* + * send a byte to a byte channel, wait if necessary + * + * This function sends a byte to a byte channel, and it waits and + * retries if the byte channel is full. It returns if the character + * has been sent, or if some error has occurred. + * + */ +static void byte_channel_spin_send(const char data) +{ + int ret, count; + + do { + count = 1; + ret = ev_byte_channel_send(CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE, + &count, &data); + } while (ret == EV_EAGAIN); +} + +/* + * The udbg subsystem calls this function to display a single character. + * We convert CR to a CR/LF. + */ +static void ehv_bc_udbg_putc(char c) +{ + if (c == '\n') + byte_channel_spin_send('\r'); + + byte_channel_spin_send(c); +} + +/* + * early console initialization + * + * PowerPC kernels support an early printk console, also known as udbg. + * This function must be called via the ppc_md.init_early function pointer. + * At this point, the device tree has been unflattened, so we can obtain the + * byte channel handle for stdout. + * + * We only support displaying of characters (putc). We do not support + * keyboard input. + */ +void __init udbg_init_ehv_bc(void) +{ + unsigned int rx_count, tx_count; + unsigned int ret; + + /* Verify the byte channel handle */ + ret = ev_byte_channel_poll(CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE, + &rx_count, &tx_count); + if (ret) + return; + + udbg_putc = ehv_bc_udbg_putc; + register_early_udbg_console(); + + udbg_printf("ehv-bc: early console using byte channel handle %u\n", + CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); +} + +#endif + +/****************************** CONSOLE DRIVER ******************************/ + +static struct tty_driver *ehv_bc_driver; + +/* + * Byte channel console sending worker function. + * + * For consoles, if the output buffer is full, we should just spin until it + * clears. + */ +static int ehv_bc_console_byte_channel_send(unsigned int handle, const char *s, + unsigned int count) +{ + unsigned int len; + int ret = 0; + + while (count) { + len = min_t(unsigned int, count, EV_BYTE_CHANNEL_MAX_BYTES); + do { + ret = ev_byte_channel_send(handle, &len, s); + } while (ret == EV_EAGAIN); + count -= len; + s += len; + } + + return ret; +} + +/* + * write a string to the console + * + * This function gets called to write a string from the kernel, typically from + * a printk(). This function spins until all data is written. + * + * We copy the data to a temporary buffer because we need to insert a \r in + * front of every \n. It's more efficient to copy the data to the buffer than + * it is to make multiple hcalls for each character or each newline. + */ +static void ehv_bc_console_write(struct console *co, const char *s, + unsigned int count) +{ + char s2[EV_BYTE_CHANNEL_MAX_BYTES]; + unsigned int i, j = 0; + char c; + + for (i = 0; i < count; i++) { + c = *s++; + + if (c == '\n') + s2[j++] = '\r'; + + s2[j++] = c; + if (j >= (EV_BYTE_CHANNEL_MAX_BYTES - 1)) { + if (ehv_bc_console_byte_channel_send(stdout_bc, s2, j)) + return; + j = 0; + } + } + + if (j) + ehv_bc_console_byte_channel_send(stdout_bc, s2, j); +} + +/* + * When /dev/console is opened, the kernel iterates the console list looking + * for one with ->device and then calls that method. On success, it expects + * the passed-in int* to contain the minor number to use. + */ +static struct tty_driver *ehv_bc_console_device(struct console *co, int *index) +{ + *index = co->index; + + return ehv_bc_driver; +} + +static struct console ehv_bc_console = { + .name = "ttyEHV", + .write = ehv_bc_console_write, + .device = ehv_bc_console_device, + .flags = CON_PRINTBUFFER | CON_ENABLED, +}; + +/* + * Console initialization + * + * This is the first function that is called after the device tree is + * available, so here is where we determine the byte channel handle and IRQ for + * stdout/stdin, even though that information is used by the tty and character + * drivers. + */ +static int __init ehv_bc_console_init(void) +{ + if (!find_console_handle()) { + pr_debug("ehv-bc: stdout is not a byte channel\n"); + return -ENODEV; + } + +#ifdef CONFIG_PPC_EARLY_DEBUG_EHV_BC + /* Print a friendly warning if the user chose the wrong byte channel + * handle for udbg. + */ + if (stdout_bc != CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE) + pr_warning("ehv-bc: udbg handle %u is not the stdout handle\n", + CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); +#endif + + /* add_preferred_console() must be called before register_console(), + otherwise it won't work. However, we don't want to enumerate all the + byte channels here, either, since we only care about one. */ + + add_preferred_console(ehv_bc_console.name, ehv_bc_console.index, NULL); + register_console(&ehv_bc_console); + + pr_info("ehv-bc: registered console driver for byte channel %u\n", + stdout_bc); + + return 0; +} +console_initcall(ehv_bc_console_init); + +/******************************** TTY DRIVER ********************************/ + +/* + * byte channel receive interupt handler + * + * This ISR is called whenever data is available on a byte channel. + */ +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' is the maximum amount of data the TTY layer can accept at + * this time. However, during testing, I was never able to get 'count' + * to be less than 'rx_count'. I'm not sure whether I'm calling it + * correctly. + */ + + while (count > 0) { + len = min_t(unsigned int, count, sizeof(buffer)); + + /* Read some data from the byte channel. This function will + * never return more than EV_BYTE_CHANNEL_MAX_BYTES bytes. + */ + ev_byte_channel_receive(bc->handle, &len, buffer); + + /* 'len' is now the amount of data that's been received. 'len' + * can't be zero, and most likely it's equal to one. + */ + + /* Pass the received data to the tty layer. */ + ret = tty_insert_flip_string(ttys, 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 + * full, which should never happen. If it does happen, we can + * exit gracefully, but we drop the last 'len - ret' characters + * that we read from the byte channel. + */ + if (ret != len) + break; + + count -= len; + } + + /* Tell the tty layer that we're done. */ + tty_flip_buffer_push(ttys); + + tty_kref_put(ttys); + + return IRQ_HANDLED; +} + +/* + * dequeue the transmit buffer to the hypervisor + * + * This function, which can be called in interrupt context, dequeues as much + * data as possible from the transmit buffer to the byte channel. + */ +static void ehv_bc_tx_dequeue(struct ehv_bc_data *bc) +{ + unsigned int count; + unsigned int len, ret; + unsigned long flags; + + do { + spin_lock_irqsave(&bc->lock, flags); + len = min_t(unsigned int, + CIRC_CNT_TO_END(bc->head, bc->tail, BUF_SIZE), + EV_BYTE_CHANNEL_MAX_BYTES); + + ret = ev_byte_channel_send(bc->handle, &len, bc->buf + bc->tail); + + /* 'len' is valid only if the return code is 0 or EV_EAGAIN */ + if (!ret || (ret == EV_EAGAIN)) + bc->tail = (bc->tail + len) & (BUF_SIZE - 1); + + count = CIRC_CNT(bc->head, bc->tail, BUF_SIZE); + spin_unlock_irqrestore(&bc->lock, flags); + } while (count && !ret); + + spin_lock_irqsave(&bc->lock, flags); + if (CIRC_CNT(bc->head, bc->tail, BUF_SIZE)) + /* + * If we haven't emptied the buffer, then enable the TX IRQ. + * We'll get an interrupt when there's more room in the + * hypervisor's output buffer. + */ + enable_tx_interrupt(bc); + else + disable_tx_interrupt(bc); + spin_unlock_irqrestore(&bc->lock, flags); +} + +/* + * byte channel transmit interupt handler + * + * This ISR is called whenever space becomes available for transmitting + * characters on a byte channel. + */ +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); + } + + return IRQ_HANDLED; +} + +/* + * This function is called when the tty layer has data for us send. We store + * the data first in a circular buffer, and then dequeue as much of that data + * as possible. + * + * We don't need to worry about whether there is enough room in the buffer for + * all the data. The purpose of ehv_bc_tty_write_room() is to tell the tty + * layer how much data it can safely send to us. We guarantee that + * ehv_bc_tty_write_room() will never lie, so the tty layer will never send us + * too much data. + */ +static int ehv_bc_tty_write(struct tty_struct *ttys, const unsigned char *s, + int count) +{ + struct ehv_bc_data *bc = ttys->driver_data; + unsigned long flags; + unsigned int len; + unsigned int written = 0; + + while (1) { + spin_lock_irqsave(&bc->lock, flags); + len = CIRC_SPACE_TO_END(bc->head, bc->tail, BUF_SIZE); + if (count < len) + len = count; + if (len) { + memcpy(bc->buf + bc->head, s, len); + bc->head = (bc->head + len) & (BUF_SIZE - 1); + } + spin_unlock_irqrestore(&bc->lock, flags); + if (!len) + break; + + s += len; + count -= len; + written += len; + } + + ehv_bc_tx_dequeue(bc); + + return written; +} + +/* + * This function can be called multiple times for a given tty_struct, which is + * why we initialize bc->ttys in ehv_bc_tty_port_activate() instead. + * + * The tty layer will still call this function even if the device was not + * registered (i.e. tty_register_device() was not called). This happens + * because tty_register_device() is optional and some legacy drivers don't + * use it. So we need to check for that. + */ +static int ehv_bc_tty_open(struct tty_struct *ttys, struct file *filp) +{ + struct ehv_bc_data *bc = &bcs[ttys->index]; + + if (!bc->dev) + return -ENODEV; + + return tty_port_open(&bc->port, ttys, filp); +} + +/* + * Amazingly, if ehv_bc_tty_open() returns an error code, the tty layer will + * still call this function to close the tty device. So we can't assume that + * the tty port has been initialized. + */ +static void ehv_bc_tty_close(struct tty_struct *ttys, struct file *filp) +{ + struct ehv_bc_data *bc = &bcs[ttys->index]; + + if (bc->dev) + tty_port_close(&bc->port, ttys, filp); +} + +/* + * Return the amount of space in the output buffer + * + * This is actually a contract between the driver and the tty layer outlining + * how much write room the driver can guarantee will be sent OR BUFFERED. This + * driver MUST honor the return value. + */ +static int ehv_bc_tty_write_room(struct tty_struct *ttys) +{ + struct ehv_bc_data *bc = ttys->driver_data; + unsigned long flags; + int count; + + spin_lock_irqsave(&bc->lock, flags); + count = CIRC_SPACE(bc->head, bc->tail, BUF_SIZE); + spin_unlock_irqrestore(&bc->lock, flags); + + return count; +} + +/* + * Stop sending data to the tty layer + * + * This function is called when the tty layer's input buffers are getting full, + * so the driver should stop sending it data. The easiest way to do this is to + * disable the RX IRQ, which will prevent ehv_bc_tty_rx_isr() from being + * called. + * + * The hypervisor will continue to queue up any incoming data. If there is any + * data in the queue when the RX interrupt is enabled, we'll immediately get an + * RX interrupt. + */ +static void ehv_bc_tty_throttle(struct tty_struct *ttys) +{ + struct ehv_bc_data *bc = ttys->driver_data; + + disable_irq(bc->rx_irq); +} + +/* + * Resume sending data to the tty layer + * + * This function is called after previously calling ehv_bc_tty_throttle(). The + * tty layer's input buffers now have more room, so the driver can resume + * sending it data. + */ +static void ehv_bc_tty_unthrottle(struct tty_struct *ttys) +{ + struct ehv_bc_data *bc = ttys->driver_data; + + /* If there is any data in the queue when the RX interrupt is enabled, + * we'll immediately get an RX interrupt. + */ + enable_irq(bc->rx_irq); +} + +static void ehv_bc_tty_hangup(struct tty_struct *ttys) +{ + struct ehv_bc_data *bc = ttys->driver_data; + + ehv_bc_tx_dequeue(bc); + tty_port_hangup(&bc->port); +} + +/* + * TTY driver operations + * + * If we could ask the hypervisor how much data is still in the TX buffer, or + * at least how big the TX buffers are, then we could implement the + * .wait_until_sent and .chars_in_buffer functions. + */ +static const struct tty_operations ehv_bc_ops = { + .open = ehv_bc_tty_open, + .close = ehv_bc_tty_close, + .write = ehv_bc_tty_write, + .write_room = ehv_bc_tty_write_room, + .throttle = ehv_bc_tty_throttle, + .unthrottle = ehv_bc_tty_unthrottle, + .hangup = ehv_bc_tty_hangup, +}; + +/* + * initialize the TTY port + * + * This function will only be called once, no matter how many times + * ehv_bc_tty_open() is called. That's why we register the ISR here, and also + * why we initialize tty_struct-related variables here. + */ +static int ehv_bc_tty_port_activate(struct tty_port *port, + struct tty_struct *ttys) +{ + struct ehv_bc_data *bc = container_of(port, struct ehv_bc_data, port); + int ret; + + ttys->driver_data = bc; + + ret = request_irq(bc->rx_irq, ehv_bc_tty_rx_isr, 0, "ehv-bc", bc); + if (ret < 0) { + dev_err(bc->dev, "could not request rx irq %u (ret=%i)\n", + bc->rx_irq, ret); + return ret; + } + + /* request_irq also enables the IRQ */ + bc->tx_irq_enabled = 1; + + ret = request_irq(bc->tx_irq, ehv_bc_tty_tx_isr, 0, "ehv-bc", bc); + if (ret < 0) { + dev_err(bc->dev, "could not request tx irq %u (ret=%i)\n", + bc->tx_irq, ret); + free_irq(bc->rx_irq, bc); + return ret; + } + + /* The TX IRQ is enabled only when we can't write all the data to the + * byte channel at once, so by default it's disabled. + */ + disable_tx_interrupt(bc); + + return 0; +} + +static void ehv_bc_tty_port_shutdown(struct tty_port *port) +{ + struct ehv_bc_data *bc = container_of(port, struct ehv_bc_data, port); + + free_irq(bc->tx_irq, bc); + free_irq(bc->rx_irq, bc); +} + +static const struct tty_port_operations ehv_bc_tty_port_ops = { + .activate = ehv_bc_tty_port_activate, + .shutdown = ehv_bc_tty_port_shutdown, +}; + +static int __devinit ehv_bc_tty_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct ehv_bc_data *bc; + const uint32_t *iprop; + unsigned int handle; + int ret; + static unsigned int index = 1; + unsigned int i; + + iprop = of_get_property(np, "hv-handle", NULL); + if (!iprop) { + dev_err(&pdev->dev, "no 'hv-handle' property in %s node\n", + np->name); + return -ENODEV; + } + + /* We already told the console layer that the index for the console + * device is zero, so we need to make sure that we use that index when + * we probe the console byte channel node. + */ + handle = be32_to_cpu(*iprop); + i = (handle == stdout_bc) ? 0 : index++; + bc = &bcs[i]; + + bc->handle = handle; + bc->head = 0; + bc->tail = 0; + spin_lock_init(&bc->lock); + + bc->rx_irq = irq_of_parse_and_map(np, 0); + bc->tx_irq = irq_of_parse_and_map(np, 1); + if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) { + dev_err(&pdev->dev, "no 'interrupts' property in %s node\n", + np->name); + ret = -ENODEV; + goto error; + } + + bc->dev = tty_register_device(ehv_bc_driver, i, &pdev->dev); + if (IS_ERR(bc->dev)) { + ret = PTR_ERR(bc->dev); + dev_err(&pdev->dev, "could not register tty (ret=%i)\n", ret); + goto error; + } + + tty_port_init(&bc->port); + bc->port.ops = &ehv_bc_tty_port_ops; + + dev_set_drvdata(&pdev->dev, bc); + + dev_info(&pdev->dev, "registered /dev/%s%u for byte channel %u\n", + ehv_bc_driver->name, i, bc->handle); + + return 0; + +error: + irq_dispose_mapping(bc->tx_irq); + irq_dispose_mapping(bc->rx_irq); + + memset(bc, 0, sizeof(struct ehv_bc_data)); + return ret; +} + +static int ehv_bc_tty_remove(struct platform_device *pdev) +{ + struct ehv_bc_data *bc = dev_get_drvdata(&pdev->dev); + + tty_unregister_device(ehv_bc_driver, bc - bcs); + + irq_dispose_mapping(bc->tx_irq); + irq_dispose_mapping(bc->rx_irq); + + return 0; +} + +static const struct of_device_id ehv_bc_tty_of_ids[] = { + { .compatible = "epapr,hv-byte-channel" }, + {} +}; + +static struct platform_driver ehv_bc_tty_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ehv-bc", + .of_match_table = ehv_bc_tty_of_ids, + }, + .probe = ehv_bc_tty_probe, + .remove = ehv_bc_tty_remove, +}; + +/** + * ehv_bc_init - ePAPR hypervisor byte channel driver initialization + * + * This function is called when this module is loaded. + */ +static int __init ehv_bc_init(void) +{ + struct device_node *np; + unsigned int count = 0; /* Number of elements in bcs[] */ + int ret; + + pr_info("ePAPR hypervisor byte channel driver\n"); + + /* Count the number of byte channels */ + for_each_compatible_node(np, NULL, "epapr,hv-byte-channel") + count++; + + if (!count) + return -ENODEV; + + /* The array index of an element in bcs[] is the same as the tty index + * for that element. If you know the address of an element in the + * array, then you can use pointer math (e.g. "bc - bcs") to get its + * tty index. + */ + bcs = kzalloc(count * sizeof(struct ehv_bc_data), GFP_KERNEL); + if (!bcs) + return -ENOMEM; + + ehv_bc_driver = alloc_tty_driver(count); + if (!ehv_bc_driver) { + ret = -ENOMEM; + goto error; + } + + ehv_bc_driver->owner = THIS_MODULE; + ehv_bc_driver->driver_name = "ehv-bc"; + ehv_bc_driver->name = ehv_bc_console.name; + ehv_bc_driver->type = TTY_DRIVER_TYPE_CONSOLE; + ehv_bc_driver->subtype = SYSTEM_TYPE_CONSOLE; + ehv_bc_driver->init_termios = tty_std_termios; + ehv_bc_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(ehv_bc_driver, &ehv_bc_ops); + + ret = tty_register_driver(ehv_bc_driver); + if (ret) { + pr_err("ehv-bc: could not register tty driver (ret=%i)\n", ret); + goto error; + } + + ret = platform_driver_register(&ehv_bc_tty_driver); + if (ret) { + pr_err("ehv-bc: could not register platform driver (ret=%i)\n", + ret); + goto error; + } + + return 0; + +error: + if (ehv_bc_driver) { + tty_unregister_driver(ehv_bc_driver); + put_tty_driver(ehv_bc_driver); + } + + kfree(bcs); + + return ret; +} + + +/** + * ehv_bc_exit - ePAPR hypervisor byte channel driver termination + * + * This function is called when this driver is unloaded. + */ +static void __exit ehv_bc_exit(void) +{ + tty_unregister_driver(ehv_bc_driver); + put_tty_driver(ehv_bc_driver); + kfree(bcs); +} + +module_init(ehv_bc_init); +module_exit(ehv_bc_exit); + +MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); +MODULE_DESCRIPTION("ePAPR hypervisor byte channel driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e1aaf4f309b3..7430bc3c8d53 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -388,7 +388,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) * there is no buffered data otherwise sleeps on a wait queue * waking periodically to check chars_in_buffer(). */ - tty_wait_until_sent(tty, HVC_CLOSE_WAIT); + tty_wait_until_sent_from_close(tty, HVC_CLOSE_WAIT); } else { if (hp->count < 0) printk(KERN_ERR "hvc_close %X: oops, count is %d\n", @@ -852,7 +852,7 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, * find index to use: * see if this vterm id matches one registered for console. */ - for (i=0; i < MAX_NR_HVC_CONSOLES; i++) + for (i = 0; i < MAX_NR_HVC_CONSOLES; i++) if (vtermnos[i] == hp->vtermno && cons_ops[i] == hp->ops) break; @@ -862,9 +862,13 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, i = ++last_hvc; hp->index = i; + hvc_console.index = i; + vtermnos[i] = vtermno; + cons_ops[i] = ops; list_add_tail(&(hp->next), &hvc_structs); spin_unlock(&hvc_structs_lock); + register_console(&hvc_console); return hp; } @@ -875,6 +879,7 @@ int hvc_remove(struct hvc_struct *hp) unsigned long flags; struct tty_struct *tty; + unregister_console(&hvc_console); spin_lock_irqsave(&hp->lock, flags); tty = tty_kref_get(hp->tty); diff --git a/drivers/tty/hvc/hvc_irq.c b/drivers/tty/hvc/hvc_irq.c index 2623e177e8d6..c9adb0559f61 100644 --- a/drivers/tty/hvc/hvc_irq.c +++ b/drivers/tty/hvc/hvc_irq.c @@ -28,7 +28,7 @@ int notifier_add_irq(struct hvc_struct *hp, int irq) hp->irq_requested = 0; return 0; } - rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, + rc = request_irq(irq, hvc_handle_interrupt, 0, "hvc_console", hp); if (!rc) hp->irq_requested = 1; diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4c8b66546930..55882b5930a6 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1057,7 +1057,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, * the conn was registered and now. */ if (!(rc = request_irq(irq, &hvcs_handle_interrupt, - IRQF_DISABLED, "ibmhvcs", hvcsd))) { + 0, "ibmhvcs", hvcsd))) { /* * It is possible the vty-server was removed after the irq was * requested but before we have time to enable interrupts. @@ -1237,7 +1237,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); - tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); + tty_wait_until_sent_from_close(tty, HVCS_CLOSE_WAIT); /* * This line is important because it tells hvcs_open that this diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index c94e2f5853d8..cdfa3e02d627 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1105,7 +1105,7 @@ static int __init hvsi_init(void) struct hvsi_struct *hp = &hvsi_ports[i]; int ret = 1; - ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); + ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp); if (ret) printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", hp->virq, ret); diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c index db1cf9c328d8..e5c295ab5dec 100644 --- a/drivers/tty/isicom.c +++ b/drivers/tty/isicom.c @@ -1598,7 +1598,7 @@ static int __devinit isicom_probe(struct pci_dev *pdev, } retval = request_irq(board->irq, isicom_interrupt, - IRQF_SHARED | IRQF_DISABLED, ISICOM_NAME, board); + IRQF_SHARED, ISICOM_NAME, board); if (retval < 0) { dev_err(&pdev->dev, "Could not install handler at Irq %d. " "Card%d will be disabled.\n", board->irq, index + 1); diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 7fc8c02fea6c..8998d527232a 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2005,16 +2005,9 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) */ if (!timeout || timeout > 2 * info->timeout) timeout = 2 * info->timeout; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...", - timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif + spin_lock_irqsave(&info->slock, flags); while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif spin_unlock_irqrestore(&info->slock, flags); schedule_timeout_interruptible(char_time); spin_lock_irqsave(&info->slock, flags); @@ -2025,10 +2018,6 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) } spin_unlock_irqrestore(&info->slock, flags); set_current_state(TASK_RUNNING); - -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif } /* diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 8a50e4eebf18..4cb0d0a3e57b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -21,7 +21,6 @@ * Mostly done: ioctls for setting modes/timing * Partly done: hooks so you can pull off frames to non tty devs * Restart DLCI 0 when it closes ? - * Test basic encoding * Improve the tx engine * Resolve tx side locking by adding a queue_head and routing * all control traffic via it @@ -810,38 +809,41 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) { struct gsm_msg *msg; u8 *dp; - int len, size; + int len, total_size, size; int h = dlci->adaption - 1; - len = kfifo_len(dlci->fifo); - if (len == 0) - return 0; - - /* MTU/MRU count only the data bits */ - if (len > gsm->mtu) - len = gsm->mtu; - - size = len + h; - - msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); - /* FIXME: need a timer or something to kick this so it can't - get stuck with no work outstanding and no buffer free */ - if (msg == NULL) - return -ENOMEM; - dp = msg->data; - switch (dlci->adaption) { - case 1: /* Unstructured */ - break; - case 2: /* Unstructed with modem bits. Always one byte as we never - send inline break data */ - *dp += gsm_encode_modem(dlci); - len--; - break; + total_size = 0; + while(1) { + len = kfifo_len(dlci->fifo); + if (len == 0) + return total_size; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) + len = gsm->mtu; + + size = len + h; + + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + switch (dlci->adaption) { + case 1: /* Unstructured */ + break; + case 2: /* Unstructed with modem bits. Always one byte as we never + send inline break data */ + *dp++ = gsm_encode_modem(dlci); + break; + } + WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); + __gsm_data_queue(dlci, msg); + total_size += size; } - WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); - __gsm_data_queue(dlci, msg); /* Bytes of data we used up */ - return size; + return total_size; } /** @@ -2004,6 +2006,7 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) int i; struct gsm_dlci *dlci = gsm->dlci[0]; struct gsm_msg *txq; + struct gsm_control *gc; gsm->dead = 1; @@ -2017,6 +2020,13 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) spin_unlock(&gsm_mux_lock); WARN_ON(i == MAX_MUX); + /* In theory disconnecting DLCI 0 is sufficient but for some + modems this is apparently not the case. */ + if (dlci) { + gc = gsm_control_send(gsm, CMD_CLD, NULL, 0); + if (gc) + gsm_control_wait(gsm, gc); + } del_timer_sync(&gsm->t2_timer); /* Now we are sure T2 has stopped */ if (dlci) { @@ -2982,7 +2992,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty, struct gsm_dlci *dlci = tty->driver_data; unsigned int modem_tx = dlci->modem_tx; - modem_tx &= clear; + modem_tx &= ~clear; modem_tx |= set; if (modem_tx != dlci->modem_tx) { diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e809e9d4683c..e18604b3fc7d 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -670,12 +670,18 @@ static int ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); + retval = tty_alloc_file(filp); + if (retval) + return retval; + /* find a device that is not in use. */ tty_lock(); index = devpts_new_index(inode); tty_unlock(); - if (index < 0) - return index; + if (index < 0) { + retval = index; + goto err_file; + } mutex_lock(&tty_mutex); tty_lock(); @@ -689,27 +695,27 @@ static int ptmx_open(struct inode *inode, struct file *filp) set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - retval = tty_add_file(tty, filp); - if (retval) - goto out; + tty_add_file(tty, filp); retval = devpts_pty_new(inode, tty->link); if (retval) - goto out1; + goto err_release; retval = ptm_driver->ops->open(tty, filp); if (retval) - goto out2; -out1: + goto err_release; + tty_unlock(); - return retval; -out2: + return 0; +err_release: tty_unlock(); tty_release(inode, filp); return retval; out: devpts_kill_index(inode, index); tty_unlock(); +err_file: + tty_free_file(filp); return retval; } diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index e0a77540b8ca..a88ef9782a4f 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -235,22 +235,6 @@ static void batten_down_hatches(void) static void status_handle(struct m68k_serial *info, unsigned short status) { -#if 0 - if(status & DCD) { - if((info->port.tty->termios->c_cflag & CRTSCTS) && - ((info->curregs[3] & AUTO_ENAB)==0)) { - info->curregs[3] |= AUTO_ENAB; - info->pendregs[3] |= AUTO_ENAB; - write_zsreg(info->m68k_channel, 3, info->curregs[3]); - } - } else { - if((info->curregs[3] & AUTO_ENAB)) { - info->curregs[3] &= ~AUTO_ENAB; - info->pendregs[3] &= ~AUTO_ENAB; - write_zsreg(info->m68k_channel, 3, info->curregs[3]); - } - } -#endif /* If this is console input and this is a * 'break asserted' status change interrupt * see if we can drop into the debugger @@ -340,9 +324,6 @@ static void transmit_chars(struct m68k_serial *info) info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; - if (info->xmit_cnt < WAKEUP_CHARS) - schedule_work(&info->tqueue); - if(info->xmit_cnt <= 0) { /* All done for now... TX ints off */ uart->ustcnt &= ~USTCNT_TX_INTR_MASK; @@ -378,21 +359,6 @@ irqreturn_t rs_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void do_softint(struct work_struct *work) -{ - struct m68k_serial *info = container_of(work, struct m68k_serial, tqueue); - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; -#if 0 - if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - tty_wakeup(tty); - } -#endif -} - static int startup(struct m68k_serial * info) { m68328_uart *uart = &uart_addr[info->line]; @@ -1324,7 +1290,6 @@ rs68328_init(void) info->event = 0; info->count = 0; info->blocked_open = 0; - INIT_WORK(&info->tqueue, do_softint); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); info->line = i; @@ -1341,7 +1306,7 @@ rs68328_init(void) if (request_irq(uart_irqs[i], rs_interrupt, - IRQF_DISABLED, + 0, "M68328_UART", info)) panic("Unable to attach 68328 serial interrupt\n"); } diff --git a/drivers/tty/serial/68328serial.h b/drivers/tty/serial/68328serial.h index 8c9c3c0745db..3d2faabd766f 100644 --- a/drivers/tty/serial/68328serial.h +++ b/drivers/tty/serial/68328serial.h @@ -158,7 +158,6 @@ struct m68k_serial { int xmit_head; int xmit_tail; int xmit_cnt; - struct work_struct tqueue; wait_queue_head_t open_wait; wait_queue_head_t close_wait; }; diff --git a/drivers/tty/serial/68360serial.c b/drivers/tty/serial/68360serial.c deleted file mode 100644 index 0a3e8787ed50..000000000000 --- a/drivers/tty/serial/68360serial.c +++ /dev/null @@ -1,2979 +0,0 @@ -/* - * UART driver for 68360 CPM SCC or SMC - * Copyright (c) 2000 D. Jeff Dionne <jeff@uclinux.org>, - * Copyright (c) 2000 Michael Leslie <mleslie@lineo.ca> - * Copyright (c) 1997 Dan Malek <dmalek@jlc.net> - * - * I used the serial.c driver as the framework for this driver. - * Give credit to those guys. - * The original code was written for the MBX860 board. I tried to make - * it generic, but there may be some assumptions in the structures that - * have to be fixed later. - * To save porting time, I did not bother to change any object names - * that are not accessed outside of this file. - * It still needs lots of work........When it was easy, I included code - * to support the SCCs, but this has never been tested, nor is it complete. - * Only the SCCs support modem control, so that is not complete either. - * - * This module exports the following rs232 io functions: - * - * int rs_360_init(void); - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/serialP.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/mm.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <asm/irq.h> -#include <asm/m68360.h> -#include <asm/commproc.h> - - -#ifdef CONFIG_KGDB -extern void breakpoint(void); -extern void set_debug_traps(void); -extern int kgdb_output_string (const char* s, unsigned int count); -#endif - - -/* #ifdef CONFIG_SERIAL_CONSOLE */ /* This seems to be a post 2.0 thing - mles */ -#include <linux/console.h> -#include <linux/jiffies.h> - -/* this defines the index into rs_table for the port to use - */ -#ifndef CONFIG_SERIAL_CONSOLE_PORT -#define CONFIG_SERIAL_CONSOLE_PORT 1 /* ie SMC2 - note USE_SMC2 must be defined */ -#endif -/* #endif */ - -#if 0 -/* SCC2 for console - */ -#undef CONFIG_SERIAL_CONSOLE_PORT -#define CONFIG_SERIAL_CONSOLE_PORT 2 -#endif - - -#define TX_WAKEUP ASYNC_SHARE_IRQ - -static char *serial_name = "CPM UART driver"; -static char *serial_version = "0.03"; - -static struct tty_driver *serial_driver; -int serial_console_setup(struct console *co, char *options); - -/* - * Serial driver configuration section. Here are the various options: - */ -#define SERIAL_PARANOIA_CHECK -#define CONFIG_SERIAL_NOPAUSE_IO -#define SERIAL_DO_RESTART - -/* Set of debugging defines */ - -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - -#define _INLINE_ inline - -#define DBG_CNT(s) - -/* We overload some of the items in the data structure to meet our - * needs. For example, the port address is the CPM parameter ram - * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and - * 2 SMCs. The "hub6" field is used to indicate the channel number, with - * a flag indicating SCC or SMC, and the number is used as an index into - * the CPM parameter area for this device. - * The "type" field is currently set to 0, for PORT_UNKNOWN. It is - * not currently used. I should probably use it to indicate the port - * type of SMC or SCC. - * The SMCs do not support any modem control signals. - */ -#define smc_scc_num hub6 -#define NUM_IS_SCC ((int)0x00010000) -#define PORT_NUM(P) ((P) & 0x0000ffff) - - -#if defined (CONFIG_UCQUICC) - -volatile extern void *_periph_base; -/* sipex transceiver - * mode bits for are on pins - * - * SCC2 d16..19 - * SCC3 d20..23 - * SCC4 d24..27 - */ -#define SIPEX_MODE(n,m) ((m & 0x0f)<<(16+4*(n-1))) - -static uint sipex_mode_bits = 0x00000000; - -#endif - -/* There is no `serial_state' defined back here in 2.0. - * Try to get by with serial_struct - */ -/* #define serial_state serial_struct */ - -/* 2.4 -> 2.0 portability problem: async_icount in 2.4 has a few - * extras: */ - -#if 0 -struct async_icount_24 { - __u32 cts, dsr, rng, dcd, tx, rx; - __u32 frame, parity, overrun, brk; - __u32 buf_overrun; -} icount; -#endif - -#if 0 - -struct serial_state { - int magic; - int baud_base; - unsigned long port; - int irq; - int flags; - int hub6; - int type; - int line; - int revision; /* Chip revision (950) */ - int xmit_fifo_size; - int custom_divisor; - int count; - u8 *iomem_base; - u16 iomem_reg_shift; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ - struct async_icount_24 icount; - int io_type; - struct async_struct *info; -}; -#endif - -#define SSTATE_MAGIC 0x5302 - - - -/* SMC2 is sometimes used for low performance TDM interfaces. Define - * this as 1 if you want SMC2 as a serial port UART managed by this driver. - * Define this as 0 if you wish to use SMC2 for something else. - */ -#define USE_SMC2 1 - -#if 0 -/* Define SCC to ttySx mapping. */ -#define SCC_NUM_BASE (USE_SMC2 + 1) /* SCC base tty "number" */ - -/* Define which SCC is the first one to use for a serial port. These - * are 0-based numbers, i.e. this assumes the first SCC (SCC1) is used - * for Ethernet, and the first available SCC for serial UART is SCC2. - * NOTE: IF YOU CHANGE THIS, you have to change the PROFF_xxx and - * interrupt vectors in the table below to match. - */ -#define SCC_IDX_BASE 1 /* table index */ -#endif - - -/* Processors other than the 860 only get SMCs configured by default. - * Either they don't have SCCs or they are allocated somewhere else. - * Of course, there are now 860s without some SCCs, so we will need to - * address that someday. - * The Embedded Planet Multimedia I/O cards use TDM interfaces to the - * stereo codec parts, and we use SMC2 to help support that. - */ -static struct serial_state rs_table[] = { -/* type line PORT IRQ FLAGS smc_scc_num (F.K.A. hub6) */ - { 0, 0, PRSLOT_SMC1, CPMVEC_SMC1, 0, 0 } /* SMC1 ttyS0 */ -#if USE_SMC2 - ,{ 0, 0, PRSLOT_SMC2, CPMVEC_SMC2, 0, 1 } /* SMC2 ttyS1 */ -#endif - -#if defined(CONFIG_SERIAL_68360_SCC) - ,{ 0, 0, PRSLOT_SCC2, CPMVEC_SCC2, 0, (NUM_IS_SCC | 1) } /* SCC2 ttyS2 */ - ,{ 0, 0, PRSLOT_SCC3, CPMVEC_SCC3, 0, (NUM_IS_SCC | 2) } /* SCC3 ttyS3 */ - ,{ 0, 0, PRSLOT_SCC4, CPMVEC_SCC4, 0, (NUM_IS_SCC | 3) } /* SCC4 ttyS4 */ -#endif -}; - -#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) - -/* The number of buffer descriptors and their sizes. - */ -#define RX_NUM_FIFO 4 -#define RX_BUF_SIZE 32 -#define TX_NUM_FIFO 4 -#define TX_BUF_SIZE 32 - -#define CONSOLE_NUM_FIFO 2 -#define CONSOLE_BUF_SIZE 4 - -char *console_fifos[CONSOLE_NUM_FIFO * CONSOLE_BUF_SIZE]; - -/* The async_struct in serial.h does not really give us what we - * need, so define our own here. - */ -typedef struct serial_info { - int magic; - int flags; - - struct serial_state *state; - /* struct serial_struct *state; */ - /* struct async_struct *state; */ - - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int line; - 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 blocked_open; /* # of blocked opens */ - struct work_struct tqueue; - struct work_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - -/* CPM Buffer Descriptor pointers. - */ - QUICC_BD *rx_bd_base; - QUICC_BD *rx_cur; - QUICC_BD *tx_bd_base; - QUICC_BD *tx_cur; -} ser_info_t; - - -/* since kmalloc_init() does not get called until much after this initialization: */ -static ser_info_t quicc_ser_info[NR_PORTS]; -static char rx_buf_pool[NR_PORTS * RX_NUM_FIFO * RX_BUF_SIZE]; -static char tx_buf_pool[NR_PORTS * TX_NUM_FIFO * TX_BUF_SIZE]; - -static void change_speed(ser_info_t *info); -static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout); - -static inline int serial_paranoia_check(ser_info_t *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null async_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * This is used to figure out the divisor speeds and the timeouts, - * indexed by the termio value. The generic CPM functions are responsible - * for setting and assigning baud rate generators for us. - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; - -/* This sucks. There is a better way: */ -#if defined(CONFIG_CONSOLE_9600) - #define CONSOLE_BAUDRATE 9600 -#elif defined(CONFIG_CONSOLE_19200) - #define CONSOLE_BAUDRATE 19200 -#elif defined(CONFIG_CONSOLE_115200) - #define CONSOLE_BAUDRATE 115200 -#else - #warning "console baud rate undefined" - #define CONSOLE_BAUDRATE 9600 -#endif - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rs_360_stop(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int idx; - unsigned long flags; - volatile struct scc_regs *sccp; - volatile struct smc_regs *smcp; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm &= ~UART_SCCM_TX; - } else { - /* smcp = &cpmp->cp_smc[idx]; */ - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm &= ~SMCM_TX; - } - local_irq_restore(flags); -} - - -static void rs_360_start(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int idx; - unsigned long flags; - volatile struct scc_regs *sccp; - volatile struct smc_regs *smcp; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm |= UART_SCCM_TX; - } else { - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm |= SMCM_TX; - } - local_irq_restore(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -static _INLINE_ void receive_chars(ser_info_t *info) -{ - struct tty_struct *tty = info->port.tty; - unsigned char ch, flag, *cp; - /*int ignored = 0;*/ - int i; - ushort status; - struct async_icount *icount; - /* struct async_icount_24 *icount; */ - volatile QUICC_BD *bdp; - - icount = &info->state->icount; - - /* Just loop through the closed BDs and copy the characters into - * the buffer. - */ - bdp = info->rx_cur; - for (;;) { - if (bdp->status & BD_SC_EMPTY) /* If this one is empty */ - break; /* we are all done */ - - /* The read status mask tell us what we should do with - * incoming characters, especially if errors occur. - * One special case is the use of BD_SC_EMPTY. If - * this is not set, we are supposed to be ignoring - * inputs. In this case, just mark the buffer empty and - * continue. - */ - if (!(info->read_status_mask & BD_SC_EMPTY)) { - bdp->status |= BD_SC_EMPTY; - bdp->status &= - ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); - - if (bdp->status & BD_SC_WRAP) - bdp = info->rx_bd_base; - else - bdp++; - continue; - } - - /* Get the number of characters and the buffer pointer. - */ - i = bdp->length; - /* cp = (unsigned char *)__va(bdp->buf); */ - cp = (char *)bdp->buf; - status = bdp->status; - - while (i-- > 0) { - ch = *cp++; - icount->rx++; - -#ifdef SERIAL_DEBUG_INTR - printk("DR%02x:%02x...", ch, status); -#endif - flag = TTY_NORMAL; - - if (status & (BD_SC_BR | BD_SC_FR | - BD_SC_PR | BD_SC_OV)) { - /* - * For statistics only - */ - if (status & BD_SC_BR) - icount->brk++; - else if (status & BD_SC_PR) - icount->parity++; - else if (status & BD_SC_FR) - icount->frame++; - if (status & BD_SC_OV) - icount->overrun++; - - /* - * Now check to see if character should be - * ignored, and mask off conditions which - * should be ignored. - if (status & info->ignore_status_mask) { - if (++ignored > 100) - break; - continue; - } - */ - status &= info->read_status_mask; - - if (status & (BD_SC_BR)) { -#ifdef SERIAL_DEBUG_INTR - printk("handling break...."); -#endif - *tty->flip.flag_buf_ptr = TTY_BREAK; - if (info->flags & ASYNC_SAK) - do_SAK(tty); - } else if (status & BD_SC_PR) - flag = TTY_PARITY; - else if (status & BD_SC_FR) - flag = TTY_FRAME; - } - tty_insert_flip_char(tty, ch, flag); - if (status & BD_SC_OV) - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - /* This BD is ready to be used again. Clear status. - * Get next BD. - */ - bdp->status |= BD_SC_EMPTY; - bdp->status &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); - - if (bdp->status & BD_SC_WRAP) - bdp = info->rx_bd_base; - else - bdp++; - } - - info->rx_cur = (QUICC_BD *)bdp; - - tty_schedule_flip(tty); -} - -static _INLINE_ void receive_break(ser_info_t *info) -{ - struct tty_struct *tty = info->port.tty; - - info->state->icount.brk++; - /* Check to see if there is room in the tty buffer for - * the break. If not, we exit now, losing the break. FIXME - */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); -} - -static _INLINE_ void transmit_chars(ser_info_t *info) -{ - - if ((info->flags & TX_WAKEUP) || - (info->port.tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) { - schedule_work(&info->tqueue); - } - -#ifdef SERIAL_DEBUG_INTR - printk("THRE..."); -#endif -} - -#ifdef notdef - /* I need to do this for the SCCs, so it is left as a reminder. - */ -static _INLINE_ void check_modem_status(struct async_struct *info) -{ - int status; - /* struct async_icount *icount; */ - struct async_icount_24 *icount; - - status = serial_in(info, UART_MSR); - - if (status & UART_MSR_ANY_DELTA) { - icount = &info->state->icount; - /* update input line counters */ - if (status & UART_MSR_TERI) - icount->rng++; - if (status & UART_MSR_DDSR) - icount->dsr++; - if (status & UART_MSR_DDCD) { - icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - (status & UART_MSR_DCD)) - hardpps(); -#endif - } - if (status & UART_MSR_DCTS) - icount->cts++; - wake_up_interruptible(&info->delta_msr_wait); - } - - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { -#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) - printk("ttys%d CD now %s...", info->line, - (status & UART_MSR_DCD) ? "on" : "off"); -#endif - if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); - else { -#ifdef SERIAL_DEBUG_OPEN - printk("scheduling hangup..."); -#endif - queue_task(&info->tqueue_hangup, - &tq_scheduler); - } - } - if (info->flags & ASYNC_CTS_FLOW) { - if (info->port.tty->hw_stopped) { - if (status & UART_MSR_CTS) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx start..."); -#endif - info->port.tty->hw_stopped = 0; - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - return; - } - } else { - if (!(status & UART_MSR_CTS)) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx stop..."); -#endif - info->port.tty->hw_stopped = 1; - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - } - } -} -#endif - -/* - * This is the serial driver's interrupt routine for a single port - */ -/* static void rs_360_interrupt(void *dev_id) */ /* until and if we start servicing irqs here */ -static void rs_360_interrupt(int vec, void *dev_id) -{ - u_char events; - int idx; - ser_info_t *info; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - info = dev_id; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - events = sccp->scc_scce; - if (events & SCCM_RX) - receive_chars(info); - if (events & SCCM_TX) - transmit_chars(info); - sccp->scc_scce = events; - } else { - smcp = &pquicc->smc_regs[idx]; - events = smcp->smc_smce; - if (events & SMCM_BRKE) - receive_break(info); - if (events & SMCM_RX) - receive_chars(info); - if (events & SMCM_TX) - transmit_chars(info); - smcp->smc_smce = events; - } - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt_single(%d, %x)...", - info->state->smc_scc_num, events); -#endif -#ifdef modem_control - check_modem_status(info); -#endif - info->last_active = jiffies; -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} - - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - - -static void do_softint(void *private_) -{ - ser_info_t *info = (ser_info_t *) private_; - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void do_serial_hangup(void *private_) -{ - struct async_struct *info = (struct async_struct *) private_; - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; - - tty_hangup(tty); -} - - -static int startup(ser_info_t *info) -{ - unsigned long flags; - int retval=0; - int idx; - /*struct serial_state *state = info->state;*/ - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - volatile struct smc_uart_pram *up; - volatile struct uart_pram *scup; - - - local_irq_save(flags); - - if (info->flags & ASYNC_INITIALIZED) { - goto errout; - } - -#ifdef maybe - if (!state->port || !state->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - goto errout; - } -#endif - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, state->irq); -#endif - - -#ifdef modem_control - info->MCR = 0; - if (info->port.tty->termios->c_cflag & CBAUD) - info->MCR = UART_MCR_DTR | UART_MCR_RTS; -#endif - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - /* - * and set the speed of the serial port - */ - change_speed(info); - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - scup = &pquicc->pram[info->state->port].scc.pscc.u; - - scup->mrblr = RX_BUF_SIZE; - scup->max_idl = RX_BUF_SIZE; - - sccp->scc_sccm |= (UART_SCCM_TX | UART_SCCM_RX); - sccp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); - - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Enable interrupts and I/O. - */ - smcp->smc_smcm |= (SMCM_RX | SMCM_TX); - smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); - - /* We can tune the buffer length and idle characters - * to take advantage of the entire incoming buffer size. - * If mrblr is something other than 1, maxidl has to be - * non-zero or we never get an interrupt. The maxidl - * is the number of character times we wait after reception - * of the last character before we decide no more characters - * are coming. - */ - /* up = (smc_uart_t *)&pquicc->cp_dparam[state->port]; */ - /* holy unionized structures, Batman: */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - - up->mrblr = RX_BUF_SIZE; - up->max_idl = RX_BUF_SIZE; - - up->brkcr = 1; /* number of break chars */ - } - - info->flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; - -errout: - local_irq_restore(flags); - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(ser_info_t *info) -{ - unsigned long flags; - struct serial_state *state; - int idx; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - state = info->state; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....", info->line, - state->irq); -#endif - - local_irq_save(flags); - - idx = PORT_NUM(state->smc_scc_num); - if (state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_gsmr.w.low &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#ifdef CONFIG_SERIAL_CONSOLE - /* We can't disable the transmitter if this is the - * system console. - */ - if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) -#endif - sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Disable interrupts and I/O. - */ - smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); -#ifdef CONFIG_SERIAL_CONSOLE - /* We can't disable the transmitter if this is the - * system console. - */ - if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) -#endif - smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void change_speed(ser_info_t *info) -{ - int baud_rate; - unsigned cflag, cval, scval, prev_mode; - int i, bits, sbits, idx; - unsigned long flags; - struct serial_state *state; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!info->port.tty || !info->port.tty->termios) - return; - cflag = info->port.tty->termios->c_cflag; - - state = info->state; - - /* Character length programmed into the mode register is the - * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, - * 1 or 2 stop bits, minus 1. - * The value 'bits' counts this for us. - */ - cval = 0; - scval = 0; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: bits = 5; break; - case CS6: bits = 6; break; - case CS7: bits = 7; break; - case CS8: bits = 8; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: bits = 8; break; - } - sbits = bits - 5; - - if (cflag & CSTOPB) { - cval |= SMCMR_SL; /* Two stops */ - scval |= SCU_PMSR_SL; - bits++; - } - if (cflag & PARENB) { - cval |= SMCMR_PEN; - scval |= SCU_PMSR_PEN; - bits++; - } - if (!(cflag & PARODD)) { - cval |= SMCMR_PM_EVEN; - scval |= (SCU_PMSR_REVP | SCU_PMSR_TEVP); - } - - /* Determine divisor based on baud rate */ - i = cflag & CBAUD; - if (i >= (sizeof(baud_table)/sizeof(int))) - baud_rate = 9600; - else - baud_rate = baud_table[i]; - - info->timeout = (TX_BUF_SIZE*HZ*bits); - info->timeout += HZ/50; /* Add .02 seconds of slop */ - -#ifdef modem_control - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - if (info->flags & ASYNC_HARDPPS_CD) - info->IER |= UART_IER_MSI; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - } else - info->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - serial_out(info, UART_IER, info->IER); -#endif - - /* - * Set up parity check flag - */ - info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); - if (I_INPCK(info->port.tty)) - info->read_status_mask |= BD_SC_FR | BD_SC_PR; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= BD_SC_BR; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= BD_SC_PR | BD_SC_FR; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask |= BD_SC_BR; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= BD_SC_OV; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - info->read_status_mask &= ~BD_SC_EMPTY; - local_irq_save(flags); - - /* Start bit has not been added (so don't, because we would just - * subtract it later), and we need to add one for the number of - * stops bits (there is always at least one). - */ - bits++; - idx = PORT_NUM(state->smc_scc_num); - if (state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_psmr = (sbits << 12) | scval; - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Set the mode register. We want to keep a copy of the - * enables, because we want to put them back if they were - * present. - */ - prev_mode = smcp->smc_smcmr; - smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; - smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); - } - - m360_cpm_setbrg((state - rs_table), baud_rate); - - local_irq_restore(flags); -} - -static void rs_360_put_char(struct tty_struct *tty, unsigned char ch) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - volatile QUICC_BD *bdp; - - if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return 0; - - if (!tty) - return 0; - - bdp = info->tx_cur; - while (bdp->status & BD_SC_READY); - - /* *((char *)__va(bdp->buf)) = ch; */ - *((char *)bdp->buf) = ch; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - - info->tx_cur = (QUICC_BD *)bdp; - return 1; - -} - -static int rs_360_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - ser_info_t *info = (ser_info_t *)tty->driver_data; - volatile QUICC_BD *bdp; - -#ifdef CONFIG_KGDB - /* Try to let stub handle output. Returns true if it did. */ - if (kgdb_output_string(buf, count)) - return ret; -#endif - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!tty) - return 0; - - bdp = info->tx_cur; - - while (1) { - c = min(count, TX_BUF_SIZE); - - if (c <= 0) - break; - - if (bdp->status & BD_SC_READY) { - info->flags |= TX_WAKEUP; - break; - } - - /* memcpy(__va(bdp->buf), buf, c); */ - memcpy((void *)bdp->buf, buf, c); - - bdp->length = c; - bdp->status |= BD_SC_READY; - - buf += c; - count -= c; - ret += c; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - info->tx_cur = (QUICC_BD *)bdp; - } - return ret; -} - -static int rs_360_write_room(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - - if ((info->tx_cur->status & BD_SC_READY) == 0) { - info->flags &= ~TX_WAKEUP; - ret = TX_BUF_SIZE; - } - else { - info->flags |= TX_WAKEUP; - ret = 0; - } - return ret; -} - -/* I could track this with transmit counters....maybe later. -*/ -static int rs_360_chars_in_buffer(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return 0; -} - -static void rs_360_flush_buffer(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - - /* There is nothing to "flush", whatever we gave the CPM - * is on its way out. - */ - tty_wakeup(tty); - info->flags &= ~TX_WAKEUP; -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void rs_360_send_xchar(struct tty_struct *tty, char ch) -{ - volatile QUICC_BD *bdp; - - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_send_char")) - return; - - bdp = info->tx_cur; - while (bdp->status & BD_SC_READY); - - /* *((char *)__va(bdp->buf)) = ch; */ - *((char *)bdp->buf) = ch; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - - info->tx_cur = (QUICC_BD *)bdp; -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_360_throttle(struct tty_struct * tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) - rs_360_send_xchar(tty, STOP_CHAR(tty)); - -#ifdef modem_control - if (tty->termios->c_cflag & CRTSCTS) - info->MCR &= ~UART_MCR_RTS; - - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif -} - -static void rs_360_unthrottle(struct tty_struct * tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - rs_360_send_xchar(tty, START_CHAR(tty)); - } -#ifdef modem_control - if (tty->termios->c_cflag & CRTSCTS) - info->MCR |= UART_MCR_RTS; - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -#ifdef maybe -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct async_struct * info, unsigned int *value) -{ - unsigned char status; - unsigned int result; - - local_irq_disable(); - status = serial_in(info, UART_LSR); - local_irq_enable(); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result,value); -} -#endif - -static int rs_360_tiocmget(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned int result = 0; -#ifdef modem_control - unsigned char control, status; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - control = info->MCR; - local_irq_disable(); - status = serial_in(info, UART_MSR); - local_irq_enable(); - result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) - | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) -#ifdef TIOCM_OUT1 - | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) - | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) -#endif - | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) - | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) - | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) - | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); -#endif - return result; -} - -static int rs_360_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ -#ifdef modem_control - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned int arg; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - /* FIXME: locking on info->mcr */ - if (set & TIOCM_RTS) - info->mcr |= UART_MCR_RTS; - if (set & TIOCM_DTR) - info->mcr |= UART_MCR_DTR; - if (clear & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; - -#ifdef TIOCM_OUT1 - if (set & TIOCM_OUT1) - info->MCR |= UART_MCR_OUT1; - if (set & TIOCM_OUT2) - info->MCR |= UART_MCR_OUT2; - if (clear & TIOCM_OUT1) - info->MCR &= ~UART_MCR_OUT1; - if (clear & TIOCM_OUT2) - info->MCR &= ~UART_MCR_OUT2; -#endif - - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif - return 0; -} - -/* Sending a break is a two step process on the SMC/SCC. It is accomplished - * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT - * command. We take advantage of the begin/end functions to make this - * happen. - */ -static ushort smc_chan_map[] = { - CPM_CR_CH_SMC1, - CPM_CR_CH_SMC2 -}; - -static ushort scc_chan_map[] = { - CPM_CR_CH_SCC1, - CPM_CR_CH_SCC2, - CPM_CR_CH_SCC3, - CPM_CR_CH_SCC4 -}; - -static void begin_break(ser_info_t *info) -{ - volatile QUICC *cp; - ushort chan; - int idx; - - cp = pquicc; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) - chan = scc_chan_map[idx]; - else - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); -} - -static void end_break(ser_info_t *info) -{ - volatile QUICC *cp; - ushort chan; - int idx; - - cp = pquicc; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) - chan = scc_chan_map[idx]; - else - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break(ser_info_t *info, unsigned int duration) -{ -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); -#endif - begin_break(info); - msleep_interruptible(duration); - end_break(info); -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("done jiffies=%lu\n", jiffies); -#endif -} - - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int rs_360_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - struct async_icount cnow; - - local_irq_disable(); - cnow = info->state->icount; - local_irq_enable(); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - - return 0; -} - -static int rs_360_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - int error; - ser_info_t *info = (ser_info_t *)tty->driver_data; - int retval; - struct async_icount cnow; - /* struct async_icount_24 cnow;*/ /* kernel counter temps */ - struct serial_icounter_struct *p_cuser; /* user space */ - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if (cmd != TIOCMIWAIT) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, 250); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*100 : 250); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCSBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - begin_break(info); - return 0; - case TIOCCBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - end_break(info); - return 0; -#ifdef maybe - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); -#endif - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: -#ifdef modem_control - local_irq_disable(); - /* note the counters on entry */ - cprev = info->state->icount; - local_irq_enable(); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - local_irq_disable(); - cnow = info->state->icount; /* atomic copy */ - local_irq_enable(); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - 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; - } - cprev = cnow; - } - /* NOTREACHED */ -#else - return 0; -#endif - - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -/* FIX UP modem control here someday...... -*/ -static void rs_360_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - change_speed(info); - -#ifdef modem_control - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(tty->termios->c_cflag & CBAUD)) { - info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (tty->termios->c_cflag & CBAUD)) { - info->MCR |= UART_MCR_DTR; - if (!tty->hw_stopped || - !(tty->termios->c_cflag & CRTSCTS)) { - info->MCR |= UART_MCR_RTS; - } - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_360_start(tty); - } -#endif - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_360_close(struct tty_struct *tty, struct file * filp) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - /* struct async_state *state; */ - struct serial_state *state; - unsigned long flags; - int idx; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) - return; - - state = info->state; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - DBG_CNT("before DEC-hung"); - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, state->count); -#endif - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("rs_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); - state->count = 0; - } - if (state->count) { - DBG_CNT("before DEC-2"); - local_irq_restore(flags); - return; - } - info->flags |= ASYNC_CLOSING; - /* - * 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); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->read_status_mask &= ~BD_SC_EMPTY; - if (info->flags & ASYNC_INITIALIZED) { - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm &= ~UART_SCCM_RX; - sccp->scc_gsmr.w.low &= ~SCC_GSMRL_ENR; - } else { - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm &= ~SMCM_RX; - smcp->smc_smcmr &= ~SMCMR_REN; - } - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - rs_360_wait_until_sent(tty, info->timeout); - } - shutdown(info); - rs_360_flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned long orig_jiffies, char_time; - /*int lsr;*/ - volatile QUICC_BD *bdp; - - if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) - return; - -#ifdef maybe - if (info->state->type == PORT_UNKNOWN) - return; -#endif - - orig_jiffies = jiffies; - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = 1; - if (timeout) - char_time = min(char_time, (unsigned long)timeout); -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - - /* We go through the loop at least once because we can't tell - * exactly when the last character exits the shifter. There can - * be at least two characters waiting to be sent after the buffers - * are empty. - */ - do { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif -/* current->counter = 0; make us low-priority */ - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && (time_after(jiffies, orig_jiffies + timeout))) - break; - /* The 'tx_cur' is really the next buffer to send. We - * have to back up to the previous BD and wait for it - * to go. This isn't perfect, because all this indicates - * is the buffer is available. There are still characters - * in the CPM FIFO. - */ - bdp = info->tx_cur; - if (bdp == info->tx_bd_base) - bdp += (TX_NUM_FIFO-1); - else - bdp--; - } while (bdp->status & BD_SC_READY); - current->state = TASK_RUNNING; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rs_360_hangup(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - struct serial_state *state = info->state; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - state = info->state; - - rs_360_flush_buffer(tty); - shutdown(info); - info->event = 0; - state->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - ser_info_t *info) -{ -#ifdef DO_THIS_LATER - DECLARE_WAITQUEUE(wait, current); -#endif - struct serial_state *state = info->state; - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - * If this is an SMC port, we don't have modem control to wait - * for, so just get out here. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR)) || - !(info->state->smc_scc_num & NUM_IS_SCC)) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * 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, state->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; -#ifdef DO_THIS_LATER - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttys%d, count = %d\n", - state->line, state->count); -#endif - local_irq_disable(); - if (!tty_hung_up_p(filp)) - state->count--; - local_irq_enable(); - info->blocked_open++; - while (1) { - local_irq_disable(); - if (tty->termios->c_cflag & CBAUD) - serial_out(info, UART_MCR, - serial_inp(info, UART_MCR) | - (UART_MCR_DTR | UART_MCR_RTS)); - local_irq_enable(); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || (serial_in(info, UART_MSR) & - UART_MSR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif - tty_unlock(); - schedule(); - tty_lock(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - state->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif -#endif /* DO_THIS_LATER */ - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int get_async_struct(int line, ser_info_t **ret_info) -{ - struct serial_state *sstate; - - sstate = rs_table + line; - if (sstate->info) { - sstate->count++; - *ret_info = (ser_info_t *)sstate->info; - return 0; - } - else { - return -ENOMEM; - } -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int rs_360_open(struct tty_struct *tty, struct file * filp) -{ - ser_info_t *info; - int retval, line; - - line = tty->index; - if ((line < 0) || (line >= NR_PORTS)) - return -ENODEV; - retval = get_async_struct(line, &info); - if (retval) - return retval; - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s, count = %d\n", tty->name, info->state->count); -#endif - tty->driver_data = info; - info->port.tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) - return retval; - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open returning after block_til_ready with %d\n", - retval); -#endif - return retval; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s successful...", tty->name); -#endif - return 0; -} - -/* - * /proc fs routines.... - */ - -static inline int line_info(char *buf, struct serial_state *state) -{ -#ifdef notdef - struct async_struct *info = state->info, scr_info; - char stat_buf[30], control, status; -#endif - int ret; - - ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", - state->line, - (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC", - (unsigned int)(state->port), state->irq); - - if (!state->port || (state->type == PORT_UNKNOWN)) { - ret += sprintf(buf+ret, "\n"); - return ret; - } - -#ifdef notdef - /* - * Figure out the current RS-232 lines - */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ - - info->magic = SERIAL_MAGIC; - info->port = state->port; - info->flags = state->flags; - info->quot = 0; - info->port.tty = NULL; - } - local_irq_disable(); - status = serial_in(info, UART_MSR); - control = info ? info->MCR : serial_in(info, UART_MCR); - local_irq_enable(); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (control & UART_MCR_RTS) - strcat(stat_buf, "|RTS"); - if (status & UART_MSR_CTS) - strcat(stat_buf, "|CTS"); - if (control & UART_MCR_DTR) - strcat(stat_buf, "|DTR"); - if (status & UART_MSR_DSR) - strcat(stat_buf, "|DSR"); - if (status & UART_MSR_DCD) - strcat(stat_buf, "|CD"); - if (status & UART_MSR_RI) - strcat(stat_buf, "|RI"); - - if (info->quot) { - ret += sprintf(buf+ret, " baud:%d", - state->baud_base / info->quot); - } - - ret += sprintf(buf+ret, " tx:%d rx:%d", - state->icount.tx, state->icount.rx); - - if (state->icount.frame) - ret += sprintf(buf+ret, " fe:%d", state->icount.frame); - - if (state->icount.parity) - ret += sprintf(buf+ret, " pe:%d", state->icount.parity); - - if (state->icount.brk) - ret += sprintf(buf+ret, " brk:%d", state->icount.brk); - - if (state->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); - - /* - * Last thing is the RS-232 status lines - */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); -#endif - return ret; -} - -int rs_360_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int i, len = 0; - off_t begin = 0; - - len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); - for (i = 0; i < NR_PORTS && len < 4000; i++) { - len += line_info(page + len, &rs_table[i]); - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } - } - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (begin-off); - return ((count < begin+len-off) ? count : begin+len-off); -} - -/* - * --------------------------------------------------------------------- - * rs_init() and friends - * - * rs_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static _INLINE_ void show_serial_version(void) -{ - printk(KERN_INFO "%s version %s\n", serial_name, serial_version); -} - - -/* - * The serial console driver used during boot. Note that these names - * clash with those found in "serial.c", so we currently can't support - * the 16xxx uarts and these at the same time. I will fix this to become - * an indirect function call from tty_io.c (or something). - */ - -#ifdef CONFIG_SERIAL_CONSOLE - -/* - * Print a string to the serial port trying not to disturb any possible - * real use of the port... - */ -static void my_console_write(int idx, const char *s, - unsigned count) -{ - struct serial_state *ser; - ser_info_t *info; - unsigned i; - QUICC_BD *bdp, *bdbase; - volatile struct smc_uart_pram *up; - volatile u_char *cp; - - ser = rs_table + idx; - - - /* If the port has been initialized for general use, we have - * to use the buffer descriptors allocated there. Otherwise, - * we simply use the single buffer allocated. - */ - if ((info = (ser_info_t *)ser->info) != NULL) { - bdp = info->tx_cur; - bdbase = info->tx_bd_base; - } - else { - /* Pointer to UART in parameter ram. - */ - /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ - up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; - - /* Get the address of the host memory buffer. - */ - bdp = bdbase = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); - } - - /* - * We need to gracefully shut down the transmitter, disable - * interrupts, then send our bytes out. - */ - - /* - * Now, do each character. This is not as bad as it looks - * since this is a holding FIFO and not a transmitting FIFO. - * We could add the complexity of filling the entire transmit - * buffer, but we would just wait longer between accesses...... - */ - for (i = 0; i < count; i++, s++) { - /* Wait for transmitter fifo to empty. - * Ready indicates output is ready, and xmt is doing - * that, not that it is ready for us to send. - */ - while (bdp->status & BD_SC_READY); - - /* Send the character out. - */ - cp = bdp->buf; - *cp = *s; - - bdp->length = 1; - bdp->status |= BD_SC_READY; - - if (bdp->status & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - - /* if a LF, also do CR... */ - if (*s == 10) { - while (bdp->status & BD_SC_READY); - /* cp = __va(bdp->buf); */ - cp = bdp->buf; - *cp = 13; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - if (bdp->status & BD_SC_WRAP) { - bdp = bdbase; - } - else { - bdp++; - } - } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - while (bdp->status & BD_SC_READY); - - if (info) - info->tx_cur = (QUICC_BD *)bdp; -} - -static void serial_console_write(struct console *c, const char *s, - unsigned count) -{ -#ifdef CONFIG_KGDB - /* Try to let stub handle output. Returns true if it did. */ - if (kgdb_output_string(s, count)) - return; -#endif - my_console_write(c->index, s, count); -} - - - -/*void console_print_68360(const char *p) -{ - const char *cp = p; - int i; - - for (i=0;cp[i]!=0;i++); - - serial_console_write (p, i); - - //Comment this if you want to have a strict interrupt-driven output - //rs_fair_output(); - - return; -}*/ - - - - - - -#ifdef CONFIG_XMON -int -xmon_360_write(const char *s, unsigned count) -{ - my_console_write(0, s, count); - return(count); -} -#endif - -#ifdef CONFIG_KGDB -void -putDebugChar(char ch) -{ - my_console_write(0, &ch, 1); -} -#endif - -/* - * Receive character from the serial port. This only works well - * before the port is initialized for real use. - */ -static int my_console_wait_key(int idx, int xmon, char *obuf) -{ - struct serial_state *ser; - u_char c, *cp; - ser_info_t *info; - QUICC_BD *bdp; - volatile struct smc_uart_pram *up; - int i; - - ser = rs_table + idx; - - /* Get the address of the host memory buffer. - * If the port has been initialized for general use, we must - * use information from the port structure. - */ - if ((info = (ser_info_t *)ser->info)) - bdp = info->rx_cur; - else - /* bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; */ - bdp = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); - - /* Pointer to UART in parameter ram. - */ - /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - - /* - * We need to gracefully shut down the receiver, disable - * interrupts, then read the input. - * XMON just wants a poll. If no character, return -1, else - * return the character. - */ - if (!xmon) { - while (bdp->status & BD_SC_EMPTY); - } - else { - if (bdp->status & BD_SC_EMPTY) - return -1; - } - - cp = (char *)bdp->buf; - - if (obuf) { - i = c = bdp->length; - while (i-- > 0) - *obuf++ = *cp++; - } - else { - c = *cp; - } - bdp->status |= BD_SC_EMPTY; - - if (info) { - if (bdp->status & BD_SC_WRAP) { - bdp = info->rx_bd_base; - } - else { - bdp++; - } - info->rx_cur = (QUICC_BD *)bdp; - } - - return((int)c); -} - -static int serial_console_wait_key(struct console *co) -{ - return(my_console_wait_key(co->index, 0, NULL)); -} - -#ifdef CONFIG_XMON -int -xmon_360_read_poll(void) -{ - return(my_console_wait_key(0, 1, NULL)); -} - -int -xmon_360_read_char(void) -{ - return(my_console_wait_key(0, 0, NULL)); -} -#endif - -#ifdef CONFIG_KGDB -static char kgdb_buf[RX_BUF_SIZE], *kgdp; -static int kgdb_chars; - -unsigned char -getDebugChar(void) -{ - if (kgdb_chars <= 0) { - kgdb_chars = my_console_wait_key(0, 0, kgdb_buf); - kgdp = kgdb_buf; - } - kgdb_chars--; - - return(*kgdp++); -} - -void kgdb_interruptible(int state) -{ -} -void kgdb_map_scc(void) -{ - struct serial_state *ser; - uint mem_addr; - volatile QUICC_BD *bdp; - volatile smc_uart_t *up; - - cpmp = (cpm360_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); - - /* To avoid data cache CPM DMA coherency problems, allocate a - * buffer in the CPM DPRAM. This will work until the CPM and - * serial ports are initialized. At that time a memory buffer - * will be allocated. - * The port is already initialized from the boot procedure, all - * we do here is give it a different buffer and make it a FIFO. - */ - - ser = rs_table; - - /* Right now, assume we are using SMCs. - */ - up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; - - /* Allocate space for an input FIFO, plus a few bytes for output. - * Allocate bytes to maintain word alignment. - */ - mem_addr = (uint)(&cpmp->cp_dpmem[0x1000]); - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; - bdp->buf = mem_addr; - - bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_tbase]; - bdp->buf = mem_addr+RX_BUF_SIZE; - - up->smc_mrblr = RX_BUF_SIZE; /* receive buffer length */ - up->smc_maxidl = RX_BUF_SIZE; -} -#endif - -static struct tty_struct *serial_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - - -struct console sercons = { - .name = "ttyS", - .write = serial_console_write, - .device = serial_console_device, - .wait_key = serial_console_wait_key, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = CONFIG_SERIAL_CONSOLE_PORT, -}; - - - -/* - * Register console. - */ -long console_360_init(long kmem_start, long kmem_end) -{ - register_console(&sercons); - /*register_console (console_print_68360); - 2.0.38 only required a write - function pointer. */ - return kmem_start; -} - -#endif - -/* Index in baud rate table of the default console baud rate. -*/ -static int baud_idx; - -static const struct tty_operations rs_360_ops = { - .owner = THIS_MODULE, - .open = rs_360_open, - .close = rs_360_close, - .write = rs_360_write, - .put_char = rs_360_put_char, - .write_room = rs_360_write_room, - .chars_in_buffer = rs_360_chars_in_buffer, - .flush_buffer = rs_360_flush_buffer, - .ioctl = rs_360_ioctl, - .throttle = rs_360_throttle, - .unthrottle = rs_360_unthrottle, - /* .send_xchar = rs_360_send_xchar, */ - .set_termios = rs_360_set_termios, - .stop = rs_360_stop, - .start = rs_360_start, - .hangup = rs_360_hangup, - /* .wait_until_sent = rs_360_wait_until_sent, */ - /* .read_proc = rs_360_read_proc, */ - .tiocmget = rs_360_tiocmget, - .tiocmset = rs_360_tiocmset, - .get_icount = rs_360_get_icount, -}; - -static int __init rs_360_init(void) -{ - struct serial_state * state; - ser_info_t *info; - void *mem_addr; - uint dp_addr, iobits; - int i, j, idx; - ushort chan; - QUICC_BD *bdp; - volatile QUICC *cp; - volatile struct smc_regs *sp; - volatile struct smc_uart_pram *up; - volatile struct scc_regs *scp; - volatile struct uart_pram *sup; - /* volatile immap_t *immap; */ - - serial_driver = alloc_tty_driver(NR_PORTS); - if (!serial_driver) - return -1; - - show_serial_version(); - - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - baud_idx | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &rs_360_ops); - - if (tty_register_driver(serial_driver)) - panic("Couldn't register serial driver\n"); - - cp = pquicc; /* Get pointer to Communication Processor */ - /* immap = (immap_t *)IMAP_ADDR; */ /* and to internal registers */ - - - /* Configure SCC2, SCC3, and SCC4 instead of port A parallel I/O. - */ - /* The "standard" configuration through the 860. - */ -/* immap->im_ioport.iop_papar |= 0x00fc; */ -/* immap->im_ioport.iop_padir &= ~0x00fc; */ -/* immap->im_ioport.iop_paodr &= ~0x00fc; */ - cp->pio_papar |= 0x00fc; - cp->pio_padir &= ~0x00fc; - /* cp->pio_paodr &= ~0x00fc; */ - - - /* Since we don't yet do modem control, connect the port C pins - * as general purpose I/O. This will assert CTS and CD for the - * SCC ports. - */ - /* FIXME: see 360um p.7-365 and 860um p.34-12 - * I can't make sense of these bits - mleslie*/ -/* immap->im_ioport.iop_pcdir |= 0x03c6; */ -/* immap->im_ioport.iop_pcpar &= ~0x03c6; */ - -/* cp->pio_pcdir |= 0x03c6; */ -/* cp->pio_pcpar &= ~0x03c6; */ - - - - /* Connect SCC2 and SCC3 to NMSI. Connect BRG3 to SCC2 and - * BRG4 to SCC3. - */ - cp->si_sicr &= ~0x00ffff00; - cp->si_sicr |= 0x001b1200; - -#ifdef CONFIG_PP04 - /* Frequentis PP04 forced to RS-232 until we know better. - * Port C 12 and 13 low enables RS-232 on SCC3 and SCC4. - */ - immap->im_ioport.iop_pcdir |= 0x000c; - immap->im_ioport.iop_pcpar &= ~0x000c; - immap->im_ioport.iop_pcdat &= ~0x000c; - - /* This enables the TX driver. - */ - cp->cp_pbpar &= ~0x6000; - cp->cp_pbdat &= ~0x6000; -#endif - - for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - state->magic = SSTATE_MAGIC; - state->line = i; - state->type = PORT_UNKNOWN; - state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; - state->icount.cts = state->icount.dsr = - state->icount.rng = state->icount.dcd = 0; - state->icount.rx = state->icount.tx = 0; - state->icount.frame = state->icount.parity = 0; - state->icount.overrun = state->icount.brk = 0; - printk(KERN_INFO "ttyS%d at irq 0x%02x is an %s\n", - i, (unsigned int)(state->irq), - (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC"); - -#ifdef CONFIG_SERIAL_CONSOLE - /* If we just printed the message on the console port, and - * we are about to initialize it for general use, we have - * to wait a couple of character times for the CR/NL to - * make it out of the transmit buffer. - */ - if (i == CONFIG_SERIAL_CONSOLE_PORT) - mdelay(8); - - -/* idx = PORT_NUM(info->state->smc_scc_num); */ -/* if (info->state->smc_scc_num & NUM_IS_SCC) */ -/* chan = scc_chan_map[idx]; */ -/* else */ -/* chan = smc_chan_map[idx]; */ - -/* cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; */ -/* while (cp->cp_cr & CPM_CR_FLG); */ - -#endif - /* info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); */ - info = &quicc_ser_info[i]; - if (info) { - memset (info, 0, sizeof(ser_info_t)); - info->magic = SERIAL_MAGIC; - info->line = i; - info->flags = state->flags; - INIT_WORK(&info->tqueue, do_softint, info); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup, info); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->state = state; - state->info = (struct async_struct *)info; - - /* We need to allocate a transmit and receive buffer - * descriptors from dual port ram, and a character - * buffer area from host mem. - */ - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * RX_NUM_FIFO); - - /* Allocate space for FIFOs in the host memory. - * (for now this is from a static array of buffers :( - */ - /* mem_addr = m360_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); */ - /* mem_addr = kmalloc (RX_NUM_FIFO * RX_BUF_SIZE, GFP_BUFFER); */ - mem_addr = &rx_buf_pool[i * RX_NUM_FIFO * RX_BUF_SIZE]; - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - info->rx_cur = info->rx_bd_base = bdp; - - /* initialize rx buffer descriptors */ - for (j=0; j<(RX_NUM_FIFO-1); j++) { - bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; - bdp->status = BD_SC_EMPTY | BD_SC_INTRPT; - mem_addr += RX_BUF_SIZE; - bdp++; - } - bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; - bdp->status = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; - - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - -#if defined (CONFIG_UCQUICC) && 1 - /* set the transceiver mode to RS232 */ - sipex_mode_bits &= ~(uint)SIPEX_MODE(idx,0x0f); /* clear current mode */ - sipex_mode_bits |= (uint)SIPEX_MODE(idx,0x02); - *(uint *)_periph_base = sipex_mode_bits; - /* printk ("sipex bits = 0x%08x\n", sipex_mode_bits); */ -#endif - } - - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * TX_NUM_FIFO); - - /* Allocate space for FIFOs in the host memory. - */ - /* mem_addr = m360_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); */ - /* mem_addr = kmalloc (TX_NUM_FIFO * TX_BUF_SIZE, GFP_BUFFER); */ - mem_addr = &tx_buf_pool[i * TX_NUM_FIFO * TX_BUF_SIZE]; - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - info->tx_cur = info->tx_bd_base = (QUICC_BD *)bdp; - - /* initialize tx buffer descriptors */ - for (j=0; j<(TX_NUM_FIFO-1); j++) { - bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; - bdp->status = BD_SC_INTRPT; - mem_addr += TX_BUF_SIZE; - bdp++; - } - bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; - bdp->status = (BD_SC_WRAP | BD_SC_INTRPT); - - if (info->state->smc_scc_num & NUM_IS_SCC) { - scp = &pquicc->scc_regs[idx]; - sup = &pquicc->pram[info->state->port].scc.pscc.u; - sup->rbase = dp_addr; - sup->tbase = dp_addr; - - /* Set up the uart parameters in the - * parameter ram. - */ - sup->rfcr = SMC_EB; - sup->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - sup->mrblr = 1; - sup->max_idl = 0; - sup->brkcr = 1; - sup->parec = 0; - sup->frmer = 0; - sup->nosec = 0; - sup->brkec = 0; - sup->uaddr1 = 0; - sup->uaddr2 = 0; - sup->toseq = 0; - { - int i; - for (i=0;i<8;i++) - sup->cc[i] = 0x8000; - } - sup->rccm = 0xc0ff; - - /* Send the CPM an initialize command. - */ - chan = scc_chan_map[idx]; - - /* execute the INIT RX & TX PARAMS command for this channel. */ - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - scp->scc_gsmr.w.high = 0; - scp->scc_gsmr.w.low = - (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Disable all interrupts and clear all pending - * events. - */ - scp->scc_sccm = 0; - scp->scc_scce = 0xffff; - scp->scc_dsr = 0x7e7e; - scp->scc_psmr = 0x3000; - - /* If the port is the console, enable Rx and Tx. - */ -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - } - else { - /* Configure SMCs Tx/Rx instead of port B - * parallel I/O. - */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - up->rbase = dp_addr; - - iobits = 0xc0 << (idx * 4); - cp->pip_pbpar |= iobits; - cp->pip_pbdir &= ~iobits; - cp->pip_pbodr &= ~iobits; - - - /* Connect the baud rate generator to the - * SMC based upon index in rs_table. Also - * make sure it is connected to NMSI. - */ - cp->si_simode &= ~(0xffff << (idx * 16)); - cp->si_simode |= (i << ((idx * 16) + 12)); - - up->tbase = dp_addr; - - /* Set up the uart parameters in the - * parameter ram. - */ - up->rfcr = SMC_EB; - up->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - up->mrblr = 1; - up->max_idl = 0; - up->brkcr = 1; - - /* Send the CPM an initialize command. - */ - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, - CPM_CR_INIT_TRX) | CPM_CR_FLG; -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - printk(""); -#endif - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp = &cp->smc_regs[idx]; - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Disable all interrupts and clear all pending - * events. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* If the port is the console, enable Rx and Tx. - */ -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; -#endif - } - - /* Install interrupt handler. - */ - /* cpm_install_handler(IRQ_MACHSPEC | state->irq, rs_360_interrupt, info); */ - /*request_irq(IRQ_MACHSPEC | state->irq, rs_360_interrupt, */ - request_irq(state->irq, rs_360_interrupt, - IRQ_FLG_LOCK, "ttyS", (void *)info); - - /* Set up the baud rate generator. - */ - m360_cpm_setbrg(i, baud_table[baud_idx]); - - } - } - - return 0; -} -module_init(rs_360_init); - -/* This must always be called before the rs_360_init() function, otherwise - * it blows away the port control information. - */ -//static int __init serial_console_setup( struct console *co, char *options) -int serial_console_setup( struct console *co, char *options) -{ - struct serial_state *ser; - uint mem_addr, dp_addr, bidx, idx, iobits; - ushort chan; - QUICC_BD *bdp; - volatile QUICC *cp; - volatile struct smc_regs *sp; - volatile struct scc_regs *scp; - volatile struct smc_uart_pram *up; - volatile struct uart_pram *sup; - -/* mleslie TODO: - * add something to the 68k bootloader to store a desired initial console baud rate */ - -/* bd_t *bd; */ /* a board info struct used by EPPC-bug */ -/* bd = (bd_t *)__res; */ - - for (bidx = 0; bidx < (sizeof(baud_table) / sizeof(int)); bidx++) - /* if (bd->bi_baudrate == baud_table[bidx]) */ - if (CONSOLE_BAUDRATE == baud_table[bidx]) - break; - - /* co->cflag = CREAD|CLOCAL|bidx|CS8; */ - baud_idx = bidx; - - ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; - - cp = pquicc; /* Get pointer to Communication Processor */ - - idx = PORT_NUM(ser->smc_scc_num); - if (ser->smc_scc_num & NUM_IS_SCC) { - - /* TODO: need to set up SCC pin assignment etc. here */ - - } - else { - iobits = 0xc0 << (idx * 4); - cp->pip_pbpar |= iobits; - cp->pip_pbdir &= ~iobits; - cp->pip_pbodr &= ~iobits; - - /* Connect the baud rate generator to the - * SMC based upon index in rs_table. Also - * make sure it is connected to NMSI. - */ - cp->si_simode &= ~(0xffff << (idx * 16)); - cp->si_simode |= (idx << ((idx * 16) + 12)); - } - - /* When we get here, the CPM has been reset, so we need - * to configure the port. - * We need to allocate a transmit and receive buffer descriptor - * from dual port ram, and a character buffer area from host mem. - */ - - /* Allocate space for two buffer descriptors in the DP ram. - */ - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * CONSOLE_NUM_FIFO); - - /* Allocate space for two 2 byte FIFOs in the host memory. - */ - /* mem_addr = m360_cpm_hostalloc(8); */ - mem_addr = (uint)console_fifos; - - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - bdp->buf = (char *)mem_addr; - (bdp+1)->buf = (char *)(mem_addr+4); - - /* For the receive, set empty and wrap. - * For transmit, set wrap. - */ - bdp->status = BD_SC_EMPTY | BD_SC_WRAP; - (bdp+1)->status = BD_SC_WRAP; - - /* Set up the uart parameters in the parameter ram. - */ - if (ser->smc_scc_num & NUM_IS_SCC) { - scp = &cp->scc_regs[idx]; - /* sup = (scc_uart_t *)&cp->cp_dparam[ser->port]; */ - sup = &pquicc->pram[ser->port].scc.pscc.u; - - sup->rbase = dp_addr; - sup->tbase = dp_addr + sizeof(QUICC_BD); - - /* Set up the uart parameters in the - * parameter ram. - */ - sup->rfcr = SMC_EB; - sup->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - sup->mrblr = 1; - sup->max_idl = 0; - sup->brkcr = 1; - sup->parec = 0; - sup->frmer = 0; - sup->nosec = 0; - sup->brkec = 0; - sup->uaddr1 = 0; - sup->uaddr2 = 0; - sup->toseq = 0; - { - int i; - for (i=0;i<8;i++) - sup->cc[i] = 0x8000; - } - sup->rccm = 0xc0ff; - - /* Send the CPM an initialize command. - */ - chan = scc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - scp->scc_gsmr.w.high = 0; - scp->scc_gsmr.w.low = - (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Disable all interrupts and clear all pending - * events. - */ - scp->scc_sccm = 0; - scp->scc_scce = 0xffff; - scp->scc_dsr = 0x7e7e; - scp->scc_psmr = 0x3000; - - scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); - - } - else { - /* up = (smc_uart_t *)&cp->cp_dparam[ser->port]; */ - up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; - - up->rbase = dp_addr; /* Base of receive buffer desc. */ - up->tbase = dp_addr+sizeof(QUICC_BD); /* Base of xmt buffer desc. */ - up->rfcr = SMC_EB; - up->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single character interrupts. - */ - up->mrblr = 1; /* receive buffer length */ - up->max_idl = 0; /* wait forever for next char */ - - /* Send the CPM an initialize command. - */ - chan = smc_chan_map[idx]; - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp = &cp->smc_regs[idx]; - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* And finally, enable Rx and Tx. - */ - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; - } - - /* Set up the baud rate generator. - */ - /* m360_cpm_setbrg((ser - rs_table), bd->bi_baudrate); */ - m360_cpm_setbrg((ser - rs_table), CONSOLE_BAUDRATE); - - return 0; -} - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 7f50999eebc2..a87a56cb5417 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -309,6 +309,13 @@ static const struct serial8250_config uart_config[] = { UART_FCR_T_TRIG_01, .flags = UART_CAP_FIFO | UART_CAP_RTOIE, }, + [PORT_XR17D15X] = { + .name = "XR17D15X", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, + }, }; #if defined(CONFIG_MIPS_ALCHEMY) @@ -461,42 +468,6 @@ static void tsi_serial_out(struct uart_port *p, int offset, int value) writeb(value, p->membase + offset); } -/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ -static inline void dwapb_save_out_value(struct uart_port *p, int offset, - int value) -{ - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); - - if (offset == UART_LCR) - up->lcr = value; -} - -/* Read the IER to ensure any interrupt is cleared before returning from ISR. */ -static inline void dwapb_check_clear_ier(struct uart_port *p, int offset) -{ - if (offset == UART_TX || offset == UART_IER) - p->serial_in(p, UART_IER); -} - -static void dwapb_serial_out(struct uart_port *p, int offset, int value) -{ - int save_offset = offset; - offset = map_8250_out_reg(p, offset) << p->regshift; - dwapb_save_out_value(p, save_offset, value); - writeb(value, p->membase + offset); - dwapb_check_clear_ier(p, save_offset); -} - -static void dwapb32_serial_out(struct uart_port *p, int offset, int value) -{ - int save_offset = offset; - offset = map_8250_out_reg(p, offset) << p->regshift; - dwapb_save_out_value(p, save_offset, value); - writel(value, p->membase + offset); - dwapb_check_clear_ier(p, save_offset); -} - static unsigned int io_serial_in(struct uart_port *p, int offset) { offset = map_8250_in_reg(p, offset) << p->regshift; @@ -509,6 +480,8 @@ static void io_serial_out(struct uart_port *p, int offset, int value) outb(value, p->iobase + offset); } +static int serial8250_default_handle_irq(struct uart_port *port); + static void set_io_from_upio(struct uart_port *p) { struct uart_8250_port *up = @@ -540,16 +513,6 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = tsi_serial_out; break; - case UPIO_DWAPB: - p->serial_in = mem_serial_in; - p->serial_out = dwapb_serial_out; - break; - - case UPIO_DWAPB32: - p->serial_in = mem32_serial_in; - p->serial_out = dwapb32_serial_out; - break; - default: p->serial_in = io_serial_in; p->serial_out = io_serial_out; @@ -557,6 +520,7 @@ static void set_io_from_upio(struct uart_port *p) } /* Remember loaded iotype */ up->cur_iotype = p->iotype; + p->handle_irq = serial8250_default_handle_irq; } static void @@ -567,8 +531,6 @@ serial_out_sync(struct uart_8250_port *up, int offset, int value) case UPIO_MEM: case UPIO_MEM32: case UPIO_AU: - case UPIO_DWAPB: - case UPIO_DWAPB32: p->serial_out(p, offset, value); p->serial_in(p, UART_LCR); /* safe, no side-effects */ break; @@ -1120,6 +1082,14 @@ static void autoconfig_16550a(struct uart_8250_port *up) serial_outp(up, UART_IER, iersave); /* + * Exar uarts have EFR in a weird location + */ + if (up->port.flags & UPF_EXAR_EFR) { + up->port.type = PORT_XR17D15X; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR; + } + + /* * We distinguish between 16550A and U6 16550A by counting * how many bytes are in the FIFO. */ @@ -1621,6 +1591,29 @@ static void serial8250_handle_port(struct uart_8250_port *up) spin_unlock_irqrestore(&up->port.lock, flags); } +int serial8250_handle_irq(struct uart_port *port, unsigned int iir) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + if (!(iir & UART_IIR_NO_INT)) { + serial8250_handle_port(up); + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(serial8250_handle_irq); + +static int serial8250_default_handle_irq(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned int iir = serial_in(up, UART_IIR); + + return serial8250_handle_irq(port, iir); +} + /* * This is the serial driver's interrupt routine. * @@ -1648,30 +1641,13 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) l = i->head; do { struct uart_8250_port *up; - unsigned int iir; + struct uart_port *port; up = list_entry(l, struct uart_8250_port, list); + port = &up->port; - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) { - serial8250_handle_port(up); - + if (port->handle_irq(port)) { handled = 1; - - end = NULL; - } else if ((up->port.iotype == UPIO_DWAPB || - up->port.iotype == UPIO_DWAPB32) && - (iir & UART_IIR_BUSY) == UART_IIR_BUSY) { - /* The DesignWare APB UART has an Busy Detect (0x07) - * interrupt meaning an LCR write attempt occurred while the - * UART was busy. The interrupt must be cleared by reading - * the UART status register (USR) and the LCR re-written. */ - unsigned int status; - status = *(volatile u32 *)up->port.private_data; - serial_out(up, UART_LCR, up->lcr); - - handled = 1; - end = NULL; } else if (end == NULL) end = l; @@ -2081,8 +2057,8 @@ static int serial8250_startup(struct uart_port *port) */ if (!(up->port.flags & UPF_BUGGY_UART) && (serial_inp(up, UART_LSR) == 0xff)) { - printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n", - serial_index(&up->port)); + printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", + serial_index(&up->port)); return -ENODEV; } @@ -2458,7 +2434,10 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, efr |= UART_EFR_CTS; serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, efr); + if (up->port.flags & UPF_EXAR_EFR) + serial_outp(up, UART_XR_EFR, efr); + else + serial_outp(up, UART_EFR, efr); } #ifdef CONFIG_ARCH_OMAP @@ -2570,8 +2549,6 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM: - case UPIO_DWAPB: - case UPIO_DWAPB32: if (!up->port.mapbase) break; @@ -2608,8 +2585,6 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM: - case UPIO_DWAPB: - case UPIO_DWAPB32: if (!up->port.mapbase) break; @@ -3050,6 +3025,10 @@ int __init early_serial_setup(struct uart_port *port) p->serial_in = port->serial_in; if (port->serial_out) p->serial_out = port->serial_out; + if (port->handle_irq) + p->handle_irq = port->handle_irq; + else + p->handle_irq = serial8250_default_handle_irq; return 0; } @@ -3118,6 +3097,7 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.type = p->type; port.serial_in = p->serial_in; port.serial_out = p->serial_out; + port.handle_irq = p->handle_irq; port.set_termios = p->set_termios; port.pm = p->pm; port.dev = &dev->dev; @@ -3283,6 +3263,8 @@ int serial8250_register_port(struct uart_port *port) uart->port.serial_in = port->serial_in; if (port->serial_out) uart->port.serial_out = port->serial_out; + if (port->handle_irq) + uart->port.handle_irq = port->handle_irq; /* Possibly override set_termios call */ if (port->set_termios) uart->port.set_termios = port->set_termios; diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c new file mode 100644 index 000000000000..bf1fba640c2d --- /dev/null +++ b/drivers/tty/serial/8250_dw.c @@ -0,0 +1,194 @@ +/* + * Synopsys DesignWare 8250 driver. + * + * Copyright 2011 Picochip, Jamie Iles. + * + * 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. + * + * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the + * LCR is written whilst busy. If it is, then a busy detect interrupt is + * 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> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct dw8250_data { + int last_lcr; + int line; +}; + +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; + + offset <<= p->regshift; + writeb(value, p->membase + offset); +} + +static unsigned int dw8250_serial_in(struct uart_port *p, int offset) +{ + offset <<= p->regshift; + + return readb(p->membase + offset); +} + +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; + + offset <<= p->regshift; + writel(value, p->membase + offset); +} + +static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) +{ + offset <<= p->regshift; + + return readl(p->membase + offset); +} + +/* 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; + unsigned int iir = p->serial_in(p, UART_IIR); + + 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, d->last_lcr, UART_LCR); + + return 1; + } + + return 0; +} + +static int __devinit dw8250_probe(struct platform_device *pdev) +{ + struct uart_port port = {}; + 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; + + 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; + port.private_data = data; + + spin_lock_init(&port.lock); + port.mapbase = regs->start; + port.irq = irq->start; + port.handle_irq = dw8250_handle_irq; + port.type = PORT_8250; + port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | + UPF_FIXED_PORT | UPF_FIXED_TYPE; + port.dev = &pdev->dev; + + port.iotype = UPIO_MEM; + port.serial_in = dw8250_serial_in; + port.serial_out = dw8250_serial_out; + if (!of_property_read_u32(np, "reg-io-width", &val)) { + switch (val) { + case 1: + break; + case 4: + port.iotype = UPIO_MEM32; + port.serial_in = dw8250_serial_in32; + 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)) + port.regshift = val; + + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(&pdev->dev, "no clock-frequency property set\n"); + return -EINVAL; + } + port.uartclk = val; + + data->line = serial8250_register_port(&port); + if (data->line < 0) + return data->line; + + platform_set_drvdata(pdev, data); + + return 0; +} + +static int __devexit dw8250_remove(struct platform_device *pdev) +{ + struct dw8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + + return 0; +} + +static const struct of_device_id dw8250_match[] = { + { .compatible = "snps,dw-apb-uart" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw8250_match); + +static struct platform_driver dw8250_platform_driver = { + .driver = { + .name = "dw-apb-uart", + .owner = THIS_MODULE, + .of_match_table = dw8250_match, + }, + .probe = dw8250_probe, + .remove = __devexit_p(dw8250_remove), +}; + +static int __init dw8250_init(void) +{ + return platform_driver_register(&dw8250_platform_driver); +} +module_init(dw8250_init); + +static void __exit dw8250_exit(void) +{ + platform_driver_unregister(&dw8250_platform_driver); +} +module_exit(dw8250_exit); + +MODULE_AUTHOR("Jamie Iles"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver"); diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 3abeca2a2a1b..825937a5f210 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1101,6 +1101,15 @@ static int pci_eg20t_init(struct pci_dev *dev) #endif } +static int +pci_xr17c154_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_EXAR_EFR; + return pci_default_setup(priv, board, port, idx); +} + /* This should be in linux/pci_ids.h */ #define PCI_VENDOR_ID_SBSMODULARIO 0x124B #define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B @@ -1506,6 +1515,30 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_timedia_setup, }, /* + * Exar cards + */ + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C152, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C154, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17C158, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17c154_setup, + }, + /* * Xircom cards */ { @@ -1558,46 +1591,55 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8811, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8812, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8813, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = PCI_VENDOR_ID_INTEL, .device = 0x8814, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8027, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8028, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x8029, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x800C, .init = pci_eg20t_init, + .setup = pci_default_setup, }, { .vendor = 0x10DB, .device = 0x800D, .init = pci_eg20t_init, + .setup = pci_default_setup, }, /* * Cronyx Omega PCI (PLX-chip based) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4dcb37bbdf92..5f479dada6f2 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -267,6 +267,13 @@ config SERIAL_8250_RM9K port hardware found on MIPS RM9122 and similar processors. If unsure, say N. +config SERIAL_8250_DW + tristate "Support for Synopsys DesignWare 8250 quirks" + depends on SERIAL_8250 && OF + help + Selecting this option will enable handling of the extra features + present in the Synopsys DesignWare APB UART. + comment "Non-8250 serial port support" config SERIAL_AMBA_PL010 @@ -522,8 +529,8 @@ config SERIAL_S3C6400 config SERIAL_S5PV210 tristate "Samsung S5PV210 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_EXYNOS4210) - select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_EXYNOS4210) + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_EXYNOS4210 || SOC_EXYNOS4212) + select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_EXYNOS4210 || SOC_EXYNOS4212) default y help Serial port support for Samsung's S5P Family of SoC's @@ -722,7 +729,7 @@ config SERIAL_BFIN Add support for the built-in UARTs on the Blackfin. To compile this driver as a module, choose M here: the - module will be called bfin_5xx. + module is named bfin_uart.ko. config SERIAL_BFIN_CONSOLE bool "Console on Blackfin serial port" @@ -1035,23 +1042,6 @@ config SERIAL_MCF_CONSOLE help Enable a ColdFire internal serial port to be the system console. -config SERIAL_68360_SMC - bool "68360 SMC uart support" - depends on M68360 - help - This driver supports the SMC serial ports of the Motorola 68360 CPU. - -config SERIAL_68360_SCC - bool "68360 SCC uart support" - depends on M68360 - help - This driver supports the SCC serial ports of the Motorola 68360 CPU. - -config SERIAL_68360 - bool - depends on SERIAL_68360_SMC || SERIAL_68360_SCC - default y - config SERIAL_PMACZILOG tristate "Mac or PowerMac z85c30 ESCC support" depends on (M68K && MAC) || (PPC_OF && PPC_PMAC) diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 83b4da6a1062..e10cf5b54b6d 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o @@ -35,7 +36,7 @@ obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o -obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o +obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o @@ -49,7 +50,6 @@ obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o -obj-$(CONFIG_SERIAL_68360) += 68360serial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_DZ) += dz.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index af46c56080db..530181e49f6b 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -219,7 +219,7 @@ static int altera_jtaguart_startup(struct uart_port *port) unsigned long flags; int ret; - ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + ret = request_irq(port->irq, altera_jtaguart_interrupt, 0, DRV_NAME, port); if (ret) { pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index 0abd31d2361b..1d04c5037f25 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -315,7 +315,7 @@ static int altera_uart_startup(struct uart_port *port) return 0; } - ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + ret = request_irq(port->irq, altera_uart_interrupt, 0, DRV_NAME, port); if (ret) { pr_err(DRV_NAME ": unable to attach Altera UART %d " diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index 19a943693e4c..77554fd68d1f 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index b922f5d2e61e..9988c0c305c2 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -33,6 +33,8 @@ #include <linux/sysrq.h> #include <linux/tty_flip.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> @@ -157,11 +159,22 @@ struct atmel_uart_port { }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; +static unsigned long atmel_ports_in_use; #ifdef SUPPORT_SYSRQ static struct console atmel_console; #endif +#if defined(CONFIG_OF) +static const struct of_device_id atmel_serial_dt_ids[] = { + { .compatible = "atmel,at91rm9200-usart" }, + { .compatible = "atmel,at91sam9260-usart" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids); +#endif + static inline struct atmel_uart_port * to_atmel_uart_port(struct uart_port *uart) { @@ -339,7 +352,8 @@ static void atmel_stop_tx(struct uart_port *port) /* Disable interrupts */ UART_PUT_IDR(port, atmel_port->tx_done_mask); - if (atmel_port->rs485.flags & SER_RS485_ENABLED) + if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && + !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_start_rx(port); } @@ -356,7 +370,8 @@ static void atmel_start_tx(struct uart_port *port) really need this.*/ return; - if (atmel_port->rs485.flags & SER_RS485_ENABLED) + if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && + !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) atmel_stop_rx(port); /* re-enable PDC transmit */ @@ -680,7 +695,8 @@ static void atmel_tx_dma(struct uart_port *port) /* Enable interrupts */ UART_PUT_IER(port, atmel_port->tx_done_mask); } else { - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + if ((atmel_port->rs485.flags & SER_RS485_ENABLED) && + !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1407,6 +1423,48 @@ static struct uart_ops atmel_pops = { #endif }; +static void __devinit 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 (rs485conf->delay_rts_before_send == 0 && + rs485conf->delay_rts_after_send == 0) { + rs485conf->flags |= SER_RS485_RTS_ON_SEND; + } else { + if (rs485conf->delay_rts_before_send) + rs485conf->flags |= SER_RS485_RTS_BEFORE_SEND; + if (rs485conf->delay_rts_after_send) + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; + } + + 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. */ @@ -1414,13 +1472,20 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev) { struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *data = pdev->dev.platform_data; + struct atmel_uart_data *pdata = pdev->dev.platform_data; + + 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; + } port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &atmel_pops; port->fifosize = 1; - port->line = data->num; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1430,10 +1495,10 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); - if (data->regs) + if (pdata && pdata->regs) { /* Already mapped by setup code */ - port->membase = data->regs; - else { + port->membase = pdata->regs; + } else { port->flags |= UPF_IOREMAP; port->membase = NULL; } @@ -1447,9 +1512,6 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, /* only enable clock when USART is in use */ } - atmel_port->use_dma_rx = data->use_dma_rx; - atmel_port->use_dma_tx = data->use_dma_tx; - atmel_port->rs485 = data->rs485; /* 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; @@ -1611,10 +1673,14 @@ static int __init atmel_console_init(void) if (atmel_default_console_device) { struct atmel_uart_data *pdata = atmel_default_console_device->dev.platform_data; + int id = pdata->num; + struct atmel_uart_port *port = &atmel_ports[id]; + + port->backup_imr = 0; + port->uart.line = id; - add_preferred_console(ATMEL_DEVICENAME, pdata->num, NULL); - atmel_init_port(&atmel_ports[pdata->num], - atmel_default_console_device); + add_preferred_console(ATMEL_DEVICENAME, id, NULL); + atmel_init_port(port, atmel_default_console_device); register_console(&atmel_console); } @@ -1711,14 +1777,39 @@ static int atmel_serial_resume(struct platform_device *pdev) static int __devinit 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; void *data; - int ret; + int ret = -ENODEV; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - port = &atmel_ports[pdata->num]; + if (np) + ret = of_alias_get_id(np, "serial"); + else + if (pdata) + ret = pdata->num; + + 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)); + + if (ret > ATMEL_MAX_UART) { + ret = -ENODEV; + goto err; + } + + if (test_and_set_bit(ret, &atmel_ports_in_use)) { + /* port already in use */ + ret = -EBUSY; + goto err; + } + + port = &atmel_ports[ret]; port->backup_imr = 0; + port->uart.line = ret; atmel_init_port(port, pdev); @@ -1764,7 +1855,7 @@ err_alloc_ring: clk_put(port->clk); port->clk = NULL; } - +err: return ret; } @@ -1784,6 +1875,8 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev) /* "port" is allocated statically, so we shouldn't free it */ + clear_bit(port->line, &atmel_ports_in_use); + clk_put(atmel_port->clk); return ret; @@ -1797,6 +1890,7 @@ static struct platform_driver atmel_serial_driver = { .driver = { .name = "atmel_usart", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_serial_dt_ids), }, }; diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c index 891d194ae754..ee101c0d358f 100644 --- a/drivers/tty/serial/bfin_sport_uart.c +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -294,7 +294,7 @@ static int sport_startup(struct uart_port *port) if (request_irq(gpio_to_irq(up->cts_pin), sport_mctrl_cts_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + 0, "BFIN_SPORT_UART_CTS", up)) { up->cts_pin = -1; dev_info(port->dev, "Unable to attach BlackFin UART over SPORT CTS interrupt. So, disable it.\n"); } diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_uart.c index ff6979181ac5..66afb98b77b5 100644 --- a/drivers/tty/serial/bfin_5xx.c +++ b/drivers/tty/serial/bfin_uart.c @@ -234,8 +234,8 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); - ch = UART_GET_CHAR(uart); - uart->port.icount.rx++; + ch = UART_GET_CHAR(uart); + uart->port.icount.rx++; #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) @@ -667,17 +667,17 @@ static int bfin_serial_startup(struct uart_port *port) kgdboc_break_enabled = 0; else { # endif - if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED, + if (request_irq(uart->rx_irq, bfin_serial_rx_int, 0, "BFIN_UART_RX", uart)) { printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n"); return -EBUSY; } if (request_irq - (uart->port.irq+1, bfin_serial_tx_int, IRQF_DISABLED, + (uart->tx_irq, bfin_serial_tx_int, 0, "BFIN_UART_TX", uart)) { printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n"); - free_irq(uart->port.irq, uart); + free_irq(uart->rx_irq, uart); return -EBUSY; } @@ -692,7 +692,7 @@ static int bfin_serial_startup(struct uart_port *port) */ unsigned uart_dma_ch_rx, uart_dma_ch_tx; - switch (uart->port.irq) { + switch (uart->rx_irq) { case IRQ_UART3_RX: uart_dma_ch_rx = CH_UART3_RX; uart_dma_ch_tx = CH_UART3_TX; @@ -709,16 +709,16 @@ static int bfin_serial_startup(struct uart_port *port) if (uart_dma_ch_rx && request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { printk(KERN_NOTICE"Fail to attach UART interrupt\n"); - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq + 1, uart); + free_irq(uart->rx_irq, uart); + free_irq(uart->tx_irq, uart); return -EBUSY; } if (uart_dma_ch_tx && request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) { printk(KERN_NOTICE "Fail to attach UART interrupt\n"); free_dma(uart_dma_ch_rx); - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq + 1, uart); + free_irq(uart->rx_irq, uart); + free_irq(uart->tx_irq, uart); return -EBUSY; } } @@ -734,19 +734,18 @@ static int bfin_serial_startup(struct uart_port *port) if (request_irq(gpio_to_irq(uart->cts_pin), bfin_serial_mctrl_cts_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_DISABLED, "BFIN_UART_CTS", uart)) { + 0, "BFIN_UART_CTS", uart)) { uart->cts_pin = -1; pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); } } - if (uart->rts_pin >= 0) { + if (uart->rts_pin >= 0) gpio_direction_output(uart->rts_pin, 0); - } #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0 && 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; pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); } @@ -786,8 +785,8 @@ static void bfin_serial_shutdown(struct uart_port *port) break; }; #endif - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq+1, uart); + free_irq(uart->rx_irq, uart); + free_irq(uart->tx_irq, uart); #endif #ifdef CONFIG_SERIAL_BFIN_CTSRTS @@ -1091,10 +1090,18 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, *parity = 'o'; } switch (lcr & 0x03) { - case 0: *bits = 5; break; - case 1: *bits = 6; break; - case 2: *bits = 7; break; - case 3: *bits = 8; break; + case 0: + *bits = 5; + break; + case 1: + *bits = 6; + break; + case 2: + *bits = 7; + break; + case 3: + *bits = 8; + break; } /* Set DLAB in LCR to Access DLL and DLH */ UART_SET_DLAB(uart); @@ -1183,7 +1190,7 @@ static struct console bfin_serial_console = { .index = -1, .data = &bfin_serial_reg, }; -#define BFIN_SERIAL_CONSOLE &bfin_serial_console +#define BFIN_SERIAL_CONSOLE (&bfin_serial_console) #else #define BFIN_SERIAL_CONSOLE NULL #endif /* CONFIG_SERIAL_BFIN_CONSOLE */ @@ -1312,14 +1319,22 @@ static int bfin_serial_probe(struct platform_device *pdev) } uart->port.mapbase = res->start; - uart->port.irq = platform_get_irq(pdev, 0); - if (uart->port.irq < 0) { - dev_err(&pdev->dev, "No uart RX/TX IRQ specified\n"); + uart->tx_irq = platform_get_irq(pdev, 0); + if (uart->tx_irq < 0) { + dev_err(&pdev->dev, "No uart TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + uart->rx_irq = platform_get_irq(pdev, 1); + if (uart->rx_irq < 0) { + dev_err(&pdev->dev, "No uart RX IRQ specified\n"); ret = -ENOENT; goto out_error_unmap; } + uart->port.irq = uart->rx_irq; - uart->status_irq = platform_get_irq(pdev, 1); + uart->status_irq = platform_get_irq(pdev, 2); if (uart->status_irq < 0) { dev_err(&pdev->dev, "No uart status IRQ specified\n"); ret = -ENOENT; diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 9488da74d4f7..b418947b7107 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -31,6 +31,7 @@ #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 58be715913cd..b7435043f2fe 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -258,7 +258,7 @@ static struct e100_serial rs_table[] = { .dma_out_enabled = 1, .dma_out_nbr = SER0_TX_DMA_NBR, .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_flags = 0, .dma_out_irq_description = "serial 0 dma tr", #else .dma_out_enabled = 0, @@ -271,7 +271,7 @@ static struct e100_serial rs_table[] = { .dma_in_enabled = 1, .dma_in_nbr = SER0_RX_DMA_NBR, .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_flags = 0, .dma_in_irq_description = "serial 0 dma rec", #else .dma_in_enabled = 0, @@ -313,7 +313,7 @@ static struct e100_serial rs_table[] = { .dma_out_enabled = 1, .dma_out_nbr = SER1_TX_DMA_NBR, .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_flags = 0, .dma_out_irq_description = "serial 1 dma tr", #else .dma_out_enabled = 0, @@ -326,7 +326,7 @@ static struct e100_serial rs_table[] = { .dma_in_enabled = 1, .dma_in_nbr = SER1_RX_DMA_NBR, .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_flags = 0, .dma_in_irq_description = "serial 1 dma rec", #else .dma_in_enabled = 0, @@ -369,7 +369,7 @@ static struct e100_serial rs_table[] = { .dma_out_enabled = 1, .dma_out_nbr = SER2_TX_DMA_NBR, .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_flags = 0, .dma_out_irq_description = "serial 2 dma tr", #else .dma_out_enabled = 0, @@ -382,7 +382,7 @@ static struct e100_serial rs_table[] = { .dma_in_enabled = 1, .dma_in_nbr = SER2_RX_DMA_NBR, .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_flags = 0, .dma_in_irq_description = "serial 2 dma rec", #else .dma_in_enabled = 0, @@ -423,7 +423,7 @@ static struct e100_serial rs_table[] = { .dma_out_enabled = 1, .dma_out_nbr = SER3_TX_DMA_NBR, .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_flags = 0, .dma_out_irq_description = "serial 3 dma tr", #else .dma_out_enabled = 0, @@ -436,7 +436,7 @@ static struct e100_serial rs_table[] = { .dma_in_enabled = 1, .dma_in_nbr = SER3_RX_DMA_NBR, .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_flags = 0, .dma_in_irq_description = "serial 3 dma rec", #else .dma_in_enabled = 0, @@ -1788,7 +1788,7 @@ static unsigned int handle_descr_data(struct e100_serial *info, struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer; if (info->recv_cnt + recvl > 65536) { - printk(KERN_CRIT + printk(KERN_WARNING "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl); return 0; } @@ -3813,13 +3813,13 @@ rs_close(struct tty_struct *tty, struct file * filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk(KERN_CRIT + printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { - printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n", + printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n", info->line, info->count); info->count = 0; } @@ -4452,7 +4452,7 @@ static int __init rs_init(void) #if defined(CONFIG_ETRAX_RS485_ON_PA) if (cris_io_interface_allocate_pins(if_serial_0, 'a', rs485_pa_bit, rs485_pa_bit)) { - printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " + printk(KERN_ERR "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); put_tty_driver(driver); return -EBUSY; @@ -4461,7 +4461,7 @@ static int __init rs_init(void) #if defined(CONFIG_ETRAX_RS485_ON_PORT_G) if (cris_io_interface_allocate_pins(if_serial_0, 'g', rs485_pa_bit, rs485_port_g_bit)) { - printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " + printk(KERN_ERR "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); put_tty_driver(driver); return -EBUSY; @@ -4494,7 +4494,7 @@ static int __init rs_init(void) if (info->enabled) { if (cris_request_io_interface(info->io_if, info->io_if_description)) { - printk(KERN_CRIT "ETRAX100LX async serial: " + printk(KERN_ERR "ETRAX100LX async serial: " "Could not allocate IO pins for " "%s, port %d\n", info->io_if_description, i); @@ -4558,7 +4558,7 @@ static int __init rs_init(void) /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, - IRQF_SHARED | IRQF_DISABLED, "serial ", driver)) + IRQF_SHARED, "serial ", driver)) panic("%s: Failed to request irq8", __func__); #endif diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index ddc487a2d42f..e3699a84049f 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -47,6 +47,7 @@ #include <linux/serial_core.h> #include <linux/sysrq.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/atomic.h> #include <asm/bootinfo.h> diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index 8a869e58f6d7..d55709a7a75a 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -1554,7 +1554,7 @@ static int __devinit icom_probe(struct pci_dev *dev, /* save off irq and request irq line */ if ( (retval = request_irq(dev->irq, icom_interrupt, - IRQF_DISABLED | IRQF_SHARED, ICOM_DRIVER_NAME, + IRQF_SHARED, ICOM_DRIVER_NAME, (void *) icom_adapter))) { goto probe_exit2; } diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 99898773706c..163fc9021f5a 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -508,8 +508,10 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) continue; - if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { - if (rx & URXD_PRERR) + if (unlikely(rx & URXD_ERR)) { + if (rx & URXD_BRK) + sport->port.icount.brk++; + else if (rx & URXD_PRERR) sport->port.icount.parity++; else if (rx & URXD_FRMERR) sport->port.icount.frame++; @@ -524,7 +526,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) rx &= sport->port.read_status_mask; - if (rx & URXD_PRERR) + if (rx & URXD_BRK) + flg = TTY_BREAK; + else if (rx & URXD_PRERR) flg = TTY_PARITY; else if (rx & URXD_FRMERR) flg = TTY_FRAME; diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c index ee43efc7bdcc..758ff310f7f8 100644 --- a/drivers/tty/serial/ioc3_serial.c +++ b/drivers/tty/serial/ioc3_serial.c @@ -13,6 +13,7 @@ */ #include <linux/errno.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/circ_buf.h> #include <linux/serial_reg.h> diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index fcfe82653ac8..6b36c1554d7e 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -14,6 +14,7 @@ */ #include <linux/errno.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/serialP.h> #include <linux/circ_buf.h> diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index b704c8ce0d71..529bec6edaf8 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -88,7 +88,6 @@ enum { /* 4 extra for alignment play space */ #define WRITEBUFLEN ((4096) + 4) -#define MYFLIPLEN N_TTY_BUF_SIZE #define JSM_VERSION "jsm: 1.2-1-INKERNEL" #define JSM_PARTNUM "40002438_A-INKERNEL" @@ -150,7 +149,6 @@ struct jsm_board u32 bd_uart_offset; /* Space between each UART */ struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */ - char *flipbuf; /* Our flip buffer, alloced if board is found */ u32 bd_dividend; /* Board/UARTs specific dividend */ @@ -177,16 +175,13 @@ struct jsm_board #define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */ #define CH_BREAK_SENDING 0x1000 /* Break is being sent */ #define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */ -#define CH_FLIPBUF_IN_USE 0x4000 /* Channel's flipbuf is in use */ #define CH_BAUD0 0x08000 /* Used for checking B0 transitions */ /* Our Read/Error/Write queue sizes */ #define RQUEUEMASK 0x1FFF /* 8 K - 1 */ #define EQUEUEMASK 0x1FFF /* 8 K - 1 */ -#define WQUEUEMASK 0x0FFF /* 4 K - 1 */ #define RQUEUESIZE (RQUEUEMASK + 1) #define EQUEUESIZE RQUEUESIZE -#define WQUEUESIZE (WQUEUEMASK + 1) /************************************************************************ @@ -226,10 +221,6 @@ struct jsm_channel { u16 ch_e_head; /* Head location of the error queue */ u16 ch_e_tail; /* Tail location of the error queue */ - u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */ - u16 ch_w_head; /* Head location of the write queue */ - u16 ch_w_tail; /* Tail location of the write queue */ - u64 ch_rxcount; /* total of data received so far */ u64 ch_txcount; /* total of data transmitted so far */ @@ -378,7 +369,6 @@ extern int jsm_debug; * Prototypes for non-static functions used in more than one module * *************************************************************************/ -int jsm_tty_write(struct uart_port *port); int jsm_tty_init(struct jsm_board *); int jsm_uart_port_init(struct jsm_board *); int jsm_remove_uart_port(struct jsm_board *); diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 96da17868cf3..648b6a3efa32 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -160,27 +160,10 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n", adapter_count, brd->rev, brd->irq); - /* - * allocate flip buffer for board. - * - * Okay to malloc with GFP_KERNEL, we are not at interrupt - * context, and there are no locks held. - */ - brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipbuf) { - /* XXX: leaking all resources from jsm_tty_init and - jsm_uart_port_init here! */ - dev_err(&pdev->dev, "memory allocation for flipbuf failed\n"); - rc = -ENOMEM; - goto out_free_uart; - } - pci_set_drvdata(pdev, brd); pci_save_state(pdev); return 0; - out_free_uart: - jsm_remove_uart_port(brd); out_free_irq: jsm_remove_uart_port(brd); free_irq(brd->irq, brd); @@ -211,14 +194,12 @@ static void __devexit jsm_remove_one(struct pci_dev *pdev) if (brd->channels[i]) { kfree(brd->channels[i]->ch_rqueue); kfree(brd->channels[i]->ch_equeue); - kfree(brd->channels[i]->ch_wqueue); kfree(brd->channels[i]); } } pci_release_regions(pdev); pci_disable_device(pdev); - kfree(brd->flipbuf); kfree(brd); } diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 4538c3e3646e..81dfafa11b0b 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -496,12 +496,15 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) int s; int qlen; u32 len_written = 0; + struct circ_buf *circ; if (!ch) return; + circ = &ch->uart_port.state->xmit; + /* No data to write to the UART */ - if (ch->ch_w_tail == ch->ch_w_head) + if (uart_circ_empty(circ)) return; /* If port is "stopped", don't send any data to the UART */ @@ -517,11 +520,10 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) if (ch->ch_cached_lsr & UART_LSR_THRE) { ch->ch_cached_lsr &= ~(UART_LSR_THRE); - writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx); + writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, - "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]); - ch->ch_w_tail++; - ch->ch_w_tail &= WQUEUEMASK; + "Tx data: %x\n", circ->buf[circ->tail]); + circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); ch->ch_txcount++; } return; @@ -536,36 +538,36 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; /* cache head and tail of queue */ - head = ch->ch_w_head & WQUEUEMASK; - tail = ch->ch_w_tail & WQUEUEMASK; - qlen = (head - tail) & WQUEUEMASK; + head = circ->head & (UART_XMIT_SIZE - 1); + tail = circ->tail & (UART_XMIT_SIZE - 1); + qlen = uart_circ_chars_pending(circ); /* Find minimum of the FIFO space, versus queue length */ n = min(n, qlen); while (n > 0) { - s = ((head >= tail) ? head : WQUEUESIZE) - tail; + s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; s = min(s, n); if (s <= 0) break; - memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s); + memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s); /* Add and flip queue if needed */ - tail = (tail + s) & WQUEUEMASK; + tail = (tail + s) & (UART_XMIT_SIZE - 1); n -= s; ch->ch_txcount += s; len_written += s; } /* Update the final tail */ - ch->ch_w_tail = tail & WQUEUEMASK; + circ->tail = tail & (UART_XMIT_SIZE - 1); if (len_written >= ch->ch_t_tlevel) ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - if (!jsm_tty_write(&ch->uart_port)) + if (uart_circ_empty(circ)) uart_write_wakeup(&ch->uart_port); } @@ -946,7 +948,6 @@ static void neo_param(struct jsm_channel *ch) if ((ch->ch_c_cflag & (CBAUD)) == 0) { ch->ch_r_head = ch->ch_r_tail = 0; ch->ch_e_head = ch->ch_e_tail = 0; - ch->ch_w_head = ch->ch_w_tail = 0; neo_flush_uart_write(ch); neo_flush_uart_read(ch); diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index 7a4a914ecff0..434bd881fcae 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -118,6 +118,19 @@ static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl) udelay(10); } +/* + * jsm_tty_write() + * + * Take data from the user or kernel and send it out to the FEP. + * In here exists all the Transparent Print magic as well. + */ +static void jsm_tty_write(struct uart_port *port) +{ + struct jsm_channel *channel; + channel = container_of(port, struct jsm_channel, uart_port); + channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); +} + static void jsm_tty_start_tx(struct uart_port *port) { struct jsm_channel *channel = (struct jsm_channel *)port; @@ -216,14 +229,6 @@ static int jsm_tty_open(struct uart_port *port) return -ENOMEM; } } - if (!channel->ch_wqueue) { - channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); - if (!channel->ch_wqueue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate write queue buf"); - return -ENOMEM; - } - } channel->ch_flags &= ~(CH_OPENING); /* @@ -237,7 +242,6 @@ static int jsm_tty_open(struct uart_port *port) */ channel->ch_r_head = channel->ch_r_tail = 0; channel->ch_e_head = channel->ch_e_tail = 0; - channel->ch_w_head = channel->ch_w_tail = 0; brd->bd_ops->flush_uart_write(channel); brd->bd_ops->flush_uart_read(channel); @@ -836,75 +840,3 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) } } } - -/* - * jsm_tty_write() - * - * Take data from the user or kernel and send it out to the FEP. - * In here exists all the Transparent Print magic as well. - */ -int jsm_tty_write(struct uart_port *port) -{ - int bufcount; - int data_count = 0,data_count1 =0; - u16 head; - u16 tail; - u16 tmask; - u32 remain; - int temp_tail = port->state->xmit.tail; - struct jsm_channel *channel = (struct jsm_channel *)port; - - tmask = WQUEUEMASK; - head = (channel->ch_w_head) & tmask; - tail = (channel->ch_w_tail) & tmask; - - if ((bufcount = tail - head - 1) < 0) - bufcount += WQUEUESIZE; - - bufcount = min(bufcount, 56); - remain = WQUEUESIZE - head; - - data_count = 0; - if (bufcount >= remain) { - bufcount -= remain; - while ((port->state->xmit.head != temp_tail) && - (data_count < remain)) { - channel->ch_wqueue[head++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - data_count++; - } - if (data_count == remain) head = 0; - } - - data_count1 = 0; - if (bufcount > 0) { - remain = bufcount; - while ((port->state->xmit.head != temp_tail) && - (data_count1 < remain)) { - channel->ch_wqueue[head++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - data_count1++; - - } - } - - port->state->xmit.tail = temp_tail; - - data_count += data_count1; - if (data_count) { - head &= tmask; - channel->ch_w_head = head; - } - - if (data_count) { - channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); - } - - return data_count; -} diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index bc95f52cad8b..96c1cacc7360 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -338,21 +338,21 @@ lqasc_startup(struct uart_port *port) ASCCON_ROEN, port->membase + LTQ_ASC_CON); retval = request_irq(ltq_port->tx_irq, lqasc_tx_int, - IRQF_DISABLED, "asc_tx", port); + 0, "asc_tx", port); if (retval) { pr_err("failed to request lqasc_tx_int\n"); return retval; } retval = request_irq(ltq_port->rx_irq, lqasc_rx_int, - IRQF_DISABLED, "asc_rx", port); + 0, "asc_rx", port); if (retval) { pr_err("failed to request lqasc_rx_int\n"); goto err1; } retval = request_irq(ltq_port->err_irq, lqasc_err_int, - IRQF_DISABLED, "asc_err", port); + 0, "asc_err", port); if (retval) { pr_err("failed to request lqasc_err_int\n"); goto err2; diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c index 8e07517f8acd..08018934e013 100644 --- a/drivers/tty/serial/m32r_sio.c +++ b/drivers/tty/serial/m32r_sio.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/console.h> diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 7b951adac54b..2af5aa5f3a80 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -47,6 +47,8 @@ #include <linux/serial.h> #include <linux/spi/spi.h> #include <linux/freezer.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial_max3100.h> diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c index a8164601c0ea..db00b595cab0 100644 --- a/drivers/tty/serial/max3107.c +++ b/drivers/tty/serial/max3107.c @@ -31,6 +31,8 @@ #include <linux/device.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/freezer.h> diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 3394b7cc1722..9afca093d6ec 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -380,7 +380,7 @@ static void mcf_config_port(struct uart_port *port, int flags) /* Clear mask, so no surprise interrupts. */ writeb(0, port->membase + MCFUART_UIMR); - if (request_irq(port->irq, mcf_interrupt, IRQF_DISABLED, "UART", port)) + if (request_irq(port->irq, mcf_interrupt, 0, "UART", port)) printk(KERN_ERR "MCF: unable to attach ColdFire UART %d " "interrupt vector=%d\n", port->line, port->irq); } diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index cab52f4a88b0..286c386d9c46 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -38,6 +38,7 @@ #include <linux/pci.h> #include <linux/io.h> #include <linux/debugfs.h> +#include <linux/pm_runtime.h> #define HSU_DMA_BUF_SIZE 2048 @@ -764,6 +765,8 @@ static int serial_hsu_startup(struct uart_port *port) container_of(port, struct uart_hsu_port, port); unsigned long flags; + pm_runtime_get_sync(up->dev); + /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -871,6 +874,8 @@ static void serial_hsu_shutdown(struct uart_port *port) UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_out(up, UART_FCR, 0); + + pm_runtime_put(up->dev); } static void @@ -1249,6 +1254,39 @@ static int serial_hsu_resume(struct pci_dev *pdev) #define serial_hsu_resume NULL #endif +#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; +} + +static int serial_hsu_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int serial_hsu_runtime_resume(struct device *dev) +{ + return 0; +} +#else +#define serial_hsu_runtime_idle NULL +#define serial_hsu_runtime_suspend NULL +#define serial_hsu_runtime_resume NULL +#endif + +static const struct dev_pm_ops serial_hsu_pm_ops = { + .runtime_suspend = serial_hsu_runtime_suspend, + .runtime_resume = serial_hsu_runtime_resume, + .runtime_idle = serial_hsu_runtime_idle, +}; + /* temp global pointer before we settle down on using one or four PCI dev */ static struct hsu_port *phsu; @@ -1315,6 +1353,9 @@ static int serial_hsu_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, uport); } + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + return 0; err_disable: @@ -1411,6 +1452,9 @@ static void serial_hsu_remove(struct pci_dev *pdev) if (!priv) return; + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + /* For port 0/1/2, priv is the address of uart_hsu_port */ if (pdev->device != 0x081E) { up = priv; @@ -1423,7 +1467,7 @@ static void serial_hsu_remove(struct pci_dev *pdev) } /* First 3 are UART ports, and the 4th is the DMA */ -static const struct pci_device_id pci_ids[] __devinitdata = { +static const struct pci_device_id pci_ids[] __devinitconst = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, @@ -1438,6 +1482,9 @@ static struct pci_driver hsu_pci_driver = { .remove = __devexit_p(serial_hsu_remove), .suspend = serial_hsu_suspend, .resume = serial_hsu_resume, + .driver = { + .pm = &serial_hsu_pm_ops, + }, }; static int __init hsu_pci_init(void) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index a0bcd8a3758d..1093a88a1fe3 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -34,6 +34,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> @@ -273,7 +274,7 @@ static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port, static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) { - port->irqflags = IRQF_DISABLED; + port->irqflags = 0; port->irq = irq_of_parse_and_map(np, 0); } diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 23bc743f2a22..4c309e869903 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -23,10 +23,14 @@ * 1 word. If SPI master controller doesn't support sclk frequency change, * then the char need be sent out one by one with some delay * - * 2. Currently only RX available interrrupt is used, no need for waiting TXE + * 2. Currently only RX available interrupt is used, no need for waiting TXE * interrupt for a low speed UART device */ +#ifdef CONFIG_MAGIC_SYSRQ +#define SUPPORT_SYSRQ +#endif + #include <linux/module.h> #include <linux/ioport.h> #include <linux/irq.h> @@ -73,9 +77,9 @@ struct uart_max3110 { /* global data structure, may need be removed */ static struct uart_max3110 *pmax; -static void receive_chars(struct uart_max3110 *max, - unsigned char *str, int len); -static int max3110_read_multi(struct uart_max3110 *max, u8 *buf); +static int receive_chars(struct uart_max3110 *max, + unsigned short *str, int len); +static int max3110_read_multi(struct uart_max3110 *max); static void max3110_con_receive(struct uart_max3110 *max); static int max3110_write_then_read(struct uart_max3110 *max, @@ -108,7 +112,6 @@ static int max3110_out(struct uart_max3110 *max, const u16 out) { void *buf; u16 *obuf, *ibuf; - u8 ch; int ret; buf = kzalloc(8, GFP_KERNEL | GFP_DMA); @@ -125,11 +128,7 @@ static int max3110_out(struct uart_max3110 *max, const u16 out) goto exit; } - /* If some valid data is read back */ - if (*ibuf & MAX3110_READ_DATA_AVAILABLE) { - ch = *ibuf & 0xff; - receive_chars(max, &ch, 1); - } + receive_chars(max, ibuf, 1); exit: kfree(buf); @@ -142,12 +141,11 @@ exit: * * Return how many valide bytes are read back */ -static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) +static int max3110_read_multi(struct uart_max3110 *max) { void *buf; u16 *obuf, *ibuf; - u8 *pbuf, valid_str[M3110_RX_FIFO_DEPTH]; - int i, j, blen; + int ret, blen; blen = M3110_RX_FIFO_DEPTH * sizeof(u16); buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); @@ -165,19 +163,10 @@ static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) return 0; } - /* If caller doesn't provide a buffer, then handle received char */ - pbuf = rxbuf ? rxbuf : valid_str; - - for (i = 0, j = 0; i < M3110_RX_FIFO_DEPTH; i++) { - if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) - pbuf[j++] = ibuf[i] & 0xff; - } - - if (j && (pbuf == valid_str)) - receive_chars(max, valid_str, j); + ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH); kfree(buf); - return j; + return ret; } static void serial_m3110_con_putchar(struct uart_port *port, int ch) @@ -207,7 +196,7 @@ static void serial_m3110_con_write(struct console *co, uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) - wake_up_process(pmax->main_thread); + wake_up(&pmax->wq); } static int __init @@ -276,8 +265,7 @@ static void send_circ_buf(struct uart_max3110 *max, { void *buf; u16 *obuf, *ibuf; - u8 valid_str[WORDS_PER_XFER]; - int i, j, len, blen, dma_size, left, ret = 0; + int i, len, blen, dma_size, left, ret = 0; dma_size = WORDS_PER_XFER * sizeof(u16) * 2; @@ -301,18 +289,13 @@ static void send_circ_buf(struct uart_max3110 *max, } /* Fail to send msg to console is not very critical */ + ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); if (ret) pr_warning(PR_FMT "%s(): get err msg %d\n", __func__, ret); - for (i = 0, j = 0; i < len; i++) { - if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) - valid_str[j++] = ibuf[i] & 0xff; - } - - if (j) - receive_chars(max, valid_str, j); + receive_chars(max, ibuf, len); max->port.icount.tx += len; left -= len; @@ -349,33 +332,54 @@ static void serial_m3110_start_tx(struct uart_port *port) container_of(port, struct uart_max3110, port); if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) - wake_up_process(max->main_thread); + wake_up(&max->wq); } -static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) +static int +receive_chars(struct uart_max3110 *max, unsigned short *str, int len) { struct uart_port *port = &max->port; struct tty_struct *tty; - int usable; + char buf[M3110_RX_FIFO_DEPTH]; + int r, w, usable; /* If uart is not opened, just return */ if (!port->state) - return; + return 0; - tty = port->state->port.tty; + tty = tty_port_tty_get(&port->state->port); if (!tty) - return; + return 0; + + for (r = 0, w = 0; r < len; r++) { + if (str[r] & MAX3110_BREAK && + uart_handle_break(port)) + continue; + + if (str[r] & MAX3110_READ_DATA_AVAILABLE) { + if (uart_handle_sysrq_char(port, str[r] & 0xff)) + continue; + + buf[w++] = str[r] & 0xff; + } + } + + if (!w) { + tty_kref_put(tty); + return 0; + } - while (len) { - usable = tty_buffer_request_room(tty, len); + for (r = 0; w; r += usable, w -= usable) { + usable = tty_buffer_request_room(tty, w); if (usable) { - tty_insert_flip_string(tty, str, usable); - str += usable; + tty_insert_flip_string(tty, buf + r, usable); port->icount.rx += usable; } - len -= usable; } tty_flip_buffer_push(tty); + tty_kref_put(tty); + + return r; } /* @@ -390,28 +394,15 @@ static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) */ static void max3110_con_receive(struct uart_max3110 *max) { - int loop = 1, num, total = 0; - u8 recv_buf[512], *pbuf; + int loop = 1, num; - pbuf = recv_buf; do { - num = max3110_read_multi(max, pbuf); + num = max3110_read_multi(max); if (num) { loop = 5; - pbuf += num; - total += num; - - if (total >= 504) { - receive_chars(max, recv_buf, total); - pbuf = recv_buf; - total = 0; - } } } while (--loop); - - if (total) - receive_chars(max, recv_buf, total); } static int max3110_main_thread(void *_max) @@ -424,7 +415,8 @@ static int max3110_main_thread(void *_max) pr_info(PR_FMT "start main thread\n"); do { - wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop()); + wait_event_interruptible(*wq, + max->uart_flags || kthread_should_stop()); mutex_lock(&max->thread_mutex); @@ -452,8 +444,9 @@ static irqreturn_t serial_m3110_irq(int irq, void *dev_id) /* max3110's irq is a falling edge, not level triggered, * so no need to disable the irq */ + if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) - wake_up_process(max->main_thread); + wake_up(&max->wq); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/mrst_max3110.h b/drivers/tty/serial/mrst_max3110.h index c37ea48c825a..35af0739513b 100644 --- a/drivers/tty/serial/mrst_max3110.h +++ b/drivers/tty/serial/mrst_max3110.h @@ -7,6 +7,7 @@ /* status bits for all 4 MAX3110 operate modes */ #define MAX3110_READ_DATA_AVAILABLE (1 << 15) #define MAX3110_WRITE_BUF_EMPTY (1 << 14) +#define MAX3110_BREAK (1 << 10) #define WC_TAG (3 << 14) #define RC_TAG (1 << 14) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index e6ba83876508..29cbfd8c4e7c 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -804,8 +804,6 @@ static int __init msm_console_setup(struct console *co, char *options) if (unlikely(!port->membase)) return -ENXIO; - port->cons = co; - msm_init_clock(port); if (options) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 624701f8138a..60c6eb850265 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -30,6 +30,8 @@ #include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index 9711e06a8374..06f6aefd5ba6 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -21,6 +21,8 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/console.h> #include <linux/delay.h> /* for udelay */ #include <linux/device.h> diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c index de173671e3d0..9beaff1cec24 100644 --- a/drivers/tty/serial/nwpserial.c +++ b/drivers/tty/serial/nwpserial.c @@ -15,6 +15,7 @@ #include <linux/serial_reg.h> #include <linux/serial_core.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/irqreturn.h> #include <linux/mutex.h> #include <linux/of_platform.h> diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index b46218d679e2..21febef926aa 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -20,6 +20,8 @@ #include <linux/module.h> #include <linux/pci.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> diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 531931c1b250..5c8e3bba6c84 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -100,6 +100,16 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status) int max_count = 256; do { + /* work around Errata #20 according to + * Intel(R) PXA27x Processor Family + * Specification Update (May 2005) + * + * Step 2 + * Disable the Reciever Time Out Interrupt via IER[RTOEI] + */ + up->ier &= ~UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); + ch = serial_in(up, UART_RX); flag = TTY_NORMAL; up->port.icount.rx++; @@ -156,6 +166,16 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status) *status = serial_in(up, UART_LSR); } while ((*status & UART_LSR_DR) && (max_count-- > 0)); tty_flip_buffer_push(tty); + + /* work around Errata #20 according to + * Intel(R) PXA27x Processor Family + * Specification Update (May 2005) + * + * Step 6: + * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE] + */ + up->ier |= UART_IER_RTOIE; + serial_out(up, UART_IER, up->ier); } static void transmit_chars(struct uart_pxa_port *up) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 6edafb5ace18..b31f1c3a2c4c 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -83,6 +83,16 @@ static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); } +/* + * s3c64xx and later SoC's include the interrupt mask and status registers in + * the controller itself, unlike the s3c24xx SoC's which have these registers + * in the interrupt controller. Check if the port type is s3c64xx or higher. + */ +static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port) +{ + return to_ourport(port)->info->type == PORT_S3C6400; +} + static void s3c24xx_serial_rx_enable(struct uart_port *port) { unsigned long flags; @@ -126,7 +136,11 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (tx_enabled(port)) { - disable_irq_nosync(ourport->tx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __set_bit(S3C64XX_UINTM_TXD, + portaddrl(port, S3C64XX_UINTM)); + else + disable_irq_nosync(ourport->tx_irq); tx_enabled(port) = 0; if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_enable(port); @@ -141,19 +155,26 @@ static void s3c24xx_serial_start_tx(struct uart_port *port) if (port->flags & UPF_CONS_FLOW) s3c24xx_serial_rx_disable(port); - enable_irq(ourport->tx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __clear_bit(S3C64XX_UINTM_TXD, + portaddrl(port, S3C64XX_UINTM)); + else + enable_irq(ourport->tx_irq); tx_enabled(port) = 1; } } - static void s3c24xx_serial_stop_rx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); if (rx_enabled(port)) { dbg("s3c24xx_serial_stop_rx: port=%p\n", port); - disable_irq_nosync(ourport->rx_irq); + if (s3c24xx_serial_has_interrupt_mask(port)) + __set_bit(S3C64XX_UINTM_RXD, + portaddrl(port, S3C64XX_UINTM)); + else + disable_irq_nosync(ourport->rx_irq); rx_enabled(port) = 0; } } @@ -320,6 +341,28 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) return IRQ_HANDLED; } +/* interrupt handler for s3c64xx and later SoC's.*/ +static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) +{ + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + unsigned int pend = rd_regl(port, S3C64XX_UINTP); + unsigned long flags; + irqreturn_t ret = IRQ_HANDLED; + + spin_lock_irqsave(&port->lock, flags); + if (pend & S3C64XX_UINTM_RXD_MSK) { + ret = s3c24xx_serial_rx_chars(irq, id); + wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK); + } + if (pend & S3C64XX_UINTM_TXD_MSK) { + ret = s3c24xx_serial_tx_chars(irq, id); + wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK); + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); @@ -377,18 +420,25 @@ static void s3c24xx_serial_shutdown(struct uart_port *port) struct s3c24xx_uart_port *ourport = to_ourport(port); if (ourport->tx_claimed) { - free_irq(ourport->tx_irq, ourport); + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->tx_irq, ourport); tx_enabled(port) = 0; ourport->tx_claimed = 0; } if (ourport->rx_claimed) { - free_irq(ourport->rx_irq, ourport); + if (!s3c24xx_serial_has_interrupt_mask(port)) + free_irq(ourport->rx_irq, ourport); ourport->rx_claimed = 0; rx_enabled(port) = 0; } -} + /* Clear pending interrupts and mask all interrupts */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTM, 0xf); + } +} static int s3c24xx_serial_startup(struct uart_port *port) { @@ -436,6 +486,33 @@ static int s3c24xx_serial_startup(struct uart_port *port) return ret; } +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); + + ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED, + s3c24xx_serial_portname(port), ourport); + if (ret) { + printk(KERN_ERR "cannot get irq %d\n", port->irq); + return ret; + } + + /* For compatibility with s3c24xx Soc's */ + rx_enabled(port) = 1; + ourport->rx_claimed = 1; + tx_enabled(port) = 0; + ourport->tx_claimed = 1; + + /* Enable Rx Interrupt */ + __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM)); + dbg("s3c64xx_serial_startup ok\n"); + return ret; +} + /* power power management control */ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, @@ -879,7 +956,6 @@ static struct uart_ops s3c24xx_serial_ops = { .verify_port = s3c24xx_serial_verify_port, }; - static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .driver_name = "s3c2410_serial", @@ -895,7 +971,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX0, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -907,7 +982,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX1, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -921,7 +995,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX2, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -935,7 +1008,6 @@ static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX3, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, @@ -1077,6 +1149,10 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, port->dev = &platdev->dev; ourport->info = info; + /* Startup sequence is different for s3c64xx and higher SoC's */ + if (s3c24xx_serial_has_interrupt_mask(port)) + s3c24xx_serial_ops.startup = s3c64xx_serial_startup; + /* copy the info in from provided structure */ ourport->port.fifosize = info->fifosize; @@ -1116,6 +1192,13 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, ourport->clk = clk_get(&platdev->dev, "uart"); + /* Keep all interrupts masked and cleared */ + if (s3c24xx_serial_has_interrupt_mask(port)) { + wr_regl(port, S3C64XX_UINTM, 0xf); + wr_regl(port, S3C64XX_UINTP, 0xf); + wr_regl(port, S3C64XX_UINTSP, 0xf); + } + dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", port->mapbase, port->membase, port->irq, ourport->rx_irq, ourport->tx_irq, port->uartclk); diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h index a69d9a54be94..8e87b788e5c6 100644 --- a/drivers/tty/serial/samsung.h +++ b/drivers/tty/serial/samsung.h @@ -61,6 +61,7 @@ struct s3c24xx_uart_port { /* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) +#define portaddrl(port, reg) ((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))) diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c index 6bc2e3f876f4..0be8a2f00d0b 100644 --- a/drivers/tty/serial/sb1250-duart.c +++ b/drivers/tty/serial/sb1250-duart.c @@ -37,6 +37,7 @@ #include <linux/spinlock.h> #include <linux/sysrq.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/types.h> #include <linux/atomic.h> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a3efbea5dbba..0406d7ff505e 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -57,7 +57,7 @@ static struct lock_class_key port_lock_key; static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); -static void __uart_wait_until_sent(struct uart_port *port, int timeout); +static void uart_wait_until_sent(struct tty_struct *tty, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); /* @@ -72,7 +72,7 @@ void uart_write_wakeup(struct uart_port *port) * closed. No cookie for you. */ BUG_ON(!state); - tasklet_schedule(&state->tlet); + tty_wakeup(state->port.tty); } static void uart_stop(struct tty_struct *tty) @@ -107,12 +107,6 @@ static void uart_start(struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); } -static void uart_tasklet_action(unsigned long data) -{ - struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->port.tty); -} - static inline void uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) { @@ -255,9 +249,11 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) } /* - * kill off our tasklet + * It's possible for shutdown to be called after suspend if we get + * a DCD drop (hangup) at just the right time. Clear suspended bit so + * we don't try to resume a port that has been shutdown. */ - tasklet_kill(&state->tlet); + clear_bit(ASYNCB_SUSPENDED, &port->flags); /* * Free the transmit buffer page. @@ -1261,8 +1257,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) struct uart_port *uport; unsigned long flags; - BUG_ON(!tty_locked()); - if (!state) return; @@ -1271,12 +1265,11 @@ static void uart_close(struct tty_struct *tty, struct file *filp) pr_debug("uart_close(%d) called\n", uport->line); - mutex_lock(&port->mutex); spin_lock_irqsave(&port->lock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&port->lock, flags); - goto done; + return; } if ((tty->count == 1) && (port->count != 1)) { @@ -1298,7 +1291,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) } if (port->count) { spin_unlock_irqrestore(&port->lock, flags); - goto done; + return; } /* @@ -1306,19 +1299,13 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters by * setting tty->closing. */ + set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { - /* - * hack: open-coded tty_wait_until_sent to avoid - * recursive tty_lock - */ - long timeout = msecs_to_jiffies(port->closing_wait); - if (wait_event_interruptible_timeout(tty->write_wait, - !tty_chars_in_buffer(tty), timeout) >= 0) - __uart_wait_until_sent(uport, timeout); - } + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent_from_close(tty, + msecs_to_jiffies(port->closing_wait)); /* * At this point, we stop accepting input. To do this, we @@ -1334,9 +1321,10 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * has completely drained; this is especially * important if there is a transmit FIFO! */ - __uart_wait_until_sent(uport, uport->timeout); + uart_wait_until_sent(tty, uport->timeout); } + mutex_lock(&port->mutex); uart_shutdown(tty, state); uart_flush_buffer(tty); @@ -1361,15 +1349,18 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * Wake up anyone trying to open this port. */ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + clear_bit(ASYNCB_CLOSING, &port->flags); spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->close_wait); -done: mutex_unlock(&port->mutex); } -static void __uart_wait_until_sent(struct uart_port *port, int timeout) +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; unsigned long char_time, expire; if (port->type == PORT_UNKNOWN || port->fifosize == 0) @@ -1421,16 +1412,6 @@ static void __uart_wait_until_sent(struct uart_port *port, int timeout) } } -static void uart_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - - tty_lock(); - __uart_wait_until_sent(port, timeout); - tty_unlock(); -} - /* * This is called with the BKL held in * linux/drivers/char/tty_io.c:do_tty_hangup() @@ -1443,7 +1424,6 @@ static void uart_hangup(struct tty_struct *tty) struct tty_port *port = &state->port; unsigned long flags; - BUG_ON(!tty_locked()); pr_debug("uart_hangup(%d)\n", state->uart_port->line); mutex_lock(&port->mutex); @@ -1530,7 +1510,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) struct tty_port *port; int retval, line = tty->index; - BUG_ON(!tty_locked()); pr_debug("uart_open(%d) called\n", line); /* @@ -2008,6 +1987,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) if (port->tty && port->tty->termios && termios.c_cflag == 0) termios = *(port->tty->termios); + if (console_suspend_enabled) + uart_change_pm(state, 0); uport->ops->set_termios(uport, &termios, NULL); if (console_suspend_enabled) console_start(uport->cons); @@ -2068,8 +2049,6 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) case UPIO_MEM32: case UPIO_AU: case UPIO_TSI: - case UPIO_DWAPB: - case UPIO_DWAPB32: snprintf(address, sizeof(address), "MMIO 0x%llx", (unsigned long long)port->mapbase); break; @@ -2298,8 +2277,6 @@ int uart_register_driver(struct uart_driver *drv) port->ops = &uart_port_ops; port->close_delay = 500; /* .5 seconds */ port->closing_wait = 30000; /* 30 seconds */ - tasklet_init(&state->tlet, uart_tasklet_action, - (unsigned long)state); } retval = tty_register_driver(normal); @@ -2460,11 +2437,6 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) */ uport->type = PORT_UNKNOWN; - /* - * Kill the tasklet, and free resources. - */ - tasklet_kill(&state->tlet); - state->uart_port = NULL; mutex_unlock(&port_mutex); @@ -2489,8 +2461,6 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2) case UPIO_MEM32: case UPIO_AU: case UPIO_TSI: - case UPIO_DWAPB: - case UPIO_DWAPB32: return (port1->mapbase == port2->mapbase); } return 0; diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index 2430319f2f52..7c13639c597e 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -13,6 +13,7 @@ */ #include <linux/module.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> @@ -336,19 +337,19 @@ static int ks8695uart_startup(struct uart_port *port) /* * Allocate the IRQ */ - retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, IRQF_DISABLED, "UART TX", port); + retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, 0, "UART TX", port); if (retval) goto err_tx; - retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, IRQF_DISABLED, "UART RX", port); + retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, 0, "UART RX", port); if (retval) goto err_rx; - retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, IRQF_DISABLED, "UART LineStatus", port); + retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, 0, "UART LineStatus", port); if (retval) goto err_ls; - retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, IRQF_DISABLED, "UART ModemStatus", port); + retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, 0, "UART ModemStatus", port); if (retval) goto err_ms; diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index 8e3fc1944e6d..34bd345da775 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -28,6 +28,8 @@ #include <linux/pci.h> #include <linux/serial_core.h> #include <linux/serial.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <asm/io.h> diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5ea6ec3442e6..9871c57b348e 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1976,7 +1976,7 @@ static int __devinit 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->irqflags = IRQF_DISABLED; + port->irqflags = 0; port->serial_in = sci_serial_in; port->serial_out = sci_serial_out; diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index 377ae74e7154..238c7df73ef5 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -39,6 +39,7 @@ #include <linux/interrupt.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/console.h> #include <linux/module.h> @@ -737,7 +738,7 @@ static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) DPRINTF("sn_console: switching to interrupt driven console\n"); if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, "SAL console driver", port) >= 0) { spin_lock_irqsave(&port->sc_port.lock, flags); port->sc_port.irq = SGI_UART_VECTOR; diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c index 1f36b7eb7351..a4b63bfeaa2f 100644 --- a/drivers/tty/serial/timbuart.c +++ b/drivers/tty/serial/timbuart.c @@ -23,6 +23,8 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/ioport.h> diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 0aed022d21b7..6cd414341d5e 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -15,6 +15,7 @@ #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/init.h> diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 9af9f0879a24..cea8918b8233 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -20,8 +20,10 @@ #include <linux/module.h> #include <linux/serial.h> -#include <linux/slab.h> #include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/io.h> #include <linux/of_platform.h> #include <linux/dma-mapping.h> diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 19cc1e8149dd..8c03b127fd03 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -12,9 +12,11 @@ */ #include <linux/platform_device.h> +#include <linux/serial.h> #include <linux/serial_core.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/console.h> -#include <linux/serial.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 0aebd7121b56..b7455b526080 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -63,6 +63,7 @@ #include <linux/spinlock.h> #include <linux/sysrq.h> #include <linux/tty.h> +#include <linux/tty_flip.h> #include <linux/types.h> #include <linux/atomic.h> diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 272e417a9b0d..e67fb20490d2 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -2124,7 +2124,6 @@ static int mgsl_write(struct tty_struct * tty, if ( info->params.mode == MGSL_MODE_HDLC || info->params.mode == MGSL_MODE_RAW ) { /* operating in synchronous (frame oriented) mode */ - /* operating in synchronous (frame oriented) mode */ if (info->tx_active) { if ( info->params.mode == MGSL_MODE_HDLC ) { diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index c77831c7675a..0f6b796c95c5 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -4950,7 +4950,7 @@ CheckAgain: if ( debug_level >= DEBUG_LEVEL_DATA ) trace_block(info,info->rx_buf_list_ex[StartIndex].virt_addr, - min_t(int, framesize,SCABUFSIZE),0); + min_t(unsigned int, framesize, SCABUFSIZE), 0); if (framesize) { if (framesize > info->max_frame_size) @@ -5015,14 +5015,14 @@ static void tx_load_dma_buffer(SLMP_INFO *info, const char *buf, unsigned int co SCADESC_EX *desc_ex; if ( debug_level >= DEBUG_LEVEL_DATA ) - trace_block(info,buf, min_t(int, count,SCABUFSIZE), 1); + trace_block(info, buf, min_t(unsigned int, count, SCABUFSIZE), 1); /* Copy source buffer to one or more DMA buffers, starting with * the first transmit dma buffer. */ for(i=0;;) { - copy_count = min_t(unsigned short,count,SCABUFSIZE); + copy_count = min_t(unsigned int, count, SCABUFSIZE); desc = &info->tx_buf_list[i]; desc_ex = &info->tx_buf_list_ex[i]; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4f1fc81112e6..05085beb83db 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -194,8 +194,7 @@ static inline struct tty_struct *file_tty(struct file *file) return ((struct tty_file_private *)file->private_data)->tty; } -/* Associate a new file with the tty structure */ -int tty_add_file(struct tty_struct *tty, struct file *file) +int tty_alloc_file(struct file *file) { struct tty_file_private *priv; @@ -203,15 +202,36 @@ int tty_add_file(struct tty_struct *tty, struct file *file) if (!priv) return -ENOMEM; + file->private_data = priv; + + return 0; +} + +/* Associate a new file with the tty structure */ +void tty_add_file(struct tty_struct *tty, struct file *file) +{ + struct tty_file_private *priv = file->private_data; + priv->tty = tty; priv->file = file; - file->private_data = priv; spin_lock(&tty_files_lock); list_add(&priv->list, &tty->tty_files); spin_unlock(&tty_files_lock); +} - return 0; +/** + * tty_free_file - free file->private_data + * + * This shall be used only for fail path handling when tty_add_file was not + * called yet. + */ +void tty_free_file(struct file *file) +{ + struct tty_file_private *priv = file->private_data; + + file->private_data = NULL; + kfree(priv); } /* Delete file from its tty */ @@ -222,8 +242,7 @@ void tty_del_file(struct file *file) spin_lock(&tty_files_lock); list_del(&priv->list); spin_unlock(&tty_files_lock); - file->private_data = NULL; - kfree(priv); + tty_free_file(file); } @@ -1811,6 +1830,10 @@ static int tty_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); retry_open: + retval = tty_alloc_file(filp); + if (retval) + return -ENOMEM; + noctty = filp->f_flags & O_NOCTTY; index = -1; retval = 0; @@ -1823,6 +1846,7 @@ retry_open: if (!tty) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENXIO; } driver = tty_driver_kref_get(tty->driver); @@ -1855,6 +1879,7 @@ retry_open: } tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENODEV; } @@ -1862,6 +1887,7 @@ retry_open: if (!driver) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENODEV; } got_driver: @@ -1872,6 +1898,8 @@ got_driver: if (IS_ERR(tty)) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); + tty_free_file(filp); return PTR_ERR(tty); } } @@ -1887,15 +1915,11 @@ got_driver: tty_driver_kref_put(driver); if (IS_ERR(tty)) { tty_unlock(); + tty_free_file(filp); return PTR_ERR(tty); } - retval = tty_add_file(tty, filp); - if (retval) { - tty_unlock(); - tty_release(inode, filp); - return retval; - } + tty_add_file(tty, filp); check_tty_count(tty, "tty_open"); if (tty->driver->type == TTY_DRIVER_TYPE_PTY && @@ -2716,6 +2740,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, ld = tty_ldisc_ref_wait(tty); if (ld->ops->compat_ioctl) retval = ld->ops->compat_ioctl(tty, file, cmd, arg); + else + retval = n_tty_compat_ioctl_helper(tty, file, cmd, arg); tty_ldisc_deref(ld); return retval; diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 53f2442c6099..9314d93c1a20 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/bitops.h> #include <linux/mutex.h> +#include <linux/compat.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -1179,3 +1180,19 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } } EXPORT_SYMBOL(n_tty_ioctl_helper); + +#ifdef CONFIG_COMPAT +long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case TIOCGLCKTRMIOS: + case TIOCSLCKTRMIOS: + return tty_mode_ioctl(tty, file, cmd, (unsigned long) compat_ptr(arg)); + default: + return -ENOIOCTLCMD; + } +} +EXPORT_SYMBOL(n_tty_compat_ioctl_helper); +#endif + diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index ef925d581713..512c49f98e85 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -450,7 +450,6 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) if (ld->ops->open) { int ret; /* BTM here locks versus a hangup event */ - WARN_ON(!tty_locked()); ret = ld->ops->open(tty); if (ret) clear_bit(TTY_LDISC_OPEN, &tty->flags); diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index 3b2bb7719442..9ff986c32a21 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -15,30 +15,18 @@ * Don't use in new code. */ static DEFINE_MUTEX(big_tty_mutex); -struct task_struct *__big_tty_mutex_owner; -EXPORT_SYMBOL_GPL(__big_tty_mutex_owner); /* * Getting the big tty mutex. */ void __lockfunc tty_lock(void) { - struct task_struct *task = current; - - WARN_ON(__big_tty_mutex_owner == task); - mutex_lock(&big_tty_mutex); - __big_tty_mutex_owner = task; } EXPORT_SYMBOL(tty_lock); void __lockfunc tty_unlock(void) { - struct task_struct *task = current; - - WARN_ON(__big_tty_mutex_owner != task); - __big_tty_mutex_owner = NULL; - mutex_unlock(&big_tty_mutex); } EXPORT_SYMBOL(tty_unlock); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 33d37d230f8f..ef9dd628ba0b 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -350,7 +350,7 @@ int tty_port_close_start(struct tty_port *port, tty_driver_flush_buffer(tty); if (test_bit(ASYNCB_INITIALIZED, &port->flags) && port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); + tty_wait_until_sent_from_close(tty, port->closing_wait); if (port->drain_delay) { unsigned int bps = tty_get_baud_rate(tty); long timeout; diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 3761ccf0f340..a605549ee28f 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -33,7 +33,6 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/irq.h> #include <linux/kbd_kern.h> #include <linux/kbd_diacr.h> @@ -43,6 +42,8 @@ #include <linux/notifier.h> #include <linux/jiffies.h> +#include <asm/irq_regs.h> + extern void ctrl_alt_del(void); /* diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index fb864e7fcd13..7a0a12ae5458 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -301,6 +301,8 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t /* Insert the contents of the selection buffer into the * queue of the tty associated with the current console. * Invoked by ioctl(). + * + * Locking: always called with BTM from vt_ioctl */ int paste_selection(struct tty_struct *tty) { @@ -310,8 +312,6 @@ int paste_selection(struct tty_struct *tty) struct tty_ldisc *ld; DECLARE_WAITQUEUE(wait, current); - /* always called with BTM from vt_ioctl */ - WARN_ON(!tty_locked()); console_lock(); poke_blanked_console(); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b3915b7ad3e2..e716839fab6e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -259,7 +259,7 @@ EXPORT_SYMBOL_GPL(unregister_vt_notifier); static void notify_write(struct vc_data *vc, unsigned int unicode) { - struct vt_notifier_param param = { .vc = vc, unicode = unicode }; + struct vt_notifier_param param = { .vc = vc, .c = unicode }; atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, ¶m); } |