2019-09-04 04:44:26

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 00/12] serial: tegra: Tegra186 support and fixes

Series of patches adding enhancements to exising UART driver and adding
support for new chip Tegra186 and Tegra194.
Tegra186 uses GPCDMA for dma transfers which is still not available in
mainstream. However, it can work in PIO/FIFO mode and support added for it.
Also Tegra186 has a hardware issue where it does not meet tolernace +/-4% and
to work around it, device tree entries provided to adjust baud rate for a
particular range.

Changes from V1:
Consistent spellings and few other cosmetic changes.
Do not ignore sysrq request when ignoring reads.
Remove WARN_ON for errors that are user triggered.
Fix max ports for Tegra186 and Tegra194
Register uart driver in init by reading max ports from dt.
Rename burst size and wait api for FIFO enable.


Ahung Cheng (1):
serial: tegra: avoid reg access when clk disabled

Krishna Yarlagadda (9):
serial: tegra: report error to upper tty layer
dt-binding: serial: tegra: add new chips
serial: tegra: check for FIFO mode enabled status
serial: tegra: set maximum num of uart ports to 8
serial: tegra: add support to use 8 bytes trigger
serial: tegra: DT for Adjusted baud rates
serial: tegra: add support to adjust baud rate
serial: tegra: report clk rate errors
serial: tegra: Add PIO mode support

Shardar Shariff Md (2):
serial: tegra: add support to ignore read
serial: tegra: flush the RX fifo on frame error

.../bindings/serial/nvidia,tegra20-hsuart.txt | 39 ++-
drivers/tty/serial/serial-tegra.c | 374 ++++++++++++++++++---
2 files changed, 368 insertions(+), 45 deletions(-)

--
2.7.4


2019-09-04 04:44:34

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 02/12] serial: tegra: avoid reg access when clk disabled

From: Ahung Cheng <[email protected]>

This avoids two race conditions from the UART shutdown sequence both
leading to 'Machine check error in AXI2APB' and kernel oops.

One was that the clock was disabled before the DMA was terminated making
it possible for the DMA callbacks to be called after the clock was
disabled. These callbacks could write to the UART registers causing
timeout.

The second was that the clock was disabled before the UART was
completely flagged as closed. This is done after the shutdown is called
and a new write could be started after the clock was disabled.
tegra_uart_start_pio_tx could be called causing timeout.

Given that the baud rate is reset at the end of shutdown sequence, this
fix is to examine the baud rate to avoid register access from both race
conditions.

Besides, terminate the DMA before disabling the clock.

Signed-off-by: Ahung Cheng <[email protected]>
Signed-off-by: Shardar Mohammed <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 9c15c87..29bf7b7 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -126,6 +126,8 @@ struct tegra_uart_port {

static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup);
+static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
+ bool dma_to_memory);

static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup,
unsigned long reg)
@@ -461,6 +463,9 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
unsigned long count;
struct circ_buf *xmit = &tup->uport.state->xmit;

+ if (!tup->current_baud)
+ return;
+
tail = (unsigned long)&xmit->buf[xmit->tail];
count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (!count)
@@ -832,6 +837,12 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
tup->current_baud = 0;
spin_unlock_irqrestore(&tup->uport.lock, flags);

+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ tegra_uart_dma_channel_free(tup, true);
+ tegra_uart_dma_channel_free(tup, false);
+
clk_disable_unprepare(tup->uart_clk);
}

@@ -1069,12 +1080,6 @@ static void tegra_uart_shutdown(struct uart_port *u)
struct tegra_uart_port *tup = to_tegra_uport(u);

tegra_uart_hw_deinit(tup);
-
- tup->rx_in_progress = 0;
- tup->tx_in_progress = 0;
-
- tegra_uart_dma_channel_free(tup, true);
- tegra_uart_dma_channel_free(tup, false);
free_irq(u->irq, tup);
}

--
2.7.4

2019-09-04 04:44:44

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 03/12] serial: tegra: flush the RX fifo on frame error

From: Shardar Shariff Md <[email protected]>

FIFO reset/flush code implemented now does not follow programming
guidelines. RTS line has to be turned off while flushing FIFOs to
avoid new transfers. Also check LSR bits UART_LSR_TEMT and UART_LSR_DR
to confirm FIFOs are flushed.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 29bf7b7..4cd6d5f 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -266,6 +266,10 @@ static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup,
static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
{
unsigned long fcr = tup->fcr_shadow;
+ unsigned int lsr, tmout = 10000;
+
+ if (tup->rts_active)
+ set_rts(tup, false);

if (tup->cdata->allow_txfifo_reset_fifo_mode) {
fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
@@ -289,6 +293,16 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
* to propagate, otherwise data could be lost.
*/
tegra_uart_wait_cycle_time(tup, 32);
+
+ do {
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr | UART_LSR_TEMT) && !(lsr & UART_LSR_DR))
+ break;
+ udelay(1);
+ } while (--tmout);
+
+ if (tup->rts_active)
+ set_rts(tup, true);
}

static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
--
2.7.4

2019-09-04 04:44:56

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 05/12] dt-binding: serial: tegra: add new chips

Add new compatible string for Tegra186. It differs from earlier chips
as it has FIFO mode enable check and 8 byte DMA buffer.
Add new compatible string for Tegra194. Tegra194 has different error
tolerance levels for baud rate compared to older chips.

Signed-off-by: Krishna Yarlagadda <[email protected]>
---
Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
index d7edf73..dab31d4 100644
--- a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
+++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
@@ -1,7 +1,12 @@
NVIDIA Tegra20/Tegra30 high speed (DMA based) UART controller driver.

Required properties:
-- compatible : should be "nvidia,tegra30-hsuart", "nvidia,tegra20-hsuart".
+- compatible : should be,
+ "nvidia,tegra20-hsuart" for Tegra20,
+ "nvidia,tegra30-hsuart" for Tegra30,
+ "nvidia,tegra186-hsuart" for Tegra186,
+ "nvidia,tegra194-hsuart" for Tegra194.
+
- reg: Should contain UART controller registers location and length.
- interrupts: Should contain UART controller interrupts.
- clocks: Must contain one entry, for the module clock.
--
2.7.4

2019-09-04 04:44:58

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 06/12] serial: tegra: check for FIFO mode enabled status

Chips prior to Tegra186 needed delay of 3 UART clock cycles to avoid
data loss. This issue is fixed in Tegra186 and a new flag is added to
check if FIFO mode is enabled. chip data updated to check if this flag
is available for a chip. Tegra186 has new compatible to enable this
flag.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 52 ++++++++++++++++++++++++++++++++++-----
1 file changed, 46 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index c3f9913..69af621 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -72,6 +72,8 @@
#define TEGRA_TX_PIO 1
#define TEGRA_TX_DMA 2

+#define TEGRA_UART_FCR_IIR_FIFO_EN 0x40
+
/**
* tegra_uart_chip_data: SOC specific data.
*
@@ -84,6 +86,7 @@ struct tegra_uart_chip_data {
bool tx_fifo_full_status;
bool allow_txfifo_reset_fifo_mode;
bool support_clk_src_div;
+ bool fifo_mode_enable_status;
};

struct tegra_uart_port {
@@ -263,6 +266,21 @@ static void tegra_uart_wait_sym_time(struct tegra_uart_port *tup,
tup->current_baud));
}

+static int tegra_uart_wait_fifo_mode_enabled(struct tegra_uart_port *tup)
+{
+ unsigned long iir;
+ unsigned int tmout = 100;
+
+ do {
+ iir = tegra_uart_read(tup, UART_IIR);
+ if (iir & TEGRA_UART_FCR_IIR_FIFO_EN)
+ return 0;
+ udelay(1);
+ } while (--tmout);
+
+ return -ETIMEDOUT;
+}
+
static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
{
unsigned long fcr = tup->fcr_shadow;
@@ -282,6 +300,8 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
tegra_uart_write(tup, fcr, UART_FCR);
fcr |= UART_FCR_ENABLE_FIFO;
tegra_uart_write(tup, fcr, UART_FCR);
+ if (tup->cdata->fifo_mode_enable_status)
+ tegra_uart_wait_fifo_mode_enabled(tup);
}

/* Dummy read to ensure the write is posted */
@@ -917,12 +937,20 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
/* Dummy read to ensure the write is posted */
tegra_uart_read(tup, UART_SCR);

- /*
- * For all tegra devices (up to t210), there is a hardware issue that
- * requires software to wait for 3 UART clock periods after enabling
- * the TX fifo, otherwise data could be lost.
- */
- tegra_uart_wait_cycle_time(tup, 3);
+ if (tup->cdata->fifo_mode_enable_status) {
+ ret = tegra_uart_wait_fifo_mode_enabled(tup);
+ dev_err(tup->uport.dev, "FIFO mode not enabled\n");
+ if (ret < 0)
+ return ret;
+ } else {
+ /*
+ * For all tegra devices (up to t210), there is a hardware
+ * issue that requires software to wait for 3 UART clock
+ * periods after enabling the TX fifo, otherwise data could
+ * be lost.
+ */
+ tegra_uart_wait_cycle_time(tup, 3);
+ }

/*
* Initialize the UART with default configuration
@@ -1293,12 +1321,21 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = {
.tx_fifo_full_status = false,
.allow_txfifo_reset_fifo_mode = true,
.support_clk_src_div = false,
+ .fifo_mode_enable_status = false,
};

static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.tx_fifo_full_status = true,
.allow_txfifo_reset_fifo_mode = false,
.support_clk_src_div = true,
+ .fifo_mode_enable_status = false,
+};
+
+static struct tegra_uart_chip_data tegra186_uart_chip_data = {
+ .tx_fifo_full_status = true,
+ .allow_txfifo_reset_fifo_mode = false,
+ .support_clk_src_div = true,
+ .fifo_mode_enable_status = true,
};

static const struct of_device_id tegra_uart_of_match[] = {
@@ -1309,6 +1346,9 @@ static const struct of_device_id tegra_uart_of_match[] = {
.compatible = "nvidia,tegra20-hsuart",
.data = &tegra20_uart_chip_data,
}, {
+ .compatible = "nvidia,tegra186-hsuart",
+ .data = &tegra186_uart_chip_data,
+ }, {
},
};
MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
--
2.7.4

2019-09-04 04:45:03

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 07/12] serial: tegra: set maximum num of uart ports to 8

Set maximum number of UART ports to 8 as older chips have 5 ports and
Tergra186 and later chips will have 8 ports. Add this info to chip
data. Read device tree compatible of this driver and register uart
driver with max ports of matching chip data.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 69af621..8422516 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -62,7 +62,7 @@
#define TEGRA_UART_TX_TRIG_4B 0x20
#define TEGRA_UART_TX_TRIG_1B 0x30

-#define TEGRA_UART_MAXIMUM 5
+#define TEGRA_UART_MAXIMUM 8

/* Default UART setting when started: 115200 no parity, stop, 8 data bits */
#define TEGRA_UART_DEFAULT_BAUD 115200
@@ -87,6 +87,7 @@ struct tegra_uart_chip_data {
bool allow_txfifo_reset_fifo_mode;
bool support_clk_src_div;
bool fifo_mode_enable_status;
+ int uart_max_port;
};

struct tegra_uart_port {
@@ -1322,6 +1323,7 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = {
.allow_txfifo_reset_fifo_mode = true,
.support_clk_src_div = false,
.fifo_mode_enable_status = false,
+ .uart_max_port = 5,
};

static struct tegra_uart_chip_data tegra30_uart_chip_data = {
@@ -1329,6 +1331,7 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.allow_txfifo_reset_fifo_mode = false,
.support_clk_src_div = true,
.fifo_mode_enable_status = false,
+ .uart_max_port = 5,
};

static struct tegra_uart_chip_data tegra186_uart_chip_data = {
@@ -1336,6 +1339,7 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = {
.allow_txfifo_reset_fifo_mode = false,
.support_clk_src_div = true,
.fifo_mode_enable_status = true,
+ .uart_max_port = 8,
};

static const struct of_device_id tegra_uart_of_match[] = {
@@ -1468,11 +1472,22 @@ static struct platform_driver tegra_uart_platform_driver = {
static int __init tegra_uart_init(void)
{
int ret;
+ struct device_node *node;
+ const struct of_device_id *match = NULL;
+ const struct tegra_uart_chip_data *cdata = NULL;
+
+ node = of_find_matching_node(NULL, tegra_uart_of_match);
+ if (node)
+ match = of_match_node(tegra_uart_of_match, node);
+ if (match)
+ cdata = match->data;
+ if (cdata)
+ tegra_uart_driver.nr = cdata->uart_max_port;

ret = uart_register_driver(&tegra_uart_driver);
if (ret < 0) {
pr_err("Could not register %s driver\n",
- tegra_uart_driver.driver_name);
+ tegra_uart_driver.driver_name);
return ret;
}

--
2.7.4

2019-09-04 04:45:20

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 04/12] serial: tegra: report error to upper tty layer

Report overrun/parity/frame/break errors to top tty layer. Add support
to ignore break character if IGNBRK is set.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 4cd6d5f..c3f9913 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -373,13 +373,21 @@ static char tegra_uart_decode_rx_error(struct tegra_uart_port *tup,
tup->uport.icount.frame++;
dev_err(tup->uport.dev, "Got frame errors\n");
} else if (lsr & UART_LSR_BI) {
- dev_err(tup->uport.dev, "Got Break\n");
- tup->uport.icount.brk++;
- /* If FIFO read error without any data, reset Rx FIFO */
+ /*
+ * Break error
+ * If FIFO read error without any data, reset Rx FIFO
+ */
if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR);
+ if (tup->uport.ignore_status_mask & UART_LSR_BI)
+ return TTY_BREAK;
+ flag = TTY_BREAK;
+ tup->uport.icount.brk++;
+ dev_dbg(tup->uport.dev, "Got Break\n");
}
+ uart_insert_char(&tup->uport, lsr, UART_LSR_OE, 0, flag);
}
+
return flag;
}

@@ -561,6 +569,9 @@ static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup,
break;

flag = tegra_uart_decode_rx_error(tup, lsr);
+ if (flag != TTY_NORMAL)
+ continue;
+
ch = (unsigned char) tegra_uart_read(tup, UART_RX);
tup->uport.icount.rx++;

@@ -1223,6 +1234,8 @@ static void tegra_uart_set_termios(struct uart_port *u,
/* Ignore all characters if CREAD is not set */
if ((termios->c_cflag & CREAD) == 0)
tup->uport.ignore_status_mask |= UART_LSR_DR;
+ if (termios->c_iflag & IGNBRK)
+ tup->uport.ignore_status_mask |= UART_LSR_BI;

spin_unlock_irqrestore(&u->lock, flags);
}
--
2.7.4

2019-09-04 04:45:31

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 09/12] serial: tegra: DT for Adjusted baud rates

Tegra186 chip has a hardware issue resulting in frame errors when
tolerance level for baud rate is negative. Provided entries to adjust
baud rate to be within acceptable range and work with devices that
can send negative baud rate. Also report error when baud rate set is
out of tolerance range of controller updated in device tree.

Signed-off-by: Krishna Yarlagadda <[email protected]>
---
.../bindings/serial/nvidia,tegra20-hsuart.txt | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)

diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
index dab31d4..f709304 100644
--- a/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
+++ b/Documentation/devicetree/bindings/serial/nvidia,tegra20-hsuart.txt
@@ -24,6 +24,37 @@ Required properties:
Optional properties:
- nvidia,enable-modem-interrupt: Enable modem interrupts. Should be enable
only if all 8 lines of UART controller are pinmuxed.
+- nvidia,adjust-baud-rates: List of entries providing percentage of baud rate
+ adjustment within a range.
+ Each entry contains sets of 3 values. Range low/high and adjusted rate.
+ <range_low range_high adjusted_rate>
+ When baud rate set on controller falls within the range mentioned in this
+ field, baud rate will be adjusted by percentage mentioned here.
+ Ex: <9600 115200 200>
+ Increase baud rate by 2% when set baud rate falls within range 9600 to 115200
+
+Baud Rate tolerance:
+ Standard UART devices are expected to have tolerance for baud rate error by
+ -4 to +4 %. All Tegra devices till Tegra210 had this support. However,
+ Tegra186 chip has a known hardware issue. UART Rx baud rate tolerance level
+ is 0% to +4% in 1-stop config. Otherwise, the received data will have
+ corruption/invalid framing errors. Parker errata suggests adjusting baud
+ rate to be higher than the deviations observed in Tx.
+
+ Tx deviation of connected device can be captured over scope (or noted from
+ its spec) for valid range and Tegra baud rate has to be set above actual
+ Tx baud rate observed. To do this we use nvidia,adjust-baud-rates
+
+ As an example, consider there is deviation observed in Tx for baud rates as
+ listed below.
+ 0 to 9600 has 1% deviation
+ 9600 to 115200 2% deviation
+ This slight deviation is expcted and Tegra UART is expected to handle it. Due
+ to the issue stated above, baud rate on Tegra UART should be set equal to or
+ above deviation observed for avoiding frame errors.
+ Property should be set like this
+ nvidia,adjust-baud-rates = <0 9600 100>,
+ <9600 115200 200>;

Example:

@@ -38,4 +69,5 @@ serial@70006000 {
reset-names = "serial";
dmas = <&apbdma 8>, <&apbdma 8>;
dma-names = "rx", "tx";
+ nvidia,adjust-baud-rates = <1000000 4000000 136>; /* 1.36% shift */
};
--
2.7.4

2019-09-04 04:45:34

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 10/12] serial: tegra: add support to adjust baud rate

Add support to adjust baud rates to fall under supported tolerance
range through DT.

Tegra186 chip has a hardware issue resulting in frame errors when
tolerance level for baud rate is negative. Provided entries to adjust
baud rate to be within acceptable range and work with devices that
can send negative baud rate. Also report error when baud rate set is
out of tolerance range of controller updated in device tree.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 68 +++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 02f8535..f970ed7 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -91,6 +91,12 @@ struct tegra_uart_chip_data {
int max_dma_burst_bytes;
};

+struct tegra_baud_tolerance {
+ u32 lower_range_baud;
+ u32 upper_range_baud;
+ s32 tolerance;
+};
+
struct tegra_uart_port {
struct uart_port uport;
const struct tegra_uart_chip_data *cdata;
@@ -127,6 +133,8 @@ struct tegra_uart_port {
dma_cookie_t rx_cookie;
unsigned int tx_bytes_requested;
unsigned int rx_bytes_requested;
+ struct tegra_baud_tolerance *baud_tolerance;
+ int n_adjustable_baud_rates;
};

static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
@@ -327,6 +335,21 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
set_rts(tup, true);
}

+static long tegra_get_tolerance_rate(struct tegra_uart_port *tup,
+ unsigned int baud, long rate)
+{
+ int i;
+
+ for (i = 0; i < tup->n_adjustable_baud_rates; ++i) {
+ if (baud >= tup->baud_tolerance[i].lower_range_baud &&
+ baud <= tup->baud_tolerance[i].upper_range_baud)
+ return (rate + (rate *
+ tup->baud_tolerance[i].tolerance) / 10000);
+ }
+
+ return rate;
+}
+
static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
{
unsigned long rate;
@@ -340,6 +363,9 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)

if (tup->cdata->support_clk_src_div) {
rate = baud * 16;
+ if (tup->n_adjustable_baud_rates)
+ rate = tegra_get_tolerance_rate(tup, baud, rate);
+
ret = clk_set_rate(tup->uart_clk, rate);
if (ret < 0) {
dev_err(tup->uport.dev,
@@ -1311,6 +1337,12 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
{
struct device_node *np = pdev->dev.of_node;
int port;
+ int ret;
+ int index;
+ u32 pval;
+ int count;
+ int n_entries;
+

port = of_alias_get_id(np, "serial");
if (port < 0) {
@@ -1321,6 +1353,42 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,

tup->enable_modem_interrupt = of_property_read_bool(np,
"nvidia,enable-modem-interrupt");
+ n_entries = of_property_count_u32_elems(np, "nvidia,adjust-baud-rates");
+ if (n_entries > 0) {
+ tup->n_adjustable_baud_rates = n_entries / 3;
+ tup->baud_tolerance =
+ devm_kzalloc(&pdev->dev, (tup->n_adjustable_baud_rates) *
+ sizeof(*tup->baud_tolerance), GFP_KERNEL);
+ if (!tup->baud_tolerance)
+ return -ENOMEM;
+ for (count = 0, index = 0; count < n_entries; count += 3,
+ index++) {
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].lower_range_baud =
+ pval;
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count + 1, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].upper_range_baud =
+ pval;
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count + 2, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].tolerance =
+ (s32)pval;
+ }
+ } else {
+ tup->n_adjustable_baud_rates = 0;
+ }
+
return 0;
}

--
2.7.4

2019-09-04 04:45:43

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 11/12] serial: tegra: report clk rate errors

Standard UART controllers support +/-4% baud rate error tolerance.
Tegra186 only supports 0% to +4% error tolerance whereas other Tegra
chips support standard +/-4% rate. Add chip data for knowing error
tolerance level for each soc. Creating new compatible for Tegra194
chip as it supports baud rate error tolerance of -2 to +2%, different
from older chips.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 59 +++++++++++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index f970ed7..d0fd417 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -89,6 +89,8 @@ struct tegra_uart_chip_data {
bool fifo_mode_enable_status;
int uart_max_port;
int max_dma_burst_bytes;
+ int error_tolerance_low_range;
+ int error_tolerance_high_range;
};

struct tegra_baud_tolerance {
@@ -135,6 +137,8 @@ struct tegra_uart_port {
unsigned int rx_bytes_requested;
struct tegra_baud_tolerance *baud_tolerance;
int n_adjustable_baud_rates;
+ int required_rate;
+ int configured_rate;
};

static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
@@ -350,6 +354,22 @@ static long tegra_get_tolerance_rate(struct tegra_uart_port *tup,
return rate;
}

+static int tegra_check_rate_in_range(struct tegra_uart_port *tup)
+{
+ long diff;
+
+ diff = ((long)(tup->configured_rate - tup->required_rate) * 10000)
+ / tup->required_rate;
+ if (diff < (tup->cdata->error_tolerance_low_range * 100) ||
+ diff > (tup->cdata->error_tolerance_high_range * 100)) {
+ dev_err(tup->uport.dev,
+ "configured baud rate is out of range by %ld", diff);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
{
unsigned long rate;
@@ -363,6 +383,8 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)

if (tup->cdata->support_clk_src_div) {
rate = baud * 16;
+ tup->required_rate = rate;
+
if (tup->n_adjustable_baud_rates)
rate = tegra_get_tolerance_rate(tup, baud, rate);

@@ -372,7 +394,11 @@ static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
"clk_set_rate() failed for rate %lu\n", rate);
return ret;
}
+ tup->configured_rate = clk_get_rate(tup->uart_clk);
divisor = 1;
+ ret = tegra_check_rate_in_range(tup);
+ if (ret < 0)
+ return ret;
} else {
rate = clk_get_rate(tup->uart_clk);
divisor = DIV_ROUND_CLOSEST(rate, baud * 16);
@@ -991,7 +1017,11 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
* enqueued
*/
tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
- tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
+ ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Failed to set baud rate\n");
+ return ret;
+ }
tup->fcr_shadow |= UART_FCR_DMA_SELECT;
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);

@@ -1190,6 +1220,7 @@ static void tegra_uart_set_termios(struct uart_port *u,
struct clk *parent_clk = clk_get_parent(tup->uart_clk);
unsigned long parent_clk_rate = clk_get_rate(parent_clk);
int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF;
+ int ret;

max_divider *= 16;
spin_lock_irqsave(&u->lock, flags);
@@ -1262,7 +1293,11 @@ static void tegra_uart_set_termios(struct uart_port *u,
parent_clk_rate/max_divider,
parent_clk_rate/16);
spin_unlock_irqrestore(&u->lock, flags);
- tegra_set_baudrate(tup, baud);
+ ret = tegra_set_baudrate(tup, baud);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Failed to set baud rate\n");
+ return;
+ }
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
spin_lock_irqsave(&u->lock, flags);
@@ -1399,6 +1434,8 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = {
.fifo_mode_enable_status = false,
.uart_max_port = 5,
.max_dma_burst_bytes = 4,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
};

static struct tegra_uart_chip_data tegra30_uart_chip_data = {
@@ -1408,6 +1445,8 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.fifo_mode_enable_status = false,
.uart_max_port = 5,
.max_dma_burst_bytes = 4,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
};

static struct tegra_uart_chip_data tegra186_uart_chip_data = {
@@ -1417,6 +1456,19 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = {
.fifo_mode_enable_status = true,
.uart_max_port = 8,
.max_dma_burst_bytes = 8,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
+};
+
+static struct tegra_uart_chip_data tegra194_uart_chip_data = {
+ .tx_fifo_full_status = true,
+ .allow_txfifo_reset_fifo_mode = false,
+ .support_clk_src_div = true,
+ .fifo_mode_enable_status = true,
+ .uart_max_port = 8,
+ .max_dma_burst_bytes = 8,
+ .error_tolerance_low_range = -2,
+ .error_tolerance_high_range = 2,
};

static const struct of_device_id tegra_uart_of_match[] = {
@@ -1430,6 +1482,9 @@ static const struct of_device_id tegra_uart_of_match[] = {
.compatible = "nvidia,tegra186-hsuart",
.data = &tegra186_uart_chip_data,
}, {
+ .compatible = "nvidia,tegra194-hsuart",
+ .data = &tegra194_uart_chip_data,
+ }, {
},
};
MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
--
2.7.4

2019-09-04 04:46:16

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 08/12] serial: tegra: add support to use 8 bytes trigger

Add support to use 8 bytes trigger for Tegra186 SOC.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 8422516..02f8535 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -88,6 +88,7 @@ struct tegra_uart_chip_data {
bool support_clk_src_div;
bool fifo_mode_enable_status;
int uart_max_port;
+ int max_dma_burst_bytes;
};

struct tegra_uart_port {
@@ -931,7 +932,12 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
* programmed in the DMA registers.
*/
tup->fcr_shadow = UART_FCR_ENABLE_FIFO;
- tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+
+ if (tup->cdata->max_dma_burst_bytes == 8)
+ tup->fcr_shadow |= UART_FCR_R_TRIG_10;
+ else
+ tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+
tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);

@@ -1045,7 +1051,7 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
}
dma_sconfig.src_addr = tup->uport.mapbase;
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma_sconfig.src_maxburst = 4;
+ dma_sconfig.src_maxburst = tup->cdata->max_dma_burst_bytes;
tup->rx_dma_chan = dma_chan;
tup->rx_dma_buf_virt = dma_buf;
tup->rx_dma_buf_phys = dma_phys;
@@ -1324,6 +1330,7 @@ static struct tegra_uart_chip_data tegra20_uart_chip_data = {
.support_clk_src_div = false,
.fifo_mode_enable_status = false,
.uart_max_port = 5,
+ .max_dma_burst_bytes = 4,
};

static struct tegra_uart_chip_data tegra30_uart_chip_data = {
@@ -1332,6 +1339,7 @@ static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.support_clk_src_div = true,
.fifo_mode_enable_status = false,
.uart_max_port = 5,
+ .max_dma_burst_bytes = 4,
};

static struct tegra_uart_chip_data tegra186_uart_chip_data = {
@@ -1340,6 +1348,7 @@ static struct tegra_uart_chip_data tegra186_uart_chip_data = {
.support_clk_src_div = true,
.fifo_mode_enable_status = true,
.uart_max_port = 8,
+ .max_dma_burst_bytes = 8,
};

static const struct of_device_id tegra_uart_of_match[] = {
--
2.7.4

2019-09-04 04:47:08

by Krishna Yarlagadda

[permalink] [raw]
Subject: [PATCH V2 12/12] serial: tegra: Add PIO mode support

Add PIO mode support in receive and transmit path with RX interrupt
trigger of 16 bytes for Tegra194 and older chips.

Signed-off-by: Shardar Shariff Md <[email protected]>
Signed-off-by: Krishna Yarlagadda <[email protected]>
---
drivers/tty/serial/serial-tegra.c | 117 ++++++++++++++++++++++++++++----------
1 file changed, 86 insertions(+), 31 deletions(-)

diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index d0fd417..2f59951 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -139,6 +139,8 @@ struct tegra_uart_port {
int n_adjustable_baud_rates;
int required_rate;
int configured_rate;
+ bool use_rx_pio;
+ bool use_tx_pio;
};

static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
@@ -567,7 +569,7 @@ static void tegra_uart_start_next_tx(struct tegra_uart_port *tup)
if (!count)
return;

- if (count < TEGRA_UART_MIN_DMA)
+ if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA)
tegra_uart_start_pio_tx(tup, count);
else if (BYTES_TO_ALIGN(tail) > 0)
tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail));
@@ -800,6 +802,18 @@ static void tegra_uart_handle_modem_signal_change(struct uart_port *u)
uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
}

+static void do_handle_rx_pio(struct tegra_uart_port *tup)
+{
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct tty_port *port = &tup->uport.state->port;
+
+ tegra_uart_handle_rx_pio(tup, port);
+ if (tty) {
+ tty_flip_buffer_push(port);
+ tty_kref_put(tty);
+ }
+}
+
static irqreturn_t tegra_uart_isr(int irq, void *data)
{
struct tegra_uart_port *tup = data;
@@ -813,7 +827,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
while (1) {
iir = tegra_uart_read(tup, UART_IIR);
if (iir & UART_IIR_NO_INT) {
- if (is_rx_int) {
+ if (!tup->use_rx_pio && is_rx_int) {
tegra_uart_handle_rx_dma(tup);
if (tup->rx_in_progress) {
ier = tup->ier_shadow;
@@ -841,7 +855,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
case 4: /* End of data */
case 6: /* Rx timeout */
case 2: /* Receive */
- if (!is_rx_int) {
+ if (!tup->use_rx_pio && !is_rx_int) {
is_rx_int = true;
/* Disable Rx interrupts */
ier = tup->ier_shadow;
@@ -851,6 +865,8 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
UART_IER_RTOIE | TEGRA_UART_IER_EORD);
tup->ier_shadow = ier;
tegra_uart_write(tup, ier, UART_IER);
+ } else {
+ do_handle_rx_pio(tup);
}
break;

@@ -869,6 +885,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data)
static void tegra_uart_stop_rx(struct uart_port *u)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct tty_port *port = &tup->uport.state->port;
struct dma_tx_state state;
unsigned long ier;

@@ -886,9 +903,13 @@ static void tegra_uart_stop_rx(struct uart_port *u)
tup->ier_shadow = ier;
tegra_uart_write(tup, ier, UART_IER);
tup->rx_in_progress = 0;
- dmaengine_terminate_all(tup->rx_dma_chan);
- dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
- tegra_uart_rx_buffer_push(tup, state.residue);
+ if (tup->rx_dma_chan && !tup->use_rx_pio) {
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ tegra_uart_rx_buffer_push(tup, state.residue);
+ } else {
+ tegra_uart_handle_rx_pio(tup, port);
+ }
}

static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
@@ -939,8 +960,10 @@ static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
tup->rx_in_progress = 0;
tup->tx_in_progress = 0;

- tegra_uart_dma_channel_free(tup, true);
- tegra_uart_dma_channel_free(tup, false);
+ if (!tup->use_rx_pio)
+ tegra_uart_dma_channel_free(tup, true);
+ if (!tup->use_tx_pio)
+ tegra_uart_dma_channel_free(tup, false);

clk_disable_unprepare(tup->uart_clk);
}
@@ -985,10 +1008,14 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
*/
tup->fcr_shadow = UART_FCR_ENABLE_FIFO;

- if (tup->cdata->max_dma_burst_bytes == 8)
- tup->fcr_shadow |= UART_FCR_R_TRIG_10;
- else
- tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+ if (tup->use_rx_pio) {
+ tup->fcr_shadow |= UART_FCR_R_TRIG_11;
+ } else {
+ if (tup->cdata->max_dma_burst_bytes == 8)
+ tup->fcr_shadow |= UART_FCR_R_TRIG_10;
+ else
+ tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+ }

tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
@@ -1016,19 +1043,23 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
* (115200, N, 8, 1) so that the receive DMA buffer may be
* enqueued
*/
- tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
if (ret < 0) {
dev_err(tup->uport.dev, "Failed to set baud rate\n");
return ret;
}
- tup->fcr_shadow |= UART_FCR_DMA_SELECT;
- tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+ if (!tup->use_rx_pio) {
+ tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
+ tup->fcr_shadow |= UART_FCR_DMA_SELECT;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);

- ret = tegra_uart_start_rx_dma(tup);
- if (ret < 0) {
- dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
- return ret;
+ ret = tegra_uart_start_rx_dma(tup);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
+ return ret;
+ }
+ } else {
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
}
tup->rx_in_progress = 1;

@@ -1050,7 +1081,12 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
* both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
* then the EORD.
*/
- tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD;
+ if (!tup->use_rx_pio)
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD;
+ else
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | UART_IER_RDI;
+
tegra_uart_write(tup, tup->ier_shadow, UART_IER);
return 0;
}
@@ -1145,16 +1181,22 @@ static int tegra_uart_startup(struct uart_port *u)
struct tegra_uart_port *tup = to_tegra_uport(u);
int ret;

- ret = tegra_uart_dma_channel_allocate(tup, false);
- if (ret < 0) {
- dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);
- return ret;
+ if (!tup->use_tx_pio) {
+ ret = tegra_uart_dma_channel_allocate(tup, false);
+ if (ret < 0) {
+ dev_err(u->dev, "Tx Dma allocation failed, err = %d\n",
+ ret);
+ return ret;
+ }
}

- ret = tegra_uart_dma_channel_allocate(tup, true);
- if (ret < 0) {
- dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret);
- goto fail_rx_dma;
+ if (!tup->use_rx_pio) {
+ ret = tegra_uart_dma_channel_allocate(tup, true);
+ if (ret < 0) {
+ dev_err(u->dev, "Rx Dma allocation failed, err = %d\n",
+ ret);
+ goto fail_rx_dma;
+ }
}

ret = tegra_uart_hw_init(tup);
@@ -1172,9 +1214,11 @@ static int tegra_uart_startup(struct uart_port *u)
return 0;

fail_hw_init:
- tegra_uart_dma_channel_free(tup, true);
+ if (!tup->use_rx_pio)
+ tegra_uart_dma_channel_free(tup, true);
fail_rx_dma:
- tegra_uart_dma_channel_free(tup, false);
+ if (!tup->use_tx_pio)
+ tegra_uart_dma_channel_free(tup, false);
return ret;
}

@@ -1378,7 +1422,6 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,
int count;
int n_entries;

-
port = of_alias_get_id(np, "serial");
if (port < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", port);
@@ -1388,6 +1431,18 @@ static int tegra_uart_parse_dt(struct platform_device *pdev,

tup->enable_modem_interrupt = of_property_read_bool(np,
"nvidia,enable-modem-interrupt");
+
+ index = of_property_match_string(np, "dma-names", "rx");
+ if (index < 0) {
+ tup->use_rx_pio = true;
+ dev_info(&pdev->dev, "RX in PIO mode\n");
+ }
+ index = of_property_match_string(np, "dma-names", "tx");
+ if (index < 0) {
+ tup->use_tx_pio = true;
+ dev_info(&pdev->dev, "TX in PIO mode\n");
+ }
+
n_entries = of_property_count_u32_elems(np, "nvidia,adjust-baud-rates");
if (n_entries > 0) {
tup->n_adjustable_baud_rates = n_entries / 3;
--
2.7.4