diff options
Diffstat (limited to 'drivers/serial')
39 files changed, 884 insertions, 122 deletions
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index ae0251ef6f4e..78ed24bb6a35 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -35,6 +35,7 @@ #include <linux/pm.h> #include <linux/bitops.h> #include <linux/delay.h> +#include <linux/gfp.h> #include <asm/io.h> #include <asm/irq.h> diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 7c4ebe6ee18b..2b1ea3d4c4f4 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -38,6 +38,7 @@ #include <linux/serial_8250.h> #include <linux/nmi.h> #include <linux/mutex.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> @@ -2408,6 +2409,21 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, } static void +serial8250_set_ldisc(struct uart_port *port) +{ + int line = port->line; + + if (line >= port->state->port.tty->driver->num) + return; + + if (port->state->port.tty->ldisc->ops->num == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial8250_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + +static void serial8250_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { @@ -2628,6 +2644,7 @@ static struct uart_ops serial8250_pops = { .startup = serial8250_startup, .shutdown = serial8250_shutdown, .set_termios = serial8250_set_termios, + .set_ldisc = serial8250_set_ldisc, .pm = serial8250_pm, .type = serial8250_type, .release_port = serial8250_release_port, diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c index 33149d982e82..d8c0ffbfa6e3 100644 --- a/drivers/serial/8250_gsc.c +++ b/drivers/serial/8250_gsc.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/serial_core.h> #include <linux/signal.h> -#include <linux/slab.h> #include <linux/types.h> #include <asm/hardware.h> diff --git a/drivers/serial/8250_hp300.c b/drivers/serial/8250_hp300.c index 0e1410f2c033..c13438c93012 100644 --- a/drivers/serial/8250_hp300.c +++ b/drivers/serial/8250_hp300.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/dio.h> #include <linux/console.h> +#include <linux/slab.h> #include <asm/io.h> #include "8250.h" diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index 24485cc62ff8..4822cb50cd0f 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -348,6 +348,8 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02E6", 0 }, /* Fujitsu Wacom 2FGT Tablet PC device */ { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in * disguise) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 746e07033dce..f55c49475a8c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -447,7 +447,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on ARM && PLAT_S3C + depends on ARM && PLAT_SAMSUNG select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -455,11 +455,18 @@ config SERIAL_SAMSUNG provide all of these ports, depending on how the serial port pins are configured. +config SERIAL_SAMSUNG_UARTS_4 + bool + depends on ARM && PLAT_SAMSUNG + default y if CPU_S3C2443 + help + Internal node for the common case of 4 Samsung compatible UARTs + config SERIAL_SAMSUNG_UARTS int - depends on ARM && PLAT_S3C + depends on ARM && PLAT_SAMSUNG default 2 if ARCH_S3C2400 - default 4 if ARCH_S5PC1XX || ARCH_S3C64XX || CPU_S3C2443 + default 4 if SERIAL_SAMSUNG_UARTS_4 default 3 help Select the number of available UART ports for the Samsung S3C @@ -526,20 +533,30 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410) + tristate "Samsung S3C6400/S3C6410/S5P6440 Seria port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440) + select SERIAL_SAMSUNG_UARTS_4 default y help - Serial port support for the Samsung S3C6400 and S3C6410 + Serial port support for the Samsung S3C6400, S3C6410 and S5P6440 SoCs config SERIAL_S5PC100 tristate "Samsung S5PC100 Serial port support" depends on SERIAL_SAMSUNG && CPU_S5PC100 + select SERIAL_SAMSUNG_UARTS_4 default y help Serial port support for the Samsung S5PC100 SoCs +config SERIAL_S5PV210 + tristate "Samsung S5PV210 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442) + select SERIAL_SAMSUNG_UARTS_4 if CPU_S5PV210 + default y + help + Serial port support for Samsung's S5P Family of SoC's + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI @@ -996,7 +1013,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on HAVE_CLK && (SUPERH || H8300) + depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE) select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS @@ -1009,6 +1026,10 @@ config SERIAL_SH_SCI_CONSOLE depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE +config SERIAL_SH_SCI_DMA + bool "DMA support" + depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL + config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 5548fe7df61d..6aa4723b74ee 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o +obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 429a8ae86933..b09a638d051f 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -47,6 +47,7 @@ #include <linux/amba/bus.h> #include <linux/amba/serial.h> #include <linux/clk.h> +#include <linux/slab.h> #include <asm/io.h> @@ -471,6 +472,20 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&uap->port.lock, flags); } +static void pl010_set_ldisc(struct uart_port *port) +{ + int line = port->line; + + if (line >= port->state->port.tty->driver->num) + return; + + if (port->state->port.tty->ldisc->ops->num == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + pl010_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + static const char *pl010_type(struct uart_port *port) { return port->type == PORT_AMBA ? "AMBA" : NULL; @@ -531,6 +546,7 @@ static struct uart_ops amba_pl010_pops = { .startup = pl010_startup, .shutdown = pl010_shutdown, .set_termios = pl010_set_termios, + .set_ldisc = pl010_set_ldisc, .type = pl010_type, .release_port = pl010_release_port, .request_port = pl010_request_port, diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index ce6c35333ff7..743ebf5f16da 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -47,6 +47,7 @@ #include <linux/amba/bus.h> #include <linux/amba/serial.h> #include <linux/clk.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/sizes.h> diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index fcf273e3f48c..96f7e7484fee 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/ioport.h> +#include <linux/gfp.h> #include <linux/io.h> #include <linux/init.h> #include <linux/console.h> diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 7c72888fbf94..c88f8ad3ff82 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -28,6 +28,7 @@ #include <linux/init.h> #include <linux/console.h> #include <linux/sysrq.h> +#include <linux/slab.h> #include <linux/platform_device.h> #include <linux/tty.h> #include <linux/tty_flip.h> diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index 1b94c56ec239..3fc1d66e32c6 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/tty.h> +#include <linux/gfp.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index a9802e76b5fa..814ac006393f 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -30,6 +30,7 @@ #include <linux/module.h> #include <linux/tty.h> #include <linux/ioport.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> @@ -61,7 +62,7 @@ void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, void __iomem *pram; unsigned long offset; struct resource res; - unsigned long len; + resource_size_t len; /* Don't remap parameter RAM if it has already been initialized * during console setup. @@ -74,7 +75,7 @@ void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, if (of_address_to_resource(np, 1, &res)) return NULL; - len = 1 + res.end - res.start; + len = resource_size(&res); pram = ioremap(res.start, len); if (!pram) return NULL; diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index d00fcf8e6c70..4315b23590bd 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -46,6 +46,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/rational.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> @@ -440,7 +441,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) temp = readl(sport->port.membase + USR2); if (temp & USR2_BRCD) { - writel(temp | USR2_BRCD, sport->port.membase + USR2); + writel(USR2_BRCD, sport->port.membase + USR2); if (uart_handle_break(&sport->port)) continue; } diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index 23ba6b40b3ac..f164ba4eba02 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -20,6 +20,7 @@ #include <linux/pci.h> #include <linux/serial_core.h> #include <linux/ioc3.h> +#include <linux/slab.h> /* * Interesting things about the ioc3 diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c index 836d9ab4f729..8ad28fc64926 100644 --- a/drivers/serial/ioc4_serial.c +++ b/drivers/serial/ioc4_serial.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/ioc4.h> #include <linux/serial_core.h> +#include <linux/slab.h> /* * interesting things about the ioc4 diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 12cb5e446a4f..eaf545014119 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -26,6 +26,7 @@ ***********************************************************************/ #include <linux/moduleparam.h> #include <linux/pci.h> +#include <linux/slab.h> #include "jsm.h" diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 5673ca9dfdc8..7a4a914ecff0 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -30,6 +30,7 @@ #include <linux/serial_reg.h> #include <linux/delay.h> /* For udelay */ #include <linux/pci.h> +#include <linux/slab.h> #include "jsm.h" diff --git a/drivers/serial/max3100.c b/drivers/serial/max3100.c index 3c30c56aa2e1..3351c3bd59e4 100644 --- a/drivers/serial/max3100.c +++ b/drivers/serial/max3100.c @@ -41,6 +41,7 @@ #define MAX_MAX3100 4 #include <linux/delay.h> +#include <linux/slab.h> #include <linux/device.h> #include <linux/serial_core.h> #include <linux/serial.h> diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index 7bb5fee639e3..b5aaef965f24 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -263,6 +263,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, } spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); @@ -379,6 +380,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) static void mcf_config_port(struct uart_port *port, int flags) { port->type = PORT_MCF; + port->fifosize = MCFUART_TXFIFOSIZE; /* Clear mask, so no surprise interrupts. */ writeb(0, port->membase + MCFUART_UIMR); @@ -424,7 +426,7 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /* * Define the basic serial functions we support. */ -static struct uart_ops mcf_uart_ops = { +static const struct uart_ops mcf_uart_ops = { .tx_empty = mcf_tx_empty, .get_mctrl = mcf_get_mctrl, .set_mctrl = mcf_set_mctrl, @@ -443,7 +445,7 @@ static struct uart_ops mcf_uart_ops = { .verify_port = mcf_verify_port, }; -static struct mcf_uart mcf_ports[3]; +static struct mcf_uart mcf_ports[4]; #define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3119fddaedb5..a176ab4bd65b 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -29,39 +29,6 @@ * kind, whether express or implied. */ -/* Platform device Usage : - * - * Since PSCs can have multiple function, the correct driver for each one - * is selected by calling mpc52xx_match_psc_function(...). The function - * handled by this driver is "uart". - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - * - * The idx field must be equal to the PSC index (e.g. 0 for PSC1, 1 for PSC2, - * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and - * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly - * fpr the console code : without this 1:1 mapping, at early boot time, when we - * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it - * will be mapped to. - */ - -/* OF Platform device Usage : - * - * This driver is only used for PSCs configured in uart mode. The device - * tree will have a node for each PSC with "mpc52xx-psc-uart" in the compatible - * list. - * - * By default, PSC devices are enumerated in the order they are found. However - * a particular PSC number can be forces by adding 'device_no = <port#>' - * to the device node. - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - */ - #undef DEBUG #include <linux/device.h> diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c index b5496c28e60b..55e113a0be03 100644 --- a/drivers/serial/mpsc.c +++ b/drivers/serial/mpsc.c @@ -70,6 +70,7 @@ #include <linux/dma-mapping.h> #include <linux/mv643xx.h> #include <linux/platform_device.h> +#include <linux/gfp.h> #include <asm/io.h> #include <asm/irq.h> diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c index 7571aaa138b0..9711e06a8374 100644 --- a/drivers/serial/mux.c +++ b/drivers/serial/mux.c @@ -22,7 +22,6 @@ #include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> -#include <linux/slab.h> #include <linux/delay.h> /* for udelay */ #include <linux/device.h> #include <asm/io.h> diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index cdf172eda2e3..4abfebdb0fcc 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -11,6 +11,7 @@ */ #include <linux/init.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> #include <linux/of_platform.h> diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index f020de1cdd50..700e10833bf9 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -54,7 +54,6 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/console.h> -#include <linux/slab.h> #include <linux/adb.h> #include <linux/pmu.h> #include <linux/bitops.h> @@ -753,8 +752,10 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) uap->curregs[R5] = new_reg; /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); return; + } write_zsreg(uap, R5, uap->curregs[R5]); } diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index 56ee082157aa..1102a39b44f5 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -44,6 +44,7 @@ #include <linux/serial_core.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/slab.h> struct uart_pxa_port { struct uart_port port; diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c index ce75e28e36ef..1700b1a2fb7e 100644 --- a/drivers/serial/s3c2412.c +++ b/drivers/serial/s3c2412.c @@ -102,6 +102,7 @@ static struct s3c24xx_uart_info s3c2412_uart_inf = { .name = "Samsung S3C2412 UART", .type = PORT_S3C2412, .fifosize = 64, + .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, .rx_fifofull = S3C2440_UFSTAT_RXFULL, diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c new file mode 100644 index 000000000000..8dc03837617b --- /dev/null +++ b/drivers/serial/s5pv210.c @@ -0,0 +1,154 @@ +/* linux/drivers/serial/s5pv210.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Based on drivers/serial/s3c6400.c + * + * Driver for Samsung S5PV210 SoC UARTs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/serial.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <plat/regs-serial.h> +#include "samsung.h" + +static int s5pv210_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "pclk") == 0) + ucon &= ~S5PV210_UCON_CLKMASK; + else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S5PV210_UCON_CLKMASK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s5pv210_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + switch (ucon & S5PV210_UCON_CLKMASK) { + case S5PV210_UCON_PCLK: + clk->name = "pclk"; + break; + case S5PV210_UCON_UCLK: + clk->name = "uclk1"; + break; + } + + return 0; +} + +static int s5pv210_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + ucon &= S5PV210_UCON_CLKMASK; + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +#define S5PV210_UART_DEFAULT_INFO(fifo_size) \ + .name = "Samsung S5PV210 UART0", \ + .type = PORT_S3C6400, \ + .fifosize = fifo_size, \ + .has_divslot = 1, \ + .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ + .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ + .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ + .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ + .get_clksrc = s5pv210_serial_getsource, \ + .set_clksrc = s5pv210_serial_setsource, \ + .reset_port = s5pv210_serial_resetport + +static struct s3c24xx_uart_info s5p_port_fifo256 = { + S5PV210_UART_DEFAULT_INFO(256), +}; + +static struct s3c24xx_uart_info s5p_port_fifo64 = { + S5PV210_UART_DEFAULT_INFO(64), +}; + +static struct s3c24xx_uart_info s5p_port_fifo16 = { + S5PV210_UART_DEFAULT_INFO(16), +}; + +static struct s3c24xx_uart_info *s5p_uart_inf[] = { + [0] = &s5p_port_fifo256, + [1] = &s5p_port_fifo64, + [2] = &s5p_port_fifo16, + [3] = &s5p_port_fifo16, +}; + +/* device management */ +static int s5p_serial_probe(struct platform_device *pdev) +{ + return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]); +} + +static struct platform_driver s5p_serial_drv = { + .probe = s5p_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s5pv210-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init s5pv210_serial_console_init(void) +{ + return s3c24xx_serial_initconsole(&s5p_serial_drv, s5p_uart_inf); +} + +console_initcall(s5pv210_serial_console_init); + +static int __init s5p_serial_init(void) +{ + return s3c24xx_serial_init(&s5p_serial_drv, *s5p_uart_inf); +} + +static void __exit s5p_serial_exit(void) +{ + platform_driver_unregister(&s5p_serial_drv); +} + +module_init(s5p_serial_init); +module_exit(s5p_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s5pv210-uart"); +MODULE_DESCRIPTION("Samsung S5PV210 UART Driver support"); +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 52e3df113ec0..a9d6c5626a0a 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -1271,7 +1271,7 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) unsigned long ufstat, utrstat; if (ufcon & S3C2410_UFCON_FIFOMODE) { - /* fifo mode - check ammount of data in fifo registers... */ + /* fifo mode - check amount of data in fifo registers... */ ufstat = rd_regl(port, S3C2410_UFSTAT); return (ufstat & info->tx_fifofull) ? 0 : 1; @@ -1374,7 +1374,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, * data. */ -static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) +static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) { struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; struct platform_device **platdev_ptr; @@ -1385,7 +1385,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) platdev_ptr = s3c24xx_uart_devs; for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { - s3c24xx_serial_init_port(ptr, info, *platdev_ptr); + s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); } return 0; @@ -1451,7 +1451,7 @@ static struct console s3c24xx_serial_console = { }; int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *info) + struct s3c24xx_uart_info **info) { struct platform_device *dev = s3c24xx_uart_devs[0]; diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index 1fb22343df42..0ac06a07d25f 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -75,19 +75,24 @@ extern int s3c24xx_serial_probe(struct platform_device *dev, extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); extern int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *uart); + struct s3c24xx_uart_info **uart); extern int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info); #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE -#define s3c24xx_console_init(__drv, __inf) \ -static int __init s3c_serial_console_init(void) \ -{ \ - return s3c24xx_serial_initconsole(__drv, __inf); \ -} \ - \ +#define s3c24xx_console_init(__drv, __inf) \ +static int __init s3c_serial_console_init(void) \ +{ \ + struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ + int i; \ + \ + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ + uinfo[i] = __inf; \ + return s3c24xx_serial_initconsole(__drv, uinfo); \ +} \ + \ console_initcall(s3c_serial_console_init) #else diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index e91db4b38012..8cfa5b12ea7a 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -105,6 +105,10 @@ struct serial_cfg_mem { * manfid 0x0160, 0x0104 * This card appears to have a 14.7456MHz clock. */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) { port->uartclk = 14745600; @@ -196,6 +200,11 @@ static const struct serial_quirk quirks[] = { .multi = -1, .setup = quirk_setup_brainboxes_0104, }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { .manfid = MANFID_IBM, .prodid = ~0, .multi = -1, @@ -745,6 +754,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c index 2e71bbc04dac..b1962025b1aa 100644 --- a/drivers/serial/serial_ks8695.c +++ b/drivers/serial/serial_ks8695.c @@ -650,6 +650,7 @@ static struct console ks8695_console = { static int __init ks8695_console_init(void) { + add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); register_console(&ks8695_console); return 0; } diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 42f3333c4ad0..8eb094c1f61b 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -48,6 +48,9 @@ #include <linux/ctype.h> #include <linux/err.h> #include <linux/list.h> +#include <linux/dmaengine.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> #ifdef CONFIG_SUPERH #include <asm/sh_bios.h> @@ -84,6 +87,27 @@ struct sci_port { struct clk *dclk; struct list_head node; + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct device *dma_dev; + enum sh_dmae_slave_chan_id slave_tx; + enum sh_dmae_slave_chan_id slave_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx[2]; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx[2]; + dma_cookie_t active_rx; + struct scatterlist sg_tx; + unsigned int sg_len_tx; + struct scatterlist sg_rx[2]; + size_t buf_len_rx; + struct sh_dmae_slave param_tx; + struct sh_dmae_slave param_rx; + struct work_struct work_tx; + struct work_struct work_rx; + struct timer_list rx_timer; +#endif }; struct sh_sci_priv { @@ -269,29 +293,44 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCTFDR) & 0xff; +} + +static int scif_txroom(struct uart_port *port) { - return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); + return SCIF_TXROOM_MAX - scif_txfill(port); } -static inline int scif_rxroom(struct uart_port *port) +static int scif_rxfill(struct uart_port *port) { return sci_in(port, SCRFDR) & 0xff; } #elif defined(CONFIG_CPU_SUBTYPE_SH7763) -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) { - if ((port->mapbase == 0xffe00000) || - (port->mapbase == 0xffe08000)) { + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) /* SCIF0/1*/ - return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); - } else { + return sci_in(port, SCTFDR) & 0xff; + else /* SCIF2 */ - return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); - } + return sci_in(port, SCFDR) >> 8; } -static inline int scif_rxroom(struct uart_port *port) +static int scif_txroom(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return SCIF_TXROOM_MAX - scif_txfill(port); + else + /* SCIF2 */ + return SCIF2_TXROOM_MAX - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) { if ((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) { @@ -303,23 +342,33 @@ static inline int scif_rxroom(struct uart_port *port) } } #else -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) >> 8; +} + +static int scif_txroom(struct uart_port *port) { - return SCIF_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); + return SCIF_TXROOM_MAX - scif_txfill(port); } -static inline int scif_rxroom(struct uart_port *port) +static int scif_rxfill(struct uart_port *port) { return sci_in(port, SCFDR) & SCIF_RFDC_MASK; } #endif -static inline int sci_txroom(struct uart_port *port) +static int sci_txfill(struct uart_port *port) +{ + return !(sci_in(port, SCxSR) & SCI_TDRE); +} + +static int sci_txroom(struct uart_port *port) { - return (sci_in(port, SCxSR) & SCI_TDRE) != 0; + return !sci_txfill(port); } -static inline int sci_rxroom(struct uart_port *port) +static int sci_rxfill(struct uart_port *port) { return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } @@ -406,9 +455,9 @@ static inline void sci_receive_chars(struct uart_port *port) while (1) { if (port->type == PORT_SCI) - count = sci_rxroom(port); + count = sci_rxfill(port); else - count = scif_rxroom(port); + count = scif_rxfill(port); /* Don't copy more bytes than there is room for in the buffer */ count = tty_buffer_request_room(tty, count); @@ -453,10 +502,10 @@ static inline void sci_receive_chars(struct uart_port *port) } /* Store data and status */ - if (status&SCxSR_FER(port)) { + if (status & SCxSR_FER(port)) { flag = TTY_FRAME; dev_notice(port->dev, "frame error\n"); - } else if (status&SCxSR_PER(port)) { + } else if (status & SCxSR_PER(port)) { flag = TTY_PARITY; dev_notice(port->dev, "parity error\n"); } else @@ -618,13 +667,39 @@ static inline int sci_handle_breaks(struct uart_port *port) return copied; } -static irqreturn_t sci_rx_interrupt(int irq, void *port) +static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + if (s->chan_rx) { + unsigned long tout; + u16 scr = sci_in(port, SCSCR); + u16 ssr = sci_in(port, SCxSR); + + /* Disable future Rx interrupts */ + sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); + /* Clear current interrupt */ + sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + /* Calculate delay for 1.5 DMA buffers */ + tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n", + tout * 1000 / HZ); + if (tout < 2) + tout = 2; + mod_timer(&s->rx_timer, jiffies + tout); + + return IRQ_HANDLED; + } +#endif + /* I think sci_receive_chars has to be called irrespective * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? */ - sci_receive_chars(port); + sci_receive_chars(ptr); return IRQ_HANDLED; } @@ -680,6 +755,7 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) { unsigned short ssr_status, scr_status, err_enabled; struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); irqreturn_t ret = IRQ_NONE; ssr_status = sci_in(port, SCxSR); @@ -687,10 +763,15 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); /* Tx Interrupt */ - if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE)) + if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) && + !s->chan_tx) ret = sci_tx_interrupt(irq, ptr); - /* Rx Interrupt */ - if ((ssr_status & SCxSR_RDxF(port)) && (scr_status & SCI_CTRL_FLAGS_RIE)) + /* + * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / + * DR flags + */ + if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && + (scr_status & SCI_CTRL_FLAGS_RIE)) ret = sci_rx_interrupt(irq, ptr); /* Error Interrupt */ if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) @@ -800,7 +881,9 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { unsigned short status = sci_in(port, SCxSR); - return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0; + unsigned short in_tx_fifo = scif_txfill(port); + + return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; } static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -812,16 +895,297 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) static unsigned int sci_get_mctrl(struct uart_port *port) { - /* This routine is used for geting signals of: DTR, DCD, DSR, RI, + /* This routine is used for getting signals of: DTR, DCD, DSR, RI, and CTS/RTS */ return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; } +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static void sci_dma_tx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + xmit->tail += s->sg_tx.length; + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += s->sg_tx.length; + + async_tx_ack(s->desc_tx); + s->cookie_tx = -EINVAL; + s->desc_tx = NULL; + + spin_unlock_irqrestore(&port->lock, flags); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_chars_pending(xmit)) + schedule_work(&s->work_tx); +} + +/* Locking: called with port lock held */ +static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, + size_t count) +{ + struct uart_port *port = &s->port; + int i, active, room; + + room = tty_buffer_request_room(tty, count); + + if (s->active_rx == s->cookie_rx[0]) { + active = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + active = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return 0; + } + + if (room < count) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + count - room); + if (!room) + return room; + + for (i = 0; i < room; i++) + tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], + TTY_NORMAL); + + port->icount.rx += room; + + return room; +} + +static void sci_dma_rx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct tty_struct *tty = port->state->port.tty; + unsigned long flags; + int count; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + count = sci_dma_rx_push(s, tty, s->buf_len_rx); + + mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); + + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + schedule_work(&s->work_rx); +} + +static void sci_start_rx(struct uart_port *port); +static void sci_start_tx(struct uart_port *port); + +static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + dma_release_channel(chan); + dma_free_coherent(port->dev, s->buf_len_rx * 2, + sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + if (enable_pio) + sci_start_rx(port); +} + +static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + + s->chan_tx = NULL; + s->cookie_tx = -EINVAL; + dma_release_channel(chan); + if (enable_pio) + sci_start_tx(port); +} + +static void sci_submit_rx(struct sci_port *s) +{ + struct dma_chan *chan = s->chan_rx; + int i; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + struct dma_async_tx_descriptor *desc; + + desc = chan->device->device_prep_slave_sg(chan, + sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); + + if (desc) { + s->desc_rx[i] = desc; + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = desc->tx_submit(desc); + } + + if (!desc || s->cookie_rx[i] < 0) { + if (i) { + async_tx_ack(s->desc_rx[0]); + s->cookie_rx[0] = -EINVAL; + } + if (desc) { + async_tx_ack(desc); + s->cookie_rx[i] = -EINVAL; + } + dev_warn(s->port.dev, + "failed to re-start DMA, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + } + + s->active_rx = s->cookie_rx[0]; + + dma_async_issue_pending(chan); +} + +static void work_fn_rx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_rx); + struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; + int new; + + if (s->active_rx == s->cookie_rx[0]) { + new = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + new = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return; + } + desc = s->desc_rx[new]; + + if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != + DMA_SUCCESS) { + /* Handle incomplete DMA receive */ + struct tty_struct *tty = port->state->port.tty; + struct dma_chan *chan = s->chan_rx; + struct sh_desc *sh_desc = container_of(desc, struct sh_desc, + async_tx); + unsigned long flags; + int count; + + chan->device->device_terminate_all(chan); + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + sh_desc->partial, sh_desc->cookie); + + spin_lock_irqsave(&port->lock, flags); + count = sci_dma_rx_push(s, tty, sh_desc->partial); + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + sci_submit_rx(s); + + return; + } + + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__, + s->cookie_rx[new], new); + + s->active_rx = s->cookie_rx[!new]; +} + +static void work_fn_tx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_tx); + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &s->sg_tx; + + /* + * DMA is idle now. + * Port xmit buffer is already mapped, and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, we have to + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ + spin_lock_irq(&port->lock); + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg->dma_address = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg->length = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); + sg->dma_length = sg->length; + spin_unlock_irq(&port->lock); + + BUG_ON(!sg->length); + + desc = chan->device->device_prep_slave_sg(chan, + sg, s->sg_len_tx, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + spin_lock_irq(&port->lock); + s->desc_tx = desc; + desc->callback = sci_dma_tx_complete; + desc->callback_param = s; + spin_unlock_irq(&port->lock); + s->cookie_tx = desc->tx_submit(desc); + if (s->cookie_tx < 0) { + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, + xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + + dma_async_issue_pending(chan); +} +#endif + static void sci_start_tx(struct uart_port *port) { unsigned short ctrl; +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); + + if (s->chan_tx) { + if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) + schedule_work(&s->work_tx); + + return; + } +#endif + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); ctrl |= SCI_CTRL_FLAGS_TIE; @@ -838,13 +1202,12 @@ static void sci_stop_tx(struct uart_port *port) sci_out(port, SCSCR, ctrl); } -static void sci_start_rx(struct uart_port *port, unsigned int tty_start) +static void sci_start_rx(struct uart_port *port) { - unsigned short ctrl; + unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; + ctrl |= sci_in(port, SCSCR); sci_out(port, SCSCR, ctrl); } @@ -868,16 +1231,154 @@ static void sci_break_ctl(struct uart_port *port, int break_state) /* Nothing here yet .. */ } +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static bool filter(struct dma_chan *chan, void *slave) +{ + struct sh_dmae_slave *param = slave; + + dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, + param->slave_id); + + if (param->dma_dev == chan->device->dev) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void rx_timer_fn(unsigned long arg) +{ + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + + u16 scr = sci_in(port, SCSCR); + sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); + dev_dbg(port->dev, "DMA Rx timed out\n"); + schedule_work(&s->work_rx); +} + +static void sci_request_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sh_dmae_slave *param; + struct dma_chan *chan; + dma_cap_mask_t mask; + int nent; + + dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, + port->line, s->dma_dev); + + if (!s->dma_dev) + return; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + param = &s->param_tx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ + param->slave_id = s->slave_tx; + param->dma_dev = s->dma_dev; + + s->cookie_tx = -EINVAL; + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); + if (chan) { + s->chan_tx = chan; + sg_init_table(&s->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) + sci_tx_dma_release(s, false); + else + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&s->sg_tx), + port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + + s->sg_len_tx = nent; + + INIT_WORK(&s->work_tx, work_fn_tx); + } + + param = &s->param_rx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ + param->slave_id = s->slave_rx; + param->dma_dev = s->dma_dev; + + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); + if (chan) { + dma_addr_t dma[2]; + void *buf[2]; + int i; + + s->chan_rx = chan; + + s->buf_len_rx = 2 * max(16, (int)port->fifosize); + buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, + &dma[0], GFP_KERNEL); + + if (!buf[0]) { + dev_warn(port->dev, + "failed to allocate dma buffer, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + + buf[1] = buf[0] + s->buf_len_rx; + dma[1] = dma[0] + s->buf_len_rx; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + + sg_init_table(sg, 1); + sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, + (int)buf[i] & ~PAGE_MASK); + sg->dma_address = dma[i]; + sg->dma_length = sg->length; + } + + INIT_WORK(&s->work_rx, work_fn_rx); + setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); + + sci_submit_rx(s); + } +} + +static void sci_free_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (!s->dma_dev) + return; + + if (s->chan_tx) + sci_tx_dma_release(s, false); + if (s->chan_rx) + sci_rx_dma_release(s, false); +} +#endif + static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + if (s->enable) s->enable(port); sci_request_irq(s); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_request_dma(port); +#endif sci_start_tx(port); - sci_start_rx(port, 1); + sci_start_rx(port); return 0; } @@ -886,8 +1387,13 @@ static void sci_shutdown(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + sci_stop_rx(port); sci_stop_tx(port); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_free_dma(port); +#endif sci_free_irq(s); if (s->disable) @@ -937,6 +1443,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSMR, smr_val); + dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, + SCSCR_INIT(port)); + if (t > 0) { if (t >= 256) { sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); @@ -954,7 +1463,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, SCSCR_INIT(port)); if ((termios->c_cflag & CREAD) != 0) - sci_start_rx(port, 0); + sci_start_rx(port); } static const char *sci_type(struct uart_port *port) @@ -1049,19 +1558,21 @@ static void __devinit sci_init_single(struct platform_device *dev, unsigned int index, struct plat_sci_port *p) { - sci_port->port.ops = &sci_uart_ops; - sci_port->port.iotype = UPIO_MEM; - sci_port->port.line = index; + struct uart_port *port = &sci_port->port; + + port->ops = &sci_uart_ops; + port->iotype = UPIO_MEM; + port->line = index; switch (p->type) { case PORT_SCIFA: - sci_port->port.fifosize = 64; + port->fifosize = 64; break; case PORT_SCIF: - sci_port->port.fifosize = 16; + port->fifosize = 16; break; default: - sci_port->port.fifosize = 1; + port->fifosize = 1; break; } @@ -1070,19 +1581,28 @@ static void __devinit sci_init_single(struct platform_device *dev, sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); sci_port->enable = sci_clk_enable; sci_port->disable = sci_clk_disable; - sci_port->port.dev = &dev->dev; + port->dev = &dev->dev; } sci_port->break_timer.data = (unsigned long)sci_port; sci_port->break_timer.function = sci_break_timer; init_timer(&sci_port->break_timer); - sci_port->port.mapbase = p->mapbase; - sci_port->port.membase = p->membase; + port->mapbase = p->mapbase; + port->membase = p->membase; - sci_port->port.irq = p->irqs[SCIx_TXI_IRQ]; - sci_port->port.flags = p->flags; - sci_port->type = sci_port->port.type = p->type; + port->irq = p->irqs[SCIx_TXI_IRQ]; + port->flags = p->flags; + sci_port->type = port->type = p->type; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_port->dma_dev = p->dma_dev; + sci_port->slave_tx = p->dma_slave_tx; + sci_port->slave_rx = p->dma_slave_rx; + + dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, + p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); +#endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); } diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index f7d2589926d2..f70c49f915fa 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -30,7 +30,10 @@ */ # define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL @@ -93,7 +96,9 @@ # define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ #elif defined(CONFIG_CPU_SUBTYPE_SH7724) # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ # define SCIF_ORER 0x0001 /* overrun error bit */ @@ -196,6 +201,8 @@ defined(CONFIG_CPU_SUBTYPE_SH7786) || \ defined(CONFIG_CPU_SUBTYPE_SHX3) #define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7724) +#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) #else #define SCI_CTRL_FLAGS_REIE 0 #endif @@ -228,7 +235,10 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define SCIF_ORER 0x0200 # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) # define SCIF_RFDC_MASK 0x007f @@ -261,7 +271,10 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) # define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) # define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) @@ -356,7 +369,10 @@ SCI_OUT(sci_size, sci_offset, value); \ } -#ifdef CONFIG_CPU_SH3 +#if defined(CONFIG_CPU_SH3) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) #if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ @@ -366,7 +382,10 @@ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) #else @@ -401,7 +420,10 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) @@ -413,7 +435,7 @@ SCIF_FNS(SCFCR, 0x18, 16) SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) -SCIF_FNS(SCLSR, 0x24, 16) +SCIF_FNS(SCLSR, 0x00, 0) #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ defined(CONFIG_CPU_SUBTYPE_SH7724) SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) @@ -583,7 +605,10 @@ static inline int sci_rxd_in(struct uart_port *port) #define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) #define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ defined(CONFIG_CPU_SUBTYPE_SH7724) diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index d514e28d0755..d2e0321049e2 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -474,7 +474,7 @@ static void sunsab_stop_rx(struct uart_port *port) { struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - up->interrupt_mask0 |= SAB82532_ISR0_TCD; + up->interrupt_mask0 |= SAB82532_IMR0_TCD; writeb(up->interrupt_mask1, &up->regs->w.imr0); } diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 170d3d68c8f0..01f7731e59b8 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -29,6 +29,7 @@ #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> +#include <linux/slab.h> #ifdef CONFIG_SERIO #include <linux/serio.h> #endif @@ -1453,8 +1454,10 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) { err = sunsu_kbd_ms_init(up); if (err) { + of_iounmap(&op->resource[0], + up->port.membase, up->reg_size); kfree(up); - goto out_unmap; + return err; } dev_set_drvdata(&op->dev, up); diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 7bf10264a6ac..786ba85c170b 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/ioport.h> +#include <linux/slab.h> #include "timbuart.h" diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index ab2ab3c81834..f0a6c61b17f7 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -19,7 +19,7 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <asm/io.h> -#if defined(CONFIG_OF) +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_platform.h> @@ -581,7 +581,7 @@ static struct platform_driver ulite_platform_driver = { /* --------------------------------------------------------------------- * OF bus bindings */ -#if defined(CONFIG_OF) +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) static int __devinit ulite_of_probe(struct of_device *op, const struct of_device_id *match) { @@ -631,11 +631,11 @@ static inline void __exit ulite_of_unregister(void) { of_unregister_platform_driver(&ulite_of_driver); } -#else /* CONFIG_OF */ -/* CONFIG_OF not enabled; do nothing helpers */ +#else /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ +/* Appropriate config not enabled; do nothing helpers */ static inline int __init ulite_of_register(void) { return 0; } static inline void __exit ulite_of_unregister(void) { } -#endif /* CONFIG_OF */ +#endif /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ /* --------------------------------------------------------------------- * Module setup/teardown diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c index 465f2fae1025..074904912f64 100644 --- a/drivers/serial/ucc_uart.c +++ b/drivers/serial/ucc_uart.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/serial.h> +#include <linux/slab.h> #include <linux/serial_core.h> #include <linux/io.h> #include <linux/of_platform.h> |