193 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Serial Port driver for Tegra devices4 *5 * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.6 */7 8#include <linux/acpi.h>9#include <linux/clk.h>10#include <linux/console.h>11#include <linux/delay.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/reset.h>16#include <linux/slab.h>17 18#include "8250.h"19 20struct tegra_uart {21 struct clk *clk;22 struct reset_control *rst;23 int line;24};25 26static void tegra_uart_handle_break(struct uart_port *p)27{28 unsigned int status, tmout = 10000;29 30 while (1) {31 status = p->serial_in(p, UART_LSR);32 if (!(status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)))33 break;34 35 p->serial_in(p, UART_RX);36 37 if (--tmout == 0)38 break;39 udelay(1);40 }41}42 43static int tegra_uart_probe(struct platform_device *pdev)44{45 struct uart_8250_port port8250;46 struct tegra_uart *uart;47 struct uart_port *port;48 struct resource *res;49 int ret;50 51 uart = devm_kzalloc(&pdev->dev, sizeof(*uart), GFP_KERNEL);52 if (!uart)53 return -ENOMEM;54 55 memset(&port8250, 0, sizeof(port8250));56 57 port = &port8250.port;58 spin_lock_init(&port->lock);59 60 port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE;61 port->type = PORT_TEGRA;62 port->dev = &pdev->dev;63 port->handle_break = tegra_uart_handle_break;64 65 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);66 if (!res)67 return -ENODEV;68 69 port->membase = devm_ioremap(&pdev->dev, res->start,70 resource_size(res));71 if (!port->membase)72 return -ENOMEM;73 74 port->mapbase = res->start;75 port->mapsize = resource_size(res);76 77 ret = uart_read_port_properties(port);78 if (ret)79 return ret;80 81 port->iotype = UPIO_MEM32;82 port->regshift = 2;83 84 uart->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);85 if (IS_ERR(uart->rst))86 return PTR_ERR(uart->rst);87 88 if (!port->uartclk) {89 uart->clk = devm_clk_get(&pdev->dev, NULL);90 if (IS_ERR(uart->clk)) {91 dev_err(&pdev->dev, "failed to get clock!\n");92 return -ENODEV;93 }94 95 ret = clk_prepare_enable(uart->clk);96 if (ret < 0)97 return ret;98 99 port->uartclk = clk_get_rate(uart->clk);100 }101 102 ret = reset_control_deassert(uart->rst);103 if (ret)104 goto err_clkdisable;105 106 ret = serial8250_register_8250_port(&port8250);107 if (ret < 0)108 goto err_ctrl_assert;109 110 platform_set_drvdata(pdev, uart);111 uart->line = ret;112 113 return 0;114 115err_ctrl_assert:116 reset_control_assert(uart->rst);117err_clkdisable:118 clk_disable_unprepare(uart->clk);119 120 return ret;121}122 123static void tegra_uart_remove(struct platform_device *pdev)124{125 struct tegra_uart *uart = platform_get_drvdata(pdev);126 127 serial8250_unregister_port(uart->line);128 reset_control_assert(uart->rst);129 clk_disable_unprepare(uart->clk);130}131 132#ifdef CONFIG_PM_SLEEP133static int tegra_uart_suspend(struct device *dev)134{135 struct tegra_uart *uart = dev_get_drvdata(dev);136 struct uart_8250_port *port8250 = serial8250_get_port(uart->line);137 struct uart_port *port = &port8250->port;138 139 serial8250_suspend_port(uart->line);140 141 if (!uart_console(port) || console_suspend_enabled)142 clk_disable_unprepare(uart->clk);143 144 return 0;145}146 147static int tegra_uart_resume(struct device *dev)148{149 struct tegra_uart *uart = dev_get_drvdata(dev);150 struct uart_8250_port *port8250 = serial8250_get_port(uart->line);151 struct uart_port *port = &port8250->port;152 153 if (!uart_console(port) || console_suspend_enabled)154 clk_prepare_enable(uart->clk);155 156 serial8250_resume_port(uart->line);157 158 return 0;159}160#endif161 162static SIMPLE_DEV_PM_OPS(tegra_uart_pm_ops, tegra_uart_suspend,163 tegra_uart_resume);164 165static const struct of_device_id tegra_uart_of_match[] = {166 { .compatible = "nvidia,tegra20-uart", },167 { },168};169MODULE_DEVICE_TABLE(of, tegra_uart_of_match);170 171static const struct acpi_device_id tegra_uart_acpi_match[] __maybe_unused = {172 { "NVDA0100", 0 },173 { },174};175MODULE_DEVICE_TABLE(acpi, tegra_uart_acpi_match);176 177static struct platform_driver tegra_uart_driver = {178 .driver = {179 .name = "tegra-uart",180 .pm = &tegra_uart_pm_ops,181 .of_match_table = tegra_uart_of_match,182 .acpi_match_table = ACPI_PTR(tegra_uart_acpi_match),183 },184 .probe = tegra_uart_probe,185 .remove_new = tegra_uart_remove,186};187 188module_platform_driver(tegra_uart_driver);189 190MODULE_AUTHOR("Jeff Brasen <jbrasen@nvidia.com>");191MODULE_DESCRIPTION("NVIDIA Tegra 8250 Driver");192MODULE_LICENSE("GPL v2");193