From dcd83aaff1c8cbd5b48c152b559e0af3ea1a7b65 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 8 Jul 2011 19:06:12 -0500 Subject: tty/powerpc: introduce the ePAPR embedded hypervisor byte channel driver The ePAPR embedded hypervisor specification provides an API for "byte channels", which are serial-like virtual devices for sending and receiving streams of bytes. This driver provides Linux kernel support for byte channels via three distinct interfaces: 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. Signed-off-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/udbg.h | 1 + arch/powerpc/kernel/udbg.c | 2 + drivers/tty/Kconfig | 34 ++ drivers/tty/Makefile | 1 + drivers/tty/ehv_bytechan.c | 888 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 926 insertions(+) create mode 100644 drivers/tty/ehv_bytechan.c diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h index 93e05d1b34b2..5354ae91bdde 100644 --- a/arch/powerpc/include/asm/udbg.h +++ b/arch/powerpc/include/asm/udbg.h @@ -54,6 +54,7 @@ extern void __init udbg_init_40x_realmode(void); extern void __init udbg_init_cpm(void); extern void __init udbg_init_usbgecko(void); extern void __init udbg_init_wsp(void); +extern void __init udbg_init_ehv_bc(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index faa82c1f3f68..b4607a91d1f4 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -67,6 +67,8 @@ void __init udbg_early_init(void) udbg_init_usbgecko(); #elif defined(CONFIG_PPC_EARLY_DEBUG_WSP) udbg_init_wsp(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_EHV_BC) + udbg_init_ehv_bc(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index bd7cc0527999..f1ea59b09444 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -350,3 +350,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/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c new file mode 100644 index 000000000000..e67f70bbf0ac --- /dev/null +++ b/drivers/tty/ehv_bytechan.c @@ -0,0 +1,888 @@ +/* ePAPR hypervisor byte channel device driver + * + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * Author: Timur Tabi + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; + + /* Check if we're running as a guest of a hypervisor */ + if (!(mfmsr() & MSR_GS)) + return; + + /* 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) +{ + unsigned int handle = (unsigned int)co->data; + 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(handle, s2, j)) + return; + j = 0; + } + } + + if (j) + ehv_bc_console_byte_channel_send(handle, 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 + + ehv_bc_console.data = (void *)stdout_bc; + + /* 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 "); +MODULE_DESCRIPTION("ePAPR hypervisor byte channel driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ae8dbd3eb97aad3929a981d3ab8c3cd2caee4ab1 Mon Sep 17 00:00:00 2001 From: Edwin van Vliet Date: Mon, 11 Jul 2011 00:14:37 +0200 Subject: drivers/tty/synclink: remove double comment Redundant comment line was removed Signed-off-by: Edwin van Vliet Reviewed-by: Jesper Juhl Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink.c | 1 - 1 file changed, 1 deletion(-) 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 ) { -- cgit v1.2.3 From 1f33a51d9771b34be3cb6f7fb96a325e17bbac7b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:10 +0200 Subject: TTY: serial, remove BTM from wait_until_sent During the BKL removal process, the BKL was switched to tty_lock (BTM). Now we should start pruning the BTM further. Let's start with wait_until_sent of the serial layer. This will allow us to switch to the tty port helpers and thus clean it up much. In wait_until_sent there are some uport members accessed, but neither of them is protected by BTM at the location they are set ('=>' means function call): * uport->fifosize (set in tty_ioctl => uart_ioctl => uart_set_info) * uport->type (set in add_one_port prior to tty_register_device) * uport->timeout (set usually in tty_ioctl => tty_mode_ioctl => tty_set_termios => uart_set_termios => uart_change_speed => uport->ops->set_termios => uart_update_timeout) * call to uport->ops->tx_empty() If the tx_empty hook needs some lock to protect accesses to registers, it should take &uport->lock spinlock like 8250 does. Otherwise there still might be races e.g. with ISRs. This should also fix the issue Andreas is seeing (BTM in comparison to BKL doesn't have any hidden functionality like unlocking during sleeping). Signed-off-by: Jiri Slaby References: https://lkml.org/lkml/2011/5/25/562 Cc: Alan Cox Acked-by: Arnd Bergmann Cc: Andreas Bombe Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index db7912cb7ae0..2cbf1bd493e2 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); /* @@ -1304,16 +1304,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) 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(tty, msecs_to_jiffies(port->closing_wait)); /* * At this point, we stop accepting input. To do this, we @@ -1329,7 +1321,7 @@ 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); } uart_shutdown(tty, state); @@ -1363,8 +1355,10 @@ 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) @@ -1416,16 +1410,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() -- cgit v1.2.3 From e150c4ccbe7246c27b5208599fe9f6677c93b5fe Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:11 +0200 Subject: TTY: msm_serial, remove unneeded console set It doesn't make sense to set console to uart_port in console->setup. At that time the console is set by uart_add_one_port already. The call chain looked like: uart_add_one_port() uport->cons = drv->cons; <= once uart_configure_port() register_console() console->setup() port->cons = co; <= second time Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 2 -- 1 file changed, 2 deletions(-) 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) -- cgit v1.2.3 From 6a3e492b6daaf7ec4dc41e51d87d2aae8ff886f2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:12 +0200 Subject: TTY: serial, remove tasklet for tty_wakeup tty_wakeup can be called from any context. So there is no need to have an extra tasklet for calling that. Hence save some space and remove the tasklet completely. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 20 +------------------- include/linux/serial_core.h | 1 - 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 2cbf1bd493e2..4786232bc532 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -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) { @@ -249,11 +243,6 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) synchronize_irq(uport->irq); } - /* - * kill off our tasklet - */ - tasklet_kill(&state->tlet); - /* * Free the transmit buffer page. */ @@ -2277,8 +2266,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); @@ -2439,11 +2426,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); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index a5c31146a337..76e110363745 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -384,7 +384,6 @@ struct uart_state { int pm_state; struct circ_buf xmit; - struct tasklet_struct tlet; struct uart_port *uart_port; }; -- cgit v1.2.3 From eff4b0b9fe37873d3dc7ade0e08e6f0f89279b8b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:13 +0200 Subject: TTY: ami_serial, remove BTM from wait_until_sent The same as in "TTY: serial, remove BTM from wait_until_sent" we don't need to take BTM in wait_until_sent of ami_serial. Exactly the same as serial, ami_serial accesses some "info" members (xmit_fifo_size, timeout), but their assignment on other places in the code is not protected by BTM anyway. So the BTM protects nothing here. This removal helps us to get rid of tty_locked() and __big_tty_mutex_owner in the following patch. This was suggested by Arnd. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Cc: Alan Cox Cc: Andreas Bombe Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 220579592c20..6d43f550b549 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")) @@ -1540,12 +1539,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 @@ -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 -- cgit v1.2.3 From 906cbe1364d94da7cbf74c1d05e3e78b2883f661 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:14 +0200 Subject: TTY: remove tty_locked We used it really only serial and ami_serial. The rest of the callsites were BUG/WARN_ONs to check if BTM is held. Now that we pruned tty_locked from both of the real users, we can get rid of tty_lock along with __big_tty_mutex_owner. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 4 ---- drivers/tty/tty_ldisc.c | 1 - drivers/tty/tty_mutex.c | 12 ------------ drivers/tty/vt/selection.c | 4 ++-- include/linux/tty.h | 2 -- 5 files changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 4786232bc532..44c29631b724 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1245,8 +1245,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; @@ -1411,7 +1409,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); @@ -1498,7 +1495,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); /* 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/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/include/linux/tty.h b/include/linux/tty.h index 44bc0c5617e1..6d5eceb165be 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -600,8 +600,6 @@ extern long vt_compat_ioctl(struct tty_struct *tty, /* functions for preparation of BKL removal */ extern void __lockfunc tty_lock(void) __acquires(tty_lock); extern void __lockfunc tty_unlock(void) __releases(tty_lock); -extern struct task_struct *__big_tty_mutex_owner; -#define tty_locked() (current == __big_tty_mutex_owner) /* * wait_event_interruptible_tty -- wait for a condition with the tty lock held -- cgit v1.2.3 From 8bab534b508230f33be5f7ba8492923754274a8d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 14 Jul 2011 14:35:15 +0200 Subject: TTY: mxser+cyclades remove wait_until_sent debug code It makes the code really ugly. And since it can be enabled only before building and only in the source files, it can be barely used by users. That said, I've not seen anybody to use it in the past few years. This crap is copied to some more drivers over the tty tree. Since I'm not their maintainer, I'm not sure if I should remove them too? Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/cyclades.c | 12 +----------- drivers/tty/mxser.c | 13 +------------ 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index c0e8f2eeb886..5beef494fc40 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) 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 } /* -- cgit v1.2.3 From bb7e58f89617861c3dbfde2edcfd08b4044ede67 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 19 Jul 2011 18:09:24 +0800 Subject: serial:blackfin: Correct coding style in bfin serial driver. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_5xx.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_5xx.c index ff6979181ac5..e08bc046c1bd 100644 --- a/drivers/tty/serial/bfin_5xx.c +++ b/drivers/tty/serial/bfin_5xx.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) @@ -739,9 +739,8 @@ static int bfin_serial_startup(struct uart_port *port) 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, @@ -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 */ -- cgit v1.2.3 From 0c6967b5a0dd54b936e859398e8a977d24bde2a7 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 19 Jul 2011 18:09:25 +0800 Subject: serial:blackfin: rename Blackfin serial driver to bfin_uart.c bfin_5xx.c is not a general name for all Blackfin chips. Signed-off-by: Sonic Zhang Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/Makefile | 2 +- drivers/tty/serial/bfin_5xx.c | 1603 ---------------------------------------- drivers/tty/serial/bfin_uart.c | 1603 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1605 insertions(+), 1605 deletions(-) delete mode 100644 drivers/tty/serial/bfin_5xx.c create mode 100644 drivers/tty/serial/bfin_uart.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4dcb37bbdf92..a9b307f582e1 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -722,7 +722,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" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 83b4da6a1062..78748136ccc8 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -35,7 +35,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 diff --git a/drivers/tty/serial/bfin_5xx.c b/drivers/tty/serial/bfin_5xx.c deleted file mode 100644 index e08bc046c1bd..000000000000 --- a/drivers/tty/serial/bfin_5xx.c +++ /dev/null @@ -1,1603 +0,0 @@ -/* - * Blackfin On-Chip Serial Driver - * - * Copyright 2006-2010 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#define DRIVER_NAME "bfin-uart" -#define pr_fmt(fmt) DRIVER_NAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) -#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) -#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) -#include - -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - -/* UART name and device definitions */ -#define BFIN_SERIAL_DEV_NAME "ttyBF" -#define BFIN_SERIAL_MAJOR 204 -#define BFIN_SERIAL_MINOR 64 - -static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - -# ifndef CONFIG_SERIAL_BFIN_PIO -# error KGDB only support UART in PIO mode. -# endif - -static int kgdboc_port_line; -static int kgdboc_break_enabled; -#endif -/* - * Setup for console. Argument comes from the menuconfig - */ -#define DMA_RX_XCOUNT 512 -#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) - -#define DMA_RX_FLUSH_JIFFIES (HZ / 50) - -#ifdef CONFIG_SERIAL_BFIN_DMA -static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart); -#else -static void bfin_serial_tx_chars(struct bfin_serial_port *uart); -#endif - -static void bfin_serial_reset_irda(struct uart_port *port); - -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) -static unsigned int bfin_serial_get_mctrl(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - if (uart->cts_pin < 0) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - - /* CTS PIN is negative assertive. */ - if (UART_GET_CTS(uart)) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - else - return TIOCM_DSR | TIOCM_CAR; -} - -static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - if (uart->rts_pin < 0) - return; - - /* RTS PIN is negative assertive. */ - if (mctrl & TIOCM_RTS) - UART_ENABLE_RTS(uart); - else - UART_DISABLE_RTS(uart); -} - -/* - * Handle any change of modem status signal. - */ -static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - unsigned int status; - - status = bfin_serial_get_mctrl(&uart->port); - uart_handle_cts_change(&uart->port, status & TIOCM_CTS); -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - uart->scts = 1; - UART_CLEAR_SCTS(uart); - UART_CLEAR_IER(uart, EDSSI); -#endif - - return IRQ_HANDLED; -} -#else -static unsigned int bfin_serial_get_mctrl(struct uart_port *port) -{ - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; -} - -static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} -#endif - -/* - * interrupts are disabled on entry - */ -static void bfin_serial_stop_tx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; -#ifdef CONFIG_SERIAL_BFIN_DMA - struct circ_buf *xmit = &uart->port.state->xmit; -#endif - - while (!(UART_GET_LSR(uart) & TEMT)) - cpu_relax(); - -#ifdef CONFIG_SERIAL_BFIN_DMA - disable_dma(uart->tx_dma_channel); - xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx += uart->tx_count; - uart->tx_count = 0; - uart->tx_done = 1; -#else -#ifdef CONFIG_BF54x - /* Clear TFI bit */ - UART_PUT_LSR(uart, TFI); -#endif - UART_CLEAR_IER(uart, ETBEI); -#endif -} - -/* - * port is locked and interrupts are disabled - */ -static void bfin_serial_start_tx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - struct tty_struct *tty = uart->port.state->port.tty; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - - /* - * To avoid losting RX interrupt, we reset IR function - * before sending data. - */ - if (tty->termios->c_line == N_IRDA) - bfin_serial_reset_irda(port); - -#ifdef CONFIG_SERIAL_BFIN_DMA - if (uart->tx_done) - bfin_serial_dma_tx_chars(uart); -#else - UART_SET_IER(uart, ETBEI); - bfin_serial_tx_chars(uart); -#endif -} - -/* - * Interrupts are enabled - */ -static void bfin_serial_stop_rx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - UART_CLEAR_IER(uart, ERBFI); -} - -/* - * Set the modem control timer to fire immediately. - */ -static void bfin_serial_enable_ms(struct uart_port *port) -{ -} - - -#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO) -# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold) -# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v)) -#else -# define UART_GET_ANOMALY_THRESHOLD(uart) 0 -# define UART_SET_ANOMALY_THRESHOLD(uart, v) -#endif - -#ifdef CONFIG_SERIAL_BFIN_PIO -static void bfin_serial_rx_chars(struct bfin_serial_port *uart) -{ - struct tty_struct *tty = NULL; - unsigned int status, ch, flg; - static struct timeval anomaly_start = { .tv_sec = 0 }; - - status = UART_GET_LSR(uart); - UART_CLEAR_LSR(uart); - - ch = UART_GET_CHAR(uart); - uart->port.icount.rx++; - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdb_connected && kgdboc_port_line == uart->port.line - && kgdboc_break_enabled) - if (ch == 0x3) {/* Ctrl + C */ - kgdb_breakpoint(); - return; - } - - if (!uart->port.state || !uart->port.state->port.tty) - return; -#endif - tty = uart->port.state->port.tty; - - if (ANOMALY_05000363) { - /* The BF533 (and BF561) family of processors have a nice anomaly - * where they continuously generate characters for a "single" break. - * We have to basically ignore this flood until the "next" valid - * character comes across. Due to the nature of the flood, it is - * not possible to reliably catch bytes that are sent too quickly - * after this break. So application code talking to the Blackfin - * which sends a break signal must allow at least 1.5 character - * times after the end of the break for things to stabilize. This - * timeout was picked as it must absolutely be larger than 1 - * character time +/- some percent. So 1.5 sounds good. All other - * Blackfin families operate properly. Woo. - */ - if (anomaly_start.tv_sec) { - struct timeval curr; - suseconds_t usecs; - - if ((~ch & (~ch + 1)) & 0xff) - goto known_good_char; - - do_gettimeofday(&curr); - if (curr.tv_sec - anomaly_start.tv_sec > 1) - goto known_good_char; - - usecs = 0; - if (curr.tv_sec != anomaly_start.tv_sec) - usecs += USEC_PER_SEC; - usecs += curr.tv_usec - anomaly_start.tv_usec; - - if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) - goto known_good_char; - - if (ch) - anomaly_start.tv_sec = 0; - else - anomaly_start = curr; - - return; - - known_good_char: - status &= ~BI; - anomaly_start.tv_sec = 0; - } - } - - if (status & BI) { - if (ANOMALY_05000363) - if (bfin_revid() < 5) - do_gettimeofday(&anomaly_start); - uart->port.icount.brk++; - if (uart_handle_break(&uart->port)) - goto ignore_char; - status &= ~(PE | FE); - } - if (status & PE) - uart->port.icount.parity++; - if (status & OE) - uart->port.icount.overrun++; - if (status & FE) - uart->port.icount.frame++; - - status &= uart->port.read_status_mask; - - if (status & BI) - flg = TTY_BREAK; - else if (status & PE) - flg = TTY_PARITY; - else if (status & FE) - flg = TTY_FRAME; - else - flg = TTY_NORMAL; - - if (uart_handle_sysrq_char(&uart->port, ch)) - goto ignore_char; - - uart_insert_char(&uart->port, status, OE, ch, flg); - - ignore_char: - tty_flip_buffer_push(tty); -} - -static void bfin_serial_tx_chars(struct bfin_serial_port *uart) -{ - struct circ_buf *xmit = &uart->port.state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { -#ifdef CONFIG_BF54x - /* Clear TFI bit */ - UART_PUT_LSR(uart, TFI); -#endif - /* Anomaly notes: - * 05000215 - we always clear ETBEI within last UART TX - * interrupt to end a string. It is always set - * when start a new tx. - */ - UART_CLEAR_IER(uart, ETBEI); - return; - } - - if (uart->port.x_char) { - UART_PUT_CHAR(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; - } - - while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { - UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uart->port); -} - -static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - - while (UART_GET_LSR(uart) & DR) - bfin_serial_rx_chars(uart); - - return IRQ_HANDLED; -} - -static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - spin_lock(&uart->port.lock); - if (UART_GET_LSR(uart) & THRE) - bfin_serial_tx_chars(uart); - spin_unlock(&uart->port.lock); - - return IRQ_HANDLED; -} -#endif - -#ifdef CONFIG_SERIAL_BFIN_DMA -static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) -{ - struct circ_buf *xmit = &uart->port.state->xmit; - - uart->tx_done = 0; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { - uart->tx_count = 0; - uart->tx_done = 1; - return; - } - - if (uart->port.x_char) { - UART_PUT_CHAR(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; - } - - uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) - uart->tx_count = UART_XMIT_SIZE - xmit->tail; - blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), - (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); - set_dma_config(uart->tx_dma_channel, - set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, - INTR_ON_BUF, - DIMENSION_LINEAR, - DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); - set_dma_x_count(uart->tx_dma_channel, uart->tx_count); - set_dma_x_modify(uart->tx_dma_channel, 1); - SSYNC(); - enable_dma(uart->tx_dma_channel); - - UART_SET_IER(uart, ETBEI); -} - -static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) -{ - struct tty_struct *tty = uart->port.state->port.tty; - int i, flg, status; - - status = UART_GET_LSR(uart); - UART_CLEAR_LSR(uart); - - uart->port.icount.rx += - CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, - UART_XMIT_SIZE); - - if (status & BI) { - uart->port.icount.brk++; - if (uart_handle_break(&uart->port)) - goto dma_ignore_char; - status &= ~(PE | FE); - } - if (status & PE) - uart->port.icount.parity++; - if (status & OE) - uart->port.icount.overrun++; - if (status & FE) - uart->port.icount.frame++; - - status &= uart->port.read_status_mask; - - if (status & BI) - flg = TTY_BREAK; - else if (status & PE) - flg = TTY_PARITY; - else if (status & FE) - flg = TTY_FRAME; - else - flg = TTY_NORMAL; - - for (i = uart->rx_dma_buf.tail; ; i++) { - if (i >= UART_XMIT_SIZE) - i = 0; - if (i == uart->rx_dma_buf.head) - break; - if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) - uart_insert_char(&uart->port, status, OE, - uart->rx_dma_buf.buf[i], flg); - } - - dma_ignore_char: - tty_flip_buffer_push(tty); -} - -void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) -{ - int x_pos, pos; - - dma_disable_irq_nosync(uart->rx_dma_channel); - spin_lock_bh(&uart->rx_lock); - - /* 2D DMA RX buffer ring is used. Because curr_y_count and - * curr_x_count can't be read as an atomic operation, - * curr_y_count should be read before curr_x_count. When - * curr_x_count is read, curr_y_count may already indicate - * next buffer line. But, the position calculated here is - * still indicate the old line. The wrong position data may - * be smaller than current buffer tail, which cause garbages - * are received if it is not prohibit. - */ - uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); - x_pos = get_dma_curr_xcount(uart->rx_dma_channel); - uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; - if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) - uart->rx_dma_nrows = 0; - x_pos = DMA_RX_XCOUNT - x_pos; - if (x_pos == DMA_RX_XCOUNT) - x_pos = 0; - - pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; - /* Ignore receiving data if new position is in the same line of - * current buffer tail and small. - */ - if (pos > uart->rx_dma_buf.tail || - uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { - uart->rx_dma_buf.head = pos; - bfin_serial_dma_rx_chars(uart); - uart->rx_dma_buf.tail = uart->rx_dma_buf.head; - } - - spin_unlock_bh(&uart->rx_lock); - dma_enable_irq(uart->rx_dma_channel); - - mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); -} - -static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - struct circ_buf *xmit = &uart->port.state->xmit; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - - spin_lock(&uart->port.lock); - if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) { - disable_dma(uart->tx_dma_channel); - clear_dma_irqstat(uart->tx_dma_channel); - /* Anomaly notes: - * 05000215 - we always clear ETBEI within last UART TX - * interrupt to end a string. It is always set - * when start a new tx. - */ - UART_CLEAR_IER(uart, ETBEI); - xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx += uart->tx_count; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uart->port); - - bfin_serial_dma_tx_chars(uart); - } - - spin_unlock(&uart->port.lock); - return IRQ_HANDLED; -} - -static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - unsigned short irqstat; - int x_pos, pos; - - spin_lock(&uart->rx_lock); - irqstat = get_dma_curr_irqstat(uart->rx_dma_channel); - clear_dma_irqstat(uart->rx_dma_channel); - - uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); - x_pos = get_dma_curr_xcount(uart->rx_dma_channel); - uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; - if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) - uart->rx_dma_nrows = 0; - - pos = uart->rx_dma_nrows * DMA_RX_XCOUNT; - if (pos > uart->rx_dma_buf.tail || - uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { - uart->rx_dma_buf.head = pos; - bfin_serial_dma_rx_chars(uart); - uart->rx_dma_buf.tail = uart->rx_dma_buf.head; - } - - spin_unlock(&uart->rx_lock); - - return IRQ_HANDLED; -} -#endif - -/* - * Return TIOCSER_TEMT when transmitter is not busy. - */ -static unsigned int bfin_serial_tx_empty(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short lsr; - - lsr = UART_GET_LSR(uart); - if (lsr & TEMT) - return TIOCSER_TEMT; - else - return 0; -} - -static void bfin_serial_break_ctl(struct uart_port *port, int break_state) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - u16 lcr = UART_GET_LCR(uart); - if (break_state) - lcr |= SB; - else - lcr &= ~SB; - UART_PUT_LCR(uart, lcr); - SSYNC(); -} - -static int bfin_serial_startup(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - -#ifdef CONFIG_SERIAL_BFIN_DMA - dma_addr_t dma_handle; - - if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) { - printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n"); - return -EBUSY; - } - - if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) { - printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n"); - free_dma(uart->rx_dma_channel); - return -EBUSY; - } - - set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart); - set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart); - - uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA); - uart->rx_dma_buf.head = 0; - uart->rx_dma_buf.tail = 0; - uart->rx_dma_nrows = 0; - - set_dma_config(uart->rx_dma_channel, - set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, - INTR_ON_ROW, DIMENSION_2D, - DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT); - set_dma_x_modify(uart->rx_dma_channel, 1); - set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT); - set_dma_y_modify(uart->rx_dma_channel, 1); - set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf); - enable_dma(uart->rx_dma_channel); - - uart->rx_dma_timer.data = (unsigned long)(uart); - uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout; - uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES; - add_timer(&(uart->rx_dma_timer)); -#else -# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled) - kgdboc_break_enabled = 0; - else { -# endif - if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED, - "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, - "BFIN_UART_TX", uart)) { - printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n"); - free_irq(uart->port.irq, uart); - return -EBUSY; - } - -# ifdef CONFIG_BF54x - { - /* - * UART2 and UART3 on BF548 share interrupt PINs and DMA - * controllers with SPORT2 and SPORT3. UART rx and tx - * interrupts are generated in PIO mode only when configure - * their peripheral mapping registers properly, which means - * request corresponding DMA channels in PIO mode as well. - */ - unsigned uart_dma_ch_rx, uart_dma_ch_tx; - - switch (uart->port.irq) { - case IRQ_UART3_RX: - uart_dma_ch_rx = CH_UART3_RX; - uart_dma_ch_tx = CH_UART3_TX; - break; - case IRQ_UART2_RX: - uart_dma_ch_rx = CH_UART2_RX; - uart_dma_ch_tx = CH_UART2_TX; - break; - default: - uart_dma_ch_rx = uart_dma_ch_tx = 0; - break; - }; - - if (uart_dma_ch_rx && - request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { - printk(KERN_NOTICE"Fail to attach UART interrupt\n"); - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq + 1, 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); - return -EBUSY; - } - } -# endif -# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - } -# endif -#endif - -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->cts_pin >= 0) { - 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)) { - uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); - } - } - 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)) { - uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); - } - - /* CTS RTS PINs are negative assertive. */ - UART_PUT_MCR(uart, ACTS); - UART_SET_IER(uart, EDSSI); -#endif - - UART_SET_IER(uart, ERBFI); - return 0; -} - -static void bfin_serial_shutdown(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - -#ifdef CONFIG_SERIAL_BFIN_DMA - disable_dma(uart->tx_dma_channel); - free_dma(uart->tx_dma_channel); - disable_dma(uart->rx_dma_channel); - free_dma(uart->rx_dma_channel); - del_timer(&(uart->rx_dma_timer)); - dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); -#else -#ifdef CONFIG_BF54x - switch (uart->port.irq) { - case IRQ_UART3_RX: - free_dma(CH_UART3_RX); - free_dma(CH_UART3_TX); - break; - case IRQ_UART2_RX: - free_dma(CH_UART2_RX); - free_dma(CH_UART2_TX); - break; - default: - break; - }; -#endif - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq+1, uart); -#endif - -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->cts_pin >= 0) - free_irq(gpio_to_irq(uart->cts_pin), uart); -#endif -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->cts_pin >= 0) - free_irq(uart->status_irq, uart); -#endif -} - -static void -bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned long flags; - unsigned int baud, quot; - unsigned short val, ier, lcr = 0; - - switch (termios->c_cflag & CSIZE) { - case CS8: - lcr = WLS(8); - break; - case CS7: - lcr = WLS(7); - break; - case CS6: - lcr = WLS(6); - break; - case CS5: - lcr = WLS(5); - break; - default: - printk(KERN_ERR "%s: word lengh not supported\n", - __func__); - } - - /* Anomaly notes: - * 05000231 - STOP bit is always set to 1 whatever the user is set. - */ - if (termios->c_cflag & CSTOPB) { - if (ANOMALY_05000231) - printk(KERN_WARNING "STOP bits other than 1 is not " - "supported in case of anomaly 05000231.\n"); - else - lcr |= STB; - } - if (termios->c_cflag & PARENB) - lcr |= PEN; - if (!(termios->c_cflag & PARODD)) - lcr |= EPS; - if (termios->c_cflag & CMSPAR) - lcr |= STP; - - spin_lock_irqsave(&uart->port.lock, flags); - - port->read_status_mask = OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BI; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= FE | PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= OE; - } - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - /* If discipline is not IRDA, apply ANOMALY_05000230 */ - if (termios->c_line != N_IRDA) - quot -= ANOMALY_05000230; - - UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); - - /* Disable UART */ - ier = UART_GET_IER(uart); - UART_DISABLE_INTS(uart); - - /* Set DLAB in LCR to Access DLL and DLH */ - UART_SET_DLAB(uart); - - UART_PUT_DLL(uart, quot & 0xFF); - UART_PUT_DLH(uart, (quot >> 8) & 0xFF); - SSYNC(); - - /* Clear DLAB in LCR to Access THR RBR IER */ - UART_CLEAR_DLAB(uart); - - UART_PUT_LCR(uart, lcr); - - /* Enable UART */ - UART_ENABLE_INTS(uart, ier); - - val = UART_GET_GCTL(uart); - val |= UCEN; - UART_PUT_GCTL(uart, val); - - /* Port speed changed, update the per-port timeout. */ - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&uart->port.lock, flags); -} - -static const char *bfin_serial_type(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void bfin_serial_release_port(struct uart_port *port) -{ -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int bfin_serial_request_port(struct uart_port *port) -{ - return 0; -} - -/* - * Configure/autoconfigure the port. - */ -static void bfin_serial_config_port(struct uart_port *port, int flags) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - if (flags & UART_CONFIG_TYPE && - bfin_serial_request_port(&uart->port) == 0) - uart->port.type = PORT_BFIN; -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - * The only change we allow are to the flags and type, and - * even then only between PORT_BFIN and PORT_UNKNOWN - */ -static int -bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return 0; -} - -/* - * Enable the IrDA function if tty->ldisc.num is N_IRDA. - * In other cases, disable IrDA function. - */ -static void bfin_serial_set_ldisc(struct uart_port *port, int ld) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; - - switch (ld) { - case N_IRDA: - val = UART_GET_GCTL(uart); - val |= (IREN | RPOLC); - UART_PUT_GCTL(uart, val); - break; - default: - val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); - UART_PUT_GCTL(uart, val); - } -} - -static void bfin_serial_reset_irda(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; - - val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); - UART_PUT_GCTL(uart, val); - SSYNC(); - val |= (IREN | RPOLC); - UART_PUT_GCTL(uart, val); - SSYNC(); -} - -#ifdef CONFIG_CONSOLE_POLL -/* Anomaly notes: - * 05000099 - Because we only use THRE in poll_put and DR in poll_get, - * losing other bits of UART_LSR is not a problem here. - */ -static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - while (!(UART_GET_LSR(uart) & THRE)) - cpu_relax(); - - UART_CLEAR_DLAB(uart); - UART_PUT_CHAR(uart, (unsigned char)chr); -} - -static int bfin_serial_poll_get_char(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned char chr; - - while (!(UART_GET_LSR(uart) & DR)) - cpu_relax(); - - UART_CLEAR_DLAB(uart); - chr = UART_GET_CHAR(uart); - - return chr; -} -#endif - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) -static void bfin_kgdboc_port_shutdown(struct uart_port *port) -{ - if (kgdboc_break_enabled) { - kgdboc_break_enabled = 0; - bfin_serial_shutdown(port); - } -} - -static int bfin_kgdboc_port_startup(struct uart_port *port) -{ - kgdboc_port_line = port->line; - kgdboc_break_enabled = !bfin_serial_startup(port); - return 0; -} -#endif - -static struct uart_ops bfin_serial_pops = { - .tx_empty = bfin_serial_tx_empty, - .set_mctrl = bfin_serial_set_mctrl, - .get_mctrl = bfin_serial_get_mctrl, - .stop_tx = bfin_serial_stop_tx, - .start_tx = bfin_serial_start_tx, - .stop_rx = bfin_serial_stop_rx, - .enable_ms = bfin_serial_enable_ms, - .break_ctl = bfin_serial_break_ctl, - .startup = bfin_serial_startup, - .shutdown = bfin_serial_shutdown, - .set_termios = bfin_serial_set_termios, - .set_ldisc = bfin_serial_set_ldisc, - .type = bfin_serial_type, - .release_port = bfin_serial_release_port, - .request_port = bfin_serial_request_port, - .config_port = bfin_serial_config_port, - .verify_port = bfin_serial_verify_port, -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - .kgdboc_port_startup = bfin_kgdboc_port_startup, - .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, -#endif -#ifdef CONFIG_CONSOLE_POLL - .poll_put_char = bfin_serial_poll_put_char, - .poll_get_char = bfin_serial_poll_get_char, -#endif -}; - -#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) -/* - * If the port was already initialised (eg, by a boot loader), - * try to determine the current setup. - */ -static void __init -bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, - int *parity, int *bits) -{ - unsigned short status; - - status = UART_GET_IER(uart) & (ERBFI | ETBEI); - if (status == (ERBFI | ETBEI)) { - /* ok, the port was enabled */ - u16 lcr, dlh, dll; - - lcr = UART_GET_LCR(uart); - - *parity = 'n'; - if (lcr & PEN) { - if (lcr & EPS) - *parity = 'e'; - else - *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; - } - /* Set DLAB in LCR to Access DLL and DLH */ - UART_SET_DLAB(uart); - - dll = UART_GET_DLL(uart); - dlh = UART_GET_DLH(uart); - - /* Clear DLAB in LCR to Access THR RBR IER */ - UART_CLEAR_DLAB(uart); - - *baud = get_sclk() / (16*(dll | dlh << 8)); - } - pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); -} - -static struct uart_driver bfin_serial_reg; - -static void bfin_serial_console_putchar(struct uart_port *port, int ch) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - while (!(UART_GET_LSR(uart) & THRE)) - barrier(); - UART_PUT_CHAR(uart, ch); -} - -#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || - defined (CONFIG_EARLY_PRINTK) */ - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE -#define CLASS_BFIN_CONSOLE "bfin-console" -/* - * Interrupts are disabled on entering - */ -static void -bfin_serial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct bfin_serial_port *uart = bfin_serial_ports[co->index]; - unsigned long flags; - - spin_lock_irqsave(&uart->port.lock, flags); - uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); - spin_unlock_irqrestore(&uart->port.lock, flags); - -} - -static int __init -bfin_serial_console_setup(struct console *co, char *options) -{ - struct bfin_serial_port *uart; - int baud = 57600; - int bits = 8; - int parity = 'n'; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - int flow = 'r'; -# else - int flow = 'n'; -# endif - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) - return -ENODEV; - - uart = bfin_serial_ports[co->index]; - if (!uart) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - bfin_serial_console_get_options(uart, &baud, &parity, &bits); - - return uart_set_options(&uart->port, co, baud, parity, bits, flow); -} - -static struct console bfin_serial_console = { - .name = BFIN_SERIAL_DEV_NAME, - .write = bfin_serial_console_write, - .device = uart_console_device, - .setup = bfin_serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &bfin_serial_reg, -}; -#define BFIN_SERIAL_CONSOLE (&bfin_serial_console) -#else -#define BFIN_SERIAL_CONSOLE NULL -#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ - -#ifdef CONFIG_EARLY_PRINTK -static struct bfin_serial_port bfin_earlyprintk_port; -#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" - -/* - * Interrupts are disabled on entering - */ -static void -bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) -{ - unsigned long flags; - - if (bfin_earlyprintk_port.port.line != co->index) - return; - - spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); - uart_console_write(&bfin_earlyprintk_port.port, s, count, - bfin_serial_console_putchar); - spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); -} - -/* - * This should have a .setup or .early_setup in it, but then things get called - * without the command line options, and the baud rate gets messed up - so - * don't let the common infrastructure play with things. (see calls to setup - * & earlysetup in ./kernel/printk.c:register_console() - */ -static struct __initdata console bfin_early_serial_console = { - .name = "early_BFuart", - .write = bfin_earlyprintk_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &bfin_serial_reg, -}; -#endif - -static struct uart_driver bfin_serial_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = BFIN_SERIAL_DEV_NAME, - .major = BFIN_SERIAL_MAJOR, - .minor = BFIN_SERIAL_MINOR, - .nr = BFIN_UART_NR_PORTS, - .cons = BFIN_SERIAL_CONSOLE, -}; - -static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - return uart_suspend_port(&bfin_serial_reg, &uart->port); -} - -static int bfin_serial_resume(struct platform_device *pdev) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - return uart_resume_port(&bfin_serial_reg, &uart->port); -} - -static int bfin_serial_probe(struct platform_device *pdev) -{ - struct resource *res; - struct bfin_serial_port *uart = NULL; - int ret = 0; - - if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { - dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); - return -ENOENT; - } - - if (bfin_serial_ports[pdev->id] == NULL) { - - uart = kzalloc(sizeof(*uart), GFP_KERNEL); - if (!uart) { - dev_err(&pdev->dev, - "fail to malloc bfin_serial_port\n"); - return -ENOMEM; - } - bfin_serial_ports[pdev->id] = uart; - -#ifdef CONFIG_EARLY_PRINTK - if (!(bfin_earlyprintk_port.port.membase - && bfin_earlyprintk_port.port.line == pdev->id)) { - /* - * If the peripheral PINs of current port is allocated - * in earlyprintk probe stage, don't do it again. - */ -#endif - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, - "fail to request bfin serial peripherals\n"); - goto out_error_free_mem; - } -#ifdef CONFIG_EARLY_PRINTK - } -#endif - - spin_lock_init(&uart->port.lock); - uart->port.uartclk = get_sclk(); - uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; - uart->port.ops = &bfin_serial_pops; - uart->port.line = pdev->id; - uart->port.iotype = UPIO_MEM; - uart->port.flags = UPF_BOOT_AUTOCONF; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); - ret = -ENOENT; - goto out_error_free_peripherals; - } - - uart->port.membase = ioremap(res->start, resource_size(res)); - if (!uart->port.membase) { - dev_err(&pdev->dev, "Cannot map uart IO\n"); - ret = -ENXIO; - goto out_error_free_peripherals; - } - 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"); - ret = -ENOENT; - goto out_error_unmap; - } - - uart->status_irq = platform_get_irq(pdev, 1); - if (uart->status_irq < 0) { - dev_err(&pdev->dev, "No uart status IRQ specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - -#ifdef CONFIG_SERIAL_BFIN_DMA - spin_lock_init(&uart->rx_lock); - uart->tx_done = 1; - uart->tx_count = 0; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res == NULL) { - dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - uart->tx_dma_channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res == NULL) { - dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - uart->rx_dma_channel = res->start; - - init_timer(&(uart->rx_dma_timer)); -#endif - -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (res == NULL) - uart->cts_pin = -1; - else - uart->cts_pin = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IO, 1); - if (res == NULL) - uart->rts_pin = -1; - else - uart->rts_pin = res->start; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) - if (uart->rts_pin >= 0) - gpio_request(uart->rts_pin, DRIVER_NAME); -# endif -#endif - } - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - if (!is_early_platform_device(pdev)) { -#endif - uart = bfin_serial_ports[pdev->id]; - uart->port.dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, uart); - ret = uart_add_one_port(&bfin_serial_reg, &uart->port); -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - } -#endif - - if (!ret) - return 0; - - if (uart) { -out_error_unmap: - iounmap(uart->port.membase); -out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); -out_error_free_mem: - kfree(uart); - bfin_serial_ports[pdev->id] = NULL; - } - - return ret; -} - -static int __devexit bfin_serial_remove(struct platform_device *pdev) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - dev_set_drvdata(&pdev->dev, NULL); - - if (uart) { - uart_remove_one_port(&bfin_serial_reg, &uart->port); -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); -#endif - iounmap(uart->port.membase); - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); - kfree(uart); - bfin_serial_ports[pdev->id] = NULL; - } - - return 0; -} - -static struct platform_driver bfin_serial_driver = { - .probe = bfin_serial_probe, - .remove = __devexit_p(bfin_serial_remove), - .suspend = bfin_serial_suspend, - .resume = bfin_serial_resume, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -#if defined(CONFIG_SERIAL_BFIN_CONSOLE) -static __initdata struct early_platform_driver early_bfin_serial_driver = { - .class_str = CLASS_BFIN_CONSOLE, - .pdrv = &bfin_serial_driver, - .requested_id = EARLY_PLATFORM_ID_UNSET, -}; - -static int __init bfin_serial_rs_console_init(void) -{ - early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); - - early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); - - register_console(&bfin_serial_console); - - return 0; -} -console_initcall(bfin_serial_rs_console_init); -#endif - -#ifdef CONFIG_EARLY_PRINTK -/* - * Memory can't be allocated dynamically during earlyprink init stage. - * So, do individual probe for earlyprink with a static uart port variable. - */ -static int bfin_earlyprintk_probe(struct platform_device *pdev) -{ - struct resource *res; - int ret; - - if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { - dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); - return -ENOENT; - } - - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, - "fail to request bfin serial peripherals\n"); - return ret; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); - ret = -ENOENT; - goto out_error_free_peripherals; - } - - bfin_earlyprintk_port.port.membase = ioremap(res->start, - resource_size(res)); - if (!bfin_earlyprintk_port.port.membase) { - dev_err(&pdev->dev, "Cannot map uart IO\n"); - ret = -ENXIO; - goto out_error_free_peripherals; - } - bfin_earlyprintk_port.port.mapbase = res->start; - bfin_earlyprintk_port.port.line = pdev->id; - bfin_earlyprintk_port.port.uartclk = get_sclk(); - bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; - spin_lock_init(&bfin_earlyprintk_port.port.lock); - - return 0; - -out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); - - return ret; -} - -static struct platform_driver bfin_earlyprintk_driver = { - .probe = bfin_earlyprintk_probe, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { - .class_str = CLASS_BFIN_EARLYPRINTK, - .pdrv = &bfin_earlyprintk_driver, - .requested_id = EARLY_PLATFORM_ID_UNSET, -}; - -struct console __init *bfin_earlyserial_init(unsigned int port, - unsigned int cflag) -{ - struct ktermios t; - char port_name[20]; - - if (port < 0 || port >= BFIN_UART_NR_PORTS) - return NULL; - - /* - * Only probe resource of the given port in earlyprintk boot arg. - * The expected port id should be indicated in port name string. - */ - snprintf(port_name, 20, DRIVER_NAME ".%d", port); - early_platform_driver_register(&early_bfin_earlyprintk_driver, - port_name); - early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); - - if (!bfin_earlyprintk_port.port.membase) - return NULL; - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - /* - * If we are using early serial, don't let the normal console rewind - * log buffer, since that causes things to be printed multiple times - */ - bfin_serial_console.flags &= ~CON_PRINTBUFFER; -#endif - - bfin_early_serial_console.index = port; - t.c_cflag = cflag; - t.c_iflag = 0; - t.c_oflag = 0; - t.c_lflag = ICANON; - t.c_line = port; - bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); - - return &bfin_early_serial_console; -} -#endif /* CONFIG_EARLY_PRINTK */ - -static int __init bfin_serial_init(void) -{ - int ret; - - pr_info("Blackfin serial driver\n"); - - ret = uart_register_driver(&bfin_serial_reg); - if (ret) { - pr_err("failed to register %s:%d\n", - bfin_serial_reg.driver_name, ret); - } - - ret = platform_driver_register(&bfin_serial_driver); - if (ret) { - pr_err("fail to register bfin uart\n"); - uart_unregister_driver(&bfin_serial_reg); - } - - return ret; -} - -static void __exit bfin_serial_exit(void) -{ - platform_driver_unregister(&bfin_serial_driver); - uart_unregister_driver(&bfin_serial_reg); -} - - -module_init(bfin_serial_init); -module_exit(bfin_serial_exit); - -MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); -MODULE_DESCRIPTION("Blackfin generic serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); -MODULE_ALIAS("platform:bfin-uart"); diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c new file mode 100644 index 000000000000..e08bc046c1bd --- /dev/null +++ b/drivers/tty/serial/bfin_uart.c @@ -0,0 +1,1603 @@ +/* + * Blackfin On-Chip Serial Driver + * + * Copyright 2006-2010 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#define DRIVER_NAME "bfin-uart" +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) +#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) +#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) +#include + +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK +#endif + +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK +#endif + +/* UART name and device definitions */ +#define BFIN_SERIAL_DEV_NAME "ttyBF" +#define BFIN_SERIAL_MAJOR 204 +#define BFIN_SERIAL_MINOR 64 + +static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + +# ifndef CONFIG_SERIAL_BFIN_PIO +# error KGDB only support UART in PIO mode. +# endif + +static int kgdboc_port_line; +static int kgdboc_break_enabled; +#endif +/* + * Setup for console. Argument comes from the menuconfig + */ +#define DMA_RX_XCOUNT 512 +#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) + +#define DMA_RX_FLUSH_JIFFIES (HZ / 50) + +#ifdef CONFIG_SERIAL_BFIN_DMA +static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart); +#else +static void bfin_serial_tx_chars(struct bfin_serial_port *uart); +#endif + +static void bfin_serial_reset_irda(struct uart_port *port); + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +static unsigned int bfin_serial_get_mctrl(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + if (uart->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (UART_GET_CTS(uart)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + if (uart->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + UART_ENABLE_RTS(uart); + else + UART_DISABLE_RTS(uart); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + unsigned int status; + + status = bfin_serial_get_mctrl(&uart->port); + uart_handle_cts_change(&uart->port, status & TIOCM_CTS); +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + uart->scts = 1; + UART_CLEAR_SCTS(uart); + UART_CLEAR_IER(uart, EDSSI); +#endif + + return IRQ_HANDLED; +} +#else +static unsigned int bfin_serial_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} +#endif + +/* + * interrupts are disabled on entry + */ +static void bfin_serial_stop_tx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; +#ifdef CONFIG_SERIAL_BFIN_DMA + struct circ_buf *xmit = &uart->port.state->xmit; +#endif + + while (!(UART_GET_LSR(uart) & TEMT)) + cpu_relax(); + +#ifdef CONFIG_SERIAL_BFIN_DMA + disable_dma(uart->tx_dma_channel); + xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx += uart->tx_count; + uart->tx_count = 0; + uart->tx_done = 1; +#else +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + UART_CLEAR_IER(uart, ETBEI); +#endif +} + +/* + * port is locked and interrupts are disabled + */ +static void bfin_serial_start_tx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + struct tty_struct *tty = uart->port.state->port.tty; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + + /* + * To avoid losting RX interrupt, we reset IR function + * before sending data. + */ + if (tty->termios->c_line == N_IRDA) + bfin_serial_reset_irda(port); + +#ifdef CONFIG_SERIAL_BFIN_DMA + if (uart->tx_done) + bfin_serial_dma_tx_chars(uart); +#else + UART_SET_IER(uart, ETBEI); + bfin_serial_tx_chars(uart); +#endif +} + +/* + * Interrupts are enabled + */ +static void bfin_serial_stop_rx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + UART_CLEAR_IER(uart, ERBFI); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void bfin_serial_enable_ms(struct uart_port *port) +{ +} + + +#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO) +# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold) +# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v)) +#else +# define UART_GET_ANOMALY_THRESHOLD(uart) 0 +# define UART_SET_ANOMALY_THRESHOLD(uart, v) +#endif + +#ifdef CONFIG_SERIAL_BFIN_PIO +static void bfin_serial_rx_chars(struct bfin_serial_port *uart) +{ + struct tty_struct *tty = NULL; + unsigned int status, ch, flg; + static struct timeval anomaly_start = { .tv_sec = 0 }; + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + ch = UART_GET_CHAR(uart); + uart->port.icount.rx++; + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + if (kgdb_connected && kgdboc_port_line == uart->port.line + && kgdboc_break_enabled) + if (ch == 0x3) {/* Ctrl + C */ + kgdb_breakpoint(); + return; + } + + if (!uart->port.state || !uart->port.state->port.tty) + return; +#endif + tty = uart->port.state->port.tty; + + if (ANOMALY_05000363) { + /* The BF533 (and BF561) family of processors have a nice anomaly + * where they continuously generate characters for a "single" break. + * We have to basically ignore this flood until the "next" valid + * character comes across. Due to the nature of the flood, it is + * not possible to reliably catch bytes that are sent too quickly + * after this break. So application code talking to the Blackfin + * which sends a break signal must allow at least 1.5 character + * times after the end of the break for things to stabilize. This + * timeout was picked as it must absolutely be larger than 1 + * character time +/- some percent. So 1.5 sounds good. All other + * Blackfin families operate properly. Woo. + */ + if (anomaly_start.tv_sec) { + struct timeval curr; + suseconds_t usecs; + + if ((~ch & (~ch + 1)) & 0xff) + goto known_good_char; + + do_gettimeofday(&curr); + if (curr.tv_sec - anomaly_start.tv_sec > 1) + goto known_good_char; + + usecs = 0; + if (curr.tv_sec != anomaly_start.tv_sec) + usecs += USEC_PER_SEC; + usecs += curr.tv_usec - anomaly_start.tv_usec; + + if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) + goto known_good_char; + + if (ch) + anomaly_start.tv_sec = 0; + else + anomaly_start = curr; + + return; + + known_good_char: + status &= ~BI; + anomaly_start.tv_sec = 0; + } + } + + if (status & BI) { + if (ANOMALY_05000363) + if (bfin_revid() < 5) + do_gettimeofday(&anomaly_start); + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + if (uart_handle_sysrq_char(&uart->port, ch)) + goto ignore_char; + + uart_insert_char(&uart->port, status, OE, ch, flg); + + ignore_char: + tty_flip_buffer_push(tty); +} + +static void bfin_serial_tx_chars(struct bfin_serial_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { + UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); +} + +static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + + while (UART_GET_LSR(uart) & DR) + bfin_serial_rx_chars(uart); + + return IRQ_HANDLED; +} + +static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + spin_lock(&uart->port.lock); + if (UART_GET_LSR(uart) & THRE) + bfin_serial_tx_chars(uart); + spin_unlock(&uart->port.lock); + + return IRQ_HANDLED; +} +#endif + +#ifdef CONFIG_SERIAL_BFIN_DMA +static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + + uart->tx_done = 0; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { + uart->tx_count = 0; + uart->tx_done = 1; + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) + uart->tx_count = UART_XMIT_SIZE - xmit->tail; + blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), + (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); + set_dma_config(uart->tx_dma_channel, + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, + INTR_ON_BUF, + DIMENSION_LINEAR, + DATA_SIZE_8, + DMA_SYNC_RESTART)); + set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); + set_dma_x_count(uart->tx_dma_channel, uart->tx_count); + set_dma_x_modify(uart->tx_dma_channel, 1); + SSYNC(); + enable_dma(uart->tx_dma_channel); + + UART_SET_IER(uart, ETBEI); +} + +static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) +{ + struct tty_struct *tty = uart->port.state->port.tty; + int i, flg, status; + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + uart->port.icount.rx += + CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, + UART_XMIT_SIZE); + + if (status & BI) { + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto dma_ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + for (i = uart->rx_dma_buf.tail; ; i++) { + if (i >= UART_XMIT_SIZE) + i = 0; + if (i == uart->rx_dma_buf.head) + break; + if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) + uart_insert_char(&uart->port, status, OE, + uart->rx_dma_buf.buf[i], flg); + } + + dma_ignore_char: + tty_flip_buffer_push(tty); +} + +void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) +{ + int x_pos, pos; + + dma_disable_irq_nosync(uart->rx_dma_channel); + spin_lock_bh(&uart->rx_lock); + + /* 2D DMA RX buffer ring is used. Because curr_y_count and + * curr_x_count can't be read as an atomic operation, + * curr_y_count should be read before curr_x_count. When + * curr_x_count is read, curr_y_count may already indicate + * next buffer line. But, the position calculated here is + * still indicate the old line. The wrong position data may + * be smaller than current buffer tail, which cause garbages + * are received if it is not prohibit. + */ + uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); + x_pos = get_dma_curr_xcount(uart->rx_dma_channel); + uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; + if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) + uart->rx_dma_nrows = 0; + x_pos = DMA_RX_XCOUNT - x_pos; + if (x_pos == DMA_RX_XCOUNT) + x_pos = 0; + + pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; + /* Ignore receiving data if new position is in the same line of + * current buffer tail and small. + */ + if (pos > uart->rx_dma_buf.tail || + uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { + uart->rx_dma_buf.head = pos; + bfin_serial_dma_rx_chars(uart); + uart->rx_dma_buf.tail = uart->rx_dma_buf.head; + } + + spin_unlock_bh(&uart->rx_lock); + dma_enable_irq(uart->rx_dma_channel); + + mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); +} + +static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + struct circ_buf *xmit = &uart->port.state->xmit; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + + spin_lock(&uart->port.lock); + if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) { + disable_dma(uart->tx_dma_channel); + clear_dma_irqstat(uart->tx_dma_channel); + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx += uart->tx_count; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + bfin_serial_dma_tx_chars(uart); + } + + spin_unlock(&uart->port.lock); + return IRQ_HANDLED; +} + +static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + unsigned short irqstat; + int x_pos, pos; + + spin_lock(&uart->rx_lock); + irqstat = get_dma_curr_irqstat(uart->rx_dma_channel); + clear_dma_irqstat(uart->rx_dma_channel); + + uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); + x_pos = get_dma_curr_xcount(uart->rx_dma_channel); + uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; + if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) + uart->rx_dma_nrows = 0; + + pos = uart->rx_dma_nrows * DMA_RX_XCOUNT; + if (pos > uart->rx_dma_buf.tail || + uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { + uart->rx_dma_buf.head = pos; + bfin_serial_dma_rx_chars(uart); + uart->rx_dma_buf.tail = uart->rx_dma_buf.head; + } + + spin_unlock(&uart->rx_lock); + + return IRQ_HANDLED; +} +#endif + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int bfin_serial_tx_empty(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short lsr; + + lsr = UART_GET_LSR(uart); + if (lsr & TEMT) + return TIOCSER_TEMT; + else + return 0; +} + +static void bfin_serial_break_ctl(struct uart_port *port, int break_state) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + u16 lcr = UART_GET_LCR(uart); + if (break_state) + lcr |= SB; + else + lcr &= ~SB; + UART_PUT_LCR(uart, lcr); + SSYNC(); +} + +static int bfin_serial_startup(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + +#ifdef CONFIG_SERIAL_BFIN_DMA + dma_addr_t dma_handle; + + if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n"); + return -EBUSY; + } + + if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) { + printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n"); + free_dma(uart->rx_dma_channel); + return -EBUSY; + } + + set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart); + set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart); + + uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA); + uart->rx_dma_buf.head = 0; + uart->rx_dma_buf.tail = 0; + uart->rx_dma_nrows = 0; + + set_dma_config(uart->rx_dma_channel, + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, + INTR_ON_ROW, DIMENSION_2D, + DATA_SIZE_8, + DMA_SYNC_RESTART)); + set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT); + set_dma_x_modify(uart->rx_dma_channel, 1); + set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT); + set_dma_y_modify(uart->rx_dma_channel, 1); + set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf); + enable_dma(uart->rx_dma_channel); + + uart->rx_dma_timer.data = (unsigned long)(uart); + uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout; + uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES; + add_timer(&(uart->rx_dma_timer)); +#else +# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled) + kgdboc_break_enabled = 0; + else { +# endif + if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED, + "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, + "BFIN_UART_TX", uart)) { + printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n"); + free_irq(uart->port.irq, uart); + return -EBUSY; + } + +# ifdef CONFIG_BF54x + { + /* + * UART2 and UART3 on BF548 share interrupt PINs and DMA + * controllers with SPORT2 and SPORT3. UART rx and tx + * interrupts are generated in PIO mode only when configure + * their peripheral mapping registers properly, which means + * request corresponding DMA channels in PIO mode as well. + */ + unsigned uart_dma_ch_rx, uart_dma_ch_tx; + + switch (uart->port.irq) { + case IRQ_UART3_RX: + uart_dma_ch_rx = CH_UART3_RX; + uart_dma_ch_tx = CH_UART3_TX; + break; + case IRQ_UART2_RX: + uart_dma_ch_rx = CH_UART2_RX; + uart_dma_ch_tx = CH_UART2_TX; + break; + default: + uart_dma_ch_rx = uart_dma_ch_tx = 0; + break; + }; + + if (uart_dma_ch_rx && + request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE"Fail to attach UART interrupt\n"); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, 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); + return -EBUSY; + } + } +# endif +# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + } +# endif +#endif + +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->cts_pin >= 0) { + 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)) { + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); + } + } + 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)) { + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); + } + + /* CTS RTS PINs are negative assertive. */ + UART_PUT_MCR(uart, ACTS); + UART_SET_IER(uart, EDSSI); +#endif + + UART_SET_IER(uart, ERBFI); + return 0; +} + +static void bfin_serial_shutdown(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + +#ifdef CONFIG_SERIAL_BFIN_DMA + disable_dma(uart->tx_dma_channel); + free_dma(uart->tx_dma_channel); + disable_dma(uart->rx_dma_channel); + free_dma(uart->rx_dma_channel); + del_timer(&(uart->rx_dma_timer)); + dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); +#else +#ifdef CONFIG_BF54x + switch (uart->port.irq) { + case IRQ_UART3_RX: + free_dma(CH_UART3_RX); + free_dma(CH_UART3_TX); + break; + case IRQ_UART2_RX: + free_dma(CH_UART2_RX); + free_dma(CH_UART2_TX); + break; + default: + break; + }; +#endif + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq+1, uart); +#endif + +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->cts_pin >= 0) + free_irq(gpio_to_irq(uart->cts_pin), uart); +#endif +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->cts_pin >= 0) + free_irq(uart->status_irq, uart); +#endif +} + +static void +bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned long flags; + unsigned int baud, quot; + unsigned short val, ier, lcr = 0; + + switch (termios->c_cflag & CSIZE) { + case CS8: + lcr = WLS(8); + break; + case CS7: + lcr = WLS(7); + break; + case CS6: + lcr = WLS(6); + break; + case CS5: + lcr = WLS(5); + break; + default: + printk(KERN_ERR "%s: word lengh not supported\n", + __func__); + } + + /* Anomaly notes: + * 05000231 - STOP bit is always set to 1 whatever the user is set. + */ + if (termios->c_cflag & CSTOPB) { + if (ANOMALY_05000231) + printk(KERN_WARNING "STOP bits other than 1 is not " + "supported in case of anomaly 05000231.\n"); + else + lcr |= STB; + } + if (termios->c_cflag & PARENB) + lcr |= PEN; + if (!(termios->c_cflag & PARODD)) + lcr |= EPS; + if (termios->c_cflag & CMSPAR) + lcr |= STP; + + spin_lock_irqsave(&uart->port.lock, flags); + + port->read_status_mask = OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (FE | PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= FE | PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= OE; + } + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + /* If discipline is not IRDA, apply ANOMALY_05000230 */ + if (termios->c_line != N_IRDA) + quot -= ANOMALY_05000230; + + UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); + + /* Disable UART */ + ier = UART_GET_IER(uart); + UART_DISABLE_INTS(uart); + + /* Set DLAB in LCR to Access DLL and DLH */ + UART_SET_DLAB(uart); + + UART_PUT_DLL(uart, quot & 0xFF); + UART_PUT_DLH(uart, (quot >> 8) & 0xFF); + SSYNC(); + + /* Clear DLAB in LCR to Access THR RBR IER */ + UART_CLEAR_DLAB(uart); + + UART_PUT_LCR(uart, lcr); + + /* Enable UART */ + UART_ENABLE_INTS(uart, ier); + + val = UART_GET_GCTL(uart); + val |= UCEN; + UART_PUT_GCTL(uart, val); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&uart->port.lock, flags); +} + +static const char *bfin_serial_type(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void bfin_serial_release_port(struct uart_port *port) +{ +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int bfin_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void bfin_serial_config_port(struct uart_port *port, int flags) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + if (flags & UART_CONFIG_TYPE && + bfin_serial_request_port(&uart->port) == 0) + uart->port.type = PORT_BFIN; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_BFIN and PORT_UNKNOWN + */ +static int +bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +/* + * Enable the IrDA function if tty->ldisc.num is N_IRDA. + * In other cases, disable IrDA function. + */ +static void bfin_serial_set_ldisc(struct uart_port *port, int ld) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short val; + + switch (ld) { + case N_IRDA: + val = UART_GET_GCTL(uart); + val |= (IREN | RPOLC); + UART_PUT_GCTL(uart, val); + break; + default: + val = UART_GET_GCTL(uart); + val &= ~(IREN | RPOLC); + UART_PUT_GCTL(uart, val); + } +} + +static void bfin_serial_reset_irda(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short val; + + val = UART_GET_GCTL(uart); + val &= ~(IREN | RPOLC); + UART_PUT_GCTL(uart, val); + SSYNC(); + val |= (IREN | RPOLC); + UART_PUT_GCTL(uart, val); + SSYNC(); +} + +#ifdef CONFIG_CONSOLE_POLL +/* Anomaly notes: + * 05000099 - Because we only use THRE in poll_put and DR in poll_get, + * losing other bits of UART_LSR is not a problem here. + */ +static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + while (!(UART_GET_LSR(uart) & THRE)) + cpu_relax(); + + UART_CLEAR_DLAB(uart); + UART_PUT_CHAR(uart, (unsigned char)chr); +} + +static int bfin_serial_poll_get_char(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned char chr; + + while (!(UART_GET_LSR(uart) & DR)) + cpu_relax(); + + UART_CLEAR_DLAB(uart); + chr = UART_GET_CHAR(uart); + + return chr; +} +#endif + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) +static void bfin_kgdboc_port_shutdown(struct uart_port *port) +{ + if (kgdboc_break_enabled) { + kgdboc_break_enabled = 0; + bfin_serial_shutdown(port); + } +} + +static int bfin_kgdboc_port_startup(struct uart_port *port) +{ + kgdboc_port_line = port->line; + kgdboc_break_enabled = !bfin_serial_startup(port); + return 0; +} +#endif + +static struct uart_ops bfin_serial_pops = { + .tx_empty = bfin_serial_tx_empty, + .set_mctrl = bfin_serial_set_mctrl, + .get_mctrl = bfin_serial_get_mctrl, + .stop_tx = bfin_serial_stop_tx, + .start_tx = bfin_serial_start_tx, + .stop_rx = bfin_serial_stop_rx, + .enable_ms = bfin_serial_enable_ms, + .break_ctl = bfin_serial_break_ctl, + .startup = bfin_serial_startup, + .shutdown = bfin_serial_shutdown, + .set_termios = bfin_serial_set_termios, + .set_ldisc = bfin_serial_set_ldisc, + .type = bfin_serial_type, + .release_port = bfin_serial_release_port, + .request_port = bfin_serial_request_port, + .config_port = bfin_serial_config_port, + .verify_port = bfin_serial_verify_port, +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + .kgdboc_port_startup = bfin_kgdboc_port_startup, + .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, +#endif +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = bfin_serial_poll_put_char, + .poll_get_char = bfin_serial_poll_get_char, +#endif +}; + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, + int *parity, int *bits) +{ + unsigned short status; + + status = UART_GET_IER(uart) & (ERBFI | ETBEI); + if (status == (ERBFI | ETBEI)) { + /* ok, the port was enabled */ + u16 lcr, dlh, dll; + + lcr = UART_GET_LCR(uart); + + *parity = 'n'; + if (lcr & PEN) { + if (lcr & EPS) + *parity = 'e'; + else + *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; + } + /* Set DLAB in LCR to Access DLL and DLH */ + UART_SET_DLAB(uart); + + dll = UART_GET_DLL(uart); + dlh = UART_GET_DLH(uart); + + /* Clear DLAB in LCR to Access THR RBR IER */ + UART_CLEAR_DLAB(uart); + + *baud = get_sclk() / (16*(dll | dlh << 8)); + } + pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); +} + +static struct uart_driver bfin_serial_reg; + +static void bfin_serial_console_putchar(struct uart_port *port, int ch) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); +} + +#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || + defined (CONFIG_EARLY_PRINTK) */ + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE +#define CLASS_BFIN_CONSOLE "bfin-console" +/* + * Interrupts are disabled on entering + */ +static void +bfin_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct bfin_serial_port *uart = bfin_serial_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + +} + +static int __init +bfin_serial_console_setup(struct console *co, char *options) +{ + struct bfin_serial_port *uart; + int baud = 57600; + int bits = 8; + int parity = 'n'; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + int flow = 'r'; +# else + int flow = 'n'; +# endif + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) + return -ENODEV; + + uart = bfin_serial_ports[co->index]; + if (!uart) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + bfin_serial_console_get_options(uart, &baud, &parity, &bits); + + return uart_set_options(&uart->port, co, baud, parity, bits, flow); +} + +static struct console bfin_serial_console = { + .name = BFIN_SERIAL_DEV_NAME, + .write = bfin_serial_console_write, + .device = uart_console_device, + .setup = bfin_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bfin_serial_reg, +}; +#define BFIN_SERIAL_CONSOLE (&bfin_serial_console) +#else +#define BFIN_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ + +#ifdef CONFIG_EARLY_PRINTK +static struct bfin_serial_port bfin_earlyprintk_port; +#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" + +/* + * Interrupts are disabled on entering + */ +static void +bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) +{ + unsigned long flags; + + if (bfin_earlyprintk_port.port.line != co->index) + return; + + spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); + uart_console_write(&bfin_earlyprintk_port.port, s, count, + bfin_serial_console_putchar); + spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); +} + +/* + * This should have a .setup or .early_setup in it, but then things get called + * without the command line options, and the baud rate gets messed up - so + * don't let the common infrastructure play with things. (see calls to setup + * & earlysetup in ./kernel/printk.c:register_console() + */ +static struct __initdata console bfin_early_serial_console = { + .name = "early_BFuart", + .write = bfin_earlyprintk_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bfin_serial_reg, +}; +#endif + +static struct uart_driver bfin_serial_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = BFIN_SERIAL_DEV_NAME, + .major = BFIN_SERIAL_MAJOR, + .minor = BFIN_SERIAL_MINOR, + .nr = BFIN_UART_NR_PORTS, + .cons = BFIN_SERIAL_CONSOLE, +}; + +static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_suspend_port(&bfin_serial_reg, &uart->port); +} + +static int bfin_serial_resume(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_resume_port(&bfin_serial_reg, &uart->port); +} + +static int bfin_serial_probe(struct platform_device *pdev) +{ + struct resource *res; + struct bfin_serial_port *uart = NULL; + int ret = 0; + + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); + return -ENOENT; + } + + if (bfin_serial_ports[pdev->id] == NULL) { + + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + dev_err(&pdev->dev, + "fail to malloc bfin_serial_port\n"); + return -ENOMEM; + } + bfin_serial_ports[pdev->id] = uart; + +#ifdef CONFIG_EARLY_PRINTK + if (!(bfin_earlyprintk_port.port.membase + && bfin_earlyprintk_port.port.line == pdev->id)) { + /* + * If the peripheral PINs of current port is allocated + * in earlyprintk probe stage, don't do it again. + */ +#endif + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + goto out_error_free_mem; + } +#ifdef CONFIG_EARLY_PRINTK + } +#endif + + spin_lock_init(&uart->port.lock); + uart->port.uartclk = get_sclk(); + uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; + uart->port.ops = &bfin_serial_pops; + uart->port.line = pdev->id; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + uart->port.membase = ioremap(res->start, resource_size(res)); + if (!uart->port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + 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"); + ret = -ENOENT; + goto out_error_unmap; + } + + uart->status_irq = platform_get_irq(pdev, 1); + if (uart->status_irq < 0) { + dev_err(&pdev->dev, "No uart status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + +#ifdef CONFIG_SERIAL_BFIN_DMA + spin_lock_init(&uart->rx_lock); + uart->tx_done = 1; + uart->tx_count = 0; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->tx_dma_channel = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->rx_dma_channel = res->start; + + init_timer(&(uart->rx_dma_timer)); +#endif + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + uart->cts_pin = -1; + else + uart->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + uart->rts_pin = -1; + else + uart->rts_pin = res->start; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) + if (uart->rts_pin >= 0) + gpio_request(uart->rts_pin, DRIVER_NAME); +# endif +#endif + } + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + uart = bfin_serial_ports[pdev->id]; + uart->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, uart); + ret = uart_add_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + } +#endif + + if (!ret) + return 0; + + if (uart) { +out_error_unmap: + iounmap(uart->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return ret; +} + +static int __devexit bfin_serial_remove(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (uart) { + uart_remove_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->rts_pin >= 0) + gpio_free(uart->rts_pin); +#endif + iounmap(uart->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return 0; +} + +static struct platform_driver bfin_serial_driver = { + .probe = bfin_serial_probe, + .remove = __devexit_p(bfin_serial_remove), + .suspend = bfin_serial_suspend, + .resume = bfin_serial_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) +static __initdata struct early_platform_driver early_bfin_serial_driver = { + .class_str = CLASS_BFIN_CONSOLE, + .pdrv = &bfin_serial_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init bfin_serial_rs_console_init(void) +{ + early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); + + early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); + + register_console(&bfin_serial_console); + + return 0; +} +console_initcall(bfin_serial_rs_console_init); +#endif + +#ifdef CONFIG_EARLY_PRINTK +/* + * Memory can't be allocated dynamically during earlyprink init stage. + * So, do individual probe for earlyprink with a static uart port variable. + */ +static int bfin_earlyprintk_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); + return -ENOENT; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + bfin_earlyprintk_port.port.membase = ioremap(res->start, + resource_size(res)); + if (!bfin_earlyprintk_port.port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + bfin_earlyprintk_port.port.mapbase = res->start; + bfin_earlyprintk_port.port.line = pdev->id; + bfin_earlyprintk_port.port.uartclk = get_sclk(); + bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; + spin_lock_init(&bfin_earlyprintk_port.port.lock); + + return 0; + +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + + return ret; +} + +static struct platform_driver bfin_earlyprintk_driver = { + .probe = bfin_earlyprintk_probe, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { + .class_str = CLASS_BFIN_EARLYPRINTK, + .pdrv = &bfin_earlyprintk_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +struct console __init *bfin_earlyserial_init(unsigned int port, + unsigned int cflag) +{ + struct ktermios t; + char port_name[20]; + + if (port < 0 || port >= BFIN_UART_NR_PORTS) + return NULL; + + /* + * Only probe resource of the given port in earlyprintk boot arg. + * The expected port id should be indicated in port name string. + */ + snprintf(port_name, 20, DRIVER_NAME ".%d", port); + early_platform_driver_register(&early_bfin_earlyprintk_driver, + port_name); + early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); + + if (!bfin_earlyprintk_port.port.membase) + return NULL; + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + /* + * If we are using early serial, don't let the normal console rewind + * log buffer, since that causes things to be printed multiple times + */ + bfin_serial_console.flags &= ~CON_PRINTBUFFER; +#endif + + bfin_early_serial_console.index = port; + t.c_cflag = cflag; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = ICANON; + t.c_line = port; + bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); + + return &bfin_early_serial_console; +} +#endif /* CONFIG_EARLY_PRINTK */ + +static int __init bfin_serial_init(void) +{ + int ret; + + pr_info("Blackfin serial driver\n"); + + ret = uart_register_driver(&bfin_serial_reg); + if (ret) { + pr_err("failed to register %s:%d\n", + bfin_serial_reg.driver_name, ret); + } + + ret = platform_driver_register(&bfin_serial_driver); + if (ret) { + pr_err("fail to register bfin uart\n"); + uart_unregister_driver(&bfin_serial_reg); + } + + return ret; +} + +static void __exit bfin_serial_exit(void) +{ + platform_driver_unregister(&bfin_serial_driver); + uart_unregister_driver(&bfin_serial_reg); +} + + +module_init(bfin_serial_init); +module_exit(bfin_serial_exit); + +MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); +MODULE_DESCRIPTION("Blackfin generic serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); +MODULE_ALIAS("platform:bfin-uart"); -- cgit v1.2.3 From 502fcb796c4e9f9966f34cb253b881d00bf90eae Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 30 Jul 2011 14:01:59 +0200 Subject: tty: clearify structure initializer in notify_write() Even though this is valid C we should not mix C99 initializers with obfuscated ANSI C. Stick to C99 and initialize c by its name. Found by clang: drivers/tty/vt/vt.c:262:55: warning: explicitly assigning a variable of type 'unsigned int' to itself [-Wself-assign] struct vt_notifier_param param = { .vc = vc, unicode = unicode }; ~~~~~~~ ^ ~~~~~~~ Signed-off-by: Mathias Krause Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); } -- cgit v1.2.3 From 47918f055851d3faae2a62d406b5d065b579a1a3 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Fri, 12 Aug 2011 11:15:51 +0800 Subject: serial:bfin_uart: Put TX IRQ in individual platform resource. Serial TX IRQ is not RX IRQ plus 1 in some blackfin chips. Give individual platform resources to both TX and RX irqs. Signed-off-by: Sonic Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/bfin_uart.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/bfin_uart.c b/drivers/tty/serial/bfin_uart.c index e08bc046c1bd..c7e592df42c0 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -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, IRQF_DISABLED, "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, IRQF_DISABLED, "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; } } @@ -785,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 @@ -1319,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->status_irq = platform_get_irq(pdev, 1); + 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, 2); if (uart->status_irq < 0) { dev_err(&pdev->dev, "No uart status IRQ specified\n"); ret = -ENOENT; -- cgit v1.2.3 From 88bb4ea14c72f89c5265029c1891e5eb6521cc0f Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 10 Aug 2011 15:51:19 +0530 Subject: serial: samsung: Add unified interrupt handler for s3c64xx and later SoC's s3c64xx and later SoC's include the interrupt mask and pending registers in the uart controller, unlike the s3c24xx SoC's which have these registers in the interrupt controller. When the mask and pending registers are part of the uart controller, a unified interrupt handler can handle the tx/rx interrupt. With this, the static reservation of interrupt numbers for the uart tx/rx/err interrupts in the linux irq space is not required and simplifies adding device tree support. Suggested-by: Grant Likely CC: Ben Dooks Signed-off-by: Thomas Abraham Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 107 ++++++++++++++++++++++++++++++++++++++----- drivers/tty/serial/samsung.h | 1 + 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index afc629423152..9b8465487253 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))) -- cgit v1.2.3 From 2a8d7bddf273477d6aa81405c9b4bae223e11ed9 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 10 Aug 2011 15:51:20 +0530 Subject: ARM: SAMSUNG: Remove uart irq handling from plaform code With uart tx/rx/err interrupt handling moved into the driver for s3c64xx and later SoC's, the uart interrupt handling in plaform code can be removed. The uart device irq resources is reduced to one and the related unused macros are removed. Suggested-by: Grant Likely CC: Ben Dooks Signed-off-by: Thomas Abraham Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig | 1 - arch/arm/mach-s3c64xx/dev-uart.c | 60 ++------------- arch/arm/mach-s3c64xx/include/mach/irqs.h | 30 -------- arch/arm/mach-s3c64xx/irq.c | 25 ------ arch/arm/plat-s5p/Kconfig | 1 - arch/arm/plat-s5p/dev-uart.c | 84 +++------------------ arch/arm/plat-s5p/include/plat/irqs.h | 35 --------- arch/arm/plat-s5p/irq.c | 34 --------- arch/arm/plat-samsung/Kconfig | 5 -- arch/arm/plat-samsung/Makefile | 1 - arch/arm/plat-samsung/include/plat/regs-serial.h | 5 ++ arch/arm/plat-samsung/irq-uart.c | 96 ------------------------ 12 files changed, 25 insertions(+), 352 deletions(-) delete mode 100644 arch/arm/plat-samsung/irq-uart.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5ebc5d922ea1..325d2f55744d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -722,7 +722,6 @@ config ARCH_S3C64XX select ARCH_REQUIRE_GPIOLIB select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER - select SAMSUNG_IRQ_UART select S3C_GPIO_TRACK select S3C_GPIO_PULL_UPDOWN select S3C_GPIO_CFG_S3C24XX diff --git a/arch/arm/mach-s3c64xx/dev-uart.c b/arch/arm/mach-s3c64xx/dev-uart.c index f797f748b999..c681b99eda08 100644 --- a/arch/arm/mach-s3c64xx/dev-uart.c +++ b/arch/arm/mach-s3c64xx/dev-uart.c @@ -37,21 +37,10 @@ static struct resource s3c64xx_uart0_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S3CUART_RX0, - .end = IRQ_S3CUART_RX0, + .start = IRQ_UART0, + .end = IRQ_UART0, .flags = IORESOURCE_IRQ, }, - [2] = { - .start = IRQ_S3CUART_TX0, - .end = IRQ_S3CUART_TX0, - .flags = IORESOURCE_IRQ, - - }, - [3] = { - .start = IRQ_S3CUART_ERR0, - .end = IRQ_S3CUART_ERR0, - .flags = IORESOURCE_IRQ, - } }; static struct resource s3c64xx_uart1_resource[] = { @@ -61,19 +50,8 @@ static struct resource s3c64xx_uart1_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S3CUART_RX1, - .end = IRQ_S3CUART_RX1, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S3CUART_TX1, - .end = IRQ_S3CUART_TX1, - .flags = IORESOURCE_IRQ, - - }, - [3] = { - .start = IRQ_S3CUART_ERR1, - .end = IRQ_S3CUART_ERR1, + .start = IRQ_UART1, + .end = IRQ_UART1, .flags = IORESOURCE_IRQ, }, }; @@ -85,19 +63,8 @@ static struct resource s3c6xx_uart2_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S3CUART_RX2, - .end = IRQ_S3CUART_RX2, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S3CUART_TX2, - .end = IRQ_S3CUART_TX2, - .flags = IORESOURCE_IRQ, - - }, - [3] = { - .start = IRQ_S3CUART_ERR2, - .end = IRQ_S3CUART_ERR2, + .start = IRQ_UART2, + .end = IRQ_UART2, .flags = IORESOURCE_IRQ, }, }; @@ -109,19 +76,8 @@ static struct resource s3c64xx_uart3_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S3CUART_RX3, - .end = IRQ_S3CUART_RX3, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S3CUART_TX3, - .end = IRQ_S3CUART_TX3, - .flags = IORESOURCE_IRQ, - - }, - [3] = { - .start = IRQ_S3CUART_ERR3, - .end = IRQ_S3CUART_ERR3, + .start = IRQ_UART3, + .end = IRQ_UART3, .flags = IORESOURCE_IRQ, }, }; diff --git a/arch/arm/mach-s3c64xx/include/mach/irqs.h b/arch/arm/mach-s3c64xx/include/mach/irqs.h index c026f67a80de..443f85b3c203 100644 --- a/arch/arm/mach-s3c64xx/include/mach/irqs.h +++ b/arch/arm/mach-s3c64xx/include/mach/irqs.h @@ -27,36 +27,6 @@ #define IRQ_VIC0_BASE S3C_IRQ(0) #define IRQ_VIC1_BASE S3C_IRQ(32) -/* UART interrupts, each UART has 4 intterupts per channel so - * use the space between the ISA and S3C main interrupts. Note, these - * are not in the same order as the S3C24XX series! */ - -#define IRQ_S3CUART_BASE0 (16) -#define IRQ_S3CUART_BASE1 (20) -#define IRQ_S3CUART_BASE2 (24) -#define IRQ_S3CUART_BASE3 (28) - -#define UART_IRQ_RXD (0) -#define UART_IRQ_ERR (1) -#define UART_IRQ_TXD (2) -#define UART_IRQ_MODEM (3) - -#define IRQ_S3CUART_RX0 (IRQ_S3CUART_BASE0 + UART_IRQ_RXD) -#define IRQ_S3CUART_TX0 (IRQ_S3CUART_BASE0 + UART_IRQ_TXD) -#define IRQ_S3CUART_ERR0 (IRQ_S3CUART_BASE0 + UART_IRQ_ERR) - -#define IRQ_S3CUART_RX1 (IRQ_S3CUART_BASE1 + UART_IRQ_RXD) -#define IRQ_S3CUART_TX1 (IRQ_S3CUART_BASE1 + UART_IRQ_TXD) -#define IRQ_S3CUART_ERR1 (IRQ_S3CUART_BASE1 + UART_IRQ_ERR) - -#define IRQ_S3CUART_RX2 (IRQ_S3CUART_BASE2 + UART_IRQ_RXD) -#define IRQ_S3CUART_TX2 (IRQ_S3CUART_BASE2 + UART_IRQ_TXD) -#define IRQ_S3CUART_ERR2 (IRQ_S3CUART_BASE2 + UART_IRQ_ERR) - -#define IRQ_S3CUART_RX3 (IRQ_S3CUART_BASE3 + UART_IRQ_RXD) -#define IRQ_S3CUART_TX3 (IRQ_S3CUART_BASE3 + UART_IRQ_TXD) -#define IRQ_S3CUART_ERR3 (IRQ_S3CUART_BASE3 + UART_IRQ_ERR) - /* VIC based IRQs */ #define S3C64XX_IRQ_VIC0(x) (IRQ_VIC0_BASE + (x)) diff --git a/arch/arm/mach-s3c64xx/irq.c b/arch/arm/mach-s3c64xx/irq.c index 75d9a0e49193..b07357e94958 100644 --- a/arch/arm/mach-s3c64xx/irq.c +++ b/arch/arm/mach-s3c64xx/irq.c @@ -25,29 +25,6 @@ #include #include -static struct s3c_uart_irq uart_irqs[] = { - [0] = { - .regs = S3C_VA_UART0, - .base_irq = IRQ_S3CUART_BASE0, - .parent_irq = IRQ_UART0, - }, - [1] = { - .regs = S3C_VA_UART1, - .base_irq = IRQ_S3CUART_BASE1, - .parent_irq = IRQ_UART1, - }, - [2] = { - .regs = S3C_VA_UART2, - .base_irq = IRQ_S3CUART_BASE2, - .parent_irq = IRQ_UART2, - }, - [3] = { - .regs = S3C_VA_UART3, - .base_irq = IRQ_S3CUART_BASE3, - .parent_irq = IRQ_UART3, - }, -}; - /* setup the sources the vic should advertise resume for, even though it * is not doing the wake (set_irq_wake needs to be valid) */ #define IRQ_VIC0_RESUME (1 << (IRQ_RTC_TIC - IRQ_VIC0_BASE)) @@ -67,6 +44,4 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) /* add the timer sub-irqs */ s3c_init_vic_timer_irq(5, IRQ_TIMER0); - - s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 9843c954c042..9a197e55f669 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -22,7 +22,6 @@ config PLAT_S5P select PLAT_SAMSUNG select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER - select SAMSUNG_IRQ_UART help Base platform code for Samsung's S5P series SoC. diff --git a/arch/arm/plat-s5p/dev-uart.c b/arch/arm/plat-s5p/dev-uart.c index afaf87fdb93e..c9308db36183 100644 --- a/arch/arm/plat-s5p/dev-uart.c +++ b/arch/arm/plat-s5p/dev-uart.c @@ -32,20 +32,10 @@ static struct resource s5p_uart0_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX0, - .end = IRQ_S5P_UART_RX0, + .start = IRQ_UART0, + .end = IRQ_UART0, .flags = IORESOURCE_IRQ, }, - [2] = { - .start = IRQ_S5P_UART_TX0, - .end = IRQ_S5P_UART_TX0, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR0, - .end = IRQ_S5P_UART_ERR0, - .flags = IORESOURCE_IRQ, - } }; static struct resource s5p_uart1_resource[] = { @@ -55,18 +45,8 @@ static struct resource s5p_uart1_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX1, - .end = IRQ_S5P_UART_RX1, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S5P_UART_TX1, - .end = IRQ_S5P_UART_TX1, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR1, - .end = IRQ_S5P_UART_ERR1, + .start = IRQ_UART1, + .end = IRQ_UART1, .flags = IORESOURCE_IRQ, }, }; @@ -78,18 +58,8 @@ static struct resource s5p_uart2_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX2, - .end = IRQ_S5P_UART_RX2, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S5P_UART_TX2, - .end = IRQ_S5P_UART_TX2, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR2, - .end = IRQ_S5P_UART_ERR2, + .start = IRQ_UART2, + .end = IRQ_UART2, .flags = IORESOURCE_IRQ, }, }; @@ -102,18 +72,8 @@ static struct resource s5p_uart3_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX3, - .end = IRQ_S5P_UART_RX3, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S5P_UART_TX3, - .end = IRQ_S5P_UART_TX3, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR3, - .end = IRQ_S5P_UART_ERR3, + .start = IRQ_UART3, + .end = IRQ_UART3, .flags = IORESOURCE_IRQ, }, #endif @@ -127,18 +87,8 @@ static struct resource s5p_uart4_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX4, - .end = IRQ_S5P_UART_RX4, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S5P_UART_TX4, - .end = IRQ_S5P_UART_TX4, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR4, - .end = IRQ_S5P_UART_ERR4, + .start = IRQ_UART4, + .end = IRQ_UART4, .flags = IORESOURCE_IRQ, }, #endif @@ -152,18 +102,8 @@ static struct resource s5p_uart5_resource[] = { .flags = IORESOURCE_MEM, }, [1] = { - .start = IRQ_S5P_UART_RX5, - .end = IRQ_S5P_UART_RX5, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = IRQ_S5P_UART_TX5, - .end = IRQ_S5P_UART_TX5, - .flags = IORESOURCE_IRQ, - }, - [3] = { - .start = IRQ_S5P_UART_ERR5, - .end = IRQ_S5P_UART_ERR5, + .start = IRQ_UART5, + .end = IRQ_UART5, .flags = IORESOURCE_IRQ, }, #endif diff --git a/arch/arm/plat-s5p/include/plat/irqs.h b/arch/arm/plat-s5p/include/plat/irqs.h index ba9121c60a2a..144dbfc6506d 100644 --- a/arch/arm/plat-s5p/include/plat/irqs.h +++ b/arch/arm/plat-s5p/include/plat/irqs.h @@ -37,41 +37,6 @@ #define IRQ_VIC1_BASE S5P_VIC1_BASE #define IRQ_VIC2_BASE S5P_VIC2_BASE -/* UART interrupts, each UART has 4 intterupts per channel so - * use the space between the ISA and S3C main interrupts. Note, these - * are not in the same order as the S3C24XX series! */ - -#define IRQ_S5P_UART_BASE0 (16) -#define IRQ_S5P_UART_BASE1 (20) -#define IRQ_S5P_UART_BASE2 (24) -#define IRQ_S5P_UART_BASE3 (28) - -#define UART_IRQ_RXD (0) -#define UART_IRQ_ERR (1) -#define UART_IRQ_TXD (2) - -#define IRQ_S5P_UART_RX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_RXD) -#define IRQ_S5P_UART_TX0 (IRQ_S5P_UART_BASE0 + UART_IRQ_TXD) -#define IRQ_S5P_UART_ERR0 (IRQ_S5P_UART_BASE0 + UART_IRQ_ERR) - -#define IRQ_S5P_UART_RX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_RXD) -#define IRQ_S5P_UART_TX1 (IRQ_S5P_UART_BASE1 + UART_IRQ_TXD) -#define IRQ_S5P_UART_ERR1 (IRQ_S5P_UART_BASE1 + UART_IRQ_ERR) - -#define IRQ_S5P_UART_RX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_RXD) -#define IRQ_S5P_UART_TX2 (IRQ_S5P_UART_BASE2 + UART_IRQ_TXD) -#define IRQ_S5P_UART_ERR2 (IRQ_S5P_UART_BASE2 + UART_IRQ_ERR) - -#define IRQ_S5P_UART_RX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_RXD) -#define IRQ_S5P_UART_TX3 (IRQ_S5P_UART_BASE3 + UART_IRQ_TXD) -#define IRQ_S5P_UART_ERR3 (IRQ_S5P_UART_BASE3 + UART_IRQ_ERR) - -/* S3C compatibilty defines */ -#define IRQ_S3CUART_RX0 IRQ_S5P_UART_RX0 -#define IRQ_S3CUART_RX1 IRQ_S5P_UART_RX1 -#define IRQ_S3CUART_RX2 IRQ_S5P_UART_RX2 -#define IRQ_S3CUART_RX3 IRQ_S5P_UART_RX3 - /* VIC based IRQs */ #define S5P_IRQ_VIC0(x) (S5P_VIC0_BASE + (x)) diff --git a/arch/arm/plat-s5p/irq.c b/arch/arm/plat-s5p/irq.c index a97c08957f49..afdaa1082b9f 100644 --- a/arch/arm/plat-s5p/irq.c +++ b/arch/arm/plat-s5p/irq.c @@ -17,42 +17,10 @@ #include -#include #include #include -#include #include #include -#include - -/* - * Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] - * are consecutive when looking up the interrupt in the demux routines. - */ -static struct s3c_uart_irq uart_irqs[] = { - [0] = { - .regs = S5P_VA_UART0, - .base_irq = IRQ_S5P_UART_BASE0, - .parent_irq = IRQ_UART0, - }, - [1] = { - .regs = S5P_VA_UART1, - .base_irq = IRQ_S5P_UART_BASE1, - .parent_irq = IRQ_UART1, - }, - [2] = { - .regs = S5P_VA_UART2, - .base_irq = IRQ_S5P_UART_BASE2, - .parent_irq = IRQ_UART2, - }, -#if CONFIG_SERIAL_SAMSUNG_UARTS > 3 - [3] = { - .regs = S5P_VA_UART3, - .base_irq = IRQ_S5P_UART_BASE3, - .parent_irq = IRQ_UART3, - }, -#endif -}; void __init s5p_init_irq(u32 *vic, u32 num_vic) { @@ -65,6 +33,4 @@ void __init s5p_init_irq(u32 *vic, u32 num_vic) #endif s3c_init_vic_timer_irq(5, IRQ_TIMER0); - - s3c_init_uart_irqs(uart_irqs, ARRAY_SIZE(uart_irqs)); } diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index b3e10659e4b8..dffa37bc4a0b 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -65,11 +65,6 @@ config SAMSUNG_IRQ_VIC_TIMER help Internal configuration to build the VIC timer interrupt code. -config SAMSUNG_IRQ_UART - bool - help - Internal configuration to build the IRQ UART demux code. - # options for gpio configuration support config SAMSUNG_GPIOLIB_4BIT diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 853764ba8cc5..1105922342fe 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -21,7 +21,6 @@ obj-y += dev-asocdma.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o -obj-$(CONFIG_SAMSUNG_IRQ_UART) += irq-uart.o obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o # ADC diff --git a/arch/arm/plat-samsung/include/plat/regs-serial.h b/arch/arm/plat-samsung/include/plat/regs-serial.h index bac36fa3becb..720734847027 100644 --- a/arch/arm/plat-samsung/include/plat/regs-serial.h +++ b/arch/arm/plat-samsung/include/plat/regs-serial.h @@ -186,6 +186,11 @@ #define S3C64XX_UINTSP 0x34 #define S3C64XX_UINTM 0x38 +#define S3C64XX_UINTM_RXD (0) +#define S3C64XX_UINTM_TXD (2) +#define S3C64XX_UINTM_RXD_MSK (1 << S3C64XX_UINTM_RXD) +#define S3C64XX_UINTM_TXD_MSK (1 << S3C64XX_UINTM_TXD) + /* Following are specific to S5PV210 */ #define S5PV210_UCON_CLKMASK (1<<10) #define S5PV210_UCON_PCLK (0<<10) diff --git a/arch/arm/plat-samsung/irq-uart.c b/arch/arm/plat-samsung/irq-uart.c deleted file mode 100644 index 3014c7226bd1..000000000000 --- a/arch/arm/plat-samsung/irq-uart.c +++ /dev/null @@ -1,96 +0,0 @@ -/* arch/arm/plat-samsung/irq-uart.c - * originally part of arch/arm/plat-s3c64xx/irq.c - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * Samsung- UART Interrupt handling - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3] - * are consecutive when looking up the interrupt in the demux routines. - */ -static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc) -{ - struct s3c_uart_irq *uirq = desc->irq_data.handler_data; - struct irq_chip *chip = irq_get_chip(irq); - u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP); - int base = uirq->base_irq; - - chained_irq_enter(chip, desc); - - if (pend & (1 << 0)) - generic_handle_irq(base); - if (pend & (1 << 1)) - generic_handle_irq(base + 1); - if (pend & (1 << 2)) - generic_handle_irq(base + 2); - if (pend & (1 << 3)) - generic_handle_irq(base + 3); - - chained_irq_exit(chip, desc); -} - -static void __init s3c_init_uart_irq(struct s3c_uart_irq *uirq) -{ - void __iomem *reg_base = uirq->regs; - struct irq_chip_generic *gc; - struct irq_chip_type *ct; - - /* mask all interrupts at the start. */ - __raw_writel(0xf, reg_base + S3C64XX_UINTM); - - gc = irq_alloc_generic_chip("s3c-uart", 1, uirq->base_irq, reg_base, - handle_level_irq); - - if (!gc) { - pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n", - __func__, uirq->base_irq); - return; - } - - ct = gc->chip_types; - ct->chip.irq_ack = irq_gc_ack_set_bit; - ct->chip.irq_mask = irq_gc_mask_set_bit; - ct->chip.irq_unmask = irq_gc_mask_clr_bit; - ct->regs.ack = S3C64XX_UINTP; - ct->regs.mask = S3C64XX_UINTM; - irq_setup_generic_chip(gc, IRQ_MSK(4), IRQ_GC_INIT_MASK_CACHE, - IRQ_NOREQUEST | IRQ_NOPROBE, 0); - - irq_set_handler_data(uirq->parent_irq, uirq); - irq_set_chained_handler(uirq->parent_irq, s3c_irq_demux_uart); -} - -/** - * s3c_init_uart_irqs() - initialise UART IRQs and the necessary demuxing - * @irq: The interrupt data for registering - * @nr_irqs: The number of interrupt descriptions in @irq. - * - * Register the UART interrupts specified by @irq including the demuxing - * routines. This supports the S3C6400 and newer style of devices. - */ -void __init s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs) -{ - for (; nr_irqs > 0; nr_irqs--, irq++) - s3c_init_uart_irq(irq); -} -- cgit v1.2.3 From a74036f51272975e9538e80cd1bb64dce164b208 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 15 Aug 2011 10:17:51 +0100 Subject: tty: serial: allow ports to override the irq handler Some serial ports may have unusal requirements for interrupt handling (e.g. the Synopsys DesignWare 8250-alike port and it's busy detect interrupt). Add a .handle_irq callback that can be used for platforms to override the interrupt behaviour in a similar fashion to the .serial_out and .serial_in callbacks. Signed-off-by: Jamie Iles Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 76e110363745..c31ae43c073d 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -300,6 +300,7 @@ struct uart_port { void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); + int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int old); unsigned int irq; /* irq number */ -- cgit v1.2.3 From 583d28e92f667eb6cc81ea87daaa7e321c23fe14 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 15 Aug 2011 10:17:52 +0100 Subject: tty: serial8250: allow platforms to override irq handler Some ports (e.g. Synopsys DesignWare 8250) have special requirements for handling the interrupts. Allow these platforms to specify their own interrupt handler that will override the default. serial8250_handle_irq() is provided so that platforms can extend the IRQ handler rather than completely replacing it. Signed-off-by: Jamie Iles Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 39 +++++++++++++++++++++++++++++++++++---- include/linux/serial_8250.h | 2 ++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index f2dfec82faf8..833e011a426f 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -509,6 +509,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 = @@ -557,6 +559,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 @@ -1621,6 +1624,28 @@ 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; +} + +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,13 +1673,12 @@ 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; @@ -3048,6 +3072,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; } @@ -3116,6 +3144,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; @@ -3281,6 +3310,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/include/linux/serial_8250.h b/include/linux/serial_8250.h index 97f5b45bbc07..1f05bbeac01e 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -35,6 +35,7 @@ struct plat_serial8250_port { void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); + int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned old); }; @@ -80,6 +81,7 @@ extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); +int serial8250_handle_irq(struct uart_port *port, unsigned int iir); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, -- cgit v1.2.3 From 91e8db593c0a2d8249ac00422af1ed42537a3b16 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 15 Aug 2011 10:17:53 +0100 Subject: mips: msp71xx/serial: convert to pr_foo() helpers Convert to pr_foo() helpers rather than printk(KERN_.*). Acked-by: Ralf Baechle Acked-by: Anoop P A Acked-by: Alan Cox Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- arch/mips/pmc-sierra/msp71xx/msp_serial.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/pmc-sierra/msp71xx/msp_serial.c b/arch/mips/pmc-sierra/msp71xx/msp_serial.c index f7261628d8a6..c3247b5801f1 100644 --- a/arch/mips/pmc-sierra/msp71xx/msp_serial.c +++ b/arch/mips/pmc-sierra/msp71xx/msp_serial.c @@ -65,7 +65,7 @@ void __init msp_serial_setup(void) up.line = 0; up.private_data = (void*)UART0_STATUS_REG; if (early_serial_setup(&up)) - printk(KERN_ERR "Early serial init of port 0 failed\n"); + pr_err("Early serial init of port 0 failed\n"); /* Initialize the second serial port, if one exists */ switch (mips_machtype) { @@ -89,5 +89,5 @@ void __init msp_serial_setup(void) up.line = 1; up.private_data = (void*)UART1_STATUS_REG; if (early_serial_setup(&up)) - printk(KERN_ERR "Early serial init of port 1 failed\n"); + pr_err("Early serial init of port 1 failed\n"); } -- cgit v1.2.3 From 03972fc21410907fa20d1442c4b073f034423d5c Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 15 Aug 2011 10:17:54 +0100 Subject: mips: msp71xx/serial: add workaround for DW UART The Synopsys DesignWare UART in pmc-sierra msp71xx has an extra feature where the UART detects a write attempt to the LCR whilst busy and raises an interrupt. The driver needs to clear the interrupt and rewrite the LCR. Move this into platform code and out of the 8250 driver. Acked-by: Ralf Baechle Acked-by: Alan Cox Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- arch/mips/pmc-sierra/msp71xx/msp_serial.c | 69 +++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/arch/mips/pmc-sierra/msp71xx/msp_serial.c b/arch/mips/pmc-sierra/msp71xx/msp_serial.c index c3247b5801f1..a1c7c7da2336 100644 --- a/arch/mips/pmc-sierra/msp71xx/msp_serial.c +++ b/arch/mips/pmc-sierra/msp71xx/msp_serial.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,55 @@ #include #include +struct msp_uart_data { + int last_lcr; +}; + +static void msp_serial_out(struct uart_port *p, int offset, int value) +{ + struct msp_uart_data *d = p->private_data; + + if (offset == UART_LCR) + d->last_lcr = value; + + offset <<= p->regshift; + writeb(value, p->membase + offset); +} + +static unsigned int msp_serial_in(struct uart_port *p, int offset) +{ + offset <<= p->regshift; + + return readb(p->membase + offset); +} + +static int msp_serial_handle_irq(struct uart_port *p) +{ + struct msp_uart_data *d = p->private_data; + unsigned int iir = readb(p->membase + (UART_IIR << p->regshift)); + + if (serial8250_handle_irq(p, iir)) { + return 1; + } else if ((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. + * + * Note: MSP reserves 0x20 bytes of address space for the UART + * and the USR is mapped in a separate block at an offset of + * 0xc0 from the start of the UART. + */ + (void)readb(p->membase + 0xc0); + writeb(d->last_lcr, p->membase + (UART_LCR << p->regshift)); + + return 1; + } + + return 0; +} + void __init msp_serial_setup(void) { char *s; @@ -59,13 +109,22 @@ void __init msp_serial_setup(void) up.irq = MSP_INT_UART0; up.uartclk = uartclk; up.regshift = 2; - up.iotype = UPIO_DWAPB; /* UPIO_MEM like */ + up.iotype = UPIO_MEM; up.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; up.type = PORT_16550A; up.line = 0; - up.private_data = (void*)UART0_STATUS_REG; - if (early_serial_setup(&up)) + up.serial_out = msp_serial_out; + up.serial_in = msp_serial_in; + up.handle_irq = msp_serial_handle_irq; + up.private_data = kzalloc(sizeof(struct msp_uart_data), GFP_KERNEL); + if (!up.private_data) { + pr_err("failed to allocate uart private data\n"); + return; + } + if (early_serial_setup(&up)) { + kfree(up.private_data); pr_err("Early serial init of port 0 failed\n"); + } /* Initialize the second serial port, if one exists */ switch (mips_machtype) { @@ -88,6 +147,8 @@ void __init msp_serial_setup(void) up.irq = MSP_INT_UART1; up.line = 1; up.private_data = (void*)UART1_STATUS_REG; - if (early_serial_setup(&up)) + if (early_serial_setup(&up)) { + kfree(up.private_data); pr_err("Early serial init of port 1 failed\n"); + } } -- cgit v1.2.3 From 4834d028978583dfe8e1fc19f1180ceb03d8dfb7 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Mon, 15 Aug 2011 10:17:55 +0100 Subject: tty: serial8250: remove UPIO_DWAPB{,32} Now that platforms can override the port IRQ handler and the only user of these UPIO modes has been converted over, kill off UPIO_DWAPB and UPIO_DWAPB32. Acked-by: Alan Cox Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 68 ---------------------------------------- drivers/tty/serial/serial_core.c | 4 --- include/linux/serial_core.h | 4 +-- 3 files changed, 1 insertion(+), 75 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 833e011a426f..6f594d2d334f 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -461,42 +461,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; @@ -542,16 +506,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; @@ -570,8 +524,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; @@ -1679,23 +1631,7 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) port = &up->port; 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; @@ -2592,8 +2528,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; @@ -2630,8 +2564,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; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 44c29631b724..3844980b397f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2032,8 +2032,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; @@ -2446,8 +2444,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/include/linux/serial_core.h b/include/linux/serial_core.h index c31ae43c073d..493773e3b46d 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -318,9 +318,7 @@ struct uart_port { #define UPIO_MEM32 (3) #define UPIO_AU (4) /* Au1x00 type IO */ #define UPIO_TSI (5) /* Tsi108/109 type IO */ -#define UPIO_DWAPB (6) /* DesignWare APB UART */ -#define UPIO_RM9000 (7) /* RM9000 type IO */ -#define UPIO_DWAPB32 (8) /* DesignWare APB UART (32 bit accesses) */ +#define UPIO_RM9000 (6) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ -- cgit v1.2.3 From 6b1a98d1c4851235d9b6764b3f7b7db7909fc760 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Tue, 16 Aug 2011 17:47:46 +0100 Subject: tty: serial8250: add helpers for the DesignWare 8250 The Synopsys DesignWare 8250 is an 8250 that has an extra interrupt that gets raised when writing to the LCR when busy. To handle this we need special serial_out, serial_in and handle_irq methods. Add a new function serial8250_use_designware_io() that configures a uart_port with these accessors. Cc: Alan Cox Acked-by: Arnd Bergmann Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_dw.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ drivers/tty/serial/Kconfig | 7 ++++ drivers/tty/serial/Makefile | 1 + include/linux/serial_8250.h | 8 ++++ 4 files changed, 115 insertions(+) create mode 100644 drivers/tty/serial/8250_dw.c diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c new file mode 100644 index 000000000000..e25782aeec7a --- /dev/null +++ b/drivers/tty/serial/8250_dw.c @@ -0,0 +1,99 @@ +/* + * Synopsys DesignWare specific 8250 operations. + * + * 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 +#include +#include +#include +#include + +struct dw8250_data { + int last_lcr; +}; + +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; +} + +int serial8250_use_designware_io(struct uart_port *up) +{ + up->private_data = kzalloc(sizeof(struct dw8250_data), GFP_KERNEL); + if (!up->private_data) + return -ENOMEM; + + if (up->iotype == UPIO_MEM32) { + up->serial_out = dw8250_serial_out32; + up->serial_in = dw8250_serial_in32; + } else { + up->serial_out = dw8250_serial_out; + up->serial_in = dw8250_serial_in; + } + up->handle_irq = dw8250_handle_irq; + + return 0; +} diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a9b307f582e1..d2d1cc2329b7 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 + bool "Support for Synopsys DesignWare 8250 quirks" + depends on SERIAL_8250 + 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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 78748136ccc8..7b59958f50ec 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 diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 1f05bbeac01e..09e2dbcd7ca3 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -86,5 +86,13 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, unsigned short *capabilities)); +#ifndef SERIAL_8250_DW +extern int serial8250_use_designware_io(struct uart_port *up); +#else +static inline int serial8250_use_designware_io(struct uart_port *up) +{ + return -EIO; +} +#endif #endif -- cgit v1.2.3 From 14a8d47d4e9f51372996914c16bdbf1c34e209b5 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Tue, 16 Aug 2011 17:47:47 +0100 Subject: tty: of_serial: add support for the DesignWare 8250 Support the DesignWare 8250 by a new compatible string and registering the DesignWare helpers. If the registration of the helpers fails, then continue as a normal 8250 as we may still get some useful debug out. Cc: Alan Cox Cc: Arnd Bergmann Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 1 + drivers/tty/serial/of_serial.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index b8b27b0aca10..b7ceaaa7602d 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -3,6 +3,7 @@ Required properties: - compatible : one of: - "ns8250" + - "ns8250dw" - "ns16450" - "ns16550a" - "ns16550" diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index e58cece6f443..024926c3be57 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -78,6 +78,12 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, } } + if (of_device_is_compatible(np, "ns8250dw")) { + ret = serial8250_use_designware_io(port); + if (ret) + dev_warn(&ofdev->dev, "unable to register DesignWare 8250 helpers, continuing as a normal 8250\n"); + } + port->type = type; port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP @@ -176,6 +182,7 @@ static int of_platform_serial_remove(struct platform_device *ofdev) */ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .compatible = "ns8250", .data = (void *)PORT_8250, }, + { .compatible = "ns8250dw", .data = (void *)PORT_8250, }, { .compatible = "ns16450", .data = (void *)PORT_16450, }, { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, { .compatible = "ns16550", .data = (void *)PORT_16550, }, -- cgit v1.2.3 From 0b37004dd0113db3b23840b4fd8835dae2395acc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Aug 2011 15:24:54 -0700 Subject: Revert "tty: of_serial: add support for the DesignWare 8250" This reverts commit 14a8d47d4e9f51372996914c16bdbf1c34e209b5. It causes a build error that needs to be resolved differently. Cc: Alan Cox Cc: Arnd Bergmann Cc: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/tty/serial/of-serial.txt | 1 - drivers/tty/serial/of_serial.c | 7 ------- 2 files changed, 8 deletions(-) diff --git a/Documentation/devicetree/bindings/tty/serial/of-serial.txt b/Documentation/devicetree/bindings/tty/serial/of-serial.txt index b7ceaaa7602d..b8b27b0aca10 100644 --- a/Documentation/devicetree/bindings/tty/serial/of-serial.txt +++ b/Documentation/devicetree/bindings/tty/serial/of-serial.txt @@ -3,7 +3,6 @@ Required properties: - compatible : one of: - "ns8250" - - "ns8250dw" - "ns16450" - "ns16550a" - "ns16550" diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 024926c3be57..e58cece6f443 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -78,12 +78,6 @@ static int __devinit of_platform_serial_setup(struct platform_device *ofdev, } } - if (of_device_is_compatible(np, "ns8250dw")) { - ret = serial8250_use_designware_io(port); - if (ret) - dev_warn(&ofdev->dev, "unable to register DesignWare 8250 helpers, continuing as a normal 8250\n"); - } - port->type = type; port->uartclk = clk; port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP @@ -182,7 +176,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev) */ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .compatible = "ns8250", .data = (void *)PORT_8250, }, - { .compatible = "ns8250dw", .data = (void *)PORT_8250, }, { .compatible = "ns16450", .data = (void *)PORT_16450, }, { .compatible = "ns16550a", .data = (void *)PORT_16550A, }, { .compatible = "ns16550", .data = (void *)PORT_16550, }, -- cgit v1.2.3 From 9a234349de01de3437784c0ef03d95353f055fae Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Aug 2011 15:25:49 -0700 Subject: Revert "tty: serial8250: add helpers for the DesignWare 8250" This reverts commit 6b1a98d1c4851235d9b6764b3f7b7db7909fc760. It causes a build error that needs to be resolved differently. Cc: Alan Cox Cc: Arnd Bergmann Cc: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_dw.c | 99 -------------------------------------------- drivers/tty/serial/Kconfig | 7 ---- drivers/tty/serial/Makefile | 1 - include/linux/serial_8250.h | 8 ---- 4 files changed, 115 deletions(-) delete mode 100644 drivers/tty/serial/8250_dw.c diff --git a/drivers/tty/serial/8250_dw.c b/drivers/tty/serial/8250_dw.c deleted file mode 100644 index e25782aeec7a..000000000000 --- a/drivers/tty/serial/8250_dw.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Synopsys DesignWare specific 8250 operations. - * - * 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 -#include -#include -#include -#include - -struct dw8250_data { - int last_lcr; -}; - -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; -} - -int serial8250_use_designware_io(struct uart_port *up) -{ - up->private_data = kzalloc(sizeof(struct dw8250_data), GFP_KERNEL); - if (!up->private_data) - return -ENOMEM; - - if (up->iotype == UPIO_MEM32) { - up->serial_out = dw8250_serial_out32; - up->serial_in = dw8250_serial_in32; - } else { - up->serial_out = dw8250_serial_out; - up->serial_in = dw8250_serial_in; - } - up->handle_irq = dw8250_handle_irq; - - return 0; -} diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index d2d1cc2329b7..a9b307f582e1 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -267,13 +267,6 @@ config SERIAL_8250_RM9K port hardware found on MIPS RM9122 and similar processors. If unsure, say N. -config SERIAL_8250_DW - bool "Support for Synopsys DesignWare 8250 quirks" - depends on SERIAL_8250 - 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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7b59958f50ec..78748136ccc8 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -28,7 +28,6 @@ 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 diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 09e2dbcd7ca3..1f05bbeac01e 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -86,13 +86,5 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, unsigned short *capabilities)); -#ifndef SERIAL_8250_DW -extern int serial8250_use_designware_io(struct uart_port *up); -#else -static inline int serial8250_use_designware_io(struct uart_port *up) -{ - return -EIO; -} -#endif #endif -- cgit v1.2.3 From 019dc9ea8d528eb3640bbba604e1e5a2f6994b1f Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 24 Aug 2011 17:41:47 +0800 Subject: serial/imx: support to handle break character The imx UART hardware controller can identify BREAK character and the imx_set_termios() can accept BRKINT set by users, but current existing imx_rxint() can't pass BREAK character and TTY_BREAK to the tty layer as other serial drivers do (8250.c omap_serial.c). Here add code to handle BREAK character and pass it to tty layer. To detect error occurrence, i use URXD_ERR to replace (URXD_OVRRUN | URXD_FRMERR | ...) because any kind of error occurs, URXD_ERR will always be set to 1. I put the URXD_BRK to the first place to check since when BREAK error occurs, not only URXD_BRK is set to 1, but also URXD_PRERR and URXD_FRMERR are all set to 1. This arrangement can filter out fake parity and frame errors when BREAK error occurs. Signed-off-by: Hui Wang Acked-by: Sascha Hauer Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 7e91b3d368cd..54ffdc6243f9 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; -- cgit v1.2.3 From 83cac9f3b45ba8e99c5305be42c67f1c83adf0aa Mon Sep 17 00:00:00 2001 From: Bernhard Roth Date: Wed, 24 Aug 2011 09:48:23 +0200 Subject: atmel_serial: RS485: receiving enabled when sending data By default the atmel_serial driver in RS485 mode disables receiving data until all data in the send buffer has been sent. This flag allows to receive data even whilst sending data. Signed-off-by: Bernhard Roth Signed-off-by: Claudio Scordino Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/serial-rs485.txt | 3 +++ drivers/tty/serial/atmel_serial.c | 9 ++++++--- include/linux/serial.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt index a4932387bbfb..c8878f821d1e 100644 --- a/Documentation/serial/serial-rs485.txt +++ b/Documentation/serial/serial-rs485.txt @@ -104,6 +104,9 @@ rs485conf.flags |= SER_RS485_RTS_AFTER_SEND; rs485conf.delay_rts_after_send = ...; + /* Set this flag if you want to receive data even whilst sending data */ + rs485conf.flags |= SER_RS485_RX_DURING_TX; + if (ioctl (fd, TIOCSRS485, &rs485conf) < 0) { /* Error handling. See errno. */ } diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index af9b7814965a..c7232a916c1b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -339,7 +339,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 +357,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 +682,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); } diff --git a/include/linux/serial.h b/include/linux/serial.h index ef914061511e..97ff8e27a6cc 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -211,6 +211,7 @@ struct serial_rs485 { #define SER_RS485_RTS_ON_SEND (1 << 1) #define SER_RS485_RTS_AFTER_SEND (1 << 2) #define SER_RS485_RTS_BEFORE_SEND (1 << 3) +#define SER_RS485_RX_DURING_TX (1 << 4) __u32 delay_rts_before_send; /* Milliseconds */ __u32 delay_rts_after_send; /* Milliseconds */ __u32 padding[5]; /* Memory is cheap, new structs -- cgit v1.2.3 From a16913a9b3b9fbb6c501eae005368064ddcc6351 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Wed, 24 Aug 2011 22:25:52 +0900 Subject: tty: Add support serial for EXYNOS4212 SoC According to add support EXYNOS4212 SoC, we need to enable SERIAL_S5PV210 on EXYNOS4212. Cc: Alan Cox Cc: Greg Kroah-Hartman Signed-off-by: Kukjin Kim Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a9b307f582e1..10babe254e57 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -522,8 +522,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 -- cgit v1.2.3 From d64bbeb57f9901b6919fabd1e5a555e94748c255 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 24 Aug 2011 13:14:21 -0300 Subject: jsm: remove remaining flip buffer code The flip buffer is not used anymore. Remove its allocation and declaration in the board structure. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm.h | 3 --- drivers/tty/serial/jsm/jsm_driver.c | 18 ------------------ 2 files changed, 21 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index b704c8ce0d71..cd53bdda77ce 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,7 +175,6 @@ 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 */ diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c index 96da17868cf3..1cc8cf602af8 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); @@ -218,7 +201,6 @@ static void __devexit jsm_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - kfree(brd->flipbuf); kfree(brd); } -- cgit v1.2.3 From 9d898966c4a07e4a5092215b5a2829d0ef02baa2 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 24 Aug 2011 13:14:22 -0300 Subject: jsm: remove buggy write queue jsm uses a write queue that copies from uart_core circular buffer. This copying however has some bugs, like not wrapping the head counter. Since this write queue is also a circular buffer, the consumer function is ready to use the uart_core circular buffer directly. This buggy copying function was making some bytes be dropped when transmitting to a raw tty, doing something like this. [root@hostname ~]$ cat /dev/ttyn1 > cascardo/dump & [1] 2658 [root@hostname ~]$ cat /proc/tty/drivers > /dev/ttyn0 [root@hostname ~]$ cat /proc/tty/drivers /dev/tty /dev/tty 5 0 system:/dev/tty /dev/console /dev/console 5 1 system:console /dev/ptmx /dev/ptmx 5 2 system /dev/vc/0 /dev/vc/0 4 0 system:vtmaster jsm /dev/ttyn 250 0-31 serial serial /dev/ttyS 4 64-95 serial hvc /dev/hvc 229 0-7 system pty_slave /dev/pts 136 0-1048575 pty:slave pty_master /dev/ptm 128 0-1048575 pty:master unknown /dev/tty 4 1-63 console [root@hostname ~]$ cat cascardo/dump /dev/tty /dev/tty 5 0 system:/dev/tty /dev/console /dev/console 5 1 system:console /dev/ptmx /dev/ptmx 5 2 system /dev/vc/0 /dev/vc/0 4 0 system:vtmaste[root@hostname ~]$ This patch drops the driver write queue entirely, using the circular buffer from uart_core only. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm.h | 7 --- drivers/tty/serial/jsm/jsm_driver.c | 1 - drivers/tty/serial/jsm/jsm_neo.c | 29 ++++++------ drivers/tty/serial/jsm/jsm_tty.c | 94 +++++-------------------------------- 4 files changed, 28 insertions(+), 103 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index cd53bdda77ce..529bec6edaf8 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -180,10 +180,8 @@ struct jsm_board /* 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) /************************************************************************ @@ -223,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 */ @@ -375,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 1cc8cf602af8..648b6a3efa32 100644 --- a/drivers/tty/serial/jsm/jsm_driver.c +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -194,7 +194,6 @@ 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]); } } diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index 4538c3e3646e..bd6e84699e11 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->head]); + 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; -} -- cgit v1.2.3 From dfc97fcebd6a9a85335adcfc0e4d0a204bbaea35 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Wed, 24 Aug 2011 13:14:23 -0300 Subject: jsm: print byte we are dequeing Instead of printing the head of the buffer, we should print the tail, which is the byte we are sending to the device. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm_neo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index bd6e84699e11..81dfafa11b0b 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -522,7 +522,7 @@ static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, - "Tx data: %x\n", circ->buf[circ->head]); + "Tx data: %x\n", circ->buf[circ->tail]); circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); ch->ch_txcount++; } -- cgit v1.2.3 From 426929f8d3514d7f727b8c464d1eeeaf74b21519 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Aug 2011 15:12:04 +0200 Subject: TTY: serial, use ASYNCB_CLOSING in uart_close We need to move port->mutex locking after wait_until_sent in uart_close (for rationale see next patches). But if we did it now, we would introduce a race between close and open. This is exactly why port->mutex is locked at the top of uart_close. To avoid the race, we add ASYNCB_CLOSING to uart_close. Like every other sane TTY driver. Thanks to tty_port_block_til_ready used in uart_open we will have this for free. Then we can move the port->mutex lock. Also note that this will make the conversion to tty_port helpers easier. They are currently handling ASYNC_CLOSING flag correctly. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 3844980b397f..849bd0d06b2c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1288,6 +1288,7 @@ 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); @@ -1335,8 +1336,10 @@ 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); -- cgit v1.2.3 From bafb0bd24d7e0e0124318625b239a55d58c757a2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Aug 2011 15:12:05 +0200 Subject: TTY: serial, move locking in uart_close So now, when we handle CLOSING flag, there is no point to hold port->mutex over the start of uart_close. Yes, there are still several things to reason about: * port->count etc is and always was protected by a spinlock * ->stop_rx is protected by a spinlock. Otherwise it would race with interrupts. * uart_wait_until_sent -- that one is already called without port->mutex from set_termios and tty_set_ldisc. Should anything be protected there, it would be tx_empty. And by a spinlock. 8250 does this internally... This step is needed to fix system stalls. To not create an AB-BA lock dependency (see next patches). Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 849bd0d06b2c..0464360781fe 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1253,7 +1253,6 @@ 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)) { @@ -1312,6 +1311,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_wait_until_sent(tty, uport->timeout); } + mutex_lock(&port->mutex); uart_shutdown(tty, state); uart_flush_buffer(tty); -- cgit v1.2.3 From a57a7bf3fc7eff00f07eb9c805774d911a3f2472 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Aug 2011 15:12:06 +0200 Subject: TTY: define tty_wait_until_sent_from_close We need this helper to fix system stalls. The issue is that the rest of the system TTYs wait for us to finish waiting. This wasn't an issue with BKL. BKL used to unlock implicitly. This is based on the Arnd suggestion. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- include/linux/tty.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/linux/tty.h b/include/linux/tty.h index 6d5eceb165be..0ad68889fc1a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -601,6 +601,24 @@ extern long vt_compat_ioctl(struct tty_struct *tty, extern void __lockfunc tty_lock(void) __acquires(tty_lock); extern void __lockfunc tty_unlock(void) __releases(tty_lock); +/* + * this shall be called only from where BTM is held (like close) + * + * We need this to ensure nobody waits for us to finish while we are waiting. + * Without this we were encountering system stalls. + * + * This should be indeed removed with BTM removal later. + * + * Locking: BTM required. Nobody is allowed to hold port->mutex. + */ +static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, + long timeout) +{ + tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */ + tty_wait_until_sent(tty, timeout); + tty_lock(); +} + /* * wait_event_interruptible_tty -- wait for a condition with the tty lock held * -- cgit v1.2.3 From 424cc0391222695225632a3f2ccb0aed3e57b2e5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Aug 2011 15:12:07 +0200 Subject: TTY: use tty_wait_until_sent_from_close in tty_port_close_start Let's use the newly added helper to avoid stalls in drivers which are already ported to tty_port helpers. We have to ensure here, that there is no user of tty_port_close_start and tty_port_close which holds port->mutex (or other) lock over them. And sure, there is none. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; -- cgit v1.2.3 From 0b058353abfcdba4403af60f06998da590ebeffe Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 25 Aug 2011 15:12:08 +0200 Subject: TTY: use tty_wait_until_sent_from_close in other drivers Let's use the newly added helper to avoid stalls in drivers which are not yet ported to tty_port helpers. Those which are broken (call tty_wait_until_sent with irqs disabled) are left untouched. They are in a deeper trouble than we are trying to solve here. This includes amiserial, 68328serial, 68360serial and crisv10. Signed-off-by: Jiri Slaby Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 2 +- drivers/tty/hvc/hvc_console.c | 2 +- drivers/tty/hvc/hvcs.c | 2 +- drivers/tty/serial/serial_core.c | 3 ++- net/irda/ircomm/ircomm_tty.c | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index d8504279e502..e5546cb3ac69 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1693,7 +1693,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * line status register. */ if (info->flags & ISDN_ASYNC_INITIALIZED) { - tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e1aaf4f309b3..b6b2d18fa38d 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", diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4c8b66546930..e523773a5480 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -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/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0464360781fe..1d3780cc3b70 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1292,7 +1292,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&port->lock, flags); if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, msecs_to_jiffies(port->closing_wait)); + tty_wait_until_sent_from_close(tty, + msecs_to_jiffies(port->closing_wait)); /* * At this point, we stop accepting input. To do this, we diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index b3cc8b3989a9..253695d43fd9 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -551,7 +551,7 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) */ tty->closing = 1; if (self->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, self->closing_wait); + tty_wait_until_sent_from_close(tty, self->closing_wait); ircomm_tty_shutdown(self); -- cgit v1.2.3 From 88e5173ff12e6832899ac74ed0f3395107af2811 Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Fri, 26 Aug 2011 11:35:16 +0100 Subject: hsu: add runtime pm support Doesn't appear to be much to do here, however having the suspend/resume functions will allow the d3/d0 transitions to be sent by the pci core. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mfd.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index cab52f4a88b0..cfa750163672 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -38,6 +38,7 @@ #include #include #include +#include #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; @@ -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) -- cgit v1.2.3 From a4c9fe8daf88b27f81be8022d49c073d37fe645e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 Aug 2011 11:27:35 +0100 Subject: n_gsm: update TODO list This is now out of date so fix it Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 8a50e4eebf18..ed429082304d 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 -- cgit v1.2.3 From f17141fdd407de78379222dd59d6f161437db4c8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 Aug 2011 11:28:11 +0100 Subject: n_gsm: Send CLD command on exit A DISC on DLCI 0 should close down the mux but Michael Lauer reports this is not the case for some modems. Send a CLD as well. Signed-off-by: Alan Cox Tested-by: Michael Lauer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index ed429082304d..8d7766bfef87 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2003,6 +2003,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; @@ -2016,6 +2017,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) { -- cgit v1.2.3 From 7b18bd52b2c63ea030364370d0d1f5cc6950aea7 Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Fri, 26 Aug 2011 11:24:49 +0100 Subject: max3110: wake up fixes The main thread is waiting on on a wait_queue but wake_up_process() is used to wake the thread. This reads weirdly. Change wake_up_process() to wake_up(). Tested on the Moorestown tablet build Signed-off-by: Dirk Brandewie Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index a764bf99743b..c8f986876229 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -207,7 +207,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 @@ -301,6 +301,7 @@ 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", @@ -349,7 +350,7 @@ 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) @@ -424,7 +425,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 +454,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; } -- cgit v1.2.3 From efe3ed9837fd2f9679659673f1d2078f1597bf18 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 26 Aug 2011 11:25:14 +0100 Subject: x86/mrst: Add platform data for Max3110 devices Those info will be used when spi controller driver setup max3110 as a slave device Signed-off-by: Feng Tang Signed-off-by: Alan Cox Signed-off-by: Dirk Brandewie Signed-off-by: Greg Kroah-Hartman --- arch/x86/platform/mrst/mrst.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 7000e74b3087..b8da1b968998 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -392,6 +394,7 @@ static void __init *max3111_platform_data(void *info) struct spi_board_info *spi_info = info; int intr = get_gpio_by_name("max3111_int"); + spi_info->mode = SPI_MODE_0; if (intr == -1) return NULL; spi_info->irq = intr + MRST_IRQ_OFFSET; -- cgit v1.2.3 From 51808f051ede81865b7af351d6c230a1ac244a22 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 26 Aug 2011 11:25:35 +0100 Subject: max3110: add sysrq support This patch moves several occurences of similar code inside receive_chars(), which now also takes care of checking for break and calling sysrq handling code. Signed-off-by: Alexander Shishkin Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 99 +++++++++++++++++---------------------- drivers/tty/serial/mrst_max3110.h | 1 + 2 files changed, 44 insertions(+), 56 deletions(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index c8f986876229..42aa43934b20 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -27,6 +27,10 @@ * interrupt for a low speed UART device */ +#ifdef CONFIG_MAGIC_SYSRQ +#define SUPPORT_SYSRQ +#endif + #include #include #include @@ -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) @@ -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; @@ -307,13 +295,7 @@ static void send_circ_buf(struct uart_max3110 *max, 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; @@ -353,30 +335,48 @@ static void serial_m3110_start_tx(struct uart_port *port) 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; 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; + } + } - while (len) { - usable = tty_buffer_request_room(tty, len); + if (!w) + return 0; + + 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); + + return r; } /* @@ -391,28 +391,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) 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) -- cgit v1.2.3 From da4e40e271a30ecf8b87f70619cca93c25ed0199 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 26 Aug 2011 11:26:00 +0100 Subject: max3110: Fix up port->tty backreferencing We want to keep refcounts properly on this against hangup. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index 42aa43934b20..1aa2c3cd2146 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -347,7 +347,7 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len) if (!port->state) return 0; - tty = port->state->port.tty; + tty = tty_port_tty_get(&port->state->port); if (!tty) return 0; @@ -364,8 +364,10 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len) } } - if (!w) + if (!w) { + tty_kref_put(tty); return 0; + } for (r = 0; w; r += usable, w -= usable) { usable = tty_buffer_request_room(tty, w); @@ -375,6 +377,7 @@ receive_chars(struct uart_max3110 *max, unsigned short *str, int len) } } tty_flip_buffer_push(tty); + tty_kref_put(tty); return r; } -- cgit v1.2.3 From 191c5cf1ffc8acf61cd2d2407052ced1a1116130 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 25 Aug 2011 13:06:57 -0500 Subject: tty/powerpc: fix build break with ehv_bytechan.c on allyesconfig The ePAPR hypervisor byte channel driver is supposed to work on all ePAPR-compliant embedded PowerPC systems, but it had a reference to the MSR_GS bit, which is available only on Book-E systems. Also fix a couple integer-to-pointer typecast problems. Reported-by: Stephen Rothwell Signed-off-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ehv_bytechan.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index e67f70bbf0ac..f733718bf8e7 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -226,10 +226,6 @@ void __init udbg_init_ehv_bc(void) unsigned int rx_count, tx_count; unsigned int ret; - /* Check if we're running as a guest of a hypervisor */ - if (!(mfmsr() & MSR_GS)) - return; - /* Verify the byte channel handle */ ret = ev_byte_channel_poll(CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE, &rx_count, &tx_count); @@ -286,7 +282,7 @@ static int ehv_bc_console_byte_channel_send(unsigned int handle, const char *s, static void ehv_bc_console_write(struct console *co, const char *s, unsigned int count) { - unsigned int handle = (unsigned int)co->data; + unsigned int handle = (uintptr_t)co->data; char s2[EV_BYTE_CHANNEL_MAX_BYTES]; unsigned int i, j = 0; char c; @@ -352,7 +348,7 @@ static int __init ehv_bc_console_init(void) CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); #endif - ehv_bc_console.data = (void *)stdout_bc; + ehv_bc_console.data = (void *)(uintptr_t)stdout_bc; /* add_preferred_console() must be called before register_console(), otherwise it won't work. However, we don't want to enumerate all the -- cgit v1.2.3 From c7a1bdc5c951aae15021d13e3f5c0b2fe9d56112 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 26 Aug 2011 19:04:49 +0100 Subject: tty: 8250: export serial8250_handle_irq Allow modules to use the normal 8250 irq handler inside their own. Cc: Arnd Bergmann Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 6f594d2d334f..435ce14e676c 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1588,6 +1588,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) return 0; } +EXPORT_SYMBOL_GPL(serial8250_handle_irq); static int serial8250_default_handle_irq(struct uart_port *port) { -- cgit v1.2.3 From 7d4008ebb1c971ce4baf57e45993690b0fa6d9f9 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Fri, 26 Aug 2011 19:04:50 +0100 Subject: tty: add a DesignWare 8250 driver The Synopsys DesignWare 8250 is an 8250 that has an extra interrupt that gets raised when writing to the LCR when busy. To handle this we need special serial_out, serial_in and handle_irq methods. Add a new platform driver that uses these accessors. Cc: Alan Cox Cc: Arnd Bergmann Signed-off-by: Jamie Iles Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/snps-dw-apb-uart.txt | 25 +++ drivers/tty/serial/8250_dw.c | 194 +++++++++++++++++++++ drivers/tty/serial/Kconfig | 7 + drivers/tty/serial/Makefile | 1 + 4 files changed, 227 insertions(+) create mode 100644 Documentation/devicetree/bindings/tty/serial/snps-dw-apb-uart.txt create mode 100644 drivers/tty/serial/8250_dw.c diff --git a/Documentation/devicetree/bindings/tty/serial/snps-dw-apb-uart.txt b/Documentation/devicetree/bindings/tty/serial/snps-dw-apb-uart.txt new file mode 100644 index 000000000000..f13f1c5be91c --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/snps-dw-apb-uart.txt @@ -0,0 +1,25 @@ +* Synopsys DesignWare ABP UART + +Required properties: +- compatible : "snps,dw-apb-uart" +- reg : offset and length of the register set for the device. +- interrupts : should contain uart interrupt. +- clock-frequency : the input clock frequency for the UART. + +Optional properties: +- reg-shift : quantity to shift the register offsets by. If this property is + not present then the register offsets are not shifted. +- reg-io-width : the size (in bytes) of the IO accesses that should be + performed on the device. If this property is not present then single byte + accesses are used. + +Example: + + uart@80230000 { + compatible = "snps,dw-apb-uart"; + reg = <0x80230000 0x100>; + clock-frequency = <3686400>; + interrupts = <10>; + reg-shift = <2>; + reg-io-width = <4>; + }; 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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/Kconfig b/drivers/tty/serial/Kconfig index 10babe254e57..8ee3a6626ceb 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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 78748136ccc8..7b59958f50ec 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 -- cgit v1.2.3 From e44aabd649c80e8be16ede3ed3cbff6fb2561ca9 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 30 Aug 2011 13:53:10 +0200 Subject: serial: pxa: work around for errata #20 Errata E20: UART: Character Timeout interrupt remains set under certain software conditions. Implication: The software servicing the UART can be trapped in an infinite loop. Signed-off-by: Marcus Folkesson Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pxa.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) 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) -- cgit v1.2.3 From 55956216f4b42fefaee70060b054359d63d2afa5 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 29 Aug 2011 15:43:36 +0900 Subject: TTY: serial: Move mutex_unlock in uart_close function When mutex_lock is not called, mutex_unlock is sometimes called. This deletes unnecessary goto and makes modifications so that mutex_unlock is called. [ 8.304000] WARNING: at kernel/muex-debug.c:78 [ 8.304000] Modules linked in: [ 8.304000] [ 8.304000] Pid : 114, Comm: modprobe [ 8.304000] CPU : 0 Not tainted (3.1.0-rc3-next-20110826 #810) [ 8.304000] [ 8.304000] PC is at debug_mutex_unlock+0xf4/0x120 [ 8.304000] PR is at debug_mutex_unlock+0xe6/0x120 [ 8.304000] PC : 80051114 SP : 9f02de58 SR : 400081f1 TEA : 295cf4f2 [ 8.304000] R0 : 00000001 R1 : 00000000 R2 : 0000000f R3 : 00000000 [ 8.304000] R4 : 9fc63158 R5 : 00000000 R6 : 00000001 R7 : 9fe1de78 [ 8.304000] R8 : 805c6b2c R9 : 80003920 R10 : 00000000 R11 : 805c6b2c [ 8.304000] R12 : 80425ca0 R13 : 00000000 R14 : 9f02de58 [ 8.304000] MACH: 00000003 MACL: 00000000 GBR : 296e1678 PR : 80051106 [ 8.304000] [ 8.304000] Call trace: [ 8.304000] [<804236c6>] __mutex_unlock_slowpath+0x46/0x120 [ 8.304000] [<804237aa>] mutex_unlock+0xa/0x20 [ 8.304000] [<80240ed6>] uart_close+0x76/0x2c0 [ 8.304000] [<80223b98>] tty_release+0xf8/0x5c0 [ 8.304000] [<800a93a6>] lookup_object+0x26/0xa0 [ 8.304000] [<80063f6a>] call_rcu+0x8a/0xc0 [ 8.304000] [<800a944a>] put_object+0x2a/0x60 [ 8.304000] [<80003920>] arch_local_irq_restore+0x0/0x40 [ 8.304000] [<800af320>] fput+0x180/0x2c0 [ 8.304000] [<800af248>] fput+0xa8/0x2c0 [ 8.304000] [<800ab1a8>] filp_close+0x48/0xc0 [ 8.304000] [<800ab29a>] sys_close+0x7a/0x100 [ 8.304000] [<8000825a>] syscall_call+0xc/0x10 [ 8.304000] [<800ab220>] sys_close+0x0/0x100 [ 8.304000] [ 8.304000] Code: [ 8.304000] 8005110e: mov.l @r1, r1 [ 8.304000] 80051110: tst r1, r1 [ 8.304000] 80051112: bf 80051116 [ 8.304000] ->80051114: trapa #62 [ 8.304000] 80051116: mov.l @r8, r1 [ 8.304000] 80051118: tst r1, r1 [ 8.304000] 8005111a: bt.s 8005104c [ 8.304000] 8005111c: mov #0, r1 [ 8.304000] 8005111e: bra 80051056 [ 8.304000] [ 8.304000] ---[ end trace e8f8e04c313f429b ]--- Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 1d3780cc3b70..a32dc4362224 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1257,7 +1257,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&port->lock, flags); - goto done; + return; } if ((tty->count == 1) && (port->count != 1)) { @@ -1279,7 +1279,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) } if (port->count) { spin_unlock_irqrestore(&port->lock, flags); - goto done; + return; } /* @@ -1342,7 +1342,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&port->open_wait); wake_up_interruptible(&port->close_wait); -done: mutex_unlock(&port->mutex); } -- cgit v1.2.3 From 2c16a353104c8ad7add9ded6913d9fa090d5e7fb Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 31 Aug 2011 21:24:57 +0200 Subject: TTY: serial, remove dead code from 68328 The code is dead at least since 2002. So remove it to not distort git grep output (about port.tty usage). Remove the whole do_softirq tasklet as it's noop now. Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/68328serial.c | 35 ----------------------------------- drivers/tty/serial/68328serial.h | 1 - 2 files changed, 36 deletions(-) diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index e0a77540b8ca..f54923186969 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; 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; }; -- cgit v1.2.3 From ee160a38eee357ed2572cf41437d5814ce53c839 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 1 Sep 2011 16:20:57 +0200 Subject: TTY: serial, fix includes in some drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit linux/tty_flip.h is included in linux/serial_core.h. But this may (and will) change in the future. Then we would get build errors such as: .../tty/serial/max3107.c: In function ‘put_data_to_circ_buf’: .../tty/serial/max3107.c:149:2: error: implicit declaration of function ‘tty_insert_flip_string’ So fix all the drviers which call tty flip buffer helpers to really include linux/tty_flip.h. And also make sure that those include linux/tty.h when operating with struct tty_struct. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/apbuart.c | 1 + drivers/tty/serial/cpm_uart/cpm_uart_core.c | 1 + drivers/tty/serial/dz.c | 1 + drivers/tty/serial/ioc3_serial.c | 1 + drivers/tty/serial/ioc4_serial.c | 1 + drivers/tty/serial/m32r_sio.c | 1 + drivers/tty/serial/max3100.c | 2 ++ drivers/tty/serial/max3107.c | 2 ++ drivers/tty/serial/mpc52xx_uart.c | 1 + drivers/tty/serial/msm_serial_hs.c | 2 ++ drivers/tty/serial/mux.c | 2 ++ drivers/tty/serial/nwpserial.c | 1 + drivers/tty/serial/pch_uart.c | 2 ++ drivers/tty/serial/sb1250-duart.c | 1 + drivers/tty/serial/serial_ks8695.c | 1 + drivers/tty/serial/serial_txx9.c | 2 ++ drivers/tty/serial/sn_console.c | 1 + drivers/tty/serial/timbuart.c | 2 ++ drivers/tty/serial/uartlite.c | 1 + drivers/tty/serial/ucc_uart.c | 4 +++- drivers/tty/serial/xilinx_uartps.c | 4 +++- drivers/tty/serial/zs.c | 1 + 22 files changed, 33 insertions(+), 2 deletions(-) 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 #include +#include #include #include #include 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 #include +#include #include #include #include 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 #include #include +#include #include #include 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 #include +#include #include #include #include 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 #include +#include #include #include #include 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 #include +#include #include #include #include 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 #include #include +#include +#include #include diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c index 750b4f627315..47602a227afe 100644 --- a/drivers/tty/serial/max3107.c +++ b/drivers/tty/serial/max3107.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index a0bcd8a3758d..c395520d77cf 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include 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 #include +#include +#include #include #include #include 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 #include #include +#include +#include #include #include /* for udelay */ #include 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 #include #include +#include #include #include #include diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 846dfcd3ce0d..ecc8506f3b42 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include 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 #include #include +#include #include #include diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index 2430319f2f52..5551f7afe740 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include #include 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 #include #include +#include +#include #include diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c index 377ae74e7154..7320507b0d85 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include 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 #include #include +#include +#include #include #include #include diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 8af1ed83a4c0..b908615ccaaf 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index c327218cad44..97b67241a783 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -20,8 +20,10 @@ #include #include -#include #include +#include +#include +#include #include #include #include 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 +#include #include +#include +#include #include -#include #include #include #include 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 #include #include +#include #include #include -- cgit v1.2.3 From 2f7861de111bb8e33e6ab9f9607583c6fbc00132 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 1 Sep 2011 13:58:29 +0800 Subject: cris: fix a build error in drivers/tty/serial/crisv10.c This patch fixes the following build error: drivers/tty/serial/crisv10.c:4453: error: 'if_ser0' undeclared (first use in this function): 2 errors in 2 logs v3.1-rc4/cris/cris-allmodconfig v3.1-rc4/cris/cris-allyesconfig drivers/tty/serial/crisv10.c:4453: error: (Each undeclared identifier is reported only once: 2 errors in 2 logs v3.1-rc4/cris/cris-allmodconfig v3.1-rc4/cris/cris-allyesconfig drivers/tty/serial/crisv10.c:4453: error: for each function it appears in.): 2 errors in 2 logs v3.1-rc4/cris/cris-allmodconfig v3.1-rc4/cris/cris-allyesconfig "if_ser0" is a typo, it should be "if_serial_0". Cc: Mikael Starvik Cc: Jesper Nilsson Signed-off-by: WANG Cong Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 225123b37f19..58be715913cd 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -4450,7 +4450,7 @@ static int __init rs_init(void) #if defined(CONFIG_ETRAX_RS485) #if defined(CONFIG_ETRAX_RS485_ON_PA) - if (cris_io_interface_allocate_pins(if_ser0, 'a', rs485_pa_bit, + if (cris_io_interface_allocate_pins(if_serial_0, 'a', rs485_pa_bit, rs485_pa_bit)) { printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); @@ -4459,7 +4459,7 @@ static int __init rs_init(void) } #endif #if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - if (cris_io_interface_allocate_pins(if_ser0, 'g', rs485_pa_bit, + 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 " "RS485 pin\n"); -- cgit v1.2.3 From 06315348b16178e4c006e7892ef8e5e65f49c66a Mon Sep 17 00:00:00 2001 From: Søren Holm Date: Fri, 2 Sep 2011 22:55:37 +0200 Subject: serial: Support the EFR-register of XR1715x uarts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The EFR (Enhenced-Features-Register) is located at a different offset than the other devices supporting UART_CAP_EFR. This change add a special setup quick to set UPF_EXAR_EFR on the port. UPF_EXAR_EFR is then used to the port type to PORT_XR17D15X since it is for sure a XR17D15X uart. Signed-off-by: Søren Holm Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 20 +++++++++++++++++++- drivers/tty/serial/8250_pci.c | 33 +++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 4 +++- include/linux/serial_reg.h | 1 + 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 435ce14e676c..82ca71aa9d10 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) @@ -1074,6 +1081,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. @@ -2417,7 +2432,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 diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index 6b887d90a205..a79caba96d12 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 @@ -1505,6 +1514,30 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .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 */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 493773e3b46d..eadf33d0abba 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -46,7 +46,8 @@ #define PORT_AR7 18 /* Texas Instruments AR7 internal UART */ #define PORT_U6_16550A 19 /* ST-Ericsson U6xxx internal UART */ #define PORT_TEGRA 20 /* NVIDIA Tegra internal UART */ -#define PORT_MAX_8250 20 /* max port ID */ +#define PORT_XR17D15X 21 /* Exar XR17D15x UART */ +#define PORT_MAX_8250 21 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed @@ -349,6 +350,7 @@ struct uart_port { #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) +#define UPF_EXAR_EFR ((__force upf_t) (1 << 25)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h index c75bda37c18e..8ce70d76f836 100644 --- a/include/linux/serial_reg.h +++ b/include/linux/serial_reg.h @@ -152,6 +152,7 @@ * LCR=0xBF (or DLAB=1 for 16C660) */ #define UART_EFR 2 /* I/O: Extended Features Register */ +#define UART_XR_EFR 9 /* I/O: Extended Features Register (XR17D15x) */ #define UART_EFR_CTS 0x80 /* CTS flow control */ #define UART_EFR_RTS 0x40 /* RTS flow control */ #define UART_EFR_SCD 0x20 /* Special character detect */ -- cgit v1.2.3 From 3d43b7d59d2cb9171ad5abd830fa094fa3dbb9b8 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 1 Sep 2011 16:47:49 +0800 Subject: cris: lower the printk level in cris serial driver KERN_CRIT is too high, replace those KERN_CRIT with KERN_ERR or KERN_WARNING. Cc: Geert Uytterhoeven Cc: Mikael Starvik Cc: Jesper Nilsson Signed-off-by: WANG Cong Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 58be715913cd..a931524629f9 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -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); -- cgit v1.2.3 From 3a0db7215c88077b61a673215756ec4a0dc0c7a5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 31 Aug 2011 21:24:59 +0200 Subject: TTY: serial, move 68360 driver to staging This driver has been broken at least since 2008. At that time, a88487c79b (Fix compile errors in SGI console drivers) broke this driver completely. And since nobody noticed for the past 3 years, move it into staging. I think this will rot there and we will throw it away completely after some time. Or maybe someone will volunteer to fix it ;). Signed-off-by: Jiri Slaby Cc: Geert Uytterhoeven Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/serial/68360serial.c | 2979 ++++++++++++++++++++++++++++++++++ drivers/staging/serial/Kconfig | 16 + drivers/staging/serial/Makefile | 1 + drivers/staging/serial/TODO | 6 + drivers/tty/serial/68360serial.c | 2979 ---------------------------------- drivers/tty/serial/Kconfig | 17 - drivers/tty/serial/Makefile | 1 - 9 files changed, 3005 insertions(+), 2997 deletions(-) create mode 100644 drivers/staging/serial/68360serial.c create mode 100644 drivers/staging/serial/Kconfig create mode 100644 drivers/staging/serial/Makefile create mode 100644 drivers/staging/serial/TODO delete mode 100644 drivers/tty/serial/68360serial.c diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 06c9081d596d..bd3d1cc1d16e 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,6 +24,8 @@ menuconfig STAGING if STAGING +source "drivers/staging/serial/Kconfig" + source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index f3c5e33bb263..d480a94cc7fd 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -3,6 +3,7 @@ # fix for build system bug... obj-$(CONFIG_STAGING) += staging.o +obj-y += serial/ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ diff --git a/drivers/staging/serial/68360serial.c b/drivers/staging/serial/68360serial.c new file mode 100644 index 000000000000..0a3e8787ed50 --- /dev/null +++ b/drivers/staging/serial/68360serial.c @@ -0,0 +1,2979 @@ +/* + * UART driver for 68360 CPM SCC or SMC + * Copyright (c) 2000 D. Jeff Dionne , + * Copyright (c) 2000 Michael Leslie + * Copyright (c) 1997 Dan Malek + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 +#include + +/* 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/staging/serial/Kconfig b/drivers/staging/serial/Kconfig new file mode 100644 index 000000000000..9489688397e0 --- /dev/null +++ b/drivers/staging/serial/Kconfig @@ -0,0 +1,16 @@ +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 diff --git a/drivers/staging/serial/Makefile b/drivers/staging/serial/Makefile new file mode 100644 index 000000000000..37a6a0b35fba --- /dev/null +++ b/drivers/staging/serial/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SERIAL_68360) += 68360serial.o diff --git a/drivers/staging/serial/TODO b/drivers/staging/serial/TODO new file mode 100644 index 000000000000..a19cda81dab4 --- /dev/null +++ b/drivers/staging/serial/TODO @@ -0,0 +1,6 @@ +These are a few serial drivers that either do not build, or do not work if they +do build, or if they seem to work, are for obsolete hardware, or are full of +unfixable races and no one uses them anymore. + +If no one steps up to adopt any of these drivers, they will be removed +in the 3.4 release. 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 , - * Copyright (c) 2000 Michael Leslie - * Copyright (c) 1997 Dan Malek - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#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 -#include - -/* 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/Kconfig b/drivers/tty/serial/Kconfig index 8ee3a6626ceb..5f479dada6f2 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1042,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 7b59958f50ec..e10cf5b54b6d 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -50,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 -- cgit v1.2.3 From 98c2b37353cd8839f86f36e9bb9fe39f33fc9626 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 11 Sep 2011 13:59:29 +0200 Subject: keyboard: Do not include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top of has this comment: * Please do not include this file in generic code. There is currently * no requirement for any architecture to implement anything held * within this file. * * Thanks. --rmk Remove inclusion of , to prevent the following compile error from happening soon: | include/linux/irq.h:132: error: redefinition of ‘struct irq_data’ | include/linux/irq.h:286: error: redefinition of ‘struct irq_chip’ drivers/tty/vt/keyboard.c needs to include for get_irq_regs(): | drivers/tty/vt/keyboard.c:497: error: implicit declaration of function ‘get_irq_regs’ | drivers/tty/vt/keyboard.c:497: warning: initialization makes pointer from integer without a cast Signed-off-by: Geert Uytterhoeven Acked-by: Thomas Gleixner Cc: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 #include #include -#include #include #include @@ -43,6 +42,8 @@ #include #include +#include + extern void ctrl_alt_del(void); /* -- cgit v1.2.3 From 153d3d9be7ce6d87a83e4871ec94ddf6b9604814 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 15 Sep 2011 13:09:49 -0700 Subject: serial: mfd: Initconst section fixes Cc: gregkh@suse.de Signed-off-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index cfa750163672..286c386d9c46 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1467,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) }, -- cgit v1.2.3 From 94abc56f4d90f289ea32a0a11d3577fcd8cb28fb Mon Sep 17 00:00:00 2001 From: Ning Jiang Date: Mon, 5 Sep 2011 16:28:18 +0800 Subject: serial-core: power up uart port early before we do set_termios when resuming The following patch removed uart_change_pm() in uart_resume_port(): commit 5933a161abcb8d83a2c145177f48027c3c0a8995 Author: Yin Kangkai serial-core: reset the console speed on resume It will break the pxa serial driver when the system resumes from suspend mode as it will try to set baud rate divider register in set_termios but with clock off. The register value can not be set correctly on some platform if the clock is disabled. The pxa driver will check the value and report the following warning: ------------[ cut here ]------------ WARNING: at drivers/tty/serial/pxa.c:545 serial_pxa_set_termios+0x1dc/0x250() Modules linked in: [] (unwind_backtrace+0x0/0xf0) from [] (warn_slowpath_common+0x4c/0x64) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x18/0x1c) [] (warn_slowpath_null+0x18/0x1c) from [] (serial_pxa_set_termios+0x1dc/0x250) [] (serial_pxa_set_termios+0x1dc/0x250) from [] (uart_resume_port+0x128/0x2dc) [] (uart_resume_port+0x128/0x2dc) from [] (serial_pxa_resume+0x18/0x24) [] (serial_pxa_resume+0x18/0x24) from [] (platform_pm_resume+0x40/0x4c) [] (platform_pm_resume+0x40/0x4c) from [] (pm_op+0x68/0xb4) [] (pm_op+0x68/0xb4) from [] (device_resume+0xb0/0xec) [] (device_resume+0xb0/0xec) from [] (dpm_resume+0xe0/0x194) [] (dpm_resume+0xe0/0x194) from [] (dpm_resume_end+0xc/0x18) [] (dpm_resume_end+0xc/0x18) from [] (suspend_devices_and_enter+0x16c/0x1ac) [] (suspend_devices_and_enter+0x16c/0x1ac) from [] (enter_state+0xac/0xdc) [] (enter_state+0xac/0xdc) from [] (state_store+0xa0/0xbc) [] (state_store+0xa0/0xbc) from [] (kobj_attr_store+0x18/0x1c) [] (kobj_attr_store+0x18/0x1c) from [] (sysfs_write_file+0x108/0x140) [] (sysfs_write_file+0x108/0x140) from [] (vfs_write+0xac/0x134) [] (vfs_write+0xac/0x134) from [] (sys_write+0x3c/0x68) [] (sys_write+0x3c/0x68) from [] (ret_fast_syscall+0x0/0x2c) ---[ end trace 88289eceb4675b04 ]--- This patch fix the problem by adding the power on opertion back for uart console when console_suspend_enabled is true. Signed-off-by: Ning Jiang Tested-by: Mayank Rana Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index a32dc4362224..e562b1224466 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1975,6 +1975,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); -- cgit v1.2.3 From 9cfb5c05fee914cc65d4706801f6bc424082b5f5 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Thu, 22 Sep 2011 16:59:15 +0800 Subject: TTY: irq: Remove IRQF_DISABLED Since commit [e58aa3d2: genirq: Run irq handlers with interrupts disabled], We run all interrupt handlers with interrupts disabled and we even check and yell when an interrupt handler returns with interrupts enabled (see commit [b738a50a: genirq: Warn when handler enables interrupts]). So now this flag is a NOOP and can be removed. Signed-off-by: Yong Zhang Acked-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 2 +- drivers/tty/cyclades.c | 2 +- drivers/tty/hvc/hvc_irq.c | 2 +- drivers/tty/hvc/hvcs.c | 2 +- drivers/tty/hvc/hvsi.c | 2 +- drivers/tty/isicom.c | 2 +- drivers/tty/serial/68328serial.c | 2 +- drivers/tty/serial/altera_jtaguart.c | 2 +- drivers/tty/serial/altera_uart.c | 2 +- drivers/tty/serial/bfin_sport_uart.c | 2 +- drivers/tty/serial/bfin_uart.c | 8 ++++---- drivers/tty/serial/crisv10.c | 18 +++++++++--------- drivers/tty/serial/icom.c | 2 +- drivers/tty/serial/lantiq.c | 6 +++--- drivers/tty/serial/mcf.c | 2 +- drivers/tty/serial/mpc52xx_uart.c | 2 +- drivers/tty/serial/serial_ks8695.c | 8 ++++---- drivers/tty/serial/sh-sci.c | 2 +- drivers/tty/serial/sn_console.c | 2 +- 19 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 6d43f550b549..b84c83456dcc 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -2017,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 5beef494fc40..c9bf779481d8 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -3367,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/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 e523773a5480..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. 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/serial/68328serial.c b/drivers/tty/serial/68328serial.c index f54923186969..a88ef9782a4f 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -1306,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/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index 60e049b041a7..00a73ecb2dfb 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -218,7 +218,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 50bc5a5ac653..d902558ccfd2 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/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_uart.c b/drivers/tty/serial/bfin_uart.c index c7e592df42c0..66afb98b77b5 100644 --- a/drivers/tty/serial/bfin_uart.c +++ b/drivers/tty/serial/bfin_uart.c @@ -667,14 +667,14 @@ static int bfin_serial_startup(struct uart_port *port) kgdboc_break_enabled = 0; else { # endif - if (request_irq(uart->rx_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->tx_irq, 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->rx_irq, uart); @@ -734,7 +734,7 @@ 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"); } @@ -745,7 +745,7 @@ static int bfin_serial_startup(struct uart_port *port) #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"); } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index a931524629f9..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, @@ -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/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/lantiq.c b/drivers/tty/serial/lantiq.c index 58cf279ed879..9e2d42bfa557 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/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/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index c395520d77cf..1093a88a1fe3 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -274,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/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c index 5551f7afe740..7c13639c597e 100644 --- a/drivers/tty/serial/serial_ks8695.c +++ b/drivers/tty/serial/serial_ks8695.c @@ -337,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/sh-sci.c b/drivers/tty/serial/sh-sci.c index 2ec57b2fb278..ca1a4c385d40 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1961,7 +1961,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 7320507b0d85..238c7df73ef5 100644 --- a/drivers/tty/serial/sn_console.c +++ b/drivers/tty/serial/sn_console.c @@ -738,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; -- cgit v1.2.3 From fd01a7a1bf6722e32efa679f15507148784af1f7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 22 Sep 2011 20:33:13 -0500 Subject: drivers/tty: don't use the byte channel handle as a parameter in ehv_bytechan.c The ePAPR hypervisor byte channel console driver only supports one byte channel as a console, and the byte channel handle is stored in a global variable. It doesn't make any sense to pass that handle as a parameter to the console functions, since these functions already have access to the global variable. Signed-off-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/ehv_bytechan.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index f733718bf8e7..1595dba0072c 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -282,7 +282,6 @@ static int ehv_bc_console_byte_channel_send(unsigned int handle, const char *s, static void ehv_bc_console_write(struct console *co, const char *s, unsigned int count) { - unsigned int handle = (uintptr_t)co->data; char s2[EV_BYTE_CHANNEL_MAX_BYTES]; unsigned int i, j = 0; char c; @@ -295,14 +294,14 @@ static void ehv_bc_console_write(struct console *co, const char *s, s2[j++] = c; if (j >= (EV_BYTE_CHANNEL_MAX_BYTES - 1)) { - if (ehv_bc_console_byte_channel_send(handle, s2, j)) + if (ehv_bc_console_byte_channel_send(stdout_bc, s2, j)) return; j = 0; } } if (j) - ehv_bc_console_byte_channel_send(handle, s2, j); + ehv_bc_console_byte_channel_send(stdout_bc, s2, j); } /* @@ -348,8 +347,6 @@ static int __init ehv_bc_console_init(void) CONFIG_PPC_EARLY_DEBUG_EHV_BC_HANDLE); #endif - ehv_bc_console.data = (void *)(uintptr_t)stdout_bc; - /* 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. */ -- cgit v1.2.3 From cf16807b61d15e42b20407c954a01a3774520ea7 Mon Sep 17 00:00:00 2001 From: Nikola Diklic-Perin Date: Fri, 23 Sep 2011 10:59:43 +0200 Subject: tty/n_gsm: fix bug in tiocmset Clear bitmask was not inverted before masking modem_tx. Calling ioctl(fd, TIOCMBIC, TIOCM_RTS) results in: [ 197.430000] pre_modem_tx: 0x00000006 [ 197.430000] clear: 0x00000004 [ 197.430000] set: 0x00000000 [ 197.440000] post_modem_tx: 0x00000004 which is wrong. Signed-off-by: Nikola Diklic-Perin Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 8d7766bfef87..da3b31a98654 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2989,7 +2989,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) { -- cgit v1.2.3 From 7808a4c4853fa0203085cf2217e01823d9f0c70c Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 26 Sep 2011 09:14:34 -0400 Subject: 8250: ratelimit LSR safety check engaged warning. On my BIOSTAR TA890FXE the ttyS0 ends up spewing: [904682.485933] ttyS0: LSR safety check engaged! [904692.505895] ttyS0: LSR safety check engaged! [904702.525972] ttyS0: LSR safety check engaged! [904712.545967] ttyS0: LSR safety check engaged! [904722.566125] ttyS0: LSR safety check engaged! .. lets limit it so it won't be the only thing visible in the ring buffer. CC: Alan Cox Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 82ca71aa9d10..076e9c6269d3 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -2055,8 +2055,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; } -- cgit v1.2.3 From f37ac5a144027cddfcf1dfab30eb7c2ba765f5ca Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Fri, 23 Sep 2011 19:22:55 +0400 Subject: tty/n_gsm: fix a bug in gsm_dlci_data_output (adaption = 2 case) in adaption=2 case we should put 1 or 2 byte with modem status bits at the beginning of a buffer pointed by "dp". n_gsm use 1 byte case, so it allocate a buffer of len + 1 size. As result we should: * put 1 byte of modem status bits * increase data pointer * put "len" bytes of data but actually we have: * increase first byte with the value of modem status bits * decrease "len" * put orig_len - 1 bytes of data starting from the buffer beggining This is evidently wrong. Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index da3b31a98654..14c26cdd8aca 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -833,8 +833,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) break; case 2: /* Unstructed with modem bits. Always one byte as we never send inline break data */ - *dp += gsm_encode_modem(dlci); - len--; + *dp++ = gsm_encode_modem(dlci); break; } WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); -- cgit v1.2.3 From 268e526b935e794386d75025577b745e6bd57f13 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Fri, 23 Sep 2011 19:22:56 +0400 Subject: tty/n_gsm: avoid fifo overflow in gsm_dlci_data_output n_gsm use a simple approach: every writing to fifo correspond exactly one reading from fifo. There are no problem in this approach until we read less bytes then we write. As result fifo may owerflow. This leads to packet loss and very slow responce. For example, this happens with ping packets (about 96 byte each) and default gsm->mtu = 64. As result we get 50 sec ping timeout and 20% packet loss. Fix the problem by reading and sending all data from the fifo Signed-off-by: Mikhail Kshevetskiy Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 58 ++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 14c26cdd8aca..4cb0d0a3e57b 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -809,37 +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); - 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; } /** -- cgit v1.2.3 From 53d785ccd918d571c227d459477793872676fc02 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Sep 2011 09:20:50 +0300 Subject: TTY: snyclinkmp: forever loop in tx_load_dma_buffer() My main concern here was the line that said: copy_count = min_t(unsigned short,count,SCABUFSIZE); "count" is an unsigned int here so the cast to unsigned short truncates the upper bits. So if count is 0x10000 then copy_count is 0 and the loop never exits. "count" comes from skb->len in hdlcdev_xmit(). The other min_t() changes are just cleanups. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclinkmp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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]; -- cgit v1.2.3 From 361162459f62dc0826b82c9690a741a940f457f0 Mon Sep 17 00:00:00 2001 From: Miche Baker-Harvey Date: Wed, 5 Oct 2011 10:48:51 -0700 Subject: hvc_console: display printk messages on console. printk only works for "registered consoles." Currently, the hvc_console code calls register_console() from hvc_instantiate(), but that's only used in the early console case. In hvc_alloc(), register_console() was not called. Add a call to register_console() in hvc_alloc(), set up the index in the hvc_console, and set up the necessary vtermnos[] and cons_op[] entries so that printk functions work. Signed-off-by: Miche Baker-Harvey Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_console.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index b6b2d18fa38d..7430bc3c8d53 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -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); -- cgit v1.2.3 From 8193c4290620d9b2a6ac116719f11aa99053a90d Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Wed, 5 Oct 2011 23:13:13 +0200 Subject: tty: Support compat_ioctl get/set termios_locked When running a Fedora 15 (x86) on an x86_64 kernel, in the boot process plymouthd complains about those two missing ioctls: [ 2.581783] ioctl32(plymouthd:186): Unknown cmd fd(10) cmd(00005457){t:'T';sz:0} arg(ffb6a5d0) on /dev/tty1 [ 2.581803] ioctl32(plymouthd:186): Unknown cmd fd(10) cmd(00005456){t:'T';sz:0} arg(ffb6a680) on /dev/tty1 both ioctl functions work on the 'struct termios' resp. 'struct termios2', which has the same size (36 bytes resp. 44 bytes) on x86 and x86_64, so it's just a matter of converting the pointer from userland. Signed-off-by: Thomas Meyer Cc: Arnd Bergmann Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 2 ++ drivers/tty/tty_ioctl.c | 17 +++++++++++++++++ include/linux/tty.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 150e4f747c7d..4ca4bcd28ff7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2717,6 +2717,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 #include #include +#include #include #include @@ -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/include/linux/tty.h b/include/linux/tty.h index 0ad68889fc1a..64c12a3e65f0 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -579,6 +579,8 @@ extern int __init tty_init(void); /* tty_ioctl.c */ extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +extern long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); /* serial.c */ -- cgit v1.2.3 From 9fdbdd062bfc187e2aa32f7880fa7e8ae717efdf Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 6 Oct 2011 14:29:48 -0700 Subject: parport_pc: release IO region properly if unsupported ITE887x card is found sio_ite_8872_probe() bails out if it detects no-parallel (1S, 2S) or unknown card. It doesn't call release_region() on the previously allocated resource though. This causes (a) leak of the resource (b) kernel oops when parport module is removed and /proc/ioports is read. This is because the string that has been associated to the IO port region is a static char array inside the already removed module. Let's call release_region() properly before baling out. Signed-off-by: Jiri Kosina Acked-by: Niels de Vos Cc: Bjorn Helgaas Cc: Alan Cox Cc: Joe Krahn Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_pc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index d1cdb9449f84..d0b597b50398 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2595,14 +2595,17 @@ static int __devinit sio_ite_8872_probe(struct pci_dev *pdev, int autoirq, break; case 0x6: printk(KERN_INFO "parport_pc: ITE8873 found (1S)\n"); + release_region(inta_addr[i], 32); return 0; case 0x8: printk(KERN_INFO "parport_pc: ITE8874 found (2S)\n"); + release_region(inta_addr[i], 32); return 0; default: printk(KERN_INFO "parport_pc: unknown ITE887x\n"); printk(KERN_INFO "parport_pc: please mail 'lspci -nvv' " "output to Rich.Liu@ite.com.tw\n"); + release_region(inta_addr[i], 32); return 0; } -- cgit v1.2.3 From a4a77b1af865454bba0539d43c18e03d7a005d64 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 11 Oct 2011 13:23:29 +0200 Subject: h8300: drivers/serial/Kconfig was moved commit ab4382d27412e7e3e7c936e8d50d8888dfac3df8 moved drivers/serial/Kconfig to drivers/tty/serial/Kconfig, so we need to source the latter file. Signed-off-by: Paul Bolle Signed-off-by: Greg Kroah-Hartman --- arch/h8300/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 091ed6192ae8..2b1eee7198c7 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -195,7 +195,7 @@ config UNIX98_PTYS source "drivers/char/pcmcia/Kconfig" -source "drivers/serial/Kconfig" +source "drivers/tty/serial/Kconfig" source "drivers/i2c/Kconfig" -- cgit v1.2.3 From 64d91cfaade2155ad048fe3b65238a052e29dde4 Mon Sep 17 00:00:00 2001 From: Tomoya MORINAGA Date: Fri, 7 Oct 2011 13:39:49 +0900 Subject: 8250_pci: Fix kernel panic when pch_uart is disabled Currently, ".setup" function is not set. As a result, when detecting our IOH's uart device without pch_uart, kernel panic occurs at the following of pciserial_init_ports(). for (i = 0; i < nr_ports; i++) { if (quirk->setup(priv, board, &serial_port, i)) break; So, this patch adds the ".setup" function. We can use pci_default_setup because our IOH's uart is compatible with 16550. Signed-off-by: Tomoya MORINAGA Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250_pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c index a79caba96d12..1b449b759990 100644 --- a/drivers/tty/serial/8250_pci.c +++ b/drivers/tty/serial/8250_pci.c @@ -1591,51 +1591,61 @@ 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, }, { .vendor = 0x10DB, .device = 0x800D, .init = pci_eg20t_init, + .setup = pci_default_setup, }, /* * Cronyx Omega PCI (PLX-chip based) -- cgit v1.2.3 From c290f8358acaeffd8e0c551ddcc24d1206143376 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 12 Oct 2011 11:32:42 +0200 Subject: TTY: drop driver reference in tty_open fail path When tty_driver_lookup_tty fails in tty_open, we forget to drop a reference to the tty driver. This was added by commit 4a2b5fddd5 (Move tty lookup/reopen to caller). Fix that by adding tty_driver_kref_put to the fail path. I will refactor the code later. This is for the ease of backporting to stable. Introduced-in: v2.6.28-rc2 Signed-off-by: Jiri Slaby Cc: stable Cc: Alan Cox Acked-by: Sukadev Bhattiprolu Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4ca4bcd28ff7..6913da8f202c 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1873,6 +1873,7 @@ got_driver: if (IS_ERR(tty)) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); return PTR_ERR(tty); } } -- cgit v1.2.3 From fa90e1c935472281de314e6d7c9a37db9cbc2e4e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 12 Oct 2011 11:32:43 +0200 Subject: TTY: make tty_add_file non-failing If tty_add_file fails at the point it is now, we have to revert all the changes we did to the tty. It means either decrease all refcounts if this was a tty reopen or delete the tty if it was newly allocated. There was a try to fix this in v3.0-rc2 using tty_release in 0259894c7 (TTY: fix fail path in tty_open). But instead it introduced a NULL dereference. It's because tty_release dereferences filp->private_data, but that one is set even in our tty_add_file. And when tty_add_file fails, it's still NULL/garbage. Hence tty_release cannot be called there. To circumvent the original leak (and the current NULL deref) we split tty_add_file into two functions, making the latter non-failing. In that case we may do the former early in open, where handling failures is easy. The latter stays as it is now. So there is no change in functionality. The original bug (leak) was introduced by f573bd176 (tty: Remove __GFP_NOFAIL from tty_add_file()). Thanks Dan for reporting this. Later, we may split tty_release into more functions and call only some of them in this fail path instead. (If at all possible.) Introduced-in: v2.6.37-rc2 Signed-off-by: Jiri Slaby Reported-by: Dan Carpenter Cc: stable Cc: Alan Cox Cc: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 16 +++++++++++----- drivers/tty/tty_io.c | 47 +++++++++++++++++++++++++++++++++++------------ include/linux/tty.h | 4 +++- 3 files changed, 49 insertions(+), 18 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 98b6e3bdb000..7613f95f2d6b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -657,12 +657,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(); @@ -676,9 +682,7 @@ 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) @@ -697,6 +701,8 @@ out2: out: devpts_kill_index(inode, index); tty_unlock(); +err_file: + tty_free_file(filp); return retval; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6913da8f202c..767ecbb4761a 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); } @@ -1812,6 +1831,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; @@ -1824,6 +1847,7 @@ retry_open: if (!tty) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENXIO; } driver = tty_driver_kref_get(tty->driver); @@ -1856,6 +1880,7 @@ retry_open: } tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENODEV; } @@ -1863,6 +1888,7 @@ retry_open: if (!driver) { tty_unlock(); mutex_unlock(&tty_mutex); + tty_free_file(filp); return -ENODEV; } got_driver: @@ -1874,6 +1900,7 @@ got_driver: tty_unlock(); mutex_unlock(&tty_mutex); tty_driver_kref_put(driver); + tty_free_file(filp); return PTR_ERR(tty); } } @@ -1889,15 +1916,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 && diff --git a/include/linux/tty.h b/include/linux/tty.h index 64c12a3e65f0..ff2925aa4e79 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -471,7 +471,9 @@ extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); extern void tty_default_fops(struct file_operations *fops); extern struct tty_struct *alloc_tty_struct(void); -extern int tty_add_file(struct tty_struct *tty, struct file *file); +extern int tty_alloc_file(struct file *file); +extern void tty_add_file(struct tty_struct *tty, struct file *file); +extern void tty_free_file(struct file *file); extern void free_tty_struct(struct tty_struct *tty); extern void initialize_tty_struct(struct tty_struct *tty, struct tty_driver *driver, int idx); -- cgit v1.2.3 From 1177c0efc04d032644895b8d757f55b433912596 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 12 Oct 2011 11:32:44 +0200 Subject: TTY: pty, release tty in all ptmx_open fail paths Mistakenly, commit 64ba3dc3143d (tty: never hold BTM while getting tty_mutex) switched one fail path in ptmx_open to not free the newly allocated tty. Fix that by jumping to the appropriate place. And rename the labels so that it's clear what is going on there. Introduced-in: v2.6.36-rc2 Signed-off-by: Jiri Slaby Cc: stable Cc: Arnd Bergmann Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 7613f95f2d6b..2feea63d497a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -686,15 +686,15 @@ static int ptmx_open(struct inode *inode, struct file *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; -- cgit v1.2.3 From 631180aca723cb92e128fdac5fd144e913ca84e5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 12 Oct 2011 11:32:45 +0200 Subject: TTY: call tty_driver_lookup_tty unconditionally Commit 4a2b5fddd5 (Move tty lookup/reopen to caller) made the call to tty_driver_lookup_tty conditional in tty_open. It doesn't look like it was an intention. Or if it was, it was not documented in the changelog and the code now looks weird. For example there would be no need to remember the tty driver and tty index. Further the condition depends on a tty which we drop a reference of already. If I'm looking correctly, this should not matter thanks to the locking currently done there. Thus, tty_driver->ttys[idx] cannot change under our hands. But anyway, it makes sense to change that to the old behaviour. Introduced-in: v2.6.28-rc2 Signed-off-by: Jiri Slaby Cc: stable Cc: Sukadev Bhattiprolu Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 767ecbb4761a..0425170d9ed6 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1821,7 +1821,7 @@ int tty_release(struct inode *inode, struct file *filp) static int tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty = NULL; + struct tty_struct *tty; int noctty, retval; struct tty_driver *driver; int index; @@ -1892,17 +1892,14 @@ retry_open: return -ENODEV; } got_driver: - if (!tty) { - /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, inode, index); - - if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); - tty_free_file(filp); - return PTR_ERR(tty); - } + /* check whether we're reopening an existing tty */ + tty = tty_driver_lookup_tty(driver, inode, index); + if (IS_ERR(tty)) { + tty_unlock(); + mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); + tty_free_file(filp); + return PTR_ERR(tty); } if (tty) { -- cgit v1.2.3 From 0331bbf3c6fd9997e5f0d165840229ccfeba6548 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Oct 2011 18:06:56 +0200 Subject: tty/serial: RS485 bindings for device tree Generic bindings for RS485 feature included in some UARTs. Those bindings have to be used withing an UART device tree node. Documentation updated to link to the bindings definition. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/rs485.txt | 31 ++++++++++++++++++++++ .../devicetree/bindings/tty/serial/atmel-usart.txt | 27 +++++++++++++++++++ Documentation/serial/serial-rs485.txt | 5 ++++ 3 files changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/rs485.txt create mode 100644 Documentation/devicetree/bindings/tty/serial/atmel-usart.txt diff --git a/Documentation/devicetree/bindings/serial/rs485.txt b/Documentation/devicetree/bindings/serial/rs485.txt new file mode 100644 index 000000000000..1e753c69fc83 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/rs485.txt @@ -0,0 +1,31 @@ +* RS485 serial communications + +The RTS signal is capable of automatically controlling line direction for +the built-in half-duplex mode. +The properties described hereafter shall be given to a half-duplex capable +UART node. + +Required properties: +- rs485-rts-delay: prop-encoded-array where: + * a is the delay beteween rts signal and beginning of data sent in milliseconds. + it corresponds to the delay before sending data. + * b is the delay between end of data sent and rts signal in milliseconds + it corresponds to the delay after sending data and actual release of the line. + +Optional properties: +- linux,rs485-enabled-at-boot-time: empty property telling to enable the rs485 + feature at boot time. It can be disabled later with proper ioctl. +- rs485-rx-during-tx: empty property that enables the receiving of data even + whilst sending data. + +RS485 example for Atmel USART: + usart0: serial@fff8c000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xfff8c000 0x4000>; + interrupts = <7>; + atmel,use-dma-rx; + atmel,use-dma-tx; + linux,rs485-enabled-at-boot-time; + rs485-rts-delay = <0 200>; // in milliseconds + }; + diff --git a/Documentation/devicetree/bindings/tty/serial/atmel-usart.txt b/Documentation/devicetree/bindings/tty/serial/atmel-usart.txt new file mode 100644 index 000000000000..a49d9a1d4ccf --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/atmel-usart.txt @@ -0,0 +1,27 @@ +* Atmel Universal Synchronous Asynchronous Receiver/Transmitter (USART) + +Required properties: +- compatible: Should be "atmel,-usart" + The compatible indicated will be the first SoC to support an + additional mode or an USART new feature. +- reg: Should contain registers location and length +- interrupts: Should contain interrupt + +Optional properties: +- atmel,use-dma-rx: use of PDC or DMA for receiving data +- atmel,use-dma-tx: use of PDC or DMA for transmitting data + + compatible description: +- at91rm9200: legacy USART support +- at91sam9260: generic USART implementation for SAM9 SoCs + +Example: + + usart0: serial@fff8c000 { + compatible = "atmel,at91sam9260-usart"; + reg = <0xfff8c000 0x4000>; + interrupts = <7>; + atmel,use-dma-rx; + atmel,use-dma-tx; + }; + diff --git a/Documentation/serial/serial-rs485.txt b/Documentation/serial/serial-rs485.txt index c8878f821d1e..079cb3df62cf 100644 --- a/Documentation/serial/serial-rs485.txt +++ b/Documentation/serial/serial-rs485.txt @@ -28,6 +28,10 @@ RS485 communications. This data structure is used to set and configure RS485 parameters in the platform data and in ioctls. + The device tree can also provide RS485 boot time parameters (see [2] + for bindings). The driver is in charge of filling this data structure from + the values given by the device tree. + Any driver for devices capable of working both as RS232 and RS485 should provide at least the following ioctls: @@ -121,3 +125,4 @@ 5. REFERENCES [1] include/linux/serial.h + [2] Documentation/devicetree/bindings/serial/rs485.txt -- cgit v1.2.3 From 1acfc7eca664783ee20d951683ed229eaa64f573 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Oct 2011 18:06:57 +0200 Subject: tty/serial: atmel_serial: change platform_data variable name Easier to follow if platform_data name is pdata. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index c7232a916c1b..a507daa05cfb 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1417,13 +1417,13 @@ 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; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF; port->ops = &atmel_pops; port->fifosize = 1; - port->line = data->num; + port->line = pdata->num; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1433,9 +1433,9 @@ 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->regs) /* Already mapped by setup code */ - port->membase = data->regs; + port->membase = pdata->regs; else { port->flags |= UPF_IOREMAP; port->membase = NULL; @@ -1450,9 +1450,9 @@ 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; + atmel_port->use_dma_rx = pdata->use_dma_rx; + atmel_port->use_dma_tx = pdata->use_dma_tx; + atmel_port->rs485 = pdata->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; -- cgit v1.2.3 From 588edbf3b8948d94a52da5d6b45c9bb7307d1f01 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Oct 2011 18:06:58 +0200 Subject: tty/serial: atmel_serial: whitespace and braces modifications Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a507daa05cfb..bb723542ad24 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1433,10 +1433,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 (pdata->regs) + if (pdata->regs) { /* Already mapped by setup code */ port->membase = pdata->regs; - else { + } else { port->flags |= UPF_IOREMAP; port->membase = NULL; } @@ -1450,9 +1450,10 @@ 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 = pdata->use_dma_rx; - atmel_port->use_dma_tx = pdata->use_dma_tx; + atmel_port->use_dma_rx = pdata->use_dma_rx; + atmel_port->use_dma_tx = pdata->use_dma_tx; atmel_port->rs485 = pdata->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; -- cgit v1.2.3 From 4cbf9f4864bd4fb2e64d555aacb93c24478e4e8d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Oct 2011 18:06:59 +0200 Subject: tty/serial: atmel_serial: auto-enumerate ports If no platform data provided to enumerate ports, use a bit field to choose port number and check if port is already initialized. Use this mechanism for both console and plain serial ports. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 44 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index bb723542ad24..10743297a001 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -157,6 +157,7 @@ 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; @@ -1423,7 +1424,6 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, port->flags = UPF_BOOT_AUTOCONF; port->ops = &atmel_pops; port->fifosize = 1; - port->line = pdata->num; port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1613,10 +1613,15 @@ static struct console atmel_console = { static int __init atmel_console_init(void) { if (atmel_default_console_device) { - add_preferred_console(ATMEL_DEVICENAME, - atmel_default_console_device->id, NULL); - atmel_init_port(&atmel_ports[atmel_default_console_device->id], - atmel_default_console_device); + int id = atmel_default_console_device->id; + struct atmel_uart_port *port = &atmel_ports[id]; + + set_bit(id, &atmel_ports_in_use); + port->backup_imr = 0; + port->uart.line = id; + + add_preferred_console(ATMEL_DEVICENAME, id, NULL); + atmel_init_port(port, atmel_default_console_device); register_console(&atmel_console); } @@ -1715,12 +1720,33 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) struct atmel_uart_port *port; 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 (pdata) + ret = pdata->num; + + if (ret < 0) + /* port id not found in platform data: + * 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); @@ -1766,7 +1792,7 @@ err_alloc_ring: clk_put(port->clk); port->clk = NULL; } - +err: return ret; } @@ -1786,6 +1812,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; -- cgit v1.2.3 From 5fbe46b67680c27aeb56228dab8cfe25f8f8f83d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Oct 2011 18:07:00 +0200 Subject: tty/serial: atmel_serial: add device tree support Will use aliases to enumerate ports, if available. Signed-off-by: Nicolas Ferre Acked-by: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 79 +++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 10743297a001..fc4081ead727 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -163,6 +165,16 @@ static unsigned long atmel_ports_in_use; 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) { @@ -1411,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. */ @@ -1420,6 +1474,14 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct uart_port *port = &atmel_port->uart; 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; @@ -1433,7 +1495,7 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); - if (pdata->regs) { + if (pdata && pdata->regs) { /* Already mapped by setup code */ port->membase = pdata->regs; } else { @@ -1450,10 +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 = pdata->use_dma_rx; - atmel_port->use_dma_tx = pdata->use_dma_tx; - atmel_port->rs485 = pdata->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; @@ -1718,17 +1776,21 @@ 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 = -ENODEV; BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - if (pdata) - ret = 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: + /* 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)); @@ -1827,6 +1889,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), }, }; -- cgit v1.2.3 From a0340703981baa6cc1e9c7c768095a0a4e718daf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 19 Oct 2011 08:33:21 -0700 Subject: Revert "TTY: call tty_driver_lookup_tty unconditionally" This reverts commit 631180aca723cb92e128fdac5fd144e913ca84e5. It caused problems when /dev/tty is a pty: https://lkml.org/lkml/2011/10/12/401 Cc: Jiri Slaby Cc: stable Cc: Sukadev Bhattiprolu Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 0425170d9ed6..767ecbb4761a 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1821,7 +1821,7 @@ int tty_release(struct inode *inode, struct file *filp) static int tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty; + struct tty_struct *tty = NULL; int noctty, retval; struct tty_driver *driver; int index; @@ -1892,14 +1892,17 @@ retry_open: return -ENODEV; } got_driver: - /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, inode, index); - if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); - tty_free_file(filp); - return PTR_ERR(tty); + if (!tty) { + /* check whether we're reopening an existing tty */ + tty = tty_driver_lookup_tty(driver, inode, index); + + if (IS_ERR(tty)) { + tty_unlock(); + mutex_unlock(&tty_mutex); + tty_driver_kref_put(driver); + tty_free_file(filp); + return PTR_ERR(tty); + } } if (tty) { -- cgit v1.2.3 From 32cffe537c7d426cfe33647fed403d409ea3f686 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 19 Oct 2011 11:36:20 +0200 Subject: tty/serial: atmel_serial: bootconsole removed from auto-enumerates Auto-enumerate mechanism conflicts with bootconsoles: remove the usage counter for this type of consoles. Signed-off-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index fc4081ead727..358bbb279d2b 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1674,7 +1674,6 @@ static int __init atmel_console_init(void) int id = atmel_default_console_device->id; struct atmel_uart_port *port = &atmel_ports[id]; - set_bit(id, &atmel_ports_in_use); port->backup_imr = 0; port->uart.line = id; -- cgit v1.2.3 From d208a3bf77f902283894f546b6b5383202cf7882 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 19 Oct 2011 11:52:01 -0700 Subject: TTY: serial_core: Fix crash if DCD drop during suspend This crash was showing up 100% of the time on Tegra CPUs when an agetty was running on the serial port and the console was not running on the serial port. The reason the Tegra saw it so reliably is that the Tegra CPU internally ties DTR to DCD/DSR. That means when we dropped DTR during suspend we would get always get an immediate DCD drop. The specific order of operations that were running: * uart_suspend_port() would be called to put the uart in suspend mode * we'd drop DTR (ops->set_mctrl(uport, 0)). * the DTR drop would be looped back in the CPU to be a DCD drop. * the DCD drop would look to the serial driver as a hangup * the hangup would call uart_shutdown() * ... suspend / resume happens ... * uart_resume_port() would be called and run the code in the (port->flags & ASYNC_SUSPENDED) block, which would startup the port (and enable tx again). * Since the UART would be available for tx, we'd immediately get an interrupt, eventually calling transmit_chars() * The transmit_chars() function would crash. The first crash would be a dereference of a NULL tty member, but since the port has been shutdown that was just a symptom. I have proposed a patch that would fix the Tegra CPUs here (see https://lkml.org/lkml/2011/10/11/444 - tty/serial: Prevent drop of DCD on suspend for Tegra UARTs). However, even with that fix it is still possible for systems that have an externally visible DCD line to see a crash if the DCD drops at just the right time during suspend: thus this patch is still useful. Signed-off-by: Doug Anderson Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index e562b1224466..9f72be28e6f2 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -243,6 +243,13 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) synchronize_irq(uport->irq); } + /* + * 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. + */ + clear_bit(ASYNCB_SUSPENDED, &port->flags); + /* * Free the transmit buffer page. */ -- cgit v1.2.3