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 */