diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 02f85353d720834af93a9a1bab2f6b7e37476c87..f970ed7b182d679856eee309b9e3bfbdf8bd7f3a 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; }