diff options
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
| -rw-r--r-- | drivers/tty/serial/atmel_serial.c | 320 | 
1 files changed, 243 insertions, 77 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index d067285a2d2..c4f75031410 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -35,21 +35,21 @@  #include <linux/platform_device.h>  #include <linux/of.h>  #include <linux/of_device.h> +#include <linux/of_gpio.h>  #include <linux/dma-mapping.h>  #include <linux/atmel_pdc.h>  #include <linux/atmel_serial.h>  #include <linux/uaccess.h>  #include <linux/platform_data/atmel.h>  #include <linux/timer.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/err.h> +#include <linux/irq.h>  #include <asm/io.h>  #include <asm/ioctls.h> -#ifdef CONFIG_ARM -#include <mach/cpu.h> -#include <asm/gpio.h> -#endif -  #define PDC_BUFFER_SIZE		512  /* Revisit: We should calculate this based on the actual port settings */  #define PDC_RX_TIMEOUT		(3 * 10)		/* 3 bytes */ @@ -60,6 +60,8 @@  #include <linux/serial_core.h> +#include "serial_mctrl_gpio.h" +  static void atmel_start_rx(struct uart_port *port);  static void atmel_stop_rx(struct uart_port *port); @@ -99,6 +101,7 @@ static void atmel_stop_rx(struct uart_port *port);  #define UART_PUT_RTOR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)  #define UART_PUT_TTGR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_TTGR)  #define UART_GET_IP_NAME(port)	__raw_readl((port)->membase + ATMEL_US_NAME) +#define UART_GET_IP_VERSION(port) __raw_readl((port)->membase + ATMEL_US_VERSION)   /* PDC registers */  #define UART_PUT_PTCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -114,9 +117,6 @@ static void atmel_stop_rx(struct uart_port *port);  #define UART_PUT_TCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TCR)  #define UART_GET_TCR(port)	__raw_readl((port)->membase + ATMEL_PDC_TCR) -static int (*atmel_open_hook)(struct uart_port *); -static void (*atmel_close_hook)(struct uart_port *); -  struct atmel_dma_buffer {  	unsigned char	*buf;  	dma_addr_t	dma_addr; @@ -167,7 +167,10 @@ struct atmel_uart_port {  	struct circ_buf		rx_ring;  	struct serial_rs485	rs485;		/* rs485 settings */ +	struct mctrl_gpios	*gpios; +	int			gpio_irq[UART_GPIO_MAX];  	unsigned int		tx_done_mask; +	bool			ms_irq_enabled;  	bool			is_usart;	/* usart or uart */  	struct timer_list	uart_timer;	/* uart timer */  	int (*prepare_rx)(struct uart_port *port); @@ -241,6 +244,50 @@ static bool atmel_use_dma_rx(struct uart_port *port)  	return atmel_port->use_dma_rx;  } +static unsigned int atmel_get_lines_status(struct uart_port *port) +{ +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); +	unsigned int status, ret = 0; + +	status = UART_GET_CSR(port); + +	mctrl_gpio_get(atmel_port->gpios, &ret); + +	if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, +						UART_GPIO_CTS))) { +		if (ret & TIOCM_CTS) +			status &= ~ATMEL_US_CTS; +		else +			status |= ATMEL_US_CTS; +	} + +	if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, +						UART_GPIO_DSR))) { +		if (ret & TIOCM_DSR) +			status &= ~ATMEL_US_DSR; +		else +			status |= ATMEL_US_DSR; +	} + +	if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, +						UART_GPIO_RI))) { +		if (ret & TIOCM_RI) +			status &= ~ATMEL_US_RI; +		else +			status |= ATMEL_US_RI; +	} + +	if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios, +						UART_GPIO_DCD))) { +		if (ret & TIOCM_CD) +			status &= ~ATMEL_US_DCD; +		else +			status |= ATMEL_US_DCD; +	} + +	return status; +} +  /* Enable or disable the rs485 support */  void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)  { @@ -300,21 +347,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)  	unsigned int mode;  	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); -#ifdef CONFIG_ARCH_AT91RM9200 -	if (cpu_is_at91rm9200()) { -		/* -		 * AT91RM9200 Errata #39: RTS0 is not internally connected -		 * to PA21. We need to drive the pin manually. -		 */ -		if (port->mapbase == AT91RM9200_BASE_US0) { -			if (mctrl & TIOCM_RTS) -				at91_set_gpio_value(AT91_PIN_PA21, 0); -			else -				at91_set_gpio_value(AT91_PIN_PA21, 1); -		} -	} -#endif -  	if (mctrl & TIOCM_RTS)  		control |= ATMEL_US_RTSEN;  	else @@ -327,6 +359,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)  	UART_PUT_CR(port, control); +	mctrl_gpio_set(atmel_port->gpios, mctrl); +  	/* Local loopback mode? */  	mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;  	if (mctrl & TIOCM_LOOP) @@ -354,7 +388,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)   */  static u_int atmel_get_mctrl(struct uart_port *port)  { -	unsigned int status, ret = 0; +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); +	unsigned int ret = 0, status;  	status = UART_GET_CSR(port); @@ -370,7 +405,7 @@ static u_int atmel_get_mctrl(struct uart_port *port)  	if (!(status & ATMEL_US_RI))  		ret |= TIOCM_RI; -	return ret; +	return mctrl_gpio_get(atmel_port->gpios, &ret);  }  /* @@ -457,8 +492,38 @@ static void atmel_stop_rx(struct uart_port *port)   */  static void atmel_enable_ms(struct uart_port *port)  { -	UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC -			| ATMEL_US_DCDIC | ATMEL_US_CTSIC); +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); +	uint32_t ier = 0; + +	/* +	 * Interrupt should not be enabled twice +	 */ +	if (atmel_port->ms_irq_enabled) +		return; + +	atmel_port->ms_irq_enabled = true; + +	if (atmel_port->gpio_irq[UART_GPIO_CTS] >= 0) +		enable_irq(atmel_port->gpio_irq[UART_GPIO_CTS]); +	else +		ier |= ATMEL_US_CTSIC; + +	if (atmel_port->gpio_irq[UART_GPIO_DSR] >= 0) +		enable_irq(atmel_port->gpio_irq[UART_GPIO_DSR]); +	else +		ier |= ATMEL_US_DSRIC; + +	if (atmel_port->gpio_irq[UART_GPIO_RI] >= 0) +		enable_irq(atmel_port->gpio_irq[UART_GPIO_RI]); +	else +		ier |= ATMEL_US_RIIC; + +	if (atmel_port->gpio_irq[UART_GPIO_DCD] >= 0) +		enable_irq(atmel_port->gpio_irq[UART_GPIO_DCD]); +	else +		ier |= ATMEL_US_DCDIC; + +	UART_PUT_IER(port, ier);  }  /* @@ -824,9 +889,6 @@ static void atmel_release_rx_dma(struct uart_port *port)  	atmel_port->desc_rx = NULL;  	atmel_port->chan_rx = NULL;  	atmel_port->cookie_rx = -EINVAL; - -	if (!atmel_port->is_usart) -		del_timer_sync(&atmel_port->uart_timer);  }  static void atmel_rx_from_dma(struct uart_port *port) @@ -1050,11 +1112,31 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,  static irqreturn_t atmel_interrupt(int irq, void *dev_id)  {  	struct uart_port *port = dev_id; +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);  	unsigned int status, pending, pass_counter = 0; +	bool gpio_handled = false;  	do { -		status = UART_GET_CSR(port); +		status = atmel_get_lines_status(port);  		pending = status & UART_GET_IMR(port); +		if (!gpio_handled) { +			/* +			 * Dealing with GPIO interrupt +			 */ +			if (irq == atmel_port->gpio_irq[UART_GPIO_CTS]) +				pending |= ATMEL_US_CTSIC; + +			if (irq == atmel_port->gpio_irq[UART_GPIO_DSR]) +				pending |= ATMEL_US_DSRIC; + +			if (irq == atmel_port->gpio_irq[UART_GPIO_RI]) +				pending |= ATMEL_US_RIIC; + +			if (irq == atmel_port->gpio_irq[UART_GPIO_DCD]) +				pending |= ATMEL_US_DCDIC; + +			gpio_handled = true; +		}  		if (!pending)  			break; @@ -1228,9 +1310,6 @@ static void atmel_release_rx_pdc(struct uart_port *port)  				 DMA_FROM_DEVICE);  		kfree(pdc->buf);  	} - -	if (!atmel_port->is_usart) -		del_timer_sync(&atmel_port->uart_timer);  }  static void atmel_rx_from_pdc(struct uart_port *port) @@ -1499,10 +1578,11 @@ static void atmel_set_ops(struct uart_port *port)  /*   * Get ip name usart or uart   */ -static int atmel_get_ip_name(struct uart_port *port) +static void atmel_get_ip_name(struct uart_port *port)  {  	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);  	int name = UART_GET_IP_NAME(port); +	u32 version;  	int usart, uart;  	/* usart and uart ascii */  	usart = 0x55534152; @@ -1517,11 +1597,62 @@ static int atmel_get_ip_name(struct uart_port *port)  		dev_dbg(port->dev, "This is uart\n");  		atmel_port->is_usart = false;  	} else { -		dev_err(port->dev, "Not supported ip name, set to uart\n"); -		return -EINVAL; +		/* fallback for older SoCs: use version field */ +		version = UART_GET_IP_VERSION(port); +		switch (version) { +		case 0x302: +		case 0x10213: +			dev_dbg(port->dev, "This version is usart\n"); +			atmel_port->is_usart = true; +			break; +		case 0x203: +		case 0x10202: +			dev_dbg(port->dev, "This version is uart\n"); +			atmel_port->is_usart = false; +			break; +		default: +			dev_err(port->dev, "Not supported ip name nor version, set to uart\n"); +		}  	} +} -	return 0; +static void atmel_free_gpio_irq(struct uart_port *port) +{ +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); +	enum mctrl_gpio_idx i; + +	for (i = 0; i < UART_GPIO_MAX; i++) +		if (atmel_port->gpio_irq[i] >= 0) +			free_irq(atmel_port->gpio_irq[i], port); +} + +static int atmel_request_gpio_irq(struct uart_port *port) +{ +	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); +	int *irq = atmel_port->gpio_irq; +	enum mctrl_gpio_idx i; +	int err = 0; + +	for (i = 0; (i < UART_GPIO_MAX) && !err; i++) { +		if (irq[i] < 0) +			continue; + +		irq_set_status_flags(irq[i], IRQ_NOAUTOEN); +		err = request_irq(irq[i], atmel_interrupt, IRQ_TYPE_EDGE_BOTH, +				  "atmel_serial", port); +		if (err) +			dev_err(port->dev, "atmel_startup - Can't get %d irq\n", +				irq[i]); +	} + +	/* +	 * If something went wrong, rollback. +	 */ +	while (err && (--i >= 0)) +		if (irq[i] >= 0) +			free_irq(irq[i], port); + +	return err;  }  /* @@ -1540,6 +1671,7 @@ static int atmel_startup(struct uart_port *port)  	 * handle an unexpected interrupt  	 */  	UART_PUT_IDR(port, -1); +	atmel_port->ms_irq_enabled = false;  	/*  	 * Allocate the IRQ @@ -1547,11 +1679,18 @@ static int atmel_startup(struct uart_port *port)  	retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,  			tty ? tty->name : "atmel_serial", port);  	if (retval) { -		printk("atmel_serial: atmel_startup - Can't get irq\n"); +		dev_err(port->dev, "atmel_startup - Can't get irq\n");  		return retval;  	}  	/* +	 * Get the GPIO lines IRQ +	 */ +	retval = atmel_request_gpio_irq(port); +	if (retval) +		goto free_irq; + +	/*  	 * Initialize DMA (if necessary)  	 */  	atmel_init_property(atmel_port, pdev); @@ -1567,20 +1706,9 @@ static int atmel_startup(struct uart_port *port)  		if (retval < 0)  			atmel_set_ops(port);  	} -	/* -	 * If there is a specific "open" function (to register -	 * control line interrupts) -	 */ -	if (atmel_open_hook) { -		retval = atmel_open_hook(port); -		if (retval) { -			free_irq(port->irq, port); -			return retval; -		} -	}  	/* Save current CSR for comparison in atmel_tasklet_func() */ -	atmel_port->irq_status_prev = UART_GET_CSR(port); +	atmel_port->irq_status_prev = atmel_get_lines_status(port);  	atmel_port->irq_status = atmel_port->irq_status_prev;  	/* @@ -1590,12 +1718,13 @@ static int atmel_startup(struct uart_port *port)  	/* enable xmit & rcvr */  	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); +	setup_timer(&atmel_port->uart_timer, +			atmel_uart_timer_callback, +			(unsigned long)port); +  	if (atmel_use_pdc_rx(port)) {  		/* set UART timeout */  		if (!atmel_port->is_usart) { -			setup_timer(&atmel_port->uart_timer, -					atmel_uart_timer_callback, -					(unsigned long)port);  			mod_timer(&atmel_port->uart_timer,  					jiffies + uart_poll_timeout(port));  		/* set USART timeout */ @@ -1610,9 +1739,6 @@ static int atmel_startup(struct uart_port *port)  	} else if (atmel_use_dma_rx(port)) {  		/* set UART timeout */  		if (!atmel_port->is_usart) { -			setup_timer(&atmel_port->uart_timer, -					atmel_uart_timer_callback, -					(unsigned long)port);  			mod_timer(&atmel_port->uart_timer,  					jiffies + uart_poll_timeout(port));  		/* set USART timeout */ @@ -1628,6 +1754,11 @@ static int atmel_startup(struct uart_port *port)  	}  	return 0; + +free_irq: +	free_irq(port->irq, port); + +	return retval;  }  /* @@ -1636,12 +1767,30 @@ static int atmel_startup(struct uart_port *port)  static void atmel_shutdown(struct uart_port *port)  {  	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + +	/* +	 * Prevent any tasklets being scheduled during +	 * cleanup +	 */ +	del_timer_sync(&atmel_port->uart_timer); + +	/* +	 * Clear out any scheduled tasklets before +	 * we destroy the buffers +	 */ +	tasklet_kill(&atmel_port->tasklet); +  	/* -	 * Ensure everything is stopped. +	 * Ensure everything is stopped and +	 * disable all interrupts, port and break condition.  	 */  	atmel_stop_rx(port);  	atmel_stop_tx(port); +	UART_PUT_CR(port, ATMEL_US_RSTSTA); +	UART_PUT_IDR(port, -1); + +  	/*  	 * Shut-down the DMA.  	 */ @@ -1651,22 +1800,18 @@ static void atmel_shutdown(struct uart_port *port)  		atmel_port->release_tx(port);  	/* -	 * Disable all interrupts, port and break condition. +	 * Reset ring buffer pointers  	 */ -	UART_PUT_CR(port, ATMEL_US_RSTSTA); -	UART_PUT_IDR(port, -1); +	atmel_port->rx_ring.head = 0; +	atmel_port->rx_ring.tail = 0;  	/* -	 * Free the interrupt +	 * Free the interrupts  	 */  	free_irq(port->irq, port); +	atmel_free_gpio_irq(port); -	/* -	 * If there is a specific "close" function (to unregister -	 * control line interrupts) -	 */ -	if (atmel_close_hook) -		atmel_close_hook(port); +	atmel_port->ms_irq_enabled = false;  }  /* @@ -1714,7 +1859,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,  		clk_disable_unprepare(atmel_port->clk);  		break;  	default: -		printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); +		dev_err(port->dev, "atmel_serial: unknown pm %d\n", state);  	}  } @@ -1787,7 +1932,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,  	port->read_status_mask = ATMEL_US_OVRE;  	if (termios->c_iflag & INPCK)  		port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); -	if (termios->c_iflag & (BRKINT | PARMRK)) +	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))  		port->read_status_mask |= ATMEL_US_RXBRK;  	if (atmel_use_pdc_rx(port)) @@ -1829,13 +1974,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,  	mode &= ~ATMEL_US_USMODE;  	if (atmel_port->rs485.flags & SER_RS485_ENABLED) { -		dev_dbg(port->dev, "Setting UART to RS485\n");  		if ((atmel_port->rs485.delay_rts_after_send) > 0)  			UART_PUT_TTGR(port,  					atmel_port->rs485.delay_rts_after_send);  		mode |= ATMEL_US_USMODE_RS485; -	} else { -		dev_dbg(port->dev, "Setting UART to RS232\n");  	}  	/* set the parity, stop bits and data size */ @@ -2330,6 +2472,26 @@ static int atmel_serial_resume(struct platform_device *pdev)  #define atmel_serial_resume NULL  #endif +static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) +{ +	enum mctrl_gpio_idx i; +	struct gpio_desc *gpiod; + +	p->gpios = mctrl_gpio_init(dev, 0); +	if (IS_ERR_OR_NULL(p->gpios)) +		return -1; + +	for (i = 0; i < UART_GPIO_MAX; i++) { +		gpiod = mctrl_gpio_to_gpiod(p->gpios, i); +		if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN)) +			p->gpio_irq[i] = gpiod_to_irq(gpiod); +		else +			p->gpio_irq[i] = -EINVAL; +	} + +	return 0; +} +  static int atmel_serial_probe(struct platform_device *pdev)  {  	struct atmel_uart_port *port; @@ -2366,6 +2528,11 @@ static int atmel_serial_probe(struct platform_device *pdev)  	port->backup_imr = 0;  	port->uart.line = ret; +	ret = atmel_init_gpios(port, &pdev->dev); +	if (ret < 0) +		dev_err(&pdev->dev, "%s", +			"Failed to initialize GPIOs. The serial port may not work as expected"); +  	ret = atmel_init_port(port, pdev);  	if (ret)  		goto err; @@ -2405,9 +2572,7 @@ static int atmel_serial_probe(struct platform_device *pdev)  	/*  	 * Get port name of usart or uart  	 */ -	ret = atmel_get_ip_name(&port->uart); -	if (ret < 0) -		goto err_add_port; +	atmel_get_ip_name(&port->uart);  	return 0; @@ -2429,11 +2594,12 @@ static int atmel_serial_remove(struct platform_device *pdev)  	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);  	int ret = 0; +	tasklet_kill(&atmel_port->tasklet); +  	device_init_wakeup(&pdev->dev, 0);  	ret = uart_remove_one_port(&atmel_uart, port); -	tasklet_kill(&atmel_port->tasklet);  	kfree(atmel_port->rx_ring.buf);  	/* "port" is allocated statically, so we shouldn't free it */  | 
