diff options
Diffstat (limited to 'drivers/tty')
51 files changed, 867 insertions, 277 deletions
| diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 208f573495dc..dea16bb8c46a 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -127,7 +127,7 @@ static struct serial_state rs_table[1];  #define NR_PORTS ARRAY_SIZE(rs_table) -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #define serial_isroot()	(capable(CAP_SYS_ADMIN)) @@ -1012,8 +1012,6 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,  {  	struct serial_struct tmp; -	if (!retinfo) -		return -EFAULT;  	memset(&tmp, 0, sizeof(tmp));  	tty_lock(tty);  	tmp.line = tty->index; diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index ce864875330e..9b5c0fb216b5 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -42,7 +42,7 @@  #include <linux/slab.h>  #include <linux/serial_core.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include "hvc_console.h" diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 3c4d7c2b4ade..7823d6d998cf 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -81,7 +81,7 @@  #include <linux/tty_flip.h>  #include <asm/hvconsole.h>  #include <asm/hvcserver.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <asm/vio.h>  /* diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 96ce6bd1cc6f..2e578d6433af 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -46,7 +46,7 @@  #include <asm/hvcall.h>  #include <asm/hvconsole.h>  #include <asm/prom.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <asm/vio.h>  #include <asm/param.h>  #include <asm/hvsi.h> diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c index 60d37b225589..4caf0c3b1f99 100644 --- a/drivers/tty/moxa.c +++ b/drivers/tty/moxa.c @@ -47,7 +47,7 @@  #include <linux/ratelimit.h>  #include <asm/io.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include "moxa.h" diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 69294ae154be..7b8f383fb090 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -43,7 +43,7 @@  #include <asm/io.h>  #include <asm/irq.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include "mxser.h" diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index a7fa016f31eb..eb278832f5ce 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -103,7 +103,7 @@  #include <linux/bitops.h>  #include <asm/termios.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  /*   * Buffers for individual HDLC frames diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 345111467b85..305b6490d405 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -65,7 +65,7 @@  #include <linux/n_r3964.h>  #include <linux/poll.h>  #include <linux/init.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  /*#define DEBUG_QUEUE*/ diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index d6fd0e802ef5..39b3723a32a6 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -63,44 +63,23 @@  #define VERSION_STRING DRIVER_DESC " 2.1d" -/*    Macros definitions */ -  /* Default debug printout level */  #define NOZOMI_DEBUG_LEVEL 0x00 - -#define P_BUF_SIZE 128 -#define NFO(_err_flag_, args...)				\ -do {								\ -	char tmp[P_BUF_SIZE];					\ -	snprintf(tmp, sizeof(tmp), ##args);			\ -	printk(_err_flag_ "[%d] %s(): %s\n", __LINE__,		\ -		__func__, tmp);				\ -} while (0) - -#define DBG1(args...) D_(0x01, ##args) -#define DBG2(args...) D_(0x02, ##args) -#define DBG3(args...) D_(0x04, ##args) -#define DBG4(args...) D_(0x08, ##args) -#define DBG5(args...) D_(0x10, ##args) -#define DBG6(args...) D_(0x20, ##args) -#define DBG7(args...) D_(0x40, ##args) -#define DBG8(args...) D_(0x80, ##args) - -#ifdef DEBUG -/* Do we need this settable at runtime? */  static int debug = NOZOMI_DEBUG_LEVEL; +module_param(debug, int, S_IRUGO | S_IWUSR); -#define D(lvl, args...)  do \ -			{if (lvl & debug) NFO(KERN_DEBUG, ##args); } \ -			while (0) -#define D_(lvl, args...) D(lvl, ##args) - -/* These printouts are always printed */ +/*    Macros definitions */ +#define DBG_(lvl, fmt, args...)				\ +do {							\ +	if (lvl & debug)				\ +		pr_debug("[%d] %s(): " fmt "\n",	\ +			 __LINE__, __func__,  ##args);	\ +} while (0) -#else -static int debug; -#define D_(lvl, args...) -#endif +#define DBG1(args...) DBG_(0x01, ##args) +#define DBG2(args...) DBG_(0x02, ##args) +#define DBG3(args...) DBG_(0x04, ##args) +#define DBG4(args...) DBG_(0x08, ##args)  /* TODO: rewrite to optimize macros... */ @@ -1320,7 +1299,7 @@ static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,  	return sprintf(buf, "%d\n", dc->card_type);  } -static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL); +static DEVICE_ATTR_RO(card_type);  static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,  			  char *buf) @@ -1329,7 +1308,7 @@ static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,  	return sprintf(buf, "%u\n", dc->open_ttys);  } -static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL); +static DEVICE_ATTR_RO(open_ttys);  static void make_sysfs_files(struct nozomi *dc)  { @@ -1943,7 +1922,5 @@ static __exit void nozomi_exit(void)  module_init(nozomi_init);  module_exit(nozomi_exit); -module_param(debug, int, S_IRUGO | S_IWUSR); -  MODULE_LICENSE("Dual BSD/GPL");  MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index b0cc47c77b40..d66c1edd9892 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1189,8 +1189,6 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo)  {  	struct rocket_config tmp; -	if (!retinfo) -		return -EFAULT;  	memset(&tmp, 0, sizeof (tmp));  	mutex_lock(&info->port.mutex);  	tmp.line = info->line; @@ -1255,8 +1253,6 @@ static int get_ports(struct r_port *info, struct rocket_ports __user *retports)  	struct rocket_ports tmp;  	int board; -	if (!retports) -		return -EFAULT;  	memset(&tmp, 0, sizeof (tmp));  	tmp.tty_major = rocket_driver->major; diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index a697a8585ddc..ce8d4ffcc425 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -80,6 +80,7 @@ struct serial8250_config {  #define UART_CAP_RTOIE	(1 << 13)	/* UART needs IER bit 4 set (Xscale, Tegra) */  #define UART_CAP_HFIFO	(1 << 14)	/* UART has a "hidden" FIFO */  #define UART_CAP_RPM	(1 << 15)	/* Runtime PM is active while idle */ +#define UART_CAP_IRDA	(1 << 16)	/* UART supports IrDA line discipline */  #define UART_BUG_QUOT	(1 << 0)	/* UART has buggy quot LSB */  #define UART_BUG_TXEN	(1 << 1)	/* UART has buggy TX IIR status */ @@ -129,8 +130,13 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)  }  struct uart_8250_port *serial8250_get_port(int line); +  void serial8250_rpm_get(struct uart_8250_port *p);  void serial8250_rpm_put(struct uart_8250_port *p); + +void serial8250_rpm_get_tx(struct uart_8250_port *p); +void serial8250_rpm_put_tx(struct uart_8250_port *p); +  int serial8250_em485_init(struct uart_8250_port *p);  void serial8250_em485_destroy(struct uart_8250_port *p); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 240a361b674f..76e03a7de9cc 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -425,10 +425,10 @@ struct uart_8250_port *serial8250_get_port(int line)  EXPORT_SYMBOL_GPL(serial8250_get_port);  static void (*serial8250_isa_config)(int port, struct uart_port *up, -	unsigned short *capabilities); +	u32 *capabilities);  void serial8250_set_isa_configurator( -	void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) +	void (*v)(int port, struct uart_port *up, u32 *capabilities))  {  	serial8250_isa_config = v;  } @@ -675,7 +675,7 @@ static struct console univ8250_console = {  	.device		= uart_console_device,  	.setup		= univ8250_console_setup,  	.match		= univ8250_console_match, -	.flags		= CON_PRINTBUFFER | CON_ANYTIME | CON_CONSDEV, +	.flags		= CON_PRINTBUFFER | CON_ANYTIME,  	.index		= -1,  	.data		= &serial8250_reg,  }; @@ -830,6 +830,7 @@ static int serial8250_probe(struct platform_device *dev)  		uart.port.handle_irq	= p->handle_irq;  		uart.port.handle_break	= p->handle_break;  		uart.port.set_termios	= p->set_termios; +		uart.port.set_ldisc	= p->set_ldisc;  		uart.port.get_mctrl	= p->get_mctrl;  		uart.port.pm		= p->pm;  		uart.port.dev		= &dev->dev; @@ -1023,6 +1024,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)  		/*  Possibly override set_termios call */  		if (up->port.set_termios)  			uart->port.set_termios = up->port.set_termios; +		if (up->port.set_ldisc) +			uart->port.set_ldisc = up->port.set_ldisc;  		if (up->port.get_mctrl)  			uart->port.get_mctrl = up->port.get_mctrl;  		if (up->port.set_mctrl) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index fdbddbc6375d..26f17456b0d7 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -72,10 +72,15 @@ int serial8250_tx_dma(struct uart_8250_port *p)  	struct dma_async_tx_descriptor	*desc;  	int ret; -	if (uart_tx_stopped(&p->port) || dma->tx_running || -	    uart_circ_empty(xmit)) +	if (dma->tx_running)  		return 0; +	if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { +		/* We have been called from __dma_tx_complete() */ +		serial8250_rpm_put_tx(p); +		return 0; +	} +  	dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);  	desc = dmaengine_prep_slave_single(dma->txchan, diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 459d726f9d59..c89fafc972b6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -53,6 +53,8 @@  /* Helper for fifo size calculation */  #define DW_UART_CPR_FIFO_SIZE(a)	(((a >> 16) & 0xff) * 16) +/* DesignWare specific register fields */ +#define DW_UART_MCR_SIRE		BIT(6)  struct dw8250_data {  	u8			usr_reg; @@ -254,6 +256,22 @@ out:  	serial8250_do_set_termios(p, termios, old);  } +static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) +{ +	struct uart_8250_port *up = up_to_u8250p(p); +	unsigned int mcr = p->serial_in(p, UART_MCR); + +	if (up->capabilities & UART_CAP_IRDA) { +		if (termios->c_line == N_IRDA) +			mcr |= DW_UART_MCR_SIRE; +		else +			mcr &= ~DW_UART_MCR_SIRE; + +		p->serial_out(p, UART_MCR, mcr); +	} +	serial8250_do_set_ldisc(p, termios); +} +  /*   * dw8250_fallback_dma_filter will prevent the UART from getting just any free   * channel on platforms that have DMA engines, but don't have any channels @@ -357,6 +375,9 @@ static void dw8250_setup_port(struct uart_port *p)  	if (reg & DW_UART_CPR_AFCE_MODE)  		up->capabilities |= UART_CAP_AFE; + +	if (reg & DW_UART_CPR_SIR_MODE) +		up->capabilities |= UART_CAP_IRDA;  }  static int dw8250_probe(struct platform_device *pdev) @@ -392,6 +413,7 @@ static int dw8250_probe(struct platform_device *pdev)  	p->iotype	= UPIO_MEM;  	p->serial_in	= dw8250_serial_in;  	p->serial_out	= dw8250_serial_out; +	p->set_ldisc	= dw8250_set_ldisc;  	p->membase = devm_ioremap(dev, regs->start, resource_size(regs));  	if (!p->membase) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 0facc789fe7d..b67e7a544935 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,8 +21,11 @@  #define EXIT_KEY 0xAA  #define CHIP_ID1  0x20  #define CHIP_ID2  0x21 -#define CHIP_ID_0 0x1602 -#define CHIP_ID_1 0x0501 +#define CHIP_ID_F81865 0x0407 +#define CHIP_ID_F81866 0x1010 +#define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216H 0x0501 +#define CHIP_ID_F81216 0x0802  #define VENDOR_ID1 0x23  #define VENDOR_ID1_VAL 0x19  #define VENDOR_ID2 0x24 @@ -43,12 +46,60 @@  #define RXW4C_IRA BIT(3)  #define TXW4C_IRA BIT(2) +#define FIFO_CTRL		0xF6 +#define FIFO_MODE_MASK		(BIT(1) | BIT(0)) +#define FIFO_MODE_128		(BIT(1) | BIT(0)) +#define RXFTHR_MODE_MASK	(BIT(5) | BIT(4)) +#define RXFTHR_MODE_4X		BIT(5) + +#define F81216_LDN_LOW	0x0 +#define F81216_LDN_HIGH	0x4 + +/* + * F81866 registers + * + * The IRQ setting mode of F81866 is not the same with F81216 series. + *	Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 + *	Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 + */ +#define F81866_IRQ_MODE		0xf0 +#define F81866_IRQ_SHARE	BIT(0) +#define F81866_IRQ_MODE0	BIT(1) + +#define F81866_FIFO_CTRL	FIFO_CTRL +#define F81866_IRQ_MODE1	BIT(3) + +#define F81866_LDN_LOW		0x10 +#define F81866_LDN_HIGH		0x16 +  struct fintek_8250 { +	u16 pid;  	u16 base_port;  	u8 index;  	u8 key;  }; +static u8 sio_read_reg(struct fintek_8250 *pdata, u8 reg) +{ +	outb(reg, pdata->base_port + ADDR_PORT); +	return inb(pdata->base_port + DATA_PORT); +} + +static void sio_write_reg(struct fintek_8250 *pdata, u8 reg, u8 data) +{ +	outb(reg, pdata->base_port + ADDR_PORT); +	outb(data, pdata->base_port + DATA_PORT); +} + +static void sio_write_mask_reg(struct fintek_8250 *pdata, u8 reg, u8 mask, +			       u8 data) +{ +	u8 tmp; + +	tmp = (sio_read_reg(pdata, reg) & ~mask) | (mask & data); +	sio_write_reg(pdata, reg, tmp); +} +  static int fintek_8250_enter_key(u16 base_port, u8 key)  {  	if (!request_muxed_region(base_port, 2, "8250_fintek")) @@ -66,29 +117,55 @@ static void fintek_8250_exit_key(u16 base_port)  	release_region(base_port + ADDR_PORT, 2);  } -static int fintek_8250_check_id(u16 base_port) +static int fintek_8250_check_id(struct fintek_8250 *pdata)  {  	u16 chip; -	outb(VENDOR_ID1, base_port + ADDR_PORT); -	if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL) +	if (sio_read_reg(pdata, VENDOR_ID1) != VENDOR_ID1_VAL)  		return -ENODEV; -	outb(VENDOR_ID2, base_port + ADDR_PORT); -	if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL) +	if (sio_read_reg(pdata, VENDOR_ID2) != VENDOR_ID2_VAL)  		return -ENODEV; -	outb(CHIP_ID1, base_port + ADDR_PORT); -	chip = inb(base_port + DATA_PORT); -	outb(CHIP_ID2, base_port + ADDR_PORT); -	chip |= inb(base_port + DATA_PORT) << 8; - -	if (chip != CHIP_ID_0 && chip != CHIP_ID_1) +	chip = sio_read_reg(pdata, CHIP_ID1); +	chip |= sio_read_reg(pdata, CHIP_ID2) << 8; + +	switch (chip) { +	case CHIP_ID_F81865: +	case CHIP_ID_F81866: +	case CHIP_ID_F81216AD: +	case CHIP_ID_F81216H: +	case CHIP_ID_F81216: +		break; +	default:  		return -ENODEV; +	} +	pdata->pid = chip;  	return 0;  } +static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, +				     int *max) +{ +	switch (pdata->pid) { +	case CHIP_ID_F81865: +	case CHIP_ID_F81866: +		*min = F81866_LDN_LOW; +		*max = F81866_LDN_HIGH; +		return 0; + +	case CHIP_ID_F81216AD: +	case CHIP_ID_F81216H: +	case CHIP_ID_F81216: +		*min = F81216_LDN_LOW; +		*max = F81216_LDN_HIGH; +		return 0; +	} + +	return -ENODEV; +} +  static int fintek_8250_rs485_config(struct uart_port *port,  			      struct serial_rs485 *rs485)  { @@ -128,10 +205,8 @@ static int fintek_8250_rs485_config(struct uart_port *port,  	if (fintek_8250_enter_key(pdata->base_port, pdata->key))  		return -EBUSY; -	outb(LDN, pdata->base_port + ADDR_PORT); -	outb(pdata->index, pdata->base_port + DATA_PORT); -	outb(RS485, pdata->base_port + ADDR_PORT); -	outb(config, pdata->base_port + DATA_PORT); +	sio_write_reg(pdata, LDN, pdata->index); +	sio_write_reg(pdata, RS485, config);  	fintek_8250_exit_key(pdata->base_port);  	port->rs485 = *rs485; @@ -139,40 +214,90 @@ static int fintek_8250_rs485_config(struct uart_port *port,  	return 0;  } -static int find_base_port(struct fintek_8250 *pdata, u16 io_address) +static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) +{ +	sio_write_reg(pdata, LDN, pdata->index); + +	switch (pdata->pid) { +	case CHIP_ID_F81866: +		sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, +				   0); +		/* fall through */ +	case CHIP_ID_F81865: +		sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, +				   F81866_IRQ_SHARE); +		sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_MODE0, +				   is_level ? 0 : F81866_IRQ_MODE0); +		break; + +	case CHIP_ID_F81216AD: +	case CHIP_ID_F81216H: +	case CHIP_ID_F81216: +		sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, +				   IRQ_SHARE); +		sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, +				   is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); +		break; +	} +} + +static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) +{ +	switch (pdata->pid) { +	case CHIP_ID_F81216H: /* 128Bytes FIFO */ +	case CHIP_ID_F81866: +		sio_write_mask_reg(pdata, FIFO_CTRL, +				   FIFO_MODE_MASK | RXFTHR_MODE_MASK, +				   FIFO_MODE_128 | RXFTHR_MODE_4X); +		break; + +	default: /* Default 16Bytes FIFO */ +		break; +	} +} + +static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, +			  unsigned int irq)  {  	static const u16 addr[] = {0x4e, 0x2e};  	static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; -	int i, j, k; +	struct irq_data *irq_data; +	bool level_mode = false; +	int i, j, k, min, max;  	for (i = 0; i < ARRAY_SIZE(addr); i++) {  		for (j = 0; j < ARRAY_SIZE(keys); j++) { +			pdata->base_port = addr[i]; +			pdata->key = keys[j];  			if (fintek_8250_enter_key(addr[i], keys[j]))  				continue; -			if (fintek_8250_check_id(addr[i])) { +			if (fintek_8250_check_id(pdata) || +			    fintek_8250_get_ldn_range(pdata, &min, &max)) {  				fintek_8250_exit_key(addr[i]);  				continue;  			} -			for (k = 0; k < 4; k++) { +			for (k = min; k < max; k++) {  				u16 aux; -				outb(LDN, addr[i] + ADDR_PORT); -				outb(k, addr[i] + DATA_PORT); - -				outb(IO_ADDR1, addr[i] + ADDR_PORT); -				aux = inb(addr[i] + DATA_PORT); -				outb(IO_ADDR2, addr[i] + ADDR_PORT); -				aux |= inb(addr[i] + DATA_PORT) << 8; +				sio_write_reg(pdata, LDN, k); +				aux = sio_read_reg(pdata, IO_ADDR1); +				aux |= sio_read_reg(pdata, IO_ADDR2) << 8;  				if (aux != io_address)  					continue; -				fintek_8250_exit_key(addr[i]); -				pdata->key = keys[j]; -				pdata->base_port = addr[i];  				pdata->index = k; +				irq_data = irq_get_irq_data(irq); +				if (irq_data) +					level_mode = +						irqd_is_level_type(irq_data); + +				fintek_8250_set_irq_mode(pdata, level_mode); +				fintek_8250_set_max_fifo(pdata); +				fintek_8250_exit_key(addr[i]); +  				return 0;  			} @@ -183,39 +308,29 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address)  	return -ENODEV;  } -static int fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool level_mode) +static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart)  { -	int status; -	u8 tmp; - -	status = fintek_8250_enter_key(pdata->base_port, pdata->key); -	if (status) -		return status; - -	outb(LDN, pdata->base_port + ADDR_PORT); -	outb(pdata->index, pdata->base_port + DATA_PORT); - -	outb(FINTEK_IRQ_MODE, pdata->base_port + ADDR_PORT); -	tmp = inb(pdata->base_port + DATA_PORT); - -	tmp &= ~IRQ_MODE_MASK; -	tmp |= IRQ_SHARE; -	if (!level_mode) -		tmp |= IRQ_EDGE_HIGH; - -	outb(tmp, pdata->base_port + DATA_PORT); -	fintek_8250_exit_key(pdata->base_port); -	return 0; +	struct fintek_8250 *pdata = uart->port.private_data; + +	switch (pdata->pid) { +	case CHIP_ID_F81216AD: +	case CHIP_ID_F81216H: +	case CHIP_ID_F81866: +	case CHIP_ID_F81865: +		uart->port.rs485_config = fintek_8250_rs485_config; +		break; + +	default: /* No RS485 Auto direction functional */ +		break; +	}  }  int fintek_8250_probe(struct uart_8250_port *uart)  {  	struct fintek_8250 *pdata;  	struct fintek_8250 probe_data; -	struct irq_data *irq_data = irq_get_irq_data(uart->port.irq); -	bool level_mode = irqd_is_level_type(irq_data); -	if (find_base_port(&probe_data, uart->port.iobase)) +	if (probe_setup_port(&probe_data, uart->port.iobase, uart->port.irq))  		return -ENODEV;  	pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); @@ -223,8 +338,8 @@ int fintek_8250_probe(struct uart_8250_port *uart)  		return -ENOMEM;  	memcpy(pdata, &probe_data, sizeof(probe_data)); -	uart->port.rs485_config = fintek_8250_rs485_config;  	uart->port.private_data = pdata; +	fintek_8250_set_rs485_handler(uart); -	return fintek_8250_set_irq_mode(pdata, level_mode); +	return 0;  } diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index b9923464599f..58cbb30a9401 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -157,12 +157,12 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)  static const struct dw_dma_platform_data qrk_serial_dma_pdata = {  	.nr_channels = 2,  	.is_private = true, -	.is_nollp = true,  	.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,  	.chan_priority = CHAN_PRIORITY_ASCENDING,  	.block_size = 4095,  	.nr_masters = 1,  	.data_width = {4}, +	.multi_block = {0},  };  static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) @@ -174,7 +174,7 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)  	int ret;  	chip->dev = &pdev->dev; -	chip->irq = pdev->irq; +	chip->irq = pci_irq_vector(pdev, 0);  	chip->regs = pci_ioremap_bar(pdev, 1);  	chip->pdata = &qrk_serial_dma_pdata; @@ -183,6 +183,9 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)  	if (ret)  		return; +	pci_set_master(pdev); +	pci_try_set_mwi(pdev); +  	/* Special DMA address for UART */  	dma->rx_dma_addr = 0xfffff000;  	dma->tx_dma_addr = 0xfffff000; @@ -280,8 +283,6 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)  	if (ret)  		return ret; -	pci_set_master(pdev); -  	lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL);  	if (!lpss)  		return -ENOMEM; diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 39c2324484dd..ac013edf4992 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -303,10 +303,10 @@ static void mid8250_remove(struct pci_dev *pdev)  {  	struct mid8250 *mid = pci_get_drvdata(pdev); +	serial8250_unregister_port(mid->line); +  	if (mid->board->exit)  		mid->board->exit(mid); - -	serial8250_unregister_port(mid->line);  }  static const struct mid8250_board pnw_board = { diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 7a8b5fc81a19..d25ab1cd4295 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -332,8 +332,6 @@ static const struct of_device_id of_platform_serial_table[] = {  		.data = (void *)PORT_ALTR_16550_F128, },  	{ .compatible = "mrvl,mmp-uart",  		.data = (void *)PORT_XSCALE, }, -	{ .compatible = "mrvl,pxa-uart", -		.data = (void *)PORT_XSCALE, },  	{ /* end of list */ },  };  MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index b98c1578f45a..116436b7fa52 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -52,6 +52,7 @@ struct serial_private {  	struct pci_dev		*dev;  	unsigned int		nr;  	struct pci_serial_quirk	*quirk; +	const struct pciserial_board *board;  	int			line[0];  }; @@ -1329,6 +1330,30 @@ static int pci_default_setup(struct serial_private *priv,  	return setup_port(priv, port, bar, offset, board->reg_shift);  } +static int pci_pericom_setup(struct serial_private *priv, +		  const struct pciserial_board *board, +		  struct uart_8250_port *port, int idx) +{ +	unsigned int bar, offset = board->first_offset, maxnr; + +	bar = FL_GET_BASE(board->flags); +	if (board->flags & FL_BASE_BARS) +		bar += idx; +	else +		offset += idx * board->uart_offset; + +	if (idx==3) +		offset = 0x38; + +	maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> +		(board->reg_shift + 3); + +	if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) +		return 1; + +	return setup_port(priv, port, bar, offset, board->reg_shift); +} +  static int  ce4100_serial_setup(struct serial_private *priv,  		  const struct pciserial_board *board, @@ -2096,6 +2121,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {  		.exit		= pci_plx9050_exit,  	},  	/* +	 * Pericom (Only 7954 - It have a offset jump for port 4) +	 */ +	{ +		.vendor		= PCI_VENDOR_ID_PERICOM, +		.device		= PCI_DEVICE_ID_PERICOM_PI7C9X7954, +		.subvendor	= PCI_ANY_ID, +		.subdevice	= PCI_ANY_ID, +		.setup		= pci_pericom_setup, +	}, +	/*  	 * PLX  	 */  	{ @@ -3862,6 +3897,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)  		}  	}  	priv->nr = i; +	priv->board = board;  	return priv;  err_deinit: @@ -3872,7 +3908,7 @@ err_out:  }  EXPORT_SYMBOL_GPL(pciserial_init_ports); -void pciserial_remove_ports(struct serial_private *priv) +void pciserial_detach_ports(struct serial_private *priv)  {  	struct pci_serial_quirk *quirk;  	int i; @@ -3886,7 +3922,11 @@ void pciserial_remove_ports(struct serial_private *priv)  	quirk = find_quirk(priv->dev);  	if (quirk->exit)  		quirk->exit(priv->dev); +} +void pciserial_remove_ports(struct serial_private *priv) +{ +	pciserial_detach_ports(priv);  	kfree(priv);  }  EXPORT_SYMBOL_GPL(pciserial_remove_ports); @@ -5577,7 +5617,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev,  		return PCI_ERS_RESULT_DISCONNECT;  	if (priv) -		pciserial_suspend_ports(priv); +		pciserial_detach_ports(priv);  	pci_disable_device(dev); @@ -5602,9 +5642,16 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev)  static void serial8250_io_resume(struct pci_dev *dev)  {  	struct serial_private *priv = pci_get_drvdata(dev); +	struct serial_private *new; -	if (priv) -		pciserial_resume_ports(priv); +	if (!priv) +		return; + +	new = pciserial_init_ports(dev, priv->board); +	if (!IS_ERR(new)) { +		pci_set_drvdata(dev, new); +		kfree(priv); +	}  }  static const struct pci_error_handlers serial8250_err_handler = { diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1731b98d2471..c13fec451d03 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -636,7 +636,7 @@ EXPORT_SYMBOL_GPL(serial8250_em485_destroy);   * once and disable_runtime_pm_tx() will still disable RPM because the fifo is   * empty and the HW can idle again.   */ -static void serial8250_rpm_get_tx(struct uart_8250_port *p) +void serial8250_rpm_get_tx(struct uart_8250_port *p)  {  	unsigned char rpm_active; @@ -648,8 +648,9 @@ static void serial8250_rpm_get_tx(struct uart_8250_port *p)  		return;  	pm_runtime_get_sync(p->port.dev);  } +EXPORT_SYMBOL_GPL(serial8250_rpm_get_tx); -static void serial8250_rpm_put_tx(struct uart_8250_port *p) +void serial8250_rpm_put_tx(struct uart_8250_port *p)  {  	unsigned char rpm_active; @@ -662,6 +663,7 @@ static void serial8250_rpm_put_tx(struct uart_8250_port *p)  	pm_runtime_mark_last_busy(p->port.dev);  	pm_runtime_put_autosuspend(p->port.dev);  } +EXPORT_SYMBOL_GPL(serial8250_rpm_put_tx);  /*   * IER sleep support.  UARTs which have EFRs need the "extended @@ -1411,7 +1413,7 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)  	 * Enable previously disabled RX interrupts.  	 */  	if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { -		serial8250_clear_fifos(p); +		serial8250_clear_and_reinit_fifos(p);  		p->ier |= UART_IER_RLSI | UART_IER_RDI;  		serial_port_out(&p->port, UART_IER, p->ier); @@ -2691,8 +2693,7 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,  		serial8250_do_set_termios(port, termios, old);  } -static void -serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) +void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios)  {  	if (termios->c_line == N_PPS) {  		port->flags |= UPF_HARDPPS_CD; @@ -2708,7 +2709,16 @@ serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios)  		}  	}  } +EXPORT_SYMBOL_GPL(serial8250_do_set_ldisc); +static void +serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) +{ +	if (port->set_ldisc) +		port->set_ldisc(port, termios); +	else +		serial8250_do_set_ldisc(port, termios); +}  void serial8250_do_pm(struct uart_port *port, unsigned int state,  		      unsigned int oldstate) diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c new file mode 100644 index 000000000000..4d68731af534 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -0,0 +1,190 @@ +/* + *  drivers/tty/serial/8250/8250_pxa.c -- driver for PXA on-board UARTS + *  Copyright:	(C) 2013 Sergei Ianovich <[email protected]> + * + *  replaces drivers/serial/pxa.c by Nicolas Pitre + *  Created:	Feb 20, 2003 + *  Copyright:	(C) 2003 Monta Vista Software, Inc. + * + *  Based on drivers/serial/8250.c by Russell King. + * + * 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. + * + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/serial_8250.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/pm_runtime.h> + +#include "8250.h" + +struct pxa8250_data { +	int			line; +	struct clk		*clk; +}; + +static int __maybe_unused serial_pxa_suspend(struct device *dev) +{ +	struct pxa8250_data *data = dev_get_drvdata(dev); + +	serial8250_suspend_port(data->line); + +	return 0; +} + +static int __maybe_unused serial_pxa_resume(struct device *dev) +{ +	struct pxa8250_data *data = dev_get_drvdata(dev); + +	serial8250_resume_port(data->line); + +	return 0; +} + +static const struct dev_pm_ops serial_pxa_pm_ops = { +	SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume) +}; + +static const struct of_device_id serial_pxa_dt_ids[] = { +	{ .compatible = "mrvl,pxa-uart", }, +	{ .compatible = "mrvl,mmp-uart", }, +	{} +}; +MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids); + +/* Uart divisor latch write */ +static void serial_pxa_dl_write(struct uart_8250_port *up, int value) +{ +	unsigned int dll; + +	serial_out(up, UART_DLL, value & 0xff); +	/* +	 * work around Erratum #74 according to Marvel(R) PXA270M Processor +	 * Specification Update (April 19, 2010) +	 */ +	dll = serial_in(up, UART_DLL); +	WARN_ON(dll != (value & 0xff)); + +	serial_out(up, UART_DLM, value >> 8 & 0xff); +} + + +static void serial_pxa_pm(struct uart_port *port, unsigned int state, +	      unsigned int oldstate) +{ +	struct pxa8250_data *data = port->private_data; + +	if (!state) +		clk_prepare_enable(data->clk); +	else +		clk_disable_unprepare(data->clk); +} + +static int serial_pxa_probe(struct platform_device *pdev) +{ +	struct uart_8250_port uart = {}; +	struct pxa8250_data *data; +	struct resource *mmres, *irqres; +	int ret; + +	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (!mmres || !irqres) +		return -ENODEV; + +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	data->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(data->clk)) +		return PTR_ERR(data->clk); + +	ret = clk_prepare(data->clk); +	if (ret) +		return ret; + +	uart.port.type = PORT_XSCALE; +	uart.port.iotype = UPIO_MEM32; +	uart.port.mapbase = mmres->start; +	uart.port.regshift = 2; +	uart.port.irq = irqres->start; +	uart.port.fifosize = 64; +	uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST; +	uart.port.dev = &pdev->dev; +	uart.port.uartclk = clk_get_rate(data->clk); +	uart.port.pm = serial_pxa_pm; +	uart.port.private_data = data; +	uart.dl_write = serial_pxa_dl_write; + +	ret = serial8250_register_8250_port(&uart); +	if (ret < 0) +		goto err_clk; + +	data->line = ret; + +	platform_set_drvdata(pdev, data); + +	return 0; + + err_clk: +	clk_unprepare(data->clk); +	return ret; +} + +static int serial_pxa_remove(struct platform_device *pdev) +{ +	struct pxa8250_data *data = platform_get_drvdata(pdev); + +	serial8250_unregister_port(data->line); + +	clk_unprepare(data->clk); + +	return 0; +} + +static struct platform_driver serial_pxa_driver = { +	.probe          = serial_pxa_probe, +	.remove         = serial_pxa_remove, + +	.driver		= { +		.name	= "pxa2xx-uart", +		.pm	= &serial_pxa_pm_ops, +		.of_match_table = serial_pxa_dt_ids, +	}, +}; + +module_platform_driver(serial_pxa_driver); + +#ifdef CONFIG_SERIAL_8250_CONSOLE +static int __init early_serial_pxa_setup(struct earlycon_device *device, +				  const char *options) +{ +	struct uart_port *port = &device->port; + +	if (!(device->port.membase || device->port.iobase)) +		return -ENODEV; + +	port->regshift = 2; +	return early_serial8250_setup(device, NULL); +} +OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); +#endif + +MODULE_AUTHOR("Sergei Ianovich"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 417d9e7038e1..746680ebf90c 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -24,10 +24,22 @@  /* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */  #define UNIPHIER_UART_DEFAULT_FIFO_SIZE	64 -#define UNIPHIER_UART_CHAR_FCR	3	/* Character / FIFO Control Register */ -#define UNIPHIER_UART_LCR_MCR	4	/* Line/Modem Control Register */ -#define   UNIPHIER_UART_LCR_SHIFT	8 -#define UNIPHIER_UART_DLR	9	/* Divisor Latch Register */ +/* + * This hardware is similar to 8250, but its register map is a bit different: + *   - MMIO32 (regshift = 2) + *   - FCR is not at 2, but 3 + *   - LCR and MCR are not at 3 and 4, they share 4 + *   - Divisor latch at 9, no divisor latch access bit + */ + +#define UNIPHIER_UART_REGSHIFT		2 + +/* bit[15:8] = CHAR (not used), bit[7:0] = FCR */ +#define UNIPHIER_UART_CHAR_FCR		(3 << (UNIPHIER_UART_REGSHIFT)) +/* bit[15:8] = LCR, bit[7:0] = MCR */ +#define UNIPHIER_UART_LCR_MCR		(4 << (UNIPHIER_UART_REGSHIFT)) +/* Divisor Latch Register */ +#define UNIPHIER_UART_DLR		(9 << (UNIPHIER_UART_REGSHIFT))  struct uniphier8250_priv {  	int line; @@ -44,7 +56,7 @@ static int __init uniphier_early_console_setup(struct earlycon_device *device,  	/* This hardware always expects MMIO32 register interface. */  	device->port.iotype = UPIO_MEM32; -	device->port.regshift = 2; +	device->port.regshift = UNIPHIER_UART_REGSHIFT;  	/*  	 * Do not touch the divisor register in early_serial8250_setup(); @@ -68,17 +80,16 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset)  	switch (offset) {  	case UART_LCR: -		valshift = UNIPHIER_UART_LCR_SHIFT; +		valshift = 8;  		/* fall through */  	case UART_MCR:  		offset = UNIPHIER_UART_LCR_MCR;  		break;  	default: +		offset <<= UNIPHIER_UART_REGSHIFT;  		break;  	} -	offset <<= p->regshift; -  	/*  	 * The return value must be masked with 0xff because LCR and MCR reside  	 * in the same register that must be accessed by 32-bit write/read. @@ -90,27 +101,26 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset)  static void uniphier_serial_out(struct uart_port *p, int offset, int value)  {  	unsigned int valshift = 0; -	bool normal = false; +	bool normal = true;  	switch (offset) {  	case UART_FCR:  		offset = UNIPHIER_UART_CHAR_FCR;  		break;  	case UART_LCR: -		valshift = UNIPHIER_UART_LCR_SHIFT; +		valshift = 8;  		/* Divisor latch access bit does not exist. */  		value &= ~UART_LCR_DLAB;  		/* fall through */  	case UART_MCR:  		offset = UNIPHIER_UART_LCR_MCR; +		normal = false;  		break;  	default: -		normal = true; +		offset <<= UNIPHIER_UART_REGSHIFT;  		break;  	} -	offset <<= p->regshift; -  	if (normal) {  		writel(value, p->membase + offset);  	} else { @@ -139,16 +149,12 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value)   */  static int uniphier_serial_dl_read(struct uart_8250_port *up)  { -	int offset = UNIPHIER_UART_DLR << up->port.regshift; - -	return readl(up->port.membase + offset); +	return readl(up->port.membase + UNIPHIER_UART_DLR);  }  static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)  { -	int offset = UNIPHIER_UART_DLR << up->port.regshift; - -	writel(value, up->port.membase + offset); +	writel(value, up->port.membase + UNIPHIER_UART_DLR);  }  static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, @@ -234,7 +240,7 @@ static int uniphier_uart_probe(struct platform_device *pdev)  	up.port.type = PORT_16550A;  	up.port.iotype = UPIO_MEM32; -	up.port.regshift = 2; +	up.port.regshift = UNIPHIER_UART_REGSHIFT;  	up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE;  	up.capabilities = UART_CAP_FIFO; diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 899834776b36..0b8b6740ba43 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -439,6 +439,16 @@ config SERIAL_8250_MOXA  	  This driver can also be built as a module. The module will be called  	  8250_moxa. If you want to do that, say M here. +config SERIAL_8250_PXA +	tristate "PXA serial port support" +	depends on SERIAL_8250 +	depends on ARCH_PXA || ARCH_MMP +	help +	  If you have a machine based on an Intel XScale PXA2xx CPU you can +	  enable its onboard serial ports by enabling this option. The option is +	  applicable to both devicetree and legacy boards, and early console is +	  part of its support. +  config SERIAL_OF_PLATFORM  	tristate "Devicetree based probing for 8250 ports"  	depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 276c6fb60337..850e721877a9 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SERIAL_8250_INGENIC)	+= 8250_ingenic.o  obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o  obj-$(CONFIG_SERIAL_8250_MID)		+= 8250_mid.o  obj-$(CONFIG_SERIAL_8250_MOXA)		+= 8250_moxa.o +obj-$(CONFIG_SERIAL_8250_PXA)		+= 8250_pxa.o  obj-$(CONFIG_SERIAL_OF_PLATFORM)	+= 8250_of.o  CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 25c1d7bc0100..e9cf5b67f1b7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -438,17 +438,27 @@ config SERIAL_MPSC_CONSOLE  	  Say Y here if you want to support a serial console on a Marvell MPSC.  config SERIAL_PXA -	bool "PXA serial port support" +	bool "PXA serial port support (DEPRECATED)"  	depends on ARCH_PXA || ARCH_MMP  	select SERIAL_CORE +	select SERIAL_8250_PXA if SERIAL_8250=y +	select SERIAL_PXA_NON8250 if !SERIAL_8250=y  	help  	  If you have a machine based on an Intel XScale PXA2xx CPU you  	  can enable its onboard serial ports by enabling this option. +	  Unless you have a specific need, you should use SERIAL_8250_PXA +	  instead of this. + +config SERIAL_PXA_NON8250 +	bool +	depends on !SERIAL_8250 +  config SERIAL_PXA_CONSOLE -	bool "Console on PXA serial port" +	bool "Console on PXA serial port (DEPRECATED)"  	depends on SERIAL_PXA  	select SERIAL_CORE_CONSOLE +	select SERIAL_8250_CONSOLE if SERIAL_8250=y  	help  	  If you have enabled the serial port on the Intel XScale PXA  	  CPU you can make it the console by answering Y to this option. @@ -460,6 +470,9 @@ config SERIAL_PXA_CONSOLE  	  your boot loader (lilo or loadlin) about how to pass options to the  	  kernel at boot time.) +	  Unless you have a specific need, you should use SERIAL_8250_PXA +	  and SERIAL_8250_CONSOLE instead of this. +  config SERIAL_SA1100  	bool "SA1100 serial port support"  	depends on ARCH_SA1100 @@ -1626,7 +1639,7 @@ config SERIAL_STM32  	tristate "STMicroelectronics STM32 serial port support"  	select SERIAL_CORE  	depends on HAS_DMA -	depends on ARM || COMPILE_TEST +	depends on ARCH_STM32 || COMPILE_TEST  	help  	  This driver is for the on-chip Serial Controller on  	  STMicroelectronics STM32 MCUs. diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 1278d376da50..2d6288bc4554 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -23,7 +23,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250/  obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o  obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o  obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o -obj-$(CONFIG_SERIAL_PXA) += pxa.o +obj-$(CONFIG_SERIAL_PXA_NON8250) += pxa.o  obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o  obj-$(CONFIG_SERIAL_SA1100) += sa1100.o  obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o @@ -62,13 +62,11 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o  obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o  obj-$(CONFIG_SERIAL_MSM) += msm_serial.o  obj-$(CONFIG_SERIAL_NETX) += netx-serial.o -obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o  obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o  obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o  obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o  obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o  obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o -obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o  obj-$(CONFIG_SERIAL_QE) += ucc_uart.o  obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o  obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o @@ -96,3 +94,6 @@ obj-$(CONFIG_SERIAL_MPS2_UART)	+= mps2-uart.o  # GPIOLIB helpers for modem control lines  obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o + +obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o +obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2c33b9528d8..d4171d71a258 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2315,12 +2315,67 @@ static int __init pl011_console_setup(struct console *co, char *options)  	return uart_set_options(&uap->port, co, baud, parity, bits, flow);  } +/** + *	pl011_console_match - non-standard console matching + *	@co:	  registering console + *	@name:	  name from console command line + *	@idx:	  index from console command line + *	@options: ptr to option string from console command line + * + *	Only attempts to match console command lines of the form: + *	    console=pl011,mmio|mmio32,<addr>[,<options>] + *	    console=pl011,0x<addr>[,<options>] + *	This form is used to register an initial earlycon boot console and + *	replace it with the amba_console at pl011 driver init. + * + *	Performs console setup for a match (as required by interface) + *	If no <options> are specified, then assume the h/w is already setup. + * + *	Returns 0 if console matches; otherwise non-zero to use default matching + */ +static int __init pl011_console_match(struct console *co, char *name, int idx, +				      char *options) +{ +	unsigned char iotype; +	resource_size_t addr; +	int i; + +	if (strcmp(name, "pl011") != 0) +		return -ENODEV; + +	if (uart_parse_earlycon(options, &iotype, &addr, &options)) +		return -ENODEV; + +	if (iotype != UPIO_MEM && iotype != UPIO_MEM32) +		return -ENODEV; + +	/* try to match the port specified on the command line */ +	for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { +		struct uart_port *port; + +		if (!amba_ports[i]) +			continue; + +		port = &amba_ports[i]->port; + +		if (port->mapbase != addr) +			continue; + +		co->index = i; +		port->cons = co; +		return pl011_console_setup(co, options); +	} + +	return -ENODEV; +} +  static struct uart_driver amba_reg;  static struct console amba_console = {  	.name		= "ttyAMA",  	.write		= pl011_console_write,  	.device		= uart_console_device,  	.setup		= pl011_console_setup, +	.match		= pl011_console_match,  	.flags		= CON_PRINTBUFFER,  	.index		= -1,  	.data		= &amba_reg, @@ -2357,6 +2412,7 @@ static int __init pl011_early_console_setup(struct earlycon_device *device,  	return 0;  }  OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); +OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup);  #else  #define AMBA_CONSOLE	NULL diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 168b10cad47b..fabbe76203bb 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -481,6 +481,14 @@ static void atmel_stop_tx(struct uart_port *port)  		/* disable PDC transmit */  		atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);  	} + +	/* +	 * Disable the transmitter. +	 * This is mandatory when DMA is used, otherwise the DMA buffer +	 * is fully transmitted. +	 */ +	atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS); +  	/* Disable interrupts */  	atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); @@ -513,6 +521,9 @@ static void atmel_start_tx(struct uart_port *port)  	/* Enable interrupts */  	atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask); + +	/* re-enable the transmitter */ +	atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);  }  /* @@ -798,6 +809,11 @@ static void atmel_complete_tx_dma(void *arg)  	 */  	if (!uart_circ_empty(xmit))  		atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx); +	else if ((port->rs485.flags & SER_RS485_ENABLED) && +		 !(port->rs485.flags & SER_RS485_RX_DURING_TX)) { +		/* DMA done, stop TX, start RX for RS485 */ +		atmel_start_rx(port); +	}  	spin_unlock_irqrestore(&port->lock, flags);  } @@ -900,12 +916,6 @@ static void atmel_tx_dma(struct uart_port *port)  		desc->callback = atmel_complete_tx_dma;  		desc->callback_param = atmel_port;  		atmel_port->cookie_tx = dmaengine_submit(desc); - -	} else { -		if (port->rs485.flags & SER_RS485_ENABLED) { -			/* DMA done, stop TX, start RX for RS485 */ -			atmel_start_rx(port); -		}  	}  	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 6450a38cb1aa..e92c23470e51 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3213,8 +3213,6 @@ get_serial_info(struct e100_serial * info,  	 * should set them to something else than 0.  	 */ -	if (!retinfo) -		return -EFAULT;  	memset(&tmp, 0, sizeof(tmp));  	tmp.type = info->type;  	tmp.line = info->line; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 76103f2c4a80..a1c6519837a4 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -430,6 +430,65 @@ static void lpuart_flush_buffer(struct uart_port *port)  	}  } +#if defined(CONFIG_CONSOLE_POLL) + +static int lpuart_poll_init(struct uart_port *port) +{ +	struct lpuart_port *sport = container_of(port, +					struct lpuart_port, port); +	unsigned long flags; +	unsigned char temp; + +	sport->port.fifosize = 0; + +	spin_lock_irqsave(&sport->port.lock, flags); +	/* Disable Rx & Tx */ +	writeb(0, sport->port.membase + UARTCR2); + +	temp = readb(sport->port.membase + UARTPFIFO); +	/* Enable Rx and Tx FIFO */ +	writeb(temp | UARTPFIFO_RXFE | UARTPFIFO_TXFE, +			sport->port.membase + UARTPFIFO); + +	/* flush Tx and Rx FIFO */ +	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, +			sport->port.membase + UARTCFIFO); + +	/* explicitly clear RDRF */ +	if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { +		readb(sport->port.membase + UARTDR); +		writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); +	} + +	writeb(0, sport->port.membase + UARTTWFIFO); +	writeb(1, sport->port.membase + UARTRWFIFO); + +	/* Enable Rx and Tx */ +	writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); +	spin_unlock_irqrestore(&sport->port.lock, flags); + +	return 0; +} + +static void lpuart_poll_put_char(struct uart_port *port, unsigned char c) +{ +	/* drain */ +	while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) +		barrier(); + +	writeb(c, port->membase + UARTDR); +} + +static int lpuart_poll_get_char(struct uart_port *port) +{ +	if (!(readb(port->membase + UARTSR1) & UARTSR1_RDRF)) +		return NO_POLL_CHAR; + +	return readb(port->membase + UARTDR); +} + +#endif +  static inline void lpuart_transmit_buffer(struct lpuart_port *sport)  {  	struct circ_buf *xmit = &sport->port.state->xmit; @@ -1595,6 +1654,11 @@ static const struct uart_ops lpuart_pops = {  	.config_port	= lpuart_config_port,  	.verify_port	= lpuart_verify_port,  	.flush_buffer	= lpuart_flush_buffer, +#if defined(CONFIG_CONSOLE_POLL) +	.poll_init	= lpuart_poll_init, +	.poll_get_char	= lpuart_poll_get_char, +	.poll_put_char	= lpuart_poll_put_char, +#endif  };  static const struct uart_ops lpuart32_pops = { diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c index c60a8d5e4020..d83783cfbade 100644 --- a/drivers/tty/serial/icom.c +++ b/drivers/tty/serial/icom.c @@ -53,7 +53,7 @@  #include <asm/io.h>  #include <asm/irq.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include "icom.h" diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index d386346248de..157883653256 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1042,6 +1042,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi)  	ret = spi_setup(spi);  	if (ret) {  		dev_err(&spi->dev, "SPI setup wasn't successful %d", ret); +		kfree(ifx_dev);  		return -ENODEV;  	} diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index e5c42fef69d2..3be051abb2a2 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -1082,7 +1082,7 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd)  		if (!port) {  			printk(KERN_WARNING  				"IOC4 serial memory not available for port\n"); -			return -ENOMEM; +			goto free;  		}  		spin_lock_init(&port->ip_lock); @@ -1190,6 +1190,11 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd)  				handle_dma_error_intr, port);  	}  	return 0; + +free: +	while (port_number) +		kfree(ports[--port_number]); +	return -ENOMEM;  }  /** diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 770454e0dfa3..8c1c9112b3fd 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1016,7 +1016,7 @@ static void mxs_auart_settermios(struct uart_port *u,  			ctrl |= AUART_LINECTRL_EPS;  	} -	u->read_status_mask = 0; +	u->read_status_mask = AUART_STAT_OERR;  	if (termios->c_iflag & INPCK)  		u->read_status_mask |= AUART_STAT_PERR; diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index cd9d9e878475..75952811c0da 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -925,6 +925,8 @@ static struct platform_driver serial_pxa_driver = {  	},  }; + +/* 8250 driver for PXA serial ports should be used */  static int __init serial_pxa_init(void)  {  	int ret; diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index fb0672554123..793395451982 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1264,7 +1264,7 @@ static int sc16is7xx_probe(struct device *dev,  	/* Setup interrupt */  	ret = devm_request_irq(dev, irq, sc16is7xx_irq, -			       IRQF_ONESHOT | flags, dev_name(dev), s); +			       flags, dev_name(dev), s);  	if (!ret)  		return 0; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f2303f390345..9939c3d9912b 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -36,7 +36,7 @@  #include <linux/mutex.h>  #include <asm/irq.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  /*   * This is used to lock changes in serial line configuration. @@ -73,7 +73,7 @@ static inline struct uart_port *uart_port_ref(struct uart_state *state)  static inline void uart_port_deref(struct uart_port *uport)  { -	if (uport && atomic_dec_and_test(&uport->state->refcount)) +	if (atomic_dec_and_test(&uport->state->refcount))  		wake_up(&uport->state->remove_wait);  } @@ -88,9 +88,10 @@ static inline void uart_port_deref(struct uart_port *uport)  #define uart_port_unlock(uport, flags)					\  	({								\  		struct uart_port *__uport = uport;			\ -		if (__uport)						\ +		if (__uport) {						\  			spin_unlock_irqrestore(&__uport->lock, flags);	\ -		uart_port_deref(__uport);				\ +			uart_port_deref(__uport);			\ +		}							\  	})  static inline struct uart_port *uart_port_check(struct uart_state *state) @@ -1515,7 +1516,10 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)  	unsigned long char_time, expire;  	port = uart_port_ref(state); -	if (!port || port->type == PORT_UNKNOWN || port->fifosize == 0) { +	if (!port) +		return; + +	if (port->type == PORT_UNKNOWN || port->fifosize == 0) {  		uart_port_deref(port);  		return;  	} @@ -2365,9 +2369,10 @@ static int uart_poll_get_char(struct tty_driver *driver, int line)  	if (state) {  		port = uart_port_ref(state); -		if (port) +		if (port) {  			ret = port->ops->poll_get_char(port); -		uart_port_deref(port); +			uart_port_deref(port); +		}  	}  	return ret;  } diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4b26252c2885..91e7dddbf72c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1142,11 +1142,8 @@ static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count)  	int copied;  	copied = tty_insert_flip_string(tport, buf, count); -	if (copied < count) { -		dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", -			 count - copied); +	if (copied < count)  		port->icount.buf_overrun++; -	}  	port->icount.rx += copied; @@ -1161,8 +1158,6 @@ static int sci_dma_rx_find_active(struct sci_port *s)  		if (s->active_rx == s->cookie_rx[i])  			return i; -	dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__, -		s->active_rx);  	return -1;  } @@ -1223,9 +1218,9 @@ static void sci_dma_rx_complete(void *arg)  	dma_async_issue_pending(chan); +	spin_unlock_irqrestore(&port->lock, flags);  	dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",  		__func__, s->cookie_rx[active], active, s->active_rx); -	spin_unlock_irqrestore(&port->lock, flags);  	return;  fail: @@ -1273,8 +1268,6 @@ static void sci_submit_rx(struct sci_port *s)  		if (dma_submit_error(s->cookie_rx[i]))  			goto fail; -		dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, -			s->cookie_rx[i], i);  	}  	s->active_rx = s->cookie_rx[0]; @@ -1288,7 +1281,6 @@ fail:  	for (i = 0; i < 2; i++)  		s->cookie_rx[i] = -EINVAL;  	s->active_rx = -EINVAL; -	dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n");  	sci_rx_dma_release(s, true);  } @@ -1358,10 +1350,10 @@ static void rx_timer_fn(unsigned long arg)  	int active, count;  	u16 scr; -	spin_lock_irqsave(&port->lock, flags); -  	dev_dbg(port->dev, "DMA Rx timed out\n"); +	spin_lock_irqsave(&port->lock, flags); +  	active = sci_dma_rx_find_active(s);  	if (active < 0) {  		spin_unlock_irqrestore(&port->lock, flags); @@ -1370,9 +1362,9 @@ static void rx_timer_fn(unsigned long arg)  	status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);  	if (status == DMA_COMPLETE) { +		spin_unlock_irqrestore(&port->lock, flags);  		dev_dbg(port->dev, "Cookie %d #%d has already completed\n",  			s->active_rx, active); -		spin_unlock_irqrestore(&port->lock, flags);  		/* Let packet complete handler take care of the packet */  		return; @@ -1396,8 +1388,6 @@ static void rx_timer_fn(unsigned long arg)  	/* Handle incomplete DMA receive */  	dmaengine_terminate_all(s->chan_rx);  	read = sg_dma_len(&s->sg_rx[active]) - state.residue; -	dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, -		s->active_rx);  	if (read) {  		count = sci_dma_rx_push(s, s->rx_buf[active], read); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 4e603d060e80..99ef5c6e4766 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -598,7 +598,8 @@ static int hv_remove(struct platform_device *dev)  	uart_remove_one_port(&sunhv_reg, port);  	sunserial_unregister_minors(&sunhv_reg, 1); - +	kfree(con_read_page); +	kfree(con_write_page);  	kfree(port);  	sunhv_port = NULL; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 9ad98eaa35bf..72df2e1b88af 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1500,6 +1500,7 @@ static int su_probe(struct platform_device *op)  out_unmap:  	of_iounmap(&op->resource[0], up->port.membase, up->reg_size); +	kfree(up);  	return err;  } diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 415885c56435..657eed82eeb3 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -107,7 +107,7 @@  #define PUT_USER(error,value,addr) error = put_user(value,addr)  #define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #define RCLRVALUE 0xffff diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 8267bcf2405e..31885f20fc15 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -77,7 +77,7 @@  #include <asm/irq.h>  #include <asm/dma.h>  #include <asm/types.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))  #define SYNCLINK_GENERIC_HDLC 1 diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index d66620f7eaa3..51e8846cd68f 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -79,7 +79,7 @@  #define PUT_USER(error,value,addr) error = put_user(value,addr)  #define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 -#include <asm/uaccess.h> +#include <linux/uaccess.h>  static MGSL_PARAMS default_params = {  	MGSL_MODE_HDLC,			/* unsigned long mode */ diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 52bbd27e93ae..701c085bb19b 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -946,8 +946,8 @@ static const struct input_device_id sysrq_ids[] = {  	{  		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |  				INPUT_DEVICE_ID_MATCH_KEYBIT, -		.evbit = { BIT_MASK(EV_KEY) }, -		.keybit = { BIT_MASK(KEY_LEFTALT) }, +		.evbit = { [BIT_WORD(EV_KEY)] = BIT_MASK(EV_KEY) }, +		.keybit = { [BIT_WORD(KEY_LEFTALT)] = BIT_MASK(KEY_LEFTALT) },  	},  	{ },  }; diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index bf36ac9aee41..f27fc0f14c11 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -22,7 +22,7 @@  #include <linux/compat.h>  #include <asm/io.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #undef TTY_DEBUG_WAIT_UNTIL_SENT diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 9d7ab7b66a8a..1f6e17fc3fb0 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -9,6 +9,17 @@   * Support for multiple unimaps by Jakub Jelinek <[email protected]>, July 1998   *   * Fix bug in inverse translation. Stanislav Voronyi <[email protected]>, Dec 1998 + * + * In order to prevent the following circular lock dependency: + *   &mm->mmap_sem --> cpu_hotplug.lock --> console_lock --> &mm->mmap_sem + * + * We cannot allow page fault to happen while holding the console_lock. + * Therefore, all the userspace copy operations have to be done outside + * the console_lock critical sections. + * + * As all the affected functions are all called directly from vt_ioctl(), we + * can allocate some small buffers directly on stack without worrying about + * stack overflow.   */  #include <linux/module.h> @@ -18,10 +29,11 @@  #include <linux/slab.h>  #include <linux/init.h>  #include <linux/tty.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <linux/console.h>  #include <linux/consolemap.h>  #include <linux/vt_kern.h> +#include <linux/string.h>  static unsigned short translations[][256] = {    /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ @@ -309,18 +321,19 @@ static void update_user_maps(void)  int con_set_trans_old(unsigned char __user * arg)  {  	int i; -	unsigned short *p = translations[USER_MAP]; +	unsigned short inbuf[E_TABSZ];  	if (!access_ok(VERIFY_READ, arg, E_TABSZ))  		return -EFAULT; -	console_lock(); -	for (i=0; i<E_TABSZ ; i++) { +	for (i = 0; i < E_TABSZ ; i++) {  		unsigned char uc;  		__get_user(uc, arg+i); -		p[i] = UNI_DIRECT_BASE | uc; +		inbuf[i] = UNI_DIRECT_BASE | uc;  	} +	console_lock(); +	memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));  	update_user_maps();  	console_unlock();  	return 0; @@ -330,35 +343,37 @@ int con_get_trans_old(unsigned char __user * arg)  {  	int i, ch;  	unsigned short *p = translations[USER_MAP]; +	unsigned char outbuf[E_TABSZ];  	if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))  		return -EFAULT;  	console_lock(); -	for (i=0; i<E_TABSZ ; i++) +	for (i = 0; i < E_TABSZ ; i++)  	{  		ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); -		__put_user((ch & ~0xff) ? 0 : ch, arg+i); +		outbuf[i] = (ch & ~0xff) ? 0 : ch;  	}  	console_unlock(); + +	for (i = 0; i < E_TABSZ ; i++) +		__put_user(outbuf[i], arg+i);  	return 0;  }  int con_set_trans_new(ushort __user * arg)  {  	int i; -	unsigned short *p = translations[USER_MAP]; +	unsigned short inbuf[E_TABSZ];  	if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))  		return -EFAULT; -	console_lock(); -	for (i=0; i<E_TABSZ ; i++) { -		unsigned short us; -		__get_user(us, arg+i); -		p[i] = us; -	} +	for (i = 0; i < E_TABSZ ; i++) +		__get_user(inbuf[i], arg+i); +	console_lock(); +	memcpy(translations[USER_MAP], inbuf, sizeof(inbuf));  	update_user_maps();  	console_unlock();  	return 0; @@ -367,16 +382,17 @@ int con_set_trans_new(ushort __user * arg)  int con_get_trans_new(ushort __user * arg)  {  	int i; -	unsigned short *p = translations[USER_MAP]; +	unsigned short outbuf[E_TABSZ];  	if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))  		return -EFAULT;  	console_lock(); -	for (i=0; i<E_TABSZ ; i++) -	  __put_user(p[i], arg+i); +	memcpy(outbuf, translations[USER_MAP], sizeof(outbuf));  	console_unlock(); -	 + +	for (i = 0; i < E_TABSZ ; i++) +		__put_user(outbuf[i], arg+i);  	return 0;  } @@ -536,10 +552,20 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)  {  	int err = 0, err1, i;  	struct uni_pagedir *p, *q; +	struct unipair *unilist, *plist;  	if (!ct)  		return 0; +	unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); +	if (!unilist) +		return -ENOMEM; + +	for (i = ct, plist = unilist; i; i--, plist++, list++) { +		__get_user(plist->unicode, &list->unicode); +		__get_user(plist->fontpos, &list->fontpos); +	} +  	console_lock();  	/* Save original vc_unipagdir_loc in case we allocate a new one */ @@ -557,8 +583,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)  		err1 = con_do_clear_unimap(vc);  		if (err1) { -			console_unlock(); -			return err1; +			err = err1; +			goto out_unlock;  		}  		/* @@ -592,8 +618,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)  						*vc->vc_uni_pagedir_loc = p;  						con_release_unimap(q);  						kfree(q); -						console_unlock(); -						return err1;  +						err = err1; +						goto out_unlock;  					}  				}  			} else { @@ -617,22 +643,17 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)  	/*  	 * Insert user specified unicode pairs into new table.  	 */ -	while (ct--) { -		unsigned short unicode, fontpos; -		__get_user(unicode, &list->unicode); -		__get_user(fontpos, &list->fontpos); -		if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) +	for (plist = unilist; ct; ct--, plist++) { +		err1 = con_insert_unipair(p, plist->unicode, plist->fontpos); +		if (err1)  			err = err1; -		list++;  	}  	/*  	 * Merge with fontmaps of any other virtual consoles.  	 */ -	if (con_unify_unimap(vc, p)) { -		console_unlock(); -		return err; -	} +	if (con_unify_unimap(vc, p)) +		goto out_unlock;  	for (i = 0; i <= 3; i++)  		set_inverse_transl(vc, p, i); /* Update inverse translations */ @@ -640,6 +661,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)  out_unlock:  	console_unlock(); +	kfree(unilist);  	return err;  } @@ -735,9 +757,15 @@ EXPORT_SYMBOL(con_copy_unimap);   */  int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list)  { -	int i, j, k, ect; +	int i, j, k; +	ushort ect;  	u16 **p1, *p2;  	struct uni_pagedir *p; +	struct unipair *unilist, *plist; + +	unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); +	if (!unilist) +		return -ENOMEM;  	console_lock(); @@ -750,21 +778,26 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni  			for (j = 0; j < 32; j++) {  			p2 = *(p1++);  			if (p2) -				for (k = 0; k < 64; k++) { -					if (*p2 < MAX_GLYPH && ect++ < ct) { -						__put_user((u_short)((i<<11)+(j<<6)+k), -							   &list->unicode); -						__put_user((u_short) *p2,  -							   &list->fontpos); -						list++; +				for (k = 0; k < 64; k++, p2++) { +					if (*p2 >= MAX_GLYPH) +						continue; +					if (ect < ct) { +						unilist[ect].unicode = +							(i<<11)+(j<<6)+k; +						unilist[ect].fontpos = *p2;  					} -					p2++; +					ect++;  				}  			}  		}  	} -	__put_user(ect, uct);  	console_unlock(); +	for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) { +		__put_user(plist->unicode, &list->unicode); +		__put_user(plist->fontpos, &list->fontpos); +	} +	__put_user(ect, uct); +	kfree(unilist);  	return ((ect <= ct) ? 0 : -ENOMEM);  } diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 0f8caae4267d..3dd6a491cdba 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev)  	KBD_LED_TRIGGER((_led_bit) + 8, _name)  static struct kbd_led_trigger kbd_led_triggers[] = { -	KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), +	KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),  	KBD_LED_TRIGGER(VC_NUMLOCK,   "kbd-numlock"),  	KBD_LED_TRIGGER(VC_CAPSLOCK,  "kbd-capslock"),  	KBD_LED_TRIGGER(VC_KANALOCK,  "kbd-kanalock"), @@ -1256,7 +1256,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,  	case KEY_SYSRQ:  		/*  		 * Real AT keyboards (that's what we're trying -		 * to emulate here emit 0xe0 0x2a 0xe0 0x37 when +		 * to emulate here) emit 0xe0 0x2a 0xe0 0x37 when  		 * pressing PrtSc/SysRq alone, but simply 0x54  		 * when pressing Alt+PrtSc/SysRq.  		 */ diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 368ce1803e8f..36e1b8c7680f 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -16,7 +16,7 @@  #include <linux/slab.h>  #include <linux/types.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <linux/kbd_kern.h>  #include <linux/vt_kern.h> diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 14a2b5f11bca..56dcff6059d3 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -39,7 +39,7 @@  #include <linux/slab.h>  #include <linux/notifier.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <asm/byteorder.h>  #include <asm/unaligned.h> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8c3bf3d613c0..4c10a9df3b91 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -315,38 +315,27 @@ void schedule_console_callback(void)  	schedule_work(&console_work);  } -static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) +static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, +		enum con_scroll dir, unsigned int nr)  { -	unsigned short *d, *s; +	u16 *clear, *d, *s; -	if (t+nr >= b) +	if (t + nr >= b)  		nr = b - t - 1;  	if (b > vc->vc_rows || t >= b || nr < 1)  		return; -	if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr)) +	if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))  		return; -	d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); -	s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); -	scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); -	scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, -		    vc->vc_size_row * nr); -} -static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) -{ -	unsigned short *s; -	unsigned int step; +	s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t); +	d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr)); -	if (t+nr >= b) -		nr = b - t - 1; -	if (b > vc->vc_rows || t >= b || nr < 1) -		return; -	if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_DOWN, nr)) -		return; -	s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); -	step = vc->vc_cols * nr; -	scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); -	scr_memsetw(s, vc->vc_video_erase_char, 2 * step); +	if (dir == SM_UP) { +		clear = s + (b - t - nr) * vc->vc_cols; +		swap(s, d); +	} +	scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); +	scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);  }  static void do_update_region(struct vc_data *vc, unsigned long start, int count) @@ -1120,7 +1109,7 @@ static void lf(struct vc_data *vc)  	 * if below scrolling region  	 */      	if (vc->vc_y + 1 == vc->vc_bottom) -		scrup(vc, vc->vc_top, vc->vc_bottom, 1); +		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_UP, 1);  	else if (vc->vc_y < vc->vc_rows - 1) {  	    	vc->vc_y++;  		vc->vc_pos += vc->vc_size_row; @@ -1135,7 +1124,7 @@ static void ri(struct vc_data *vc)  	 * if above scrolling region  	 */  	if (vc->vc_y == vc->vc_top) -		scrdown(vc, vc->vc_top, vc->vc_bottom, 1); +		con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_DOWN, 1);  	else if (vc->vc_y > 0) {  		vc->vc_y--;  		vc->vc_pos -= vc->vc_size_row; @@ -1631,7 +1620,7 @@ static void csi_L(struct vc_data *vc, unsigned int nr)  		nr = vc->vc_rows - vc->vc_y;  	else if (!nr)  		nr = 1; -	scrdown(vc, vc->vc_y, vc->vc_bottom, nr); +	con_scroll(vc, vc->vc_y, vc->vc_bottom, SM_DOWN, nr);  	vc->vc_need_wrap = 0;  } @@ -1652,7 +1641,7 @@ static void csi_M(struct vc_data *vc, unsigned int nr)  		nr = vc->vc_rows - vc->vc_y;  	else if (!nr)  		nr=1; -	scrup(vc, vc->vc_y, vc->vc_bottom, nr); +	con_scroll(vc, vc->vc_y, vc->vc_bottom, SM_UP, nr);  	vc->vc_need_wrap = 0;  } @@ -3934,10 +3923,6 @@ void unblank_screen(void)   */  static void blank_screen_t(unsigned long dummy)  { -	if (unlikely(!keventd_up())) { -		mod_timer(&console_timer, jiffies + (blankinterval * HZ)); -		return; -	}  	blank_timer_expired = 1;  	schedule_work(&console_work);  } @@ -4295,6 +4280,46 @@ void vcs_scr_updated(struct vc_data *vc)  	notify_update(vc);  } +void vc_scrolldelta_helper(struct vc_data *c, int lines, +		unsigned int rolled_over, void *base, unsigned int size) +{ +	unsigned long ubase = (unsigned long)base; +	ptrdiff_t scr_end = (void *)c->vc_scr_end - base; +	ptrdiff_t vorigin = (void *)c->vc_visible_origin - base; +	ptrdiff_t origin = (void *)c->vc_origin - base; +	int margin = c->vc_size_row * 4; +	int from, wrap, from_off, avail; + +	/* Turn scrollback off */ +	if (!lines) { +		c->vc_visible_origin = c->vc_origin; +		return; +	} + +	/* Do we have already enough to allow jumping from 0 to the end? */ +	if (rolled_over > scr_end + margin) { +		from = scr_end; +		wrap = rolled_over + c->vc_size_row; +	} else { +		from = 0; +		wrap = size; +	} + +	from_off = (vorigin - from + wrap) % wrap + lines * c->vc_size_row; +	avail = (origin - from + wrap) % wrap; + +	/* Only a little piece would be left? Show all incl. the piece! */ +	if (avail < 2 * margin) +		margin = 0; +	if (from_off < margin) +		from_off = 0; +	if (from_off > avail - margin) +		from_off = avail; + +	c->vc_visible_origin = ubase + (from + from_off) % wrap; +} +EXPORT_SYMBOL_GPL(vc_scrolldelta_helper); +  /*   *	Visible symbols for modules   */ diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index f62c598810ff..a56edf2d58eb 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -29,7 +29,7 @@  #include <linux/timex.h>  #include <asm/io.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h>  #include <linux/kbd_kern.h>  #include <linux/vt_kern.h> |