Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 496c9077 authored by Quoc-Viet Nguyen's avatar Quoc-Viet Nguyen Committed by Greg Kroah-Hartman
Browse files

serial: mcf: Add support RS485 in ColdFire serial driver

parent b81273a1
Loading
Loading
Loading
Loading
+71 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/serial.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_core.h>
#include <linux/io.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <asm/coldfire.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include <asm/mcfsim.h>
#include <asm/mcfuart.h>
#include <asm/mcfuart.h>
@@ -55,6 +56,7 @@ struct mcf_uart {
	struct uart_port	port;
	struct uart_port	port;
	unsigned int		sigs;		/* Local copy of line sigs */
	unsigned int		sigs;		/* Local copy of line sigs */
	unsigned char		imr;		/* Local IMR mirror */
	unsigned char		imr;		/* Local IMR mirror */
	struct serial_rs485	rs485;		/* RS485 settings */
};
};


/****************************************************************************/
/****************************************************************************/
@@ -101,6 +103,12 @@ static void mcf_start_tx(struct uart_port *port)
{
{
	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);


	if (pp->rs485.flags & SER_RS485_ENABLED) {
		/* Enable Transmitter */
		writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR);
		/* Manually assert RTS */
		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
	}
	pp->imr |= MCFUART_UIR_TXREADY;
	pp->imr |= MCFUART_UIR_TXREADY;
	writeb(pp->imr, port->membase + MCFUART_UIMR);
	writeb(pp->imr, port->membase + MCFUART_UIMR);
}
}
@@ -196,6 +204,7 @@ static void mcf_shutdown(struct uart_port *port)
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
	struct ktermios *old)
	struct ktermios *old)
{
{
	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
	unsigned long flags;
	unsigned long flags;
	unsigned int baud, baudclk;
	unsigned int baud, baudclk;
#if defined(CONFIG_M5272)
#if defined(CONFIG_M5272)
@@ -248,6 +257,11 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
		mr2 |= MCFUART_MR2_TXCTS;
		mr2 |= MCFUART_MR2_TXCTS;
	}
	}


	if (pp->rs485.flags & SER_RS485_ENABLED) {
		dev_dbg(port->dev, "Setting UART to RS485\n");
		mr2 |= MCFUART_MR2_TXRTS;
	}

	spin_lock_irqsave(&port->lock, flags);
	spin_lock_irqsave(&port->lock, flags);
	uart_update_timeout(port, termios->c_cflag, baud);
	uart_update_timeout(port, termios->c_cflag, baud);
	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
@@ -342,6 +356,10 @@ static void mcf_tx_chars(struct mcf_uart *pp)
	if (xmit->head == xmit->tail) {
	if (xmit->head == xmit->tail) {
		pp->imr &= ~MCFUART_UIR_TXREADY;
		pp->imr &= ~MCFUART_UIR_TXREADY;
		writeb(pp->imr, port->membase + MCFUART_UIMR);
		writeb(pp->imr, port->membase + MCFUART_UIMR);
		/* Disable TX to negate RTS automatically */
		if (pp->rs485.flags & SER_RS485_ENABLED)
			writeb(MCFUART_UCR_TXDISABLE,
				port->membase + MCFUART_UCR);
	}
	}
}
}


@@ -418,6 +436,58 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)


/****************************************************************************/
/****************************************************************************/


/* Enable or disable the RS485 support */
static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
{
	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
	unsigned long flags;
	unsigned char mr1, mr2;

	spin_lock_irqsave(&port->lock, flags);
	/* Get mode registers */
	mr1 = readb(port->membase + MCFUART_UMR);
	mr2 = readb(port->membase + MCFUART_UMR);
	if (rs485->flags & SER_RS485_ENABLED) {
		dev_dbg(port->dev, "Setting UART to RS485\n");
		/* Automatically negate RTS after TX completes */
		mr2 |= MCFUART_MR2_TXRTS;
	} else {
		dev_dbg(port->dev, "Setting UART to RS232\n");
		mr2 &= ~MCFUART_MR2_TXRTS;
	}
	writeb(mr1, port->membase + MCFUART_UMR);
	writeb(mr2, port->membase + MCFUART_UMR);
	pp->rs485 = *rs485;
	spin_unlock_irqrestore(&port->lock, flags);
}

static int mcf_ioctl(struct uart_port *port, unsigned int cmd,
		unsigned long arg)
{
	switch (cmd) {
	case TIOCSRS485: {
		struct serial_rs485 rs485;
		if (copy_from_user(&rs485, (struct serial_rs485 *)arg,
				sizeof(struct serial_rs485)))
			return -EFAULT;
		mcf_config_rs485(port, &rs485);
		break;
	}
	case TIOCGRS485: {
		struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
		if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485,
				sizeof(struct serial_rs485)))
			return -EFAULT;
		break;
	}
	default:
		return -ENOIOCTLCMD;
	}
	return 0;
}

/****************************************************************************/

/*
/*
 *	Define the basic serial functions we support.
 *	Define the basic serial functions we support.
 */
 */
@@ -438,6 +508,7 @@ static const struct uart_ops mcf_uart_ops = {
	.release_port	= mcf_release_port,
	.release_port	= mcf_release_port,
	.config_port	= mcf_config_port,
	.config_port	= mcf_config_port,
	.verify_port	= mcf_verify_port,
	.verify_port	= mcf_verify_port,
	.ioctl		= mcf_ioctl,
};
};


static struct mcf_uart mcf_ports[4];
static struct mcf_uart mcf_ports[4];