diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-03-08 20:21:04 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-03-08 20:21:04 +0000 |
commit | 988addf82e4c03739375279de73929580a2d4a6a (patch) | |
tree | 989ae1cd4e264bbad80c65f04480486246e7b9f3 /drivers/serial | |
parent | 004c1c7096659d352b83047a7593e91d8a30e3c5 (diff) | |
parent | 25cf84cf377c0aae5dbcf937ea89bc7893db5176 (diff) | |
download | linux-988addf82e4c03739375279de73929580a2d4a6a.tar.bz2 |
Merge branch 'origin' into devel-stable
Conflicts:
arch/arm/mach-mx2/devices.c
arch/arm/mach-mx2/devices.h
sound/soc/pxa/pxa-ssp.c
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/68328serial.c | 8 | ||||
-rw-r--r-- | drivers/serial/8250.c | 36 | ||||
-rw-r--r-- | drivers/serial/8250_pci.c | 31 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 69 | ||||
-rw-r--r-- | drivers/serial/amba-pl011.c | 19 | ||||
-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 | 6 | ||||
-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/mpc52xx_uart.c | 251 | ||||
-rw-r--r-- | drivers/serial/msm_serial.c | 6 | ||||
-rw-r--r-- | drivers/serial/pmac_zilog.c | 246 | ||||
-rw-r--r-- | drivers/serial/pmac_zilog.h | 34 | ||||
-rw-r--r-- | drivers/serial/serial_cs.c | 7 | ||||
-rw-r--r-- | drivers/serial/sh-sci.c | 616 | ||||
-rw-r--r-- | drivers/serial/timbuart.c | 7 |
22 files changed, 1664 insertions, 480 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 e9b15c3746fa..7c4ebe6ee18b 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1217,12 +1217,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) } #endif -#ifdef CONFIG_SERIAL_8250_AU1X00 - /* if access method is AU, it is a 16550 with a quirk */ - if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) - up->bugs |= UART_BUG_NOMSR; -#endif - serial_outp(up, UART_LCR, save_lcr); if (up->capabilities != uart_config[up->port.type].flags) { @@ -2428,7 +2422,7 @@ serial8250_pm(struct uart_port *port, unsigned int state, static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) - return 0x100000; + return 0x1000; #ifdef CONFIG_ARCH_OMAP if (is_omap_port(pt)) return 0x16 << pt->port.regshift; @@ -2585,6 +2579,13 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); + +#ifdef CONFIG_SERIAL_8250_AU1X00 + /* if access method is AU, it is a 16550 with a quirk */ + if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) + up->bugs |= UART_BUG_NOMSR; +#endif + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); @@ -2689,6 +2690,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) { @@ -2705,6 +2715,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); } } @@ -3117,12 +3131,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 5351848cfff7..f55c49475a8c 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1026,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) @@ -1103,12 +1107,12 @@ config SERIAL_68360 default y config SERIAL_PMACZILOG - tristate "PowerMac z85c30 ESCC support" - depends on PPC_OF && PPC_PMAC + tristate "Mac or PowerMac z85c30 ESCC support" + depends on (M68K && MAC) || (PPC_OF && PPC_PMAC) select SERIAL_CORE help This driver supports the Zilog z85C30 serial ports found on - PowerMac machines. + (Power)Mac machines. Say Y or M if you want to be able to these serial ports. config SERIAL_PMACZILOG_TTYS @@ -1133,16 +1137,16 @@ config SERIAL_PMACZILOG_TTYS unable to use the 8250 module for PCMCIA or other 16C550-style UARTs. - Say N unless you need the z85c30 ports on your powermac + Say N unless you need the z85c30 ports on your (Power)Mac to appear as /dev/ttySn. config SERIAL_PMACZILOG_CONSOLE - bool "Console on PowerMac z85c30 serial port" + bool "Console on Mac or PowerMac z85c30 serial port" depends on SERIAL_PMACZILOG=y select SERIAL_CORE_CONSOLE help If you would like to be able to use the z85c30 serial port - on your PowerMac as the console, you can do so by answering + on your (Power)Mac as the console, you can do so by answering Y to this option. config SERIAL_LH7A40X @@ -1435,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/amba-pl011.c b/drivers/serial/amba-pl011.c index ef7adc8135dd..ce6c35333ff7 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -71,6 +71,7 @@ struct uart_amba_port { unsigned int im; /* interrupt mask */ unsigned int old_status; unsigned int ifls; /* vendor-specific */ + bool autorts; }; /* There is by now at least one vendor with differing details, so handle it */ @@ -308,6 +309,11 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1); TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2); TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE); + + if (uap->autorts) { + /* We need to disable auto-RTS if we want to turn RTS off */ + TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN); + } #undef TIOCMBIT writew(cr, uap->port.membase + UART011_CR); @@ -437,6 +443,7 @@ static void pl011_shutdown(struct uart_port *port) /* * disable the port */ + uap->autorts = false; writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); /* @@ -456,6 +463,7 @@ static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot; @@ -532,6 +540,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr = readw(port->membase + UART011_CR); writew(0, port->membase + UART011_CR); + if (termios->c_cflag & CRTSCTS) { + if (old_cr & UART011_CR_RTS) + old_cr |= UART011_CR_RTSEN; + + old_cr |= UART011_CR_CTSEN; + uap->autorts = true; + } else { + old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN); + uap->autorts = false; + } + /* Set baud rate */ writew(quot & 0x3f, port->membase + UART011_FBRD); writew(quot >> 6, port->membase + UART011_IBRD); 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 b801f5ab2b7b..e579d7a1807a 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -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/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 7ce9e9f567a3..3119fddaedb5 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -74,6 +74,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/clk.h> #include <asm/mpc52xx.h> #include <asm/mpc52xx_psc.h> @@ -113,6 +114,7 @@ static void mpc52xx_uart_of_enumerate(void); /* Forward declaration of the interruption handling routine */ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); +static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); /* Simple macro to test if a port is console or not. This one is taken @@ -145,6 +147,11 @@ struct psc_ops { void (*cw_disable_ints)(struct uart_port *port); void (*cw_restore_ints)(struct uart_port *port); unsigned long (*getuartclk)(void *p); + int (*clock)(struct uart_port *port, int enable); + int (*fifoc_init)(void); + void (*fifoc_uninit)(void); + void (*get_irq)(struct uart_port *, struct device_node *); + irqreturn_t (*handle_irq)(struct uart_port *port); }; #ifdef CONFIG_PPC_MPC52xx @@ -256,6 +263,18 @@ static unsigned long mpc52xx_getuartclk(void *p) return mpc5xxx_get_bus_frequency(p) / 2; } +static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_DISABLED; + port->irq = irq_of_parse_and_map(np, 0); +} + +/* 52xx specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port) +{ + return mpc5xxx_uart_process_int(port); +} + static struct psc_ops mpc52xx_psc_ops = { .fifo_init = mpc52xx_psc_fifo_init, .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, @@ -273,14 +292,32 @@ static struct psc_ops mpc52xx_psc_ops = { .cw_disable_ints = mpc52xx_psc_cw_disable_ints, .cw_restore_ints = mpc52xx_psc_cw_restore_ints, .getuartclk = mpc52xx_getuartclk, + .get_irq = mpc52xx_psc_get_irq, + .handle_irq = mpc52xx_psc_handle_irq, }; #endif /* CONFIG_MPC52xx */ #ifdef CONFIG_PPC_MPC512x #define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) + +/* PSC FIFO Controller for mpc512x */ +struct psc_fifoc { + u32 fifoc_cmd; + u32 fifoc_int; + u32 fifoc_dma; + u32 fifoc_axe; + u32 fifoc_debug; +}; + +static struct psc_fifoc __iomem *psc_fifoc; +static unsigned int psc_fifoc_irq; + static void mpc512x_psc_fifo_init(struct uart_port *port) { + /* /32 prescaler */ + out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); out_be32(&FIFO_512x(port)->txalarm, 1); @@ -393,6 +430,160 @@ static unsigned long mpc512x_getuartclk(void *p) return mpc5xxx_get_bus_frequency(p); } +#define DEFAULT_FIFO_SIZE 16 + +static unsigned int __init get_fifo_size(struct device_node *np, + char *fifo_name) +{ + const unsigned int *fp; + + fp = of_get_property(np, fifo_name, NULL); + if (fp) + return *fp; + + pr_warning("no %s property in %s node, defaulting to %d\n", + fifo_name, np->full_name, DEFAULT_FIFO_SIZE); + + return DEFAULT_FIFO_SIZE; +} + +#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \ + ((u32)(_base) + sizeof(struct mpc52xx_psc))) + +/* Init PSC FIFO Controller */ +static int __init mpc512x_psc_fifoc_init(void) +{ + struct device_node *np; + void __iomem *psc; + unsigned int tx_fifo_size; + unsigned int rx_fifo_size; + int fifobase = 0; /* current fifo address in 32 bit words */ + + np = of_find_compatible_node(NULL, NULL, + "fsl,mpc5121-psc-fifo"); + if (!np) { + pr_err("%s: Can't find FIFOC node\n", __func__); + return -ENODEV; + } + + psc_fifoc = of_iomap(np, 0); + if (!psc_fifoc) { + pr_err("%s: Can't map FIFOC\n", __func__); + return -ENODEV; + } + + psc_fifoc_irq = irq_of_parse_and_map(np, 0); + of_node_put(np); + if (psc_fifoc_irq == NO_IRQ) { + pr_err("%s: Can't get FIFOC irq\n", __func__); + iounmap(psc_fifoc); + return -ENODEV; + } + + for_each_compatible_node(np, NULL, "fsl,mpc5121-psc-uart") { + tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size"); + rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size"); + + /* size in register is in 4 byte units */ + tx_fifo_size /= 4; + rx_fifo_size /= 4; + if (!tx_fifo_size) + tx_fifo_size = 1; + if (!rx_fifo_size) + rx_fifo_size = 1; + + psc = of_iomap(np, 0); + if (!psc) { + pr_err("%s: Can't map %s device\n", + __func__, np->full_name); + continue; + } + + /* FIFO space is 4KiB, check if requested size is available */ + if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) { + pr_err("%s: no fifo space available for %s\n", + __func__, np->full_name); + iounmap(psc); + /* + * chances are that another device requests less + * fifo space, so we continue. + */ + continue; + } + /* set tx and rx fifo size registers */ + out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size); + fifobase += tx_fifo_size; + out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size); + fifobase += rx_fifo_size; + + /* reset and enable the slices */ + out_be32(&FIFOC(psc)->txcmd, 0x80); + out_be32(&FIFOC(psc)->txcmd, 0x01); + out_be32(&FIFOC(psc)->rxcmd, 0x80); + out_be32(&FIFOC(psc)->rxcmd, 0x01); + + iounmap(psc); + } + + return 0; +} + +static void __exit mpc512x_psc_fifoc_uninit(void) +{ + iounmap(psc_fifoc); +} + +/* 512x specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) +{ + unsigned long fifoc_int; + int psc_num; + + /* Read pending PSC FIFOC interrupts */ + fifoc_int = in_be32(&psc_fifoc->fifoc_int); + + /* Check if it is an interrupt for this port */ + psc_num = (port->mapbase & 0xf00) >> 8; + if (test_bit(psc_num, &fifoc_int) || + test_bit(psc_num + 16, &fifoc_int)) + return mpc5xxx_uart_process_int(port); + + return IRQ_NONE; +} + +static int mpc512x_psc_clock(struct uart_port *port, int enable) +{ + struct clk *psc_clk; + int psc_num; + char clk_name[10]; + + if (uart_console(port)) + return 0; + + psc_num = (port->mapbase & 0xf00) >> 8; + snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); + psc_clk = clk_get(port->dev, clk_name); + if (IS_ERR(psc_clk)) { + dev_err(port->dev, "Failed to get PSC clock entry!\n"); + return -ENODEV; + } + + dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); + + if (enable) + clk_enable(psc_clk); + else + clk_disable(psc_clk); + + return 0; +} + +static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_SHARED; + port->irq = psc_fifoc_irq; +} + static struct psc_ops mpc512x_psc_ops = { .fifo_init = mpc512x_psc_fifo_init, .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, @@ -410,6 +601,11 @@ static struct psc_ops mpc512x_psc_ops = { .cw_disable_ints = mpc512x_psc_cw_disable_ints, .cw_restore_ints = mpc512x_psc_cw_restore_ints, .getuartclk = mpc512x_getuartclk, + .clock = mpc512x_psc_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, }; #endif @@ -519,10 +715,15 @@ mpc52xx_uart_startup(struct uart_port *port) struct mpc52xx_psc __iomem *psc = PSC(port); int ret; + if (psc_ops->clock) { + ret = psc_ops->clock(port, 1); + if (ret) + return ret; + } + /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - "mpc52xx_psc_uart", port); + port->irqflags, "mpc52xx_psc_uart", port); if (ret) return ret; @@ -553,6 +754,9 @@ mpc52xx_uart_shutdown(struct uart_port *port) port->read_status_mask = 0; out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + if (psc_ops->clock) + psc_ops->clock(port, 0); + /* Release interrupt */ free_irq(port->irq, port); } @@ -851,15 +1055,12 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) } static irqreturn_t -mpc52xx_uart_int(int irq, void *dev_id) +mpc5xxx_uart_process_int(struct uart_port *port) { - struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; u8 status; - spin_lock(&port->lock); - /* While we have stuff to do, we continue */ do { /* If we don't find anything to do, we stop */ @@ -886,11 +1087,23 @@ mpc52xx_uart_int(int irq, void *dev_id) } while (keepgoing); - spin_unlock(&port->lock); - return IRQ_HANDLED; } +static irqreturn_t +mpc52xx_uart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + irqreturn_t ret; + + spin_lock(&port->lock); + + ret = psc_ops->handle_irq(port); + + spin_unlock(&port->lock); + + return ret; +} /* ======================================================================== */ /* Console ( if applicable ) */ @@ -1152,7 +1365,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) return -EINVAL; } - port->irq = irq_of_parse_and_map(op->node, 0); + psc_ops->get_irq(port, op->node); if (port->irq == NO_IRQ) { dev_dbg(&op->dev, "Could not get irq\n"); return -EINVAL; @@ -1163,10 +1376,8 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) /* Add the port to the uart sub-system */ ret = uart_add_one_port(&mpc52xx_uart_driver, port); - if (ret) { - irq_dispose_mapping(port->irq); + if (ret) return ret; - } dev_set_drvdata(&op->dev, (void *)port); return 0; @@ -1178,10 +1389,8 @@ mpc52xx_uart_of_remove(struct of_device *op) struct uart_port *port = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); - if (port) { + if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); - irq_dispose_mapping(port->irq); - } return 0; } @@ -1288,6 +1497,15 @@ mpc52xx_uart_init(void) mpc52xx_uart_of_enumerate(); + /* + * Map the PSC FIFO Controller and init if on MPC512x. + */ + if (psc_ops->fifoc_init) { + ret = psc_ops->fifoc_init(); + if (ret) + return ret; + } + ret = of_register_platform_driver(&mpc52xx_uart_of_driver); if (ret) { printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n", @@ -1302,6 +1520,9 @@ mpc52xx_uart_init(void) static void __exit mpc52xx_uart_exit(void) { + if (psc_ops->fifoc_uninit) + psc_ops->fifoc_uninit(); + of_unregister_platform_driver(&mpc52xx_uart_of_driver); uart_unregister_driver(&mpc52xx_uart_driver); } 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/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 683e66f18e8c..f020de1cdd50 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -63,11 +63,17 @@ #include <asm/sections.h> #include <asm/io.h> #include <asm/irq.h> + +#ifdef CONFIG_PPC_PMAC #include <asm/prom.h> #include <asm/machdep.h> #include <asm/pmac_feature.h> #include <asm/dbdma.h> #include <asm/macio.h> +#else +#include <linux/platform_device.h> +#define of_machine_is_compatible(x) (0) +#endif #if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -83,11 +89,9 @@ static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); -MODULE_DESCRIPTION("Driver for the PowerMac serial ports."); +MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports."); MODULE_LICENSE("GPL"); -#define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) - #ifdef CONFIG_SERIAL_PMACZILOG_TTYS #define PMACZILOG_MAJOR TTY_MAJOR #define PMACZILOG_MINOR 64 @@ -153,8 +157,8 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) write_zsreg(uap, R10, regs[R10]); /* Set TX/RX controls sans the enable bits. */ - write_zsreg(uap, R3, regs[R3] & ~RxENABLE); - write_zsreg(uap, R5, regs[R5] & ~TxENABLE); + write_zsreg(uap, R3, regs[R3] & ~RxENABLE); + write_zsreg(uap, R5, regs[R5] & ~TxENABLE); /* now set R7 "prime" on ESCC */ write_zsreg(uap, R15, regs[R15] | EN85C30); @@ -205,7 +209,7 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) */ static void pmz_maybe_update_regs(struct uart_pmac_port *uap) { - if (!ZS_REGS_HELD(uap)) { + if (!ZS_REGS_HELD(uap)) { if (ZS_TX_ACTIVE(uap)) { uap->flags |= PMACZILOG_FLAG_REGS_HELD; } else { @@ -281,7 +285,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) spin_lock(&uap->port.lock); if (swallow) goto next_char; - } + } #endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */ /* A real serial line, record the character and status. */ @@ -317,7 +321,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) if (uap->port.ignore_status_mask == 0xff || (r1 & uap->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tty, ch, flag); } if (r1 & Rx_OVR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); @@ -341,7 +345,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); write_zsreg(uap, R1, uap->curregs[R1]); zssync(uap); - dev_err(&uap->dev->ofdev.dev, "pmz: rx irq flood !\n"); + pmz_error("pmz: rx irq flood !\n"); return tty; } @@ -470,47 +474,47 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) uap_a = pmz_get_port_A(uap); uap_b = uap_a->mate; - - spin_lock(&uap_a->port.lock); + + spin_lock(&uap_a->port.lock); r3 = read_zsreg(uap_a, R3); #ifdef DEBUG_HARD pmz_debug("irq, r3: %x\n", r3); #endif - /* Channel A */ + /* Channel A */ tty = NULL; - if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { + if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { write_zsreg(uap_a, R0, RES_H_IUS); zssync(uap_a); - if (r3 & CHAEXT) - pmz_status_handle(uap_a); + if (r3 & CHAEXT) + pmz_status_handle(uap_a); if (r3 & CHARxIP) tty = pmz_receive_chars(uap_a); - if (r3 & CHATxIP) - pmz_transmit_chars(uap_a); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_a->port.lock); + if (r3 & CHATxIP) + pmz_transmit_chars(uap_a); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_a->port.lock); if (tty != NULL) tty_flip_buffer_push(tty); if (uap_b->node == NULL) goto out; - spin_lock(&uap_b->port.lock); + spin_lock(&uap_b->port.lock); tty = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { write_zsreg(uap_b, R0, RES_H_IUS); zssync(uap_b); - if (r3 & CHBEXT) - pmz_status_handle(uap_b); - if (r3 & CHBRxIP) - tty = pmz_receive_chars(uap_b); - if (r3 & CHBTxIP) - pmz_transmit_chars(uap_b); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_b->port.lock); + if (r3 & CHBEXT) + pmz_status_handle(uap_b); + if (r3 & CHBRxIP) + tty = pmz_receive_chars(uap_b); + if (r3 & CHBTxIP) + pmz_transmit_chars(uap_b); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_b->port.lock); if (tty != NULL) tty_flip_buffer_push(tty); @@ -718,7 +722,7 @@ static void pmz_enable_ms(struct uart_port *port) if (ZS_IS_ASLEEP(uap)) return; - /* NOTE: Not subject to 'transmitter active' rule. */ + /* NOTE: Not subject to 'transmitter active' rule. */ write_zsreg(uap, R15, uap->curregs[R15]); } } @@ -748,7 +752,7 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) if (new_reg != uap->curregs[R5]) { uap->curregs[R5] = new_reg; - /* NOTE: Not subject to 'transmitter active' rule. */ + /* NOTE: Not subject to 'transmitter active' rule. */ if (ZS_IS_ASLEEP(uap)) return; write_zsreg(uap, R5, uap->curregs[R5]); @@ -757,6 +761,8 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&port->lock, flags); } +#ifdef CONFIG_PPC_PMAC + /* * Turn power on or off to the SCC and associated stuff * (port drivers, modem, IR port, etc.) @@ -792,6 +798,15 @@ static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) return delay; } +#else + +static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + /* * FixZeroBug....Works around a bug in the SCC receving channel. * Inspired from Darwin code, 15 Sept. 2000 -DanM @@ -908,7 +923,6 @@ static int __pmz_startup(struct uart_pmac_port *uap) /* Remember status for DCD/CTS changes */ uap->prev_status = read_zsreg(uap, R0); - return pwr_delay; } @@ -955,9 +969,9 @@ static int pmz_startup(struct uart_port *port) } pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; - if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, "PowerMac Zilog", uap)) { - dev_err(&uap->dev->ofdev.dev, - "Unable to register zs interrupt handler.\n"); + if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, + "SCC", uap)) { + pmz_error("Unable to register zs interrupt handler.\n"); pmz_set_scc_power(uap, 0); mutex_unlock(&pmz_irq_mutex); return -ENXIO; @@ -983,7 +997,7 @@ static int pmz_startup(struct uart_port *port) if (!ZS_IS_EXTCLK(uap)) uap->curregs[R1] |= EXT_INT_ENAB; write_zsreg(uap, R1, uap->curregs[R1]); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); pmz_debug("pmz: startup() done.\n"); @@ -1003,7 +1017,7 @@ static void pmz_shutdown(struct uart_port *port) mutex_lock(&pmz_irq_mutex); /* Release interrupt handler */ - free_irq(uap->port.irq, uap); + free_irq(uap->port.irq, uap); spin_lock_irqsave(&port->lock, flags); @@ -1051,7 +1065,6 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, { int brg; - /* Switch to external clocking for IrDA high clock rates. That * code could be re-used for Midi interfaces with different * multipliers @@ -1198,7 +1211,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0 || (read_zsreg(uap, R1) & ALL_SNT) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, "transmitter didn't drain\n"); + pmz_error("transmitter didn't drain\n"); return; } udelay(10); @@ -1214,7 +1227,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) read_zsdata(uap); mdelay(10); if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, "receiver didn't drain\n"); + pmz_error("receiver didn't drain\n"); return; } } @@ -1223,20 +1236,19 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) uap->curregs[R5] |= DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - mdelay(1); + mdelay(1); /* Switch SCC to 19200 */ pmz_convert_to_zs(uap, CS8, 0, 19200); pmz_load_zsregs(uap, uap->curregs); - mdelay(1); + mdelay(1); /* Write get_version command byte */ write_zsdata(uap, 1); t = 5000; while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, - "irda_setup timed out on get_version byte\n"); + pmz_error("irda_setup timed out on get_version byte\n"); goto out; } udelay(10); @@ -1244,8 +1256,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) version = read_zsdata(uap); if (version < 4) { - dev_info(&uap->dev->ofdev.dev, "IrDA: dongle version %d not supported\n", - version); + pmz_info("IrDA: dongle version %d not supported\n", version); goto out; } @@ -1254,18 +1265,16 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) t = 5000; while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, - "irda_setup timed out on speed mode byte\n"); + pmz_error("irda_setup timed out on speed mode byte\n"); goto out; } udelay(10); } t = read_zsdata(uap); if (t != cmdbyte) - dev_err(&uap->dev->ofdev.dev, - "irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); + pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); - dev_info(&uap->dev->ofdev.dev, "IrDA setup for %ld bps, dongle version: %d\n", + pmz_info("IrDA setup for %ld bps, dongle version: %d\n", *baud, version); (void)read_zsdata(uap); @@ -1415,7 +1424,7 @@ static void pmz_poll_put_char(struct uart_port *port, unsigned char c) write_zsdata(uap, c); } -#endif +#endif /* CONFIG_CONSOLE_POLL */ static struct uart_ops pmz_pops = { .tx_empty = pmz_tx_empty, @@ -1440,6 +1449,8 @@ static struct uart_ops pmz_pops = { #endif }; +#ifdef CONFIG_PPC_PMAC + /* * Setup one port structure after probing, HW is down at this point, * Unlike sunzilog, we don't need to pre-init the spinlock as we don't @@ -1463,7 +1474,7 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) return -ENODEV; uap->port.mapbase = r_ports.start; uap->port.membase = ioremap(uap->port.mapbase, 0x1000); - + uap->control_reg = uap->port.membase; uap->data_reg = uap->control_reg + 0x10; @@ -1590,7 +1601,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) } /* - * Called upon match with an escc node in the devive-tree. + * Called upon match with an escc node in the device-tree. */ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) { @@ -1812,7 +1823,7 @@ static int __init pmz_probe(void) pmz_ports[count].node = node_a; pmz_ports[count+1].node = node_b; pmz_ports[count].port.line = count; - pmz_ports[count+1].port.line = count+1; + pmz_ports[count+1].port.line = count+1; /* * Setup the ports for real @@ -1836,6 +1847,88 @@ next: return 0; } +#else + +extern struct platform_device scc_a_pdev, scc_b_pdev; + +static int __init pmz_init_port(struct uart_pmac_port *uap) +{ + struct resource *r_ports; + int irq; + + r_ports = platform_get_resource(uap->node, IORESOURCE_MEM, 0); + irq = platform_get_irq(uap->node, 0); + if (!r_ports || !irq) + return -ENODEV; + + uap->port.mapbase = r_ports->start; + uap->port.membase = (unsigned char __iomem *) r_ports->start; + uap->port.iotype = UPIO_MEM; + uap->port.irq = irq; + uap->port.uartclk = ZS_CLOCK; + uap->port.fifosize = 1; + uap->port.ops = &pmz_pops; + uap->port.type = PORT_PMAC_ZILOG; + uap->port.flags = 0; + + uap->control_reg = uap->port.membase; + uap->data_reg = uap->control_reg + 4; + uap->port_type = 0; + + pmz_convert_to_zs(uap, CS8, 0, 9600); + + return 0; +} + +static int __init pmz_probe(void) +{ + int err; + + pmz_ports_count = 0; + + pmz_ports[0].mate = &pmz_ports[1]; + pmz_ports[0].port.line = 0; + pmz_ports[0].flags = PMACZILOG_FLAG_IS_CHANNEL_A; + pmz_ports[0].node = &scc_a_pdev; + err = pmz_init_port(&pmz_ports[0]); + if (err) + return err; + pmz_ports_count++; + + pmz_ports[1].mate = &pmz_ports[0]; + pmz_ports[1].port.line = 1; + pmz_ports[1].flags = 0; + pmz_ports[1].node = &scc_b_pdev; + err = pmz_init_port(&pmz_ports[1]); + if (err) + return err; + pmz_ports_count++; + + return 0; +} + +static void pmz_dispose_port(struct uart_pmac_port *uap) +{ + memset(uap, 0, sizeof(struct uart_pmac_port)); +} + +static int __init pmz_attach(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < pmz_ports_count; i++) + if (pmz_ports[i].node == pdev) + return 0; + return -ENODEV; +} + +static int __exit pmz_detach(struct platform_device *pdev) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + #ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE static void pmz_console_write(struct console *con, const char *s, unsigned int count); @@ -1896,28 +1989,41 @@ err_out: return rc; } +#ifdef CONFIG_PPC_PMAC + static struct of_device_id pmz_match[] = { { - .name = "ch-a", + .name = "ch-a", }, { - .name = "ch-b", + .name = "ch-b", }, {}, }; MODULE_DEVICE_TABLE (of, pmz_match); -static struct macio_driver pmz_driver = -{ +static struct macio_driver pmz_driver = { .name = "pmac_zilog", .match_table = pmz_match, .probe = pmz_attach, .remove = pmz_detach, .suspend = pmz_suspend, - .resume = pmz_resume, + .resume = pmz_resume, }; +#else + +static struct platform_driver pmz_driver = { + .remove = __exit_p(pmz_detach), + .driver = { + .name = "scc", + .owner = THIS_MODULE, + }, +}; + +#endif /* !CONFIG_PPC_PMAC */ + static int __init init_pmz(void) { int rc, i; @@ -1952,19 +2058,27 @@ static int __init init_pmz(void) pmz_dispose_port(&pmz_ports[i]); return rc; } - + /* * Then we register the macio driver itself */ +#ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); +#else + return platform_driver_probe(&pmz_driver, pmz_attach); +#endif } static void __exit exit_pmz(void) { int i; +#ifdef CONFIG_PPC_PMAC /* Get rid of macio-driver (detach from macio) */ macio_unregister_driver(&pmz_driver); +#else + platform_driver_unregister(&pmz_driver); +#endif for (i = 0; i < pmz_ports_count; i++) { struct uart_pmac_port *uport = &pmz_ports[i]; @@ -2031,10 +2145,10 @@ static int __init pmz_console_setup(struct console *co, char *options) /* * XServe's default to 57600 bps */ - if (machine_is_compatible("RackMac1,1") - || machine_is_compatible("RackMac1,2") - || machine_is_compatible("MacRISC4")) - baud = 57600; + if (of_machine_is_compatible("RackMac1,1") + || of_machine_is_compatible("RackMac1,2") + || of_machine_is_compatible("MacRISC4")) + baud = 57600; /* * Check whether an invalid uart number has been specified, and diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h index f6e77f12acd5..cbc34fbb1b20 100644 --- a/drivers/serial/pmac_zilog.h +++ b/drivers/serial/pmac_zilog.h @@ -1,7 +1,15 @@ #ifndef __PMAC_ZILOG_H__ #define __PMAC_ZILOG_H__ -#define pmz_debug(fmt,arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) +#ifdef CONFIG_PPC_PMAC +#define pmz_debug(fmt, arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->dev->ofdev.dev, fmt, ## arg) +#else +#define pmz_debug(fmt, arg...) dev_dbg(&uap->node->dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->node->dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->node->dev, fmt, ## arg) +#endif /* * At most 2 ESCCs with 2 ports each @@ -17,6 +25,7 @@ struct uart_pmac_port { struct uart_port port; struct uart_pmac_port *mate; +#ifdef CONFIG_PPC_PMAC /* macio_dev for the escc holding this port (maybe be null on * early inited port) */ @@ -25,6 +34,9 @@ struct uart_pmac_port { * of "escc" node (ie. ch-a or ch-b) */ struct device_node *node; +#else + struct platform_device *node; +#endif /* Port type as obtained from device tree (IRDA, modem, ...) */ int port_type; @@ -55,10 +67,12 @@ struct uart_pmac_port { volatile u8 __iomem *control_reg; volatile u8 __iomem *data_reg; +#ifdef CONFIG_PPC_PMAC unsigned int tx_dma_irq; unsigned int rx_dma_irq; volatile struct dbdma_regs __iomem *tx_dma_regs; volatile struct dbdma_regs __iomem *rx_dma_regs; +#endif struct ktermios termios_cache; }; @@ -113,7 +127,7 @@ static inline void zssync(struct uart_pmac_port *port) #define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) #define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) -#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ +#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ /* The Zilog register set */ @@ -171,7 +185,7 @@ static inline void zssync(struct uart_pmac_port *port) /* Write Register 3 */ -#define RxENABLE 0x1 /* Rx Enable */ +#define RxENABLE 0x1 /* Rx Enable */ #define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ #define ADD_SM 0x4 /* Address Search Mode (SDLC) */ #define RxCRC_ENAB 0x8 /* Rx CRC Enable */ @@ -185,7 +199,7 @@ static inline void zssync(struct uart_pmac_port *port) /* Write Register 4 */ -#define PAR_ENAB 0x1 /* Parity Enable */ +#define PAR_ENAB 0x1 /* Parity Enable */ #define PAR_EVEN 0x2 /* Parity Even/Odd* */ #define SYNC_ENAB 0 /* Sync Modes Enable */ @@ -210,7 +224,7 @@ static inline void zssync(struct uart_pmac_port *port) #define TxCRC_ENAB 0x1 /* Tx CRC Enable */ #define RTS 0x2 /* RTS */ #define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENABLE 0x8 /* Tx Enable */ +#define TxENABLE 0x8 /* Tx Enable */ #define SND_BRK 0x10 /* Send Break */ #define Tx5 0x0 /* Tx 5 bits (or less)/character */ #define Tx7 0x20 /* Tx 7 bits/character */ @@ -372,11 +386,11 @@ static inline void zssync(struct uart_pmac_port *port) #define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE) #define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS) #define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA) -#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) +#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) #define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA) -#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) -#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) -#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) -#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) +#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) +#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) +#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) +#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) #endif /* __PMAC_ZILOG_H__ */ diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 95421fa3b304..e91db4b38012 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -696,11 +696,11 @@ static int serial_config(struct pcmcia_device * link) info->multi = info->quirk->multi; if (info->multi > 1) - multi_config(link); + i = multi_config(link); else - simple_config(link); + i = simple_config(link); - if (info->ndev == 0) + if (i || info->ndev == 0) goto failed; /* @@ -715,6 +715,7 @@ static int serial_config(struct pcmcia_device * link) return 0; failed: + dev_warn(&link->dev, "serial_cs: failed to initialize\n"); serial_remove(link); return -ENODEV; } 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/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); |