serial: sirf: fix the hardware-flow-ctrl for USP-based UART
for USP-based UART, we use two gpios as RTS and CST pins. Signed-off-by: Qipan Li <Qipan.Li@csr.com> Signed-off-by: Barry Song <Baohua.Song@csr.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
a343756e07
commit
2eb5618de8
@ -20,6 +20,7 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
#include <asm/mach/irq.h>
|
#include <asm/mach/irq.h>
|
||||||
|
|
||||||
@ -110,14 +111,19 @@ static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
|
|||||||
{
|
{
|
||||||
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
||||||
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
||||||
if (!(sirfport->ms_enabled)) {
|
if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
|
||||||
goto cts_asserted;
|
goto cts_asserted;
|
||||||
} else if (sirfport->hw_flow_ctrl) {
|
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||||
if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) &
|
if (!(rd_regl(port, ureg->sirfsoc_afc_ctrl) &
|
||||||
SIRFUART_AFC_CTS_STATUS))
|
SIRFUART_AFC_CTS_STATUS))
|
||||||
goto cts_asserted;
|
goto cts_asserted;
|
||||||
else
|
else
|
||||||
goto cts_deasserted;
|
goto cts_deasserted;
|
||||||
|
} else {
|
||||||
|
if (!gpio_get_value(sirfport->cts_gpio))
|
||||||
|
goto cts_asserted;
|
||||||
|
else
|
||||||
|
goto cts_deasserted;
|
||||||
}
|
}
|
||||||
cts_deasserted:
|
cts_deasserted:
|
||||||
return TIOCM_CAR | TIOCM_DSR;
|
return TIOCM_CAR | TIOCM_DSR;
|
||||||
@ -132,10 +138,18 @@ static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|||||||
unsigned int assert = mctrl & TIOCM_RTS;
|
unsigned int assert = mctrl & TIOCM_RTS;
|
||||||
unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
|
unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
|
||||||
unsigned int current_val;
|
unsigned int current_val;
|
||||||
if (sirfport->hw_flow_ctrl) {
|
|
||||||
|
if (!sirfport->hw_flow_ctrl || !sirfport->ms_enabled)
|
||||||
|
return;
|
||||||
|
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||||
current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF;
|
current_val = rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0xFF;
|
||||||
val |= current_val;
|
val |= current_val;
|
||||||
wr_regl(port, ureg->sirfsoc_afc_ctrl, val);
|
wr_regl(port, ureg->sirfsoc_afc_ctrl, val);
|
||||||
|
} else {
|
||||||
|
if (!val)
|
||||||
|
gpio_set_value(sirfport->rts_gpio, 1);
|
||||||
|
else
|
||||||
|
gpio_set_value(sirfport->rts_gpio, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,21 +209,32 @@ static void sirfsoc_uart_disable_ms(struct uart_port *port)
|
|||||||
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
||||||
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
||||||
struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
|
struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
|
||||||
unsigned long reg;
|
|
||||||
|
|
||||||
sirfport->ms_enabled = 0;
|
|
||||||
if (!sirfport->hw_flow_ctrl)
|
if (!sirfport->hw_flow_ctrl)
|
||||||
return;
|
return;
|
||||||
|
sirfport->ms_enabled = false;
|
||||||
reg = rd_regl(port, ureg->sirfsoc_afc_ctrl);
|
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||||
wr_regl(port, ureg->sirfsoc_afc_ctrl, reg & ~0x3FF);
|
wr_regl(port, ureg->sirfsoc_afc_ctrl,
|
||||||
if (!sirfport->is_marco) {
|
rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
|
||||||
reg = rd_regl(port, ureg->sirfsoc_int_en_reg);
|
if (!sirfport->is_marco)
|
||||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||||
reg & ~uint_en->sirfsoc_cts_en);
|
rd_regl(port, ureg->sirfsoc_int_en_reg)&
|
||||||
} else
|
~uint_en->sirfsoc_cts_en);
|
||||||
|
else
|
||||||
wr_regl(port, SIRFUART_INT_EN_CLR,
|
wr_regl(port, SIRFUART_INT_EN_CLR,
|
||||||
uint_en->sirfsoc_cts_en);
|
uint_en->sirfsoc_cts_en);
|
||||||
|
} else
|
||||||
|
disable_irq(gpio_to_irq(sirfport->cts_gpio));
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sirfsoc_uart_usp_cts_handler(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
|
||||||
|
struct uart_port *port = &sirfport->port;
|
||||||
|
if (gpio_is_valid(sirfport->cts_gpio) && sirfport->ms_enabled)
|
||||||
|
uart_handle_cts_change(port,
|
||||||
|
!gpio_get_value(sirfport->cts_gpio));
|
||||||
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sirfsoc_uart_enable_ms(struct uart_port *port)
|
static void sirfsoc_uart_enable_ms(struct uart_port *port)
|
||||||
@ -217,25 +242,23 @@ static void sirfsoc_uart_enable_ms(struct uart_port *port)
|
|||||||
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
|
||||||
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
|
||||||
struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
|
struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
|
||||||
unsigned long reg;
|
|
||||||
unsigned long flg;
|
|
||||||
|
|
||||||
if (!sirfport->hw_flow_ctrl)
|
if (!sirfport->hw_flow_ctrl)
|
||||||
return;
|
return;
|
||||||
flg = SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN;
|
sirfport->ms_enabled = true;
|
||||||
reg = rd_regl(port, ureg->sirfsoc_afc_ctrl);
|
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||||
wr_regl(port, ureg->sirfsoc_afc_ctrl, reg | flg);
|
wr_regl(port, ureg->sirfsoc_afc_ctrl,
|
||||||
if (!sirfport->is_marco) {
|
rd_regl(port, ureg->sirfsoc_afc_ctrl) |
|
||||||
reg = rd_regl(port, ureg->sirfsoc_int_en_reg);
|
SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
|
||||||
|
if (!sirfport->is_marco)
|
||||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||||
reg | uint_en->sirfsoc_cts_en);
|
rd_regl(port, ureg->sirfsoc_int_en_reg)
|
||||||
} else
|
| uint_en->sirfsoc_cts_en);
|
||||||
|
else
|
||||||
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
wr_regl(port, ureg->sirfsoc_int_en_reg,
|
||||||
uint_en->sirfsoc_cts_en);
|
uint_en->sirfsoc_cts_en);
|
||||||
uart_handle_cts_change(port,
|
} else
|
||||||
!(rd_regl(port, ureg->sirfsoc_afc_ctrl) &
|
enable_irq(gpio_to_irq(sirfport->cts_gpio));
|
||||||
SIRFUART_AFC_CTS_STATUS));
|
|
||||||
sirfport->ms_enabled = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
|
static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
|
||||||
@ -505,8 +528,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
|
|||||||
if (termios->c_iflag & INPCK)
|
if (termios->c_iflag & INPCK)
|
||||||
port->read_status_mask |= uint_en->sirfsoc_frm_err_en |
|
port->read_status_mask |= uint_en->sirfsoc_frm_err_en |
|
||||||
uint_en->sirfsoc_parity_err_en;
|
uint_en->sirfsoc_parity_err_en;
|
||||||
}
|
} else {
|
||||||
if (sirfport->uart_reg->uart_type == SIRF_USP_UART) {
|
|
||||||
if (termios->c_iflag & INPCK)
|
if (termios->c_iflag & INPCK)
|
||||||
port->read_status_mask |= uint_en->sirfsoc_frm_err_en;
|
port->read_status_mask |= uint_en->sirfsoc_frm_err_en;
|
||||||
}
|
}
|
||||||
@ -529,8 +551,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
|
|||||||
config_reg |= SIRFUART_STICK_BIT_EVEN;
|
config_reg |= SIRFUART_STICK_BIT_EVEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
if (sirfport->uart_reg->uart_type == SIRF_USP_UART) {
|
|
||||||
if (termios->c_iflag & IGNPAR)
|
if (termios->c_iflag & IGNPAR)
|
||||||
port->ignore_status_mask |=
|
port->ignore_status_mask |=
|
||||||
uint_en->sirfsoc_frm_err_en;
|
uint_en->sirfsoc_frm_err_en;
|
||||||
@ -567,8 +588,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
|
|||||||
clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate,
|
clk_div_reg = sirfsoc_uart_calc_sample_div(baud_rate,
|
||||||
ioclk_rate, &set_baud);
|
ioclk_rate, &set_baud);
|
||||||
wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg);
|
wr_regl(port, ureg->sirfsoc_divisor, clk_div_reg);
|
||||||
}
|
} else {
|
||||||
if (sirfport->uart_reg->uart_type == SIRF_USP_UART) {
|
|
||||||
clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate,
|
clk_div_reg = sirfsoc_usp_calc_sample_div(baud_rate,
|
||||||
ioclk_rate, &sample_div_reg);
|
ioclk_rate, &sample_div_reg);
|
||||||
sample_div_reg--;
|
sample_div_reg--;
|
||||||
@ -593,8 +613,7 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
|
|||||||
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
|
||||||
config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out);
|
config_reg |= SIRFUART_RECV_TIMEOUT(port, rx_time_out);
|
||||||
wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg);
|
wr_regl(port, ureg->sirfsoc_line_ctrl, config_reg);
|
||||||
}
|
} else {
|
||||||
if (sirfport->uart_reg->uart_type == SIRF_USP_UART) {
|
|
||||||
/*tx frame ctrl*/
|
/*tx frame ctrl*/
|
||||||
len_val = (data_bit_len - 1) << 0;
|
len_val = (data_bit_len - 1) << 0;
|
||||||
len_val |= (data_bit_len + 1 + stop_bit_len - 1) << 16;
|
len_val |= (data_bit_len + 1 + stop_bit_len - 1) << 16;
|
||||||
@ -675,7 +694,25 @@ static int sirfsoc_uart_startup(struct uart_port *port)
|
|||||||
goto irq_err;
|
goto irq_err;
|
||||||
}
|
}
|
||||||
startup_uart_controller(port);
|
startup_uart_controller(port);
|
||||||
|
|
||||||
|
sirfport->ms_enabled = false;
|
||||||
|
if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
|
||||||
|
sirfport->hw_flow_ctrl) {
|
||||||
|
set_irq_flags(gpio_to_irq(sirfport->cts_gpio),
|
||||||
|
IRQF_VALID | IRQF_NOAUTOEN);
|
||||||
|
ret = request_irq(gpio_to_irq(sirfport->cts_gpio),
|
||||||
|
sirfsoc_uart_usp_cts_handler, IRQF_TRIGGER_FALLING |
|
||||||
|
IRQF_TRIGGER_RISING, "usp_cts_irq", sirfport);
|
||||||
|
if (ret != 0) {
|
||||||
|
dev_err(port->dev, "UART-USP:request gpio irq fail\n");
|
||||||
|
goto init_rx_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enable_irq(port->irq);
|
enable_irq(port->irq);
|
||||||
|
|
||||||
|
init_rx_err:
|
||||||
|
free_irq(port->irq, sirfport);
|
||||||
irq_err:
|
irq_err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -690,9 +727,12 @@ static void sirfsoc_uart_shutdown(struct uart_port *port)
|
|||||||
wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
|
wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
|
||||||
|
|
||||||
free_irq(port->irq, sirfport);
|
free_irq(port->irq, sirfport);
|
||||||
if (sirfport->ms_enabled) {
|
if (sirfport->ms_enabled)
|
||||||
sirfsoc_uart_disable_ms(port);
|
sirfsoc_uart_disable_ms(port);
|
||||||
sirfport->ms_enabled = 0;
|
if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
|
||||||
|
sirfport->hw_flow_ctrl) {
|
||||||
|
gpio_set_value(sirfport->rts_gpio, 1);
|
||||||
|
free_irq(gpio_to_irq(sirfport->cts_gpio), sirfport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -852,16 +892,51 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
|
|||||||
port->private_data = sirfport;
|
port->private_data = sirfport;
|
||||||
sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data;
|
sirfport->uart_reg = (struct sirfsoc_uart_register *)match->data;
|
||||||
|
|
||||||
|
sirfport->hw_flow_ctrl = of_property_read_bool(pdev->dev.of_node,
|
||||||
|
"sirf,uart-has-rtscts");
|
||||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart"))
|
if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-uart"))
|
||||||
sirfport->uart_reg->uart_type = SIRF_REAL_UART;
|
sirfport->uart_reg->uart_type = SIRF_REAL_UART;
|
||||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart"))
|
if (of_device_is_compatible(pdev->dev.of_node, "sirf,prima2-usp-uart")) {
|
||||||
sirfport->uart_reg->uart_type = SIRF_USP_UART;
|
sirfport->uart_reg->uart_type = SIRF_USP_UART;
|
||||||
|
if (!sirfport->hw_flow_ctrl)
|
||||||
|
goto usp_no_flow_control;
|
||||||
|
if (of_find_property(pdev->dev.of_node, "cts-gpios", NULL))
|
||||||
|
sirfport->cts_gpio = of_get_named_gpio(
|
||||||
|
pdev->dev.of_node, "cts-gpios", 0);
|
||||||
|
else
|
||||||
|
sirfport->cts_gpio = -1;
|
||||||
|
if (of_find_property(pdev->dev.of_node, "rts-gpios", NULL))
|
||||||
|
sirfport->rts_gpio = of_get_named_gpio(
|
||||||
|
pdev->dev.of_node, "rts-gpios", 0);
|
||||||
|
else
|
||||||
|
sirfport->rts_gpio = -1;
|
||||||
|
|
||||||
|
if ((!gpio_is_valid(sirfport->cts_gpio) ||
|
||||||
|
!gpio_is_valid(sirfport->rts_gpio))) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Usp flow control must have rfs and tfs gpio");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
ret = devm_gpio_request(&pdev->dev, sirfport->cts_gpio,
|
||||||
|
"usp-rfs-gpio");
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Unable request rfs gpio");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
gpio_direction_input(sirfport->cts_gpio);
|
||||||
|
ret = devm_gpio_request(&pdev->dev, sirfport->rts_gpio,
|
||||||
|
"usp-tfs-gpio");
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "Unable request tfs gpio");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
gpio_direction_output(sirfport->rts_gpio, 1);
|
||||||
|
}
|
||||||
|
usp_no_flow_control:
|
||||||
if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
|
if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
|
||||||
sirfport->is_marco = true;
|
sirfport->is_marco = true;
|
||||||
|
|
||||||
if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
|
|
||||||
sirfport->hw_flow_ctrl = 1;
|
|
||||||
|
|
||||||
if (of_property_read_u32(pdev->dev.of_node,
|
if (of_property_read_u32(pdev->dev.of_node,
|
||||||
"fifosize",
|
"fifosize",
|
||||||
&port->fifosize)) {
|
&port->fifosize)) {
|
||||||
|
@ -363,14 +363,16 @@ struct sirfsoc_baudrate_to_regv {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sirfsoc_uart_port {
|
struct sirfsoc_uart_port {
|
||||||
unsigned char hw_flow_ctrl;
|
bool hw_flow_ctrl;
|
||||||
unsigned char ms_enabled;
|
bool ms_enabled;
|
||||||
|
|
||||||
struct uart_port port;
|
struct uart_port port;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
/* for SiRFmarco, there are SET/CLR for UART_INT_EN */
|
/* for SiRFmarco, there are SET/CLR for UART_INT_EN */
|
||||||
bool is_marco;
|
bool is_marco;
|
||||||
struct sirfsoc_uart_register *uart_reg;
|
struct sirfsoc_uart_register *uart_reg;
|
||||||
|
unsigned int cts_gpio;
|
||||||
|
unsigned int rts_gpio;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Hardware Flow Control */
|
/* Hardware Flow Control */
|
||||||
|
Loading…
Reference in New Issue
Block a user