diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/68328serial.c | 8 | ||||
-rw-r--r-- | drivers/serial/8250.c | 37 | ||||
-rw-r--r-- | drivers/serial/8250_pci.c | 31 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 88 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/amba-pl010.c | 15 | ||||
-rw-r--r-- | drivers/serial/atmel_serial.c | 22 | ||||
-rw-r--r-- | drivers/serial/bcm63xx_uart.c | 7 | ||||
-rw-r--r-- | drivers/serial/bfin_5xx.c | 22 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.c | 701 | ||||
-rw-r--r-- | drivers/serial/bfin_sport_uart.h | 38 | ||||
-rw-r--r-- | drivers/serial/icom.c | 5 | ||||
-rw-r--r-- | drivers/serial/imx.c | 8 | ||||
-rw-r--r-- | drivers/serial/ioc3_serial.c | 3 | ||||
-rw-r--r-- | drivers/serial/jsm/jsm_driver.c | 1 | ||||
-rw-r--r-- | drivers/serial/jsm/jsm_tty.c | 9 | ||||
-rw-r--r-- | drivers/serial/msm_serial.c | 6 | ||||
-rw-r--r-- | drivers/serial/s3c2412.c | 1 | ||||
-rw-r--r-- | drivers/serial/s5pv210.c | 154 | ||||
-rw-r--r-- | drivers/serial/samsung.c | 8 | ||||
-rw-r--r-- | drivers/serial/samsung.h | 19 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 616 | ||||
-rw-r--r-- | drivers/serial/sh-sci.h | 22 | ||||
-rw-r--r-- | drivers/serial/sunsab.c | 2 | ||||
-rw-r--r-- | drivers/serial/timbuart.c | 7 |
25 files changed, 1430 insertions, 401 deletions
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index d935b2d04f93..ae0251ef6f4e 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -153,8 +153,6 @@ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 }; -#define BAUD_TABLE_SIZE (sizeof(baud_table)/sizeof(baud_table[0])) - /* Sets or clears DTR/RTS on the requested line */ static inline void m68k_rtsdtr(struct m68k_serial *ss, int set) { @@ -1406,10 +1404,10 @@ static void m68328_set_baud(void) USTCNT = ustcnt & ~USTCNT_TXEN; again: - for (i = 0; i < sizeof(baud_table) / sizeof(baud_table[0]); i++) + for (i = 0; i < ARRAY_SIZE(baud_table); i++) if (baud_table[i] == m68328_console_baud) break; - if (i >= sizeof(baud_table) / sizeof(baud_table[0])) { + if (i >= ARRAY_SIZE(baud_table)) { m68328_console_baud = 9600; goto again; } @@ -1435,7 +1433,7 @@ int m68328_console_setup(struct console *cp, char *arg) if (arg) n = simple_strtoul(arg,NULL,0); - for (i = 0; i < BAUD_TABLE_SIZE; i++) + for (i = 0; i < ARRAY_SIZE(baud_table); i++) if (baud_table[i] == n) break; if (i < BAUD_TABLE_SIZE) { diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index a81ff7bc5fa1..c3db16b7afa1 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2408,6 +2408,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 +2643,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, @@ -2690,6 +2706,15 @@ static void __init serial8250_isa_init_ports(void) } } +static void +serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) +{ + up->port.type = type; + up->port.fifosize = uart_config[type].fifo_size; + up->capabilities = uart_config[type].flags; + up->tx_loadsz = uart_config[type].tx_loadsz; +} + static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) { @@ -2706,6 +2731,10 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) struct uart_8250_port *up = &serial8250_ports[i]; up->port.dev = dev; + + if (up->port.flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(up, up->port.type); + uart_add_one_port(drv, &up->port); } } @@ -3118,12 +3147,8 @@ int serial8250_register_port(struct uart_port *port) if (port->dev) uart->port.dev = port->dev; - if (port->flags & UPF_FIXED_TYPE) { - uart->port.type = port->type; - uart->port.fifosize = uart_config[port->type].fifo_size; - uart->capabilities = uart_config[port->type].flags; - uart->tx_loadsz = uart_config[port->type].tx_loadsz; - } + if (port->flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(uart, port->type); set_io_from_upio(&uart->port); /* Possibly override default I/O functions. */ diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index b28af13c45a1..01c012da4e26 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -760,7 +760,8 @@ static int pci_netmos_init(struct pci_dev *dev) /* subdevice 0x00PS means <P> parallel, <S> serial */ unsigned int num_serial = dev->subsystem_device & 0xf; - if (dev->device == PCI_DEVICE_ID_NETMOS_9901) + if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || + (dev->device == PCI_DEVICE_ID_NETMOS_9865)) return 0; if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) @@ -1479,6 +1480,7 @@ enum pci_board_num_t { pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, + pbn_b0_bt_4_115200, pbn_b0_bt_8_115200, pbn_b0_bt_1_460800, @@ -1703,6 +1705,12 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 8, }, + [pbn_b0_bt_4_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, [pbn_b0_bt_8_115200] = { .flags = FL_BASE0|FL_BASE_BARS, .num_ports = 8, @@ -3191,6 +3199,15 @@ static struct pci_device_id serial_pci_tbl[] = { 0x1208, 0x0004, 0, 0, pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, /* * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com */ @@ -3649,6 +3666,18 @@ static struct pci_device_id serial_pci_tbl[] = { 0, 0, pbn_b0_1_115200 }, /* + * Best Connectivity PCI Multi I/O cards + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004, + 0, 0, pbn_b0_bt_4_115200 }, + + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 888a0ce91c4b..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) @@ -1418,42 +1439,37 @@ config SERIAL_BFIN_SPORT To compile this driver as a module, choose M here: the module will be called bfin_sport_uart. -choice - prompt "Baud rate for Blackfin SPORT UART" - depends on SERIAL_BFIN_SPORT - default SERIAL_SPORT_BAUD_RATE_57600 - help - Choose a baud rate for the SPORT UART, other uart settings are - 8 bit, 1 stop bit, no parity, no flow control. - -config SERIAL_SPORT_BAUD_RATE_115200 - bool "115200" - -config SERIAL_SPORT_BAUD_RATE_57600 - bool "57600" +config SERIAL_BFIN_SPORT_CONSOLE + bool "Console on Blackfin sport emulated uart" + depends on SERIAL_BFIN_SPORT=y + select SERIAL_CORE_CONSOLE -config SERIAL_SPORT_BAUD_RATE_38400 - bool "38400" +config SERIAL_BFIN_SPORT0_UART + bool "Enable UART over SPORT0" + depends on SERIAL_BFIN_SPORT && !(BF542 || BF542M || BF544 || BF544M) + help + Enable UART over SPORT0 -config SERIAL_SPORT_BAUD_RATE_19200 - bool "19200" +config SERIAL_BFIN_SPORT1_UART + bool "Enable UART over SPORT1" + depends on SERIAL_BFIN_SPORT + help + Enable UART over SPORT1 -config SERIAL_SPORT_BAUD_RATE_9600 - bool "9600" -endchoice +config SERIAL_BFIN_SPORT2_UART + bool "Enable UART over SPORT2" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT2 -config SPORT_BAUD_RATE - int - depends on SERIAL_BFIN_SPORT - default 115200 if (SERIAL_SPORT_BAUD_RATE_115200) - default 57600 if (SERIAL_SPORT_BAUD_RATE_57600) - default 38400 if (SERIAL_SPORT_BAUD_RATE_38400) - default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) - default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) +config SERIAL_BFIN_SPORT3_UART + bool "Enable UART over SPORT3" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT3 config SERIAL_TIMBERDALE tristate "Support for timberdale UART" - depends on MFD_TIMBERDALE select SERIAL_CORE ---help--- Add support for UART controller on timberdale. 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..e4b3c2c88bb6 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -471,6 +471,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 +545,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/atmel_serial.c b/drivers/serial/atmel_serial.c index 9d948bccafaf..2c9bf9b68327 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -1213,6 +1213,24 @@ static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } +#ifdef CONFIG_CONSOLE_POLL +static int atmel_poll_get_char(struct uart_port *port) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY)) + cpu_relax(); + + return UART_GET_CHAR(port); +} + +static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + + UART_PUT_CHAR(port, ch); +} +#endif + static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -1232,6 +1250,10 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = atmel_poll_get_char, + .poll_put_char = atmel_poll_put_char, +#endif }; /* diff --git a/drivers/serial/bcm63xx_uart.c b/drivers/serial/bcm63xx_uart.c index 37ad0c449937..a1a0e55d0807 100644 --- a/drivers/serial/bcm63xx_uart.c +++ b/drivers/serial/bcm63xx_uart.c @@ -35,7 +35,7 @@ #include <bcm63xx_regs.h> #include <bcm63xx_io.h> -#define BCM63XX_NR_UARTS 1 +#define BCM63XX_NR_UARTS 2 static struct uart_port ports[BCM63XX_NR_UARTS]; @@ -784,7 +784,7 @@ static struct uart_driver bcm_uart_driver = { .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, - .nr = 1, + .nr = BCM63XX_NR_UARTS, .cons = BCM63XX_CONSOLE, }; @@ -826,11 +826,12 @@ static int __devinit bcm_uart_probe(struct platform_device *pdev) port->dev = &pdev->dev; port->fifosize = 16; port->uartclk = clk_get_rate(clk) / 2; + port->line = pdev->id; clk_put(clk); ret = uart_add_one_port(&bcm_uart_driver, port); if (ret) { - kfree(port); + ports[pdev->id].membase = 0; return ret; } platform_set_drvdata(pdev, port); diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 50abb7e557f4..fcf273e3f48c 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/io.h> #include <linux/init.h> #include <linux/console.h> #include <linux/sysrq.h> @@ -237,7 +238,8 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdb_connected && kgdboc_port_line == uart->port.line) + if (kgdb_connected && kgdboc_port_line == uart->port.line + && kgdboc_break_enabled) if (ch == 0x3) {/* Ctrl + C */ kgdb_breakpoint(); return; @@ -488,6 +490,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos; + dma_disable_irq(uart->tx_dma_channel); dma_disable_irq(uart->rx_dma_channel); spin_lock_bh(&uart->port.lock); @@ -521,6 +524,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) } spin_unlock_bh(&uart->port.lock); + dma_enable_irq(uart->tx_dma_channel); dma_enable_irq(uart->rx_dma_channel); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); @@ -746,15 +750,6 @@ static int bfin_serial_startup(struct uart_port *port) Status interrupt.\n"); } - if (uart->cts_pin >= 0) { - gpio_request(uart->cts_pin, DRIVER_NAME); - gpio_direction_output(uart->cts_pin, 1); - } - if (uart->rts_pin >= 0) { - gpio_request(uart->rts_pin, DRIVER_NAME); - gpio_direction_output(uart->rts_pin, 0); - } - /* CTS RTS PINs are negative assertive. */ UART_PUT_MCR(uart, ACTS); UART_SET_IER(uart, EDSSI); @@ -801,10 +796,6 @@ static void bfin_serial_shutdown(struct uart_port *port) gpio_free(uart->rts_pin); #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->cts_pin >= 0) - gpio_free(uart->cts_pin); - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); if (UART_GET_IER(uart) && EDSSI) free_irq(uart->status_irq, uart); #endif @@ -1409,8 +1400,7 @@ static int bfin_serial_remove(struct platform_device *dev) continue; uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); bfin_serial_ports[i].port.dev = NULL; -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) gpio_free(bfin_serial_ports[i].cts_pin); gpio_free(bfin_serial_ports[i].rts_pin); #endif diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 088bb35475f1..7c72888fbf94 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -1,27 +1,11 @@ /* - * File: linux/drivers/serial/bfin_sport_uart.c + * Blackfin On-Chip Sport Emulated UART Driver * - * Based on: drivers/serial/bfin_5xx.c by Aubrey Li. - * Author: Roy Huang <roy.huang@analog.com> + * Copyright 2006-2009 Analog Devices Inc. * - * Created: Nov 22, 2006 - * Copyright: (c) 2006-2007 Analog Devices Inc. - * Description: this driver enable SPORTs on Blackfin emulate UART. + * Enter bugs at http://blackfin.uclinux.org/ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ /* @@ -29,39 +13,18 @@ * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf * This application note describe how to implement a UART on a Sharc DSP, * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. */ -/* After reset, there is a prelude of low level pulse when transmit data first - * time. No addtional pulse in following transmit. - * According to document: - * The SPORTs are ready to start transmitting or receiving data no later than - * three serial clock cycles after they are enabled in the SPORTx_TCR1 or - * SPORTx_RCR1 register. No serial clock cycles are lost from this point on. - * The first internal frame sync will occur one frame sync delay after the - * SPORTs are ready. External frame syncs can occur as soon as the SPORT is - * ready. - */ +/* #define DEBUG */ -/* Thanks to Axel Alatalo <axel@rubico.se> for fixing sport rx bug. Sometimes - * sport receives data incorrectly. The following is Axel's words. - * As EE-191, sport rx samples 3 times of the UART baudrate and takes the - * middle smaple of every 3 samples as the data bit. For a 8-N-1 UART setting, - * 30 samples will be required for a byte. If transmitter sends a 1/3 bit short - * byte due to buadrate drift, then the 30th sample of a byte, this sample is - * also the third sample of the stop bit, will happens on the immediately - * following start bit which will be thrown away and missed. Thus since parts - * of the startbit will be missed and the receiver will begin to drift, the - * effect accumulates over time until synchronization is lost. - * If only require 2 samples of the stopbit (by sampling in total 29 samples), - * then a to short byte as in the case above will be tolerated. Then the 1/3 - * early startbit will trigger a framesync since the last read is complete - * after only 2/3 stopbit and framesync is active during the last 1/3 looking - * for a possible early startbit. */ - -//#define DEBUG +#define DRV_NAME "bfin-sport-uart" +#define DEVICE_NAME "ttySS" +#define pr_fmt(fmt) DRV_NAME ": " fmt #include <linux/module.h> #include <linux/ioport.h> +#include <linux/io.h> #include <linux/init.h> #include <linux/console.h> #include <linux/sysrq.h> @@ -75,23 +38,36 @@ #include "bfin_sport_uart.h" +#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART unsigned short bfin_uart_pin_req_sport0[] = {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; - +#endif +#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART unsigned short bfin_uart_pin_req_sport1[] = {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; - -#define DRV_NAME "bfin-sport-uart" +#endif +#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART +unsigned short bfin_uart_pin_req_sport2[] = + {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, \ + P_SPORT2_DRPRI, P_SPORT2_RSCLK, P_SPORT2_DRSEC, P_SPORT2_DTSEC, 0}; +#endif +#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART +unsigned short bfin_uart_pin_req_sport3[] = + {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, \ + P_SPORT3_DRPRI, P_SPORT3_RSCLK, P_SPORT3_DRSEC, P_SPORT3_DTSEC, 0}; +#endif struct sport_uart_port { struct uart_port port; - char *name; - - int tx_irq; - int rx_irq; int err_irq; + unsigned short csize; + unsigned short rxmask; + unsigned short txmask1; + unsigned short txmask2; + unsigned char stopb; +/* unsigned char parib; */ }; static void sport_uart_tx_chars(struct sport_uart_port *up); @@ -99,36 +75,42 @@ static void sport_stop_tx(struct uart_port *port); static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) { - pr_debug("%s value:%x\n", __func__, value); - /* Place a Start and Stop bit */ + pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value, + up->txmask1, up->txmask2); + + /* Place Start and Stop bits */ __asm__ __volatile__ ( - "R2 = b#01111111100;" - "R3 = b#10000000001;" - "%0 <<= 2;" - "%0 = %0 & R2;" - "%0 = %0 | R3;" - : "=d"(value) - : "d"(value) - : "ASTAT", "R2", "R3" + "%[val] <<= 1;" + "%[val] = %[val] & %[mask1];" + "%[val] = %[val] | %[mask2];" + : [val]"+d"(value) + : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2) + : "ASTAT" ); pr_debug("%s value:%x\n", __func__, value); SPORT_PUT_TX(up, value); } -static inline unsigned int rx_one_byte(struct sport_uart_port *up) +static inline unsigned char rx_one_byte(struct sport_uart_port *up) { - unsigned int value, extract; + unsigned int value; + unsigned char extract; u32 tmp_mask1, tmp_mask2, tmp_shift, tmp; - value = SPORT_GET_RX32(up); - pr_debug("%s value:%x\n", __func__, value); + if ((up->csize + up->stopb) > 7) + value = SPORT_GET_RX32(up); + else + value = SPORT_GET_RX(up); + + pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value, + up->csize, up->rxmask); - /* Extract 8 bits data */ + /* Extract data */ __asm__ __volatile__ ( "%[extr] = 0;" - "%[mask1] = 0x1801(Z);" - "%[mask2] = 0x0300(Z);" + "%[mask1] = %[rxmask];" + "%[mask2] = 0x0200(Z);" "%[shift] = 0;" "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];" ".Lloop_s:" @@ -138,9 +120,9 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) "%[mask1] = %[mask1] - %[mask2];" ".Lloop_e:" "%[shift] += 1;" - : [val]"=d"(value), [extr]"=d"(extract), [shift]"=d"(tmp_shift), [tmp]"=d"(tmp), - [mask1]"=d"(tmp_mask1), [mask2]"=d"(tmp_mask2) - : "d"(value), [lc]"a"(8) + : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp), + [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2) + : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize) : "ASTAT", "LB0", "LC0", "LT0" ); @@ -148,29 +130,28 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) return extract; } -static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) +static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate) { - int tclkdiv, tfsdiv, rclkdiv; + int tclkdiv, rclkdiv; + unsigned int sclk = get_sclk(); - /* Set TCR1 and TCR2 */ - SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK)); - SPORT_PUT_TCR2(up, 10); + /* Set TCR1 and TCR2, TFSR is not enabled for uart */ + SPORT_PUT_TCR1(up, (ITFS | TLSBIT | ITCLK)); + SPORT_PUT_TCR2(up, size + 1); pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); /* Set RCR1 and RCR2 */ SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); - SPORT_PUT_RCR2(up, 28); + SPORT_PUT_RCR2(up, (size + 1) * 2 - 1); pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); - tclkdiv = sclk/(2 * baud_rate) - 1; - tfsdiv = 12; - rclkdiv = sclk/(2 * baud_rate * 3) - 1; + tclkdiv = sclk / (2 * baud_rate) - 1; + rclkdiv = sclk / (2 * baud_rate * 2) - 1; SPORT_PUT_TCLKDIV(up, tclkdiv); - SPORT_PUT_TFSDIV(up, tfsdiv); SPORT_PUT_RCLKDIV(up, rclkdiv); SSYNC(); - pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, tfsdiv:%d, rclkdiv:%d\n", - __func__, sclk, baud_rate, tclkdiv, tfsdiv, rclkdiv); + pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n", + __func__, sclk, baud_rate, tclkdiv, rclkdiv); return 0; } @@ -181,23 +162,29 @@ static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) struct tty_struct *tty = up->port.state->port.tty; unsigned int ch; - do { + spin_lock(&up->port.lock); + + while (SPORT_GET_STAT(up) & RXNE) { ch = rx_one_byte(up); up->port.icount.rx++; - if (uart_handle_sysrq_char(&up->port, ch)) - ; - else + if (!uart_handle_sysrq_char(&up->port, ch)) tty_insert_flip_char(tty, ch, TTY_NORMAL); - } while (SPORT_GET_STAT(up) & RXNE); + } tty_flip_buffer_push(tty); + spin_unlock(&up->port.lock); + return IRQ_HANDLED; } static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) { - sport_uart_tx_chars(dev_id); + struct sport_uart_port *up = dev_id; + + spin_lock(&up->port.lock); + sport_uart_tx_chars(up); + spin_unlock(&up->port.lock); return IRQ_HANDLED; } @@ -208,6 +195,8 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) struct tty_struct *tty = up->port.state->port.tty; unsigned int stat = SPORT_GET_STAT(up); + spin_lock(&up->port.lock); + /* Overflow in RX FIFO */ if (stat & ROVF) { up->port.icount.overrun++; @@ -216,15 +205,16 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) } /* These should not happen */ if (stat & (TOVF | TUVF | RUVF)) { - printk(KERN_ERR "SPORT Error:%s %s %s\n", - (stat & TOVF)?"TX overflow":"", - (stat & TUVF)?"TX underflow":"", - (stat & RUVF)?"RX underflow":""); + pr_err("SPORT Error:%s %s %s\n", + (stat & TOVF) ? "TX overflow" : "", + (stat & TUVF) ? "TX underflow" : "", + (stat & RUVF) ? "RX underflow" : ""); SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); } SSYNC(); + spin_unlock(&up->port.lock); return IRQ_HANDLED; } @@ -232,60 +222,37 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) static int sport_startup(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - char buffer[20]; - int retval; + int ret; pr_debug("%s enter\n", __func__); - snprintf(buffer, 20, "%s rx", up->name); - retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); - return retval; + ret = request_irq(up->port.irq, sport_uart_rx_irq, 0, + "SPORT_UART_RX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT RX interrupt\n"); + return ret; } - snprintf(buffer, 20, "%s tx", up->name); - retval = request_irq(up->tx_irq, sport_uart_tx_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0, + "SPORT_UART_TX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT TX interrupt\n"); goto fail1; } - snprintf(buffer, 20, "%s err", up->name); - retval = request_irq(up->err_irq, sport_uart_err_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + ret = request_irq(up->err_irq, sport_uart_err_irq, 0, + "SPORT_UART_STATUS", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT status interrupt\n"); goto fail2; } - if (port->line) { - if (peripheral_request_list(bfin_uart_pin_req_sport1, DRV_NAME)) - goto fail3; - } else { - if (peripheral_request_list(bfin_uart_pin_req_sport0, DRV_NAME)) - goto fail3; - } - - sport_uart_setup(up, get_sclk(), port->uartclk); - - /* Enable receive interrupt */ - SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) | RSPEN)); - SSYNC(); - return 0; + fail2: + free_irq(up->port.irq+1, up); + fail1: + free_irq(up->port.irq, up); - -fail3: - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); - - free_irq(up->err_irq, up); -fail2: - free_irq(up->tx_irq, up); -fail1: - free_irq(up->rx_irq, up); - - return retval; - + return ret; } static void sport_uart_tx_chars(struct sport_uart_port *up) @@ -344,20 +311,17 @@ static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) static void sport_stop_tx(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - unsigned int stat; pr_debug("%s enter\n", __func__); - stat = SPORT_GET_STAT(up); - while(!(stat & TXHRE)) { - udelay(1); - stat = SPORT_GET_STAT(up); - } /* Although the hold register is empty, last byte is still in shift - * register and not sent out yet. If baud rate is lower than default, - * delay should be longer. For example, if the baud rate is 9600, - * the delay must be at least 2ms by experience */ - udelay(500); + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SSYNC(); @@ -370,6 +334,7 @@ static void sport_start_tx(struct uart_port *port) struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); + /* Write data into SPORT FIFO before enable SPROT to transmit */ sport_uart_tx_chars(up); @@ -403,37 +368,24 @@ static void sport_shutdown(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - pr_debug("%s enter\n", __func__); + dev_dbg(port->dev, "%s enter\n", __func__); /* Disable sport */ SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); SSYNC(); - if (port->line) { - peripheral_free_list(bfin_uart_pin_req_sport1); - } else { - peripheral_free_list(bfin_uart_pin_req_sport0); - } - - free_irq(up->rx_irq, up); - free_irq(up->tx_irq, up); + free_irq(up->port.irq, up); + free_irq(up->port.irq+1, up); free_irq(up->err_irq, up); } -static void sport_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); - uart_update_timeout(port, CS8 ,port->uartclk); -} - static const char *sport_type(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); - return up->name; + return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL; } static void sport_release_port(struct uart_port *port) @@ -461,6 +413,110 @@ static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static void sport_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned long flags; + int i; + + pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); + + switch (termios->c_cflag & CSIZE) { + case CS8: + up->csize = 8; + break; + case CS7: + up->csize = 7; + break; + case CS6: + up->csize = 6; + break; + case CS5: + up->csize = 5; + break; + default: + pr_warning("requested word length not supported\n"); + } + + if (termios->c_cflag & CSTOPB) { + up->stopb = 1; + } + if (termios->c_cflag & PARENB) { + pr_warning("PAREN bits is not supported yet\n"); + /* up->parib = 1; */ + } + + 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; + } + + /* RX extract mask */ + up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); + /* TX masks, 8 bit data and 1 bit stop for example: + * mask1 = b#0111111110 + * mask2 = b#1000000000 + */ + for (i = 0, up->txmask1 = 0; i < up->csize; i++) + up->txmask1 |= (1<<i); + up->txmask2 = (1<<i); + if (up->stopb) { + ++i; + up->txmask2 |= (1<<i); + } + up->txmask1 <<= 1; + up->txmask2 <<= 1; + /* uart baud rate */ + port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); + + spin_lock_irqsave(&up->port.lock, flags); + + /* Disable UART */ + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); + + sport_uart_setup(up, up->csize + up->stopb, port->uartclk); + + /* driver TX line high after config, one dummy data is + * necessary to stop sport after shift one byte + */ + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SSYNC(); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, port->uartclk); + + /* Enable sport rx */ + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN); + SSYNC(); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + struct uart_ops sport_uart_ops = { .tx_empty = sport_tx_empty, .set_mctrl = sport_set_mctrl, @@ -480,138 +536,319 @@ struct uart_ops sport_uart_ops = { .verify_port = sport_verify_port, }; -static struct sport_uart_port sport_uart_ports[] = { - { /* SPORT 0 */ - .name = "SPORT0", - .tx_irq = IRQ_SPORT0_TX, - .rx_irq = IRQ_SPORT0_RX, - .err_irq= IRQ_SPORT0_ERROR, - .port = { - .type = PORT_BFIN_SPORT, - .iotype = UPIO_MEM, - .membase = (void __iomem *)SPORT0_TCR1, - .mapbase = SPORT0_TCR1, - .irq = IRQ_SPORT0_RX, - .uartclk = CONFIG_SPORT_BAUD_RATE, - .fifosize = 8, - .ops = &sport_uart_ops, - .line = 0, - }, - }, { /* SPORT 1 */ - .name = "SPORT1", - .tx_irq = IRQ_SPORT1_TX, - .rx_irq = IRQ_SPORT1_RX, - .err_irq= IRQ_SPORT1_ERROR, - .port = { - .type = PORT_BFIN_SPORT, - .iotype = UPIO_MEM, - .membase = (void __iomem *)SPORT1_TCR1, - .mapbase = SPORT1_TCR1, - .irq = IRQ_SPORT1_RX, - .uartclk = CONFIG_SPORT_BAUD_RATE, - .fifosize = 8, - .ops = &sport_uart_ops, - .line = 1, - }, +#define BFIN_SPORT_UART_MAX_PORTS 4 + +static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +static int __init +sport_uart_console_setup(struct console *co, char *options) +{ + struct sport_uart_port *up; + int baud = 57600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* Check whether an invalid uart number has been specified */ + if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) + return -ENODEV; + + up = bfin_sport_uart_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static void sport_uart_console_putchar(struct uart_port *port, int ch) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + while (SPORT_GET_STAT(up) & TXF) + barrier(); + + tx_one_byte(up, ch); +} + +/* + * Interrupts are disabled on entering + */ +static void +sport_uart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sport_uart_port *up = bfin_sport_uart_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + if (SPORT_GET_TCR1(up) & TSPEN) + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + else { + /* dummy data to start sport */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + + /* Although the hold register is empty, last byte is still in shift + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + barrier(); + + /* Stop sport tx transfer */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SSYNC(); } + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver sport_uart_reg; + +static struct console sport_uart_console = { + .name = DEVICE_NAME, + .write = sport_uart_console_write, + .device = uart_console_device, + .setup = sport_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sport_uart_reg, }; +#define SPORT_UART_CONSOLE (&sport_uart_console) +#else +#define SPORT_UART_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */ + + static struct uart_driver sport_uart_reg = { .owner = THIS_MODULE, - .driver_name = "SPORT-UART", - .dev_name = "ttySS", + .driver_name = DRV_NAME, + .dev_name = DEVICE_NAME, .major = 204, .minor = 84, - .nr = ARRAY_SIZE(sport_uart_ports), - .cons = NULL, + .nr = BFIN_SPORT_UART_MAX_PORTS, + .cons = SPORT_UART_CONSOLE, }; -static int sport_uart_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM +static int sport_uart_suspend(struct device *dev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = dev_get_drvdata(dev); - pr_debug("%s enter\n", __func__); + dev_dbg(dev, "%s enter\n", __func__); if (sport) uart_suspend_port(&sport_uart_reg, &sport->port); return 0; } -static int sport_uart_resume(struct platform_device *dev) +static int sport_uart_resume(struct device *dev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = dev_get_drvdata(dev); - pr_debug("%s enter\n", __func__); + dev_dbg(dev, "%s enter\n", __func__); if (sport) uart_resume_port(&sport_uart_reg, &sport->port); return 0; } -static int sport_uart_probe(struct platform_device *dev) +static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { + .suspend = sport_uart_suspend, + .resume = sport_uart_resume, +}; +#endif + +static int __devinit sport_uart_probe(struct platform_device *pdev) { - pr_debug("%s enter\n", __func__); - sport_uart_ports[dev->id].port.dev = &dev->dev; - uart_add_one_port(&sport_uart_reg, &sport_uart_ports[dev->id].port); - platform_set_drvdata(dev, &sport_uart_ports[dev->id]); + struct resource *res; + struct sport_uart_port *sport; + int ret = 0; - return 0; + dev_dbg(&pdev->dev, "%s enter\n", __func__); + + if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) { + dev_err(&pdev->dev, "Wrong sport uart platform device id.\n"); + return -ENOENT; + } + + if (bfin_sport_uart_ports[pdev->id] == NULL) { + bfin_sport_uart_ports[pdev->id] = + kmalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + sport = bfin_sport_uart_ports[pdev->id]; + if (!sport) { + dev_err(&pdev->dev, + "Fail to kmalloc sport_uart_port\n"); + return -ENOMEM; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, + "Fail to request SPORT peripherals\n"); + goto out_error_free_mem; + } + + spin_lock_init(&sport->port.lock); + sport->port.fifosize = SPORT_TX_FIFO_SIZE, + sport->port.ops = &sport_uart_ops; + sport->port.line = pdev->id; + sport->port.iotype = UPIO_MEM; + sport->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; + } + + sport->port.membase = ioremap(res->start, + res->end - res->start); + if (!sport->port.membase) { + dev_err(&pdev->dev, "Cannot map sport IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + + sport->port.irq = platform_get_irq(pdev, 0); + if (sport->port.irq < 0) { + dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + sport->err_irq = platform_get_irq(pdev, 1); + if (sport->err_irq < 0) { + dev_err(&pdev->dev, "No sport status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + } + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + sport = bfin_sport_uart_ports[pdev->id]; + sport->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, sport); + ret = uart_add_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + } +#endif + if (!ret) + return 0; + + if (sport) { +out_error_unmap: + iounmap(sport->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } + + return ret; } -static int sport_uart_remove(struct platform_device *dev) +static int __devexit sport_uart_remove(struct platform_device *pdev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = platform_get_drvdata(pdev); - pr_debug("%s enter\n", __func__); - platform_set_drvdata(dev, NULL); + dev_dbg(&pdev->dev, "%s enter\n", __func__); + dev_set_drvdata(&pdev->dev, NULL); - if (sport) + if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); + iounmap(sport->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } return 0; } static struct platform_driver sport_uart_driver = { .probe = sport_uart_probe, - .remove = sport_uart_remove, - .suspend = sport_uart_suspend, - .resume = sport_uart_resume, + .remove = __devexit_p(sport_uart_remove), .driver = { .name = DRV_NAME, +#ifdef CONFIG_PM + .pm = &bfin_sport_uart_dev_pm_ops, +#endif }, }; +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +static __initdata struct early_platform_driver early_sport_uart_driver = { + .class_str = DRV_NAME, + .pdrv = &sport_uart_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init sport_uart_rs_console_init(void) +{ + early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); + + early_platform_driver_probe(DRV_NAME, BFIN_SPORT_UART_MAX_PORTS, 0); + + register_console(&sport_uart_console); + + return 0; +} +console_initcall(sport_uart_rs_console_init); +#endif + static int __init sport_uart_init(void) { int ret; - pr_debug("%s enter\n", __func__); + pr_info("Serial: Blackfin uart over sport driver\n"); + ret = uart_register_driver(&sport_uart_reg); - if (ret != 0) { - printk(KERN_ERR "Failed to register %s:%d\n", + if (ret) { + pr_err("failed to register %s:%d\n", sport_uart_reg.driver_name, ret); return ret; } ret = platform_driver_register(&sport_uart_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register sport uart driver:%d\n", ret); + if (ret) { + pr_err("failed to register sport uart driver:%d\n", ret); uart_unregister_driver(&sport_uart_reg); } - - pr_debug("%s exit\n", __func__); return ret; } +module_init(sport_uart_init); static void __exit sport_uart_exit(void) { - pr_debug("%s enter\n", __func__); platform_driver_unregister(&sport_uart_driver); uart_unregister_driver(&sport_uart_reg); } - -module_init(sport_uart_init); module_exit(sport_uart_exit); +MODULE_AUTHOR("Sonic Zhang, Roy Huang"); +MODULE_DESCRIPTION("Blackfin serial over SPORT driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index 671d41cc1a3f..abe03614e4df 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -1,29 +1,23 @@ /* - * File: linux/drivers/serial/bfin_sport_uart.h + * Blackfin On-Chip Sport Emulated UART Driver * - * Based on: include/asm-blackfin/mach-533/bfin_serial_5xx.h - * Author: Roy Huang <roy.huang>analog.com> + * Copyright 2006-2008 Analog Devices Inc. * - * Created: Nov 22, 2006 - * Copyright: (C) Analog Device Inc. - * Description: this driver enable SPORTs on Blackfin emulate UART. + * Enter bugs at http://blackfin.uclinux.org/ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ +/* + * This driver and the hardware supported are in term of EE-191 of ADI. + * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf + * This application note describe how to implement a UART on a Sharc DSP, + * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. + */ + +#ifndef _BFIN_SPORT_UART_H +#define _BFIN_SPORT_UART_H #define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ #define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ @@ -61,3 +55,7 @@ #define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) #define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) #define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) + +#define SPORT_TX_FIFO_SIZE 8 + +#endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 0028b6f89ce6..53a468227056 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -751,7 +751,6 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) trace(icom_port, "FID_STATUS", status); count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength); - count = tty_buffer_request_room(tty, count); trace(icom_port, "RCV_COUNT", count); trace(icom_port, "REAL_COUNT", count); @@ -1654,4 +1653,6 @@ MODULE_DESCRIPTION("IBM iSeries Serial IOA driver"); MODULE_SUPPORTED_DEVICE ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters"); MODULE_LICENSE("GPL"); - +MODULE_FIRMWARE("icom_call_setup.bin"); +MODULE_FIRMWARE("icom_res_dce.bin"); +MODULE_FIRMWARE("icom_asc.bin"); diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 60d665a17a88..e579d7a1807a 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -440,7 +440,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; } @@ -1279,7 +1279,7 @@ static int serial_imx_probe(struct platform_device *pdev) sport->use_irda = 1; #endif - if (pdata->init) { + if (pdata && pdata->init) { ret = pdata->init(pdev); if (ret) goto clkput; @@ -1292,7 +1292,7 @@ static int serial_imx_probe(struct platform_device *pdev) return 0; deinit: - if (pdata->exit) + if (pdata && pdata->exit) pdata->exit(pdev); clkput: clk_put(sport->clk); @@ -1321,7 +1321,7 @@ static int serial_imx_remove(struct platform_device *pdev) clk_disable(sport->clk); - if (pdata->exit) + if (pdata && pdata->exit) pdata->exit(pdev); iounmap(sport->port.membase); diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index 85dc0410ac1a..23ba6b40b3ac 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -1411,8 +1411,7 @@ static int receive_chars(struct uart_port *the_port) read_count = do_read(the_port, ch, MAX_CHARS); if (read_count > 0) { flip = 1; - read_room = tty_buffer_request_room(tty, read_count); - tty_insert_flip_string(tty, ch, read_room); + read_room = tty_insert_flip_string(tty, ch, read_count); the_port->icount.rx += read_count; } spin_unlock_irqrestore(&the_port->lock, pflags); diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 108c3e0471fd..12cb5e446a4f 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -179,6 +179,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device return 0; out_free_irq: + jsm_remove_uart_port(brd); free_irq(brd->irq, brd); out_iounmap: iounmap(brd->re_map_membase); diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index cd95e215550d..5673ca9dfdc8 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -432,7 +432,7 @@ int __devinit jsm_tty_init(struct jsm_board *brd) int jsm_uart_port_init(struct jsm_board *brd) { - int i; + int i, rc; unsigned int line; struct jsm_channel *ch; @@ -467,8 +467,11 @@ int jsm_uart_port_init(struct jsm_board *brd) } else set_bit(line, linemap); brd->channels[i]->uart_port.line = line; - if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port)) - printk(KERN_INFO "jsm: add device failed\n"); + rc = uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port); + if (rc){ + printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); + return rc; + } else printk(KERN_INFO "jsm: Port %d added\n", i); } diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index b05c5aa02cb4..ecdc0facf7ee 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c @@ -691,6 +691,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) struct msm_port *msm_port; struct resource *resource; struct uart_port *port; + int irq; if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) return -ENXIO; @@ -711,9 +712,10 @@ static int __init msm_serial_probe(struct platform_device *pdev) return -ENXIO; port->mapbase = resource->start; - port->irq = platform_get_irq(pdev, 0); - if (unlikely(port->irq < 0)) + irq = platform_get_irq(pdev, 0); + if (unlikely(irq < 0)) return -ENXIO; + port->irq = irq; platform_set_drvdata(pdev, 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/sh-sci.c b/drivers/serial/sh-sci.c index 42f3333c4ad0..980f39449ee5 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/timer.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 SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); + return sci_in(port, SCTFDR) & 0xff; } -static inline int scif_rxroom(struct uart_port *port) +static int scif_txroom(struct uart_port *port) +{ + return SCIF_TXROOM_MAX - scif_txfill(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) != 0; + return !(sci_in(port, SCxSR) & SCI_TDRE); } -static inline int sci_rxroom(struct uart_port *port) +static int sci_txroom(struct uart_port *port) +{ + return !sci_txfill(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) @@ -699,6 +780,10 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) if ((ssr_status & SCxSR_BRK(port)) && err_enabled) ret = sci_br_interrupt(irq, ptr); + WARN_ONCE(ret == IRQ_NONE, + "%s: %d IRQ %d, status %x, control %x\n", __func__, + irq, port->line, ssr_status, scr_status); + return ret; } @@ -800,7 +885,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 +899,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 +1206,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 +1235,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 +1391,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 +1447,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 +1467,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 +1562,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 +1585,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..fad67d33b0bd 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -30,7 +30,8 @@ */ # 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_SHMOBILE) # define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL @@ -228,7 +229,8 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SHMOBILE) # define SCIF_ORER 0x0200 # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) # define SCIF_RFDC_MASK 0x007f @@ -261,7 +263,8 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SHMOBILE) # 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 +359,7 @@ SCI_OUT(sci_size, sci_offset, value); \ } -#ifdef CONFIG_CPU_SH3 +#if defined(CONFIG_CPU_SH3) || defined(CONFIG_ARCH_SHMOBILE) #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 +369,8 @@ 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_SHMOBILE) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) #else @@ -401,7 +405,8 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SHMOBILE) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) @@ -413,7 +418,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 +588,8 @@ 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_SHMOBILE) #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/timbuart.c b/drivers/serial/timbuart.c index 34b31da01d09..7bf10264a6ac 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -421,7 +421,7 @@ static struct uart_driver timbuart_driver = { static int timbuart_probe(struct platform_device *dev) { - int err; + int err, irq; struct timbuart_port *uart; struct resource *iomem; @@ -453,11 +453,12 @@ static int timbuart_probe(struct platform_device *dev) uart->port.mapbase = iomem->start; uart->port.membase = NULL; - uart->port.irq = platform_get_irq(dev, 0); - if (uart->port.irq < 0) { + irq = platform_get_irq(dev, 0); + if (irq < 0) { err = -EINVAL; goto err_register; } + uart->port.irq = irq; tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); |