This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Add rs485 rts function into omap_serial.c

Hi

I using kernel 3.2 psp 6.0

I reference this guthub : 

gitorious.org/.../omap-serial.c

and attach my patch what I revised. What let me confuse is that 

       if (up->rs485.flags & SER_RS485_ENABLED)  --> does not work in function serial_omap_set_mctrl


How do I tell driver we will enable rs485 mode from user level? I check  kernel/include/linux/serial.h

struct serial_rs485 {
    __u32    flags;            /* RS485 feature flags */
#define SER_RS485_ENABLED        (1 << 0)    /* If enabled */
#define SER_RS485_RTS_ON_SEND        (1 << 1)    /* Logical level for
                               RTS pin when
                               sending */
#define SER_RS485_RTS_AFTER_SEND    (1 << 2)    /* Logical level for
                               RTS pin after sent*/
#define SER_RS485_RX_DURING_TX        (1 << 4)
    __u32    delay_rts_before_send;    /* Delay before send (milliseconds) */
    __u32    delay_rts_after_send;    /* Delay after send (milliseconds) */
    __u32    padding[5];        /* Memory is cheap, new structs
                       are a royal PITA .. */
};


I really need some hints, thank you.

 

diff --git a/arch/arm/mach-omap2/board-am335xevm.c b/arch/arm/mach-omap2/board-am335xevm.c
index 28578af..2271238 100644
--- a/arch/arm/mach-omap2/board-am335xevm.c
+++ b/arch/arm/mach-omap2/board-am335xevm.c
@@ -240,10 +240,8 @@ static struct omap_board_mux board_mux[] __initdata = {
 	 * module init call will change the mux accordingly.
 	 */
 	AM33XX_MUX(XDMA_EVENT_INTR0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT),
-	AM33XX_MUX(I2C0_SDA, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
-			AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),
-	AM33XX_MUX(I2C0_SCL, OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
-			AM33XX_INPUT_EN | AM33XX_PIN_OUTPUT),
+	AM33XX_MUX(I2C0_SDA, OMAP_MUX_MODE2 | AM33XX_INPUT_EN),
+	AM33XX_MUX(I2C0_SCL, OMAP_MUX_MODE2 | AM33XX_PIN_OUTPUT),
 	{ .reg_offset = OMAP_MUX_TERMINATOR },
 };
 #else
@@ -2014,7 +2012,6 @@ static void am335x_rtc_init(int evm_id, int profile)
 	struct omap_hwmod *oh;
 	struct platform_device *pdev;
 	char *dev_name = "am33xx-rtc";
-
 	clk = clk_get(NULL, "rtc_fck");
 	if (IS_ERR(clk)) {
 		pr_err("rtc : Failed to get RTC clock\n");
diff --git a/arch/arm/mach-omap2/mux33xx.c b/arch/arm/mach-omap2/mux33xx.c
index 2db09b2..591eb6b 100644
--- a/arch/arm/mach-omap2/mux33xx.c
+++ b/arch/arm/mach-omap2/mux33xx.c
@@ -334,10 +334,10 @@ static struct omap_mux am33xx_muxmodes[] = {
 		"uart1_txd", "mmc2_sdwp", NULL, "i2c1_scl",
 		NULL, "pr1_uart0_txd_mux1", NULL, "gpio0_15"),
 	_AM33XX_MUXENTRY(I2C0_SDA, 0,
-		"i2c0_sda", NULL, NULL, NULL,
+		"i2c0_sda", NULL, "uart2_ctsn", NULL,
 		NULL, NULL, NULL, "gpio3_5"),
 	_AM33XX_MUXENTRY(I2C0_SCL, 0,
-		"i2c0_scl", NULL, NULL, NULL,
+		"i2c0_scl", NULL, "uart2_rtsn", NULL,
 		NULL, NULL, NULL, "gpio3_6"),
 	_AM33XX_MUXENTRY(MCASP0_ACLKX, 0,
 		"mcasp0_aclkx", NULL, NULL, "spi1_sclk",
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index 7f5281e..4f32ffa 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -136,6 +136,9 @@ struct uart_omap_port {
 	u32			latency;
 	u32			calc_latency;
 	struct work_struct	qos_work;
+	struct serial_rs485     rs485;
+	unsigned int            tx_in_progress:1, tx_wait_end:1;
+	unsigned int		rts_gpio;
 };
 
 #endif /* __OMAP_SERIAL_H__ */
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
old mode 100644
new mode 100755
index ca24ab3..eb7eee5
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -39,6 +39,8 @@
 #include <linux/irq.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
 
 #include <plat/dma.h>
 #include <plat/dmtimer.h>
@@ -126,6 +128,68 @@ static void serial_omap_enable_ms(struct uart_port *port)
 	pm_runtime_put(&up->pdev->dev);
 }
 
+static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
+{
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void serial_omap_disable_ier_thri(struct uart_omap_port *up)
+{
+	if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void serial_omap_thri_mode(struct uart_omap_port *up)
+{
+	unsigned char scr = serial_in(up, UART_OMAP_SCR);
+
+	if (up->tx_wait_end)
+		scr |= UART_OMAP_SCR_TX_EMPTY_CTL_IT;
+	else
+		scr &= ~UART_OMAP_SCR_TX_EMPTY_CTL_IT;
+	serial_out(up, UART_OMAP_SCR, scr);
+}
+
+static inline void serial_omap_update_gpio_rts(struct uart_omap_port *up)
+{
+	if(up->mcr & UART_MCR_RTS){
+		gpio_direction_output(up->rts_gpio, 1);
+		mdelay(1);
+	}
+	else{
+		gpio_direction_output(up->rts_gpio, 0);
+	}
+}
+
+static inline void serial_omap_update_rts(struct uart_omap_port *up)
+{
+	unsigned char mcr = up->mcr;
+	int rts_on_send = up->rs485.flags & SER_RS485_RTS_ON_SEND;
+	if (up->rs485.flags & SER_RS485_ENABLED) {
+		if (up->tx_in_progress) {
+			if (rts_on_send)
+				mcr |= UART_MCR_RTS;
+			else
+				mcr &= ~UART_MCR_RTS;
+		} else {
+			if (rts_on_send)
+				mcr &= ~UART_MCR_RTS;
+			else
+				mcr |= ~UART_MCR_RTS;
+		}
+	}
+
+	up->mcr = mcr;
+	serial_out(up, UART_MCR, mcr);
+	if (up->rts_gpio != -1)
+		serial_omap_update_gpio_rts(up);
+}
+
 static void serial_omap_stop_tx(struct uart_port *port)
 {
 	struct uart_omap_port *up = (struct uart_omap_port *)port;
@@ -146,9 +210,18 @@ static void serial_omap_stop_tx(struct uart_port *port)
 	}
 
 	pm_runtime_get_sync(&up->pdev->dev);
-	if (up->ier & UART_IER_THRI) {
-		up->ier &= ~UART_IER_THRI;
-		serial_out(up, UART_IER, up->ier);
+	if (!(up->rs485.flags & SER_RS485_ENABLED))
+		serial_omap_disable_ier_thri(up);
+	else {
+	if(up->tx_in_progress == 1){
+		/*Tx finish, wait end*/
+		up->tx_in_progress = 0;
+		up->tx_wait_end = 1;
+		serial_omap_thri_mode(up);
+		serial_omap_enable_ier_thri(up);
+	}
+	else
+		serial_omap_disable_ier_thri(up);
 	}
 
 	pm_runtime_mark_last_busy(&up->pdev->dev);
@@ -268,14 +341,6 @@ static void transmit_chars(struct uart_omap_port *up)
 		serial_omap_stop_tx(&up->port);
 }
 
-static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
-{
-	if (!(up->ier & UART_IER_THRI)) {
-		up->ier |= UART_IER_THRI;
-		serial_out(up, UART_IER, up->ier);
-	}
-}
-
 static void serial_omap_start_tx(struct uart_port *port)
 {
 	struct uart_omap_port *up = (struct uart_omap_port *)port;
@@ -283,6 +348,18 @@ static void serial_omap_start_tx(struct uart_port *port)
 	unsigned int start;
 	int ret = 0;
 
+	if (up->rs485.flags & SER_RS485_ENABLED) {
+		if (!up->tx_in_progress) {
+			up->tx_in_progress = 1;
+			serial_omap_update_rts(up);
+		}
+		if (up->tx_wait_end) {
+			up->tx_wait_end = 0;
+			serial_omap_thri_mode(up);
+			serial_omap_disable_ier_thri(up);
+		}
+	}
+
 	if (!up->use_dma) {
 		pm_runtime_get_sync(&up->pdev->dev);
 		serial_omap_enable_ier_thri(up);
@@ -369,6 +446,11 @@ static unsigned int check_modem_status(struct uart_omap_port *up)
 	return status;
 }
 
+static inline unsigned int __serial_omap_tx_empty(struct uart_omap_port *up)
+{
+	return serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
 /**
  * serial_omap_irq() - This handles the interrupt from one port
  * @irq: uart port irq number
@@ -389,6 +471,16 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
 	}
 
 	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->tx_wait_end && (iir & UART_IIR_THRI) &&
+			__serial_omap_tx_empty(up)) {
+		/*fifo tx finish*/
+		up->tx_wait_end = 0;
+		up->tx_in_progress = 0;
+		serial_omap_thri_mode(up);
+		serial_omap_disable_ier_thri(up);
+		serial_omap_update_rts(up);
+		goto handled;
+	}
 	lsr = serial_in(up, UART_LSR);
 	if (iir & UART_IIR_RLSI) {
 		if (!up->use_dma) {
@@ -407,6 +499,7 @@ static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
 	if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI))
 		transmit_chars(up);
 
+handled:
 	spin_unlock_irqrestore(&up->port.lock, flags);
 	pm_runtime_mark_last_busy(&up->pdev->dev);
 	pm_runtime_put_autosuspend(&up->pdev->dev);
@@ -424,7 +517,7 @@ static unsigned int serial_omap_tx_empty(struct uart_port *port)
 	pm_runtime_get_sync(&up->pdev->dev);
 	dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line);
 	spin_lock_irqsave(&up->port.lock, flags);
-	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	ret = __serial_omap_tx_empty(up);
 	spin_unlock_irqrestore(&up->port.lock, flags);
 	pm_runtime_put(&up->pdev->dev);
 	return ret;
@@ -473,6 +566,9 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	pm_runtime_get_sync(&up->pdev->dev);
 	up->mcr = serial_in(up, UART_MCR);
 	up->mcr |= mcr;
+	if (up->rs485.flags & SER_RS485_ENABLED)
+		serial_omap_update_rts(up);
+
 	serial_out(up, UART_MCR, up->mcr);
 	pm_runtime_put(&up->pdev->dev);
 }
@@ -1128,6 +1224,41 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up)
 
 #endif
 
+static int
+serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+	struct serial_rs485 rs485conf;
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned long flags;
+
+	switch (cmd) {
+	case TIOCSRS485:
+		if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+				sizeof(rs485conf)))
+			return -EFAULT;
+
+		spin_lock_irqsave(&up->port.lock, flags);
+		if (!(rs485conf.flags & SER_RS485_ENABLED)) {
+			up->tx_in_progress = 0;
+			up->tx_wait_end = 0;
+		}
+		up->rs485 = rs485conf;
+		serial_omap_update_rts(up);
+		serial_omap_thri_mode(up);
+		spin_unlock_irqrestore(&up->port.lock, flags);
+
+	case TIOCGRS485:
+		if (copy_to_user((struct serial_rs485 *) arg,
+				&(up->rs485), sizeof(rs485conf)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
 static struct uart_ops serial_omap_pops = {
 	.tx_empty	= serial_omap_tx_empty,
 	.set_mctrl	= serial_omap_set_mctrl,
@@ -1141,6 +1272,7 @@ static struct uart_ops serial_omap_pops = {
 	.shutdown	= serial_omap_shutdown,
 	.set_termios	= serial_omap_set_termios,
 	.pm		= serial_omap_pm,
+	.ioctl          = serial_omap_ioctl,
 	.type		= serial_omap_type,
 	.release_port	= serial_omap_release_port,
 	.request_port	= serial_omap_request_port,
@@ -1339,6 +1471,33 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
 	return omap_up_info;
 }
 
+int uart_omap_port_set_rts_gpio(int port_num, int gpio)
+{
+	struct uart_omap_port   *op;
+	struct uart_port	*up;
+	struct uart_state 	*us;
+	struct uart_driver	*ud;
+
+	ud = &serial_omap_reg;
+	if(port_num >= ud->nr)
+		return -EINVAL;
+
+	us = ud->state + port_num;
+	up = us->uart_port;
+
+	op = container_of(up, struct uart_omap_port, port);
+	printk("Cfg gpio %d as %s's RTS signal\n", gpio, op->name);
+	if(gpio_request(gpio, op->name)){
+		printk("request gpio failed\n");
+		return -EBUSY;
+	}
+	gpio_direction_output(gpio, 0);
+	op->rts_gpio = gpio;
+
+	return 0;
+}
+EXPORT_SYMBOL(uart_omap_port_set_rts_gpio);
+
 static int serial_omap_probe(struct platform_device *pdev)
 {
 	struct uart_omap_port	*up;
@@ -1454,6 +1613,8 @@ static int serial_omap_probe(struct platform_device *pdev)
 	pm_runtime_get_sync(&pdev->dev);
 
 	ui[up->port.line] = up;
+	/*make rts gpio inval*/
+	up->rts_gpio = -1;
 	serial_omap_add_console_port(up);
 
 	ret = uart_add_one_port(&serial_omap_reg, &up->port);
diff --git a/include/linux/serial_reg.h b/include/linux/serial_reg.h
index 8ce70d7..5695f19 100644
--- a/include/linux/serial_reg.h
+++ b/include/linux/serial_reg.h
@@ -363,5 +363,7 @@
 #define UART_OMAP_MDR1_CIR_MODE		0x06	/* CIR mode */
 #define UART_OMAP_MDR1_DISABLE		0x07	/* Disable (default state) */
 
+#define UART_OMAP_SCR_TX_EMPTY_CTL_IT	0x08    /* TX Empty IRQ mode */
+
 #endif /* _LINUX_SERIAL_REG_H */