xref: /linux/drivers/tty/serial/tegra-utc.c (revision 378ec25aec5a8444879f8696d580c94950a1f1df)
1eb07e3a9SKartik Rajput // SPDX-License-Identifier: GPL-2.0-only
2eb07e3a9SKartik Rajput // SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3eb07e3a9SKartik Rajput // NVIDIA Tegra UTC (UART Trace Controller) driver.
4eb07e3a9SKartik Rajput 
5eb07e3a9SKartik Rajput #include <linux/bits.h>
6eb07e3a9SKartik Rajput #include <linux/console.h>
7eb07e3a9SKartik Rajput #include <linux/container_of.h>
8eb07e3a9SKartik Rajput #include <linux/device.h>
9eb07e3a9SKartik Rajput #include <linux/err.h>
10eb07e3a9SKartik Rajput #include <linux/iopoll.h>
11eb07e3a9SKartik Rajput #include <linux/kfifo.h>
12eb07e3a9SKartik Rajput #include <linux/module.h>
13eb07e3a9SKartik Rajput #include <linux/mod_devicetable.h>
14eb07e3a9SKartik Rajput #include <linux/property.h>
15eb07e3a9SKartik Rajput #include <linux/platform_device.h>
16eb07e3a9SKartik Rajput #include <linux/serial.h>
17eb07e3a9SKartik Rajput #include <linux/serial_core.h>
18eb07e3a9SKartik Rajput #include <linux/slab.h>
19eb07e3a9SKartik Rajput #include <linux/tty.h>
20eb07e3a9SKartik Rajput #include <linux/tty_flip.h>
21eb07e3a9SKartik Rajput #include <linux/types.h>
22eb07e3a9SKartik Rajput 
23eb07e3a9SKartik Rajput #define TEGRA_UTC_ENABLE			0x000
24eb07e3a9SKartik Rajput #define TEGRA_UTC_ENABLE_CLIENT_ENABLE		BIT(0)
25eb07e3a9SKartik Rajput 
26eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_THRESHOLD		0x008
27eb07e3a9SKartik Rajput 
28eb07e3a9SKartik Rajput #define TEGRA_UTC_COMMAND			0x00c
29eb07e3a9SKartik Rajput #define TEGRA_UTC_COMMAND_RESET			BIT(0)
30eb07e3a9SKartik Rajput #define TEGRA_UTC_COMMAND_FLUSH			BIT(1)
31eb07e3a9SKartik Rajput 
32eb07e3a9SKartik Rajput #define TEGRA_UTC_DATA				0x020
33eb07e3a9SKartik Rajput 
34eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_STATUS			0x100
35eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_EMPTY			BIT(0)
36eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_FULL			BIT(1)
37eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_REQ			BIT(2)
38eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_OVERFLOW			BIT(3)
39eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_TIMEOUT			BIT(4)
40eb07e3a9SKartik Rajput 
41eb07e3a9SKartik Rajput #define TEGRA_UTC_FIFO_OCCUPANCY		0x104
42eb07e3a9SKartik Rajput 
43eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_STATUS			0x108
44eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_SET			0x10c
45eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_MASK			0x110
46eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_CLEAR			0x114
47eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_EMPTY			BIT(0)
48eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_FULL			BIT(1)
49eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_REQ			BIT(2)
50eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_OVERFLOW			BIT(3)
51eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_TIMEOUT			BIT(4)
52eb07e3a9SKartik Rajput 
53eb07e3a9SKartik Rajput #define TEGRA_UTC_UART_NR			16
54eb07e3a9SKartik Rajput 
55eb07e3a9SKartik Rajput #define TEGRA_UTC_INTR_COMMON	(TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_FULL | TEGRA_UTC_INTR_EMPTY)
56eb07e3a9SKartik Rajput 
57eb07e3a9SKartik Rajput struct tegra_utc_port {
58eb07e3a9SKartik Rajput #if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
59eb07e3a9SKartik Rajput 	struct console console;
60eb07e3a9SKartik Rajput #endif
61eb07e3a9SKartik Rajput 	struct uart_port port;
62eb07e3a9SKartik Rajput 
63eb07e3a9SKartik Rajput 	void __iomem *rx_base;
64eb07e3a9SKartik Rajput 	void __iomem *tx_base;
65eb07e3a9SKartik Rajput 
66eb07e3a9SKartik Rajput 	u32 tx_irqmask;
67eb07e3a9SKartik Rajput 	u32 rx_irqmask;
68eb07e3a9SKartik Rajput 
69eb07e3a9SKartik Rajput 	unsigned int fifosize;
70eb07e3a9SKartik Rajput 	u32 tx_threshold;
71eb07e3a9SKartik Rajput 	u32 rx_threshold;
72eb07e3a9SKartik Rajput };
73eb07e3a9SKartik Rajput 
tegra_utc_rx_readl(struct tegra_utc_port * tup,unsigned int offset)74eb07e3a9SKartik Rajput static u32 tegra_utc_rx_readl(struct tegra_utc_port *tup, unsigned int offset)
75eb07e3a9SKartik Rajput {
76eb07e3a9SKartik Rajput 	void __iomem *addr = tup->rx_base + offset;
77eb07e3a9SKartik Rajput 
78eb07e3a9SKartik Rajput 	return readl_relaxed(addr);
79eb07e3a9SKartik Rajput }
80eb07e3a9SKartik Rajput 
tegra_utc_rx_writel(struct tegra_utc_port * tup,u32 val,unsigned int offset)81eb07e3a9SKartik Rajput static void tegra_utc_rx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset)
82eb07e3a9SKartik Rajput {
83eb07e3a9SKartik Rajput 	void __iomem *addr = tup->rx_base + offset;
84eb07e3a9SKartik Rajput 
85eb07e3a9SKartik Rajput 	writel_relaxed(val, addr);
86eb07e3a9SKartik Rajput }
87eb07e3a9SKartik Rajput 
tegra_utc_tx_readl(struct tegra_utc_port * tup,unsigned int offset)88eb07e3a9SKartik Rajput static u32 tegra_utc_tx_readl(struct tegra_utc_port *tup, unsigned int offset)
89eb07e3a9SKartik Rajput {
90eb07e3a9SKartik Rajput 	void __iomem *addr = tup->tx_base + offset;
91eb07e3a9SKartik Rajput 
92eb07e3a9SKartik Rajput 	return readl_relaxed(addr);
93eb07e3a9SKartik Rajput }
94eb07e3a9SKartik Rajput 
tegra_utc_tx_writel(struct tegra_utc_port * tup,u32 val,unsigned int offset)95eb07e3a9SKartik Rajput static void tegra_utc_tx_writel(struct tegra_utc_port *tup, u32 val, unsigned int offset)
96eb07e3a9SKartik Rajput {
97eb07e3a9SKartik Rajput 	void __iomem *addr = tup->tx_base + offset;
98eb07e3a9SKartik Rajput 
99eb07e3a9SKartik Rajput 	writel_relaxed(val, addr);
100eb07e3a9SKartik Rajput }
101eb07e3a9SKartik Rajput 
tegra_utc_enable_tx_irq(struct tegra_utc_port * tup)102eb07e3a9SKartik Rajput static void tegra_utc_enable_tx_irq(struct tegra_utc_port *tup)
103eb07e3a9SKartik Rajput {
104eb07e3a9SKartik Rajput 	tup->tx_irqmask = TEGRA_UTC_INTR_REQ;
105eb07e3a9SKartik Rajput 
106eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK);
107eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET);
108eb07e3a9SKartik Rajput }
109eb07e3a9SKartik Rajput 
tegra_utc_disable_tx_irq(struct tegra_utc_port * tup)110eb07e3a9SKartik Rajput static void tegra_utc_disable_tx_irq(struct tegra_utc_port *tup)
111eb07e3a9SKartik Rajput {
112eb07e3a9SKartik Rajput 	tup->tx_irqmask = 0x0;
113eb07e3a9SKartik Rajput 
114eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_MASK);
115eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_SET);
116eb07e3a9SKartik Rajput }
117eb07e3a9SKartik Rajput 
tegra_utc_stop_tx(struct uart_port * port)118eb07e3a9SKartik Rajput static void tegra_utc_stop_tx(struct uart_port *port)
119eb07e3a9SKartik Rajput {
120eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
121eb07e3a9SKartik Rajput 
122eb07e3a9SKartik Rajput 	tegra_utc_disable_tx_irq(tup);
123eb07e3a9SKartik Rajput }
124eb07e3a9SKartik Rajput 
tegra_utc_init_tx(struct tegra_utc_port * tup)125eb07e3a9SKartik Rajput static void tegra_utc_init_tx(struct tegra_utc_port *tup)
126eb07e3a9SKartik Rajput {
127eb07e3a9SKartik Rajput 	/* Disable TX. */
128eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, 0x0, TEGRA_UTC_ENABLE);
129eb07e3a9SKartik Rajput 
130eb07e3a9SKartik Rajput 	/* Update the FIFO Threshold. */
131eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, tup->tx_threshold, TEGRA_UTC_FIFO_THRESHOLD);
132eb07e3a9SKartik Rajput 
133eb07e3a9SKartik Rajput 	/* Clear and mask all the interrupts. */
134eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR);
135eb07e3a9SKartik Rajput 	tegra_utc_disable_tx_irq(tup);
136eb07e3a9SKartik Rajput 
137eb07e3a9SKartik Rajput 	/* Enable TX. */
138eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE);
139eb07e3a9SKartik Rajput }
140eb07e3a9SKartik Rajput 
tegra_utc_init_rx(struct tegra_utc_port * tup)141eb07e3a9SKartik Rajput static void tegra_utc_init_rx(struct tegra_utc_port *tup)
142eb07e3a9SKartik Rajput {
143eb07e3a9SKartik Rajput 	tup->rx_irqmask = TEGRA_UTC_INTR_REQ | TEGRA_UTC_INTR_TIMEOUT;
144eb07e3a9SKartik Rajput 
145eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, TEGRA_UTC_COMMAND_RESET, TEGRA_UTC_COMMAND);
146eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, tup->rx_threshold, TEGRA_UTC_FIFO_THRESHOLD);
147eb07e3a9SKartik Rajput 
148eb07e3a9SKartik Rajput 	/* Clear all the pending interrupts. */
149eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, TEGRA_UTC_INTR_TIMEOUT | TEGRA_UTC_INTR_OVERFLOW |
150eb07e3a9SKartik Rajput 			    TEGRA_UTC_INTR_COMMON, TEGRA_UTC_INTR_CLEAR);
151eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK);
152eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET);
153eb07e3a9SKartik Rajput 
154eb07e3a9SKartik Rajput 	/* Enable RX. */
155eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, TEGRA_UTC_ENABLE_CLIENT_ENABLE, TEGRA_UTC_ENABLE);
156eb07e3a9SKartik Rajput }
157eb07e3a9SKartik Rajput 
tegra_utc_tx_chars(struct tegra_utc_port * tup)158eb07e3a9SKartik Rajput static bool tegra_utc_tx_chars(struct tegra_utc_port *tup)
159eb07e3a9SKartik Rajput {
160eb07e3a9SKartik Rajput 	struct uart_port *port = &tup->port;
161eb07e3a9SKartik Rajput 	unsigned int pending;
162eb07e3a9SKartik Rajput 	u8 c;
163eb07e3a9SKartik Rajput 
164eb07e3a9SKartik Rajput 	pending = uart_port_tx(port, c,
165eb07e3a9SKartik Rajput 		     !(tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_FULL),
166eb07e3a9SKartik Rajput 		     tegra_utc_tx_writel(tup, c, TEGRA_UTC_DATA));
167eb07e3a9SKartik Rajput 
168eb07e3a9SKartik Rajput 	return pending;
169eb07e3a9SKartik Rajput }
170eb07e3a9SKartik Rajput 
tegra_utc_rx_chars(struct tegra_utc_port * tup)171eb07e3a9SKartik Rajput static void tegra_utc_rx_chars(struct tegra_utc_port *tup)
172eb07e3a9SKartik Rajput {
173eb07e3a9SKartik Rajput 	struct tty_port *port = &tup->port.state->port;
174eb07e3a9SKartik Rajput 	unsigned int max_chars = 256;
175eb07e3a9SKartik Rajput 	u32 status;
176eb07e3a9SKartik Rajput 	int sysrq;
177eb07e3a9SKartik Rajput 	u32 ch;
178eb07e3a9SKartik Rajput 
179eb07e3a9SKartik Rajput 	while (max_chars--) {
180eb07e3a9SKartik Rajput 		status = tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS);
181eb07e3a9SKartik Rajput 		if (status & TEGRA_UTC_FIFO_EMPTY)
182eb07e3a9SKartik Rajput 			break;
183eb07e3a9SKartik Rajput 
184eb07e3a9SKartik Rajput 		ch = tegra_utc_rx_readl(tup, TEGRA_UTC_DATA);
185eb07e3a9SKartik Rajput 		tup->port.icount.rx++;
186eb07e3a9SKartik Rajput 
187eb07e3a9SKartik Rajput 		if (status & TEGRA_UTC_FIFO_OVERFLOW)
188eb07e3a9SKartik Rajput 			tup->port.icount.overrun++;
189eb07e3a9SKartik Rajput 
190eb07e3a9SKartik Rajput 		uart_port_unlock(&tup->port);
191eb07e3a9SKartik Rajput 		sysrq = uart_handle_sysrq_char(&tup->port, ch);
192eb07e3a9SKartik Rajput 		uart_port_lock(&tup->port);
193eb07e3a9SKartik Rajput 
194eb07e3a9SKartik Rajput 		if (!sysrq)
195eb07e3a9SKartik Rajput 			tty_insert_flip_char(port, ch, TTY_NORMAL);
196eb07e3a9SKartik Rajput 	}
197eb07e3a9SKartik Rajput 
198eb07e3a9SKartik Rajput 	tty_flip_buffer_push(port);
199eb07e3a9SKartik Rajput }
200eb07e3a9SKartik Rajput 
tegra_utc_isr(int irq,void * dev_id)201eb07e3a9SKartik Rajput static irqreturn_t tegra_utc_isr(int irq, void *dev_id)
202eb07e3a9SKartik Rajput {
203eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = dev_id;
204eb07e3a9SKartik Rajput 	unsigned int handled = 0;
205eb07e3a9SKartik Rajput 	u32 status;
206eb07e3a9SKartik Rajput 
207eb07e3a9SKartik Rajput 	uart_port_lock(&tup->port);
208eb07e3a9SKartik Rajput 
209eb07e3a9SKartik Rajput 	/* Process RX_REQ and RX_TIMEOUT interrupts. */
210eb07e3a9SKartik Rajput 	do {
211eb07e3a9SKartik Rajput 		status = tegra_utc_rx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->rx_irqmask;
212eb07e3a9SKartik Rajput 		if (status) {
213eb07e3a9SKartik Rajput 			tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_CLEAR);
214eb07e3a9SKartik Rajput 			tegra_utc_rx_chars(tup);
215eb07e3a9SKartik Rajput 			handled = 1;
216eb07e3a9SKartik Rajput 		}
217eb07e3a9SKartik Rajput 	} while (status);
218eb07e3a9SKartik Rajput 
219eb07e3a9SKartik Rajput 	/* Process TX_REQ interrupt. */
220eb07e3a9SKartik Rajput 	do {
221eb07e3a9SKartik Rajput 		status = tegra_utc_tx_readl(tup, TEGRA_UTC_INTR_STATUS) & tup->tx_irqmask;
222eb07e3a9SKartik Rajput 		if (status) {
223eb07e3a9SKartik Rajput 			tegra_utc_tx_writel(tup, tup->tx_irqmask, TEGRA_UTC_INTR_CLEAR);
224eb07e3a9SKartik Rajput 			tegra_utc_tx_chars(tup);
225eb07e3a9SKartik Rajput 			handled = 1;
226eb07e3a9SKartik Rajput 		}
227eb07e3a9SKartik Rajput 	} while (status);
228eb07e3a9SKartik Rajput 
229eb07e3a9SKartik Rajput 	uart_port_unlock(&tup->port);
230eb07e3a9SKartik Rajput 
231eb07e3a9SKartik Rajput 	return IRQ_RETVAL(handled);
232eb07e3a9SKartik Rajput }
233eb07e3a9SKartik Rajput 
tegra_utc_tx_empty(struct uart_port * port)234eb07e3a9SKartik Rajput static unsigned int tegra_utc_tx_empty(struct uart_port *port)
235eb07e3a9SKartik Rajput {
236eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
237eb07e3a9SKartik Rajput 
238eb07e3a9SKartik Rajput 	return tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY) ? 0 : TIOCSER_TEMT;
239eb07e3a9SKartik Rajput }
240eb07e3a9SKartik Rajput 
tegra_utc_set_mctrl(struct uart_port * port,unsigned int mctrl)241eb07e3a9SKartik Rajput static void tegra_utc_set_mctrl(struct uart_port *port, unsigned int mctrl)
242eb07e3a9SKartik Rajput {
243eb07e3a9SKartik Rajput }
244eb07e3a9SKartik Rajput 
tegra_utc_get_mctrl(struct uart_port * port)245eb07e3a9SKartik Rajput static unsigned int tegra_utc_get_mctrl(struct uart_port *port)
246eb07e3a9SKartik Rajput {
247eb07e3a9SKartik Rajput 	return 0;
248eb07e3a9SKartik Rajput }
249eb07e3a9SKartik Rajput 
tegra_utc_start_tx(struct uart_port * port)250eb07e3a9SKartik Rajput static void tegra_utc_start_tx(struct uart_port *port)
251eb07e3a9SKartik Rajput {
252eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
253eb07e3a9SKartik Rajput 
254eb07e3a9SKartik Rajput 	if (tegra_utc_tx_chars(tup))
255eb07e3a9SKartik Rajput 		tegra_utc_enable_tx_irq(tup);
256eb07e3a9SKartik Rajput }
257eb07e3a9SKartik Rajput 
tegra_utc_stop_rx(struct uart_port * port)258eb07e3a9SKartik Rajput static void tegra_utc_stop_rx(struct uart_port *port)
259eb07e3a9SKartik Rajput {
260eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
261eb07e3a9SKartik Rajput 
262eb07e3a9SKartik Rajput 	tup->rx_irqmask = 0x0;
263eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_MASK);
264eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, tup->rx_irqmask, TEGRA_UTC_INTR_SET);
265eb07e3a9SKartik Rajput }
266eb07e3a9SKartik Rajput 
tegra_utc_hw_init(struct tegra_utc_port * tup)267eb07e3a9SKartik Rajput static void tegra_utc_hw_init(struct tegra_utc_port *tup)
268eb07e3a9SKartik Rajput {
269eb07e3a9SKartik Rajput 	tegra_utc_init_tx(tup);
270eb07e3a9SKartik Rajput 	tegra_utc_init_rx(tup);
271eb07e3a9SKartik Rajput }
272eb07e3a9SKartik Rajput 
tegra_utc_startup(struct uart_port * port)273eb07e3a9SKartik Rajput static int tegra_utc_startup(struct uart_port *port)
274eb07e3a9SKartik Rajput {
275eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
276eb07e3a9SKartik Rajput 	int ret;
277eb07e3a9SKartik Rajput 
278eb07e3a9SKartik Rajput 	tegra_utc_hw_init(tup);
279eb07e3a9SKartik Rajput 
280eb07e3a9SKartik Rajput 	/* Interrupt is dedicated to this UTC client. */
281eb07e3a9SKartik Rajput 	ret = request_irq(port->irq, tegra_utc_isr, 0, dev_name(port->dev), tup);
282eb07e3a9SKartik Rajput 	if (ret < 0)
283eb07e3a9SKartik Rajput 		dev_err(port->dev, "failed to register interrupt handler\n");
284eb07e3a9SKartik Rajput 
285eb07e3a9SKartik Rajput 	return ret;
286eb07e3a9SKartik Rajput }
287eb07e3a9SKartik Rajput 
tegra_utc_shutdown(struct uart_port * port)288eb07e3a9SKartik Rajput static void tegra_utc_shutdown(struct uart_port *port)
289eb07e3a9SKartik Rajput {
290eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
291eb07e3a9SKartik Rajput 
292eb07e3a9SKartik Rajput 	tegra_utc_rx_writel(tup, 0x0, TEGRA_UTC_ENABLE);
293eb07e3a9SKartik Rajput 	free_irq(port->irq, tup);
294eb07e3a9SKartik Rajput }
295eb07e3a9SKartik Rajput 
tegra_utc_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)296eb07e3a9SKartik Rajput static void tegra_utc_set_termios(struct uart_port *port, struct ktermios *termios,
297eb07e3a9SKartik Rajput 				  const struct ktermios *old)
298eb07e3a9SKartik Rajput {
299eb07e3a9SKartik Rajput 	/* The Tegra UTC clients supports only 8-N-1 configuration without HW flow control */
300eb07e3a9SKartik Rajput 	termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
301eb07e3a9SKartik Rajput 	termios->c_cflag &= ~(CMSPAR | CRTSCTS);
302eb07e3a9SKartik Rajput 	termios->c_cflag |= CS8 | CLOCAL;
303eb07e3a9SKartik Rajput }
304eb07e3a9SKartik Rajput 
305eb07e3a9SKartik Rajput #ifdef CONFIG_CONSOLE_POLL
306eb07e3a9SKartik Rajput 
tegra_utc_poll_init(struct uart_port * port)307eb07e3a9SKartik Rajput static int tegra_utc_poll_init(struct uart_port *port)
308eb07e3a9SKartik Rajput {
309eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
310eb07e3a9SKartik Rajput 
311eb07e3a9SKartik Rajput 	tegra_utc_hw_init(tup);
312eb07e3a9SKartik Rajput 	return 0;
313eb07e3a9SKartik Rajput }
314eb07e3a9SKartik Rajput 
tegra_utc_get_poll_char(struct uart_port * port)315eb07e3a9SKartik Rajput static int tegra_utc_get_poll_char(struct uart_port *port)
316eb07e3a9SKartik Rajput {
317eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
318eb07e3a9SKartik Rajput 
319eb07e3a9SKartik Rajput 	if (tegra_utc_rx_readl(tup, TEGRA_UTC_FIFO_STATUS) & TEGRA_UTC_FIFO_EMPTY)
320eb07e3a9SKartik Rajput 		return NO_POLL_CHAR;
321eb07e3a9SKartik Rajput 
322eb07e3a9SKartik Rajput 	return tegra_utc_rx_readl(tup, TEGRA_UTC_DATA);
323eb07e3a9SKartik Rajput }
324eb07e3a9SKartik Rajput 
tegra_utc_put_poll_char(struct uart_port * port,unsigned char ch)325eb07e3a9SKartik Rajput static void tegra_utc_put_poll_char(struct uart_port *port, unsigned char ch)
326eb07e3a9SKartik Rajput {
327eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
328eb07e3a9SKartik Rajput 	u32 val;
329eb07e3a9SKartik Rajput 
330eb07e3a9SKartik Rajput 	read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL),
331eb07e3a9SKartik Rajput 				 0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS);
332eb07e3a9SKartik Rajput 
333eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA);
334eb07e3a9SKartik Rajput }
335eb07e3a9SKartik Rajput 
336eb07e3a9SKartik Rajput #endif
337eb07e3a9SKartik Rajput 
338eb07e3a9SKartik Rajput static const struct uart_ops tegra_utc_uart_ops = {
339eb07e3a9SKartik Rajput 	.tx_empty = tegra_utc_tx_empty,
340eb07e3a9SKartik Rajput 	.set_mctrl = tegra_utc_set_mctrl,
341eb07e3a9SKartik Rajput 	.get_mctrl = tegra_utc_get_mctrl,
342eb07e3a9SKartik Rajput 	.stop_tx = tegra_utc_stop_tx,
343eb07e3a9SKartik Rajput 	.start_tx = tegra_utc_start_tx,
344eb07e3a9SKartik Rajput 	.stop_rx = tegra_utc_stop_rx,
345eb07e3a9SKartik Rajput 	.startup = tegra_utc_startup,
346eb07e3a9SKartik Rajput 	.shutdown = tegra_utc_shutdown,
347eb07e3a9SKartik Rajput 	.set_termios = tegra_utc_set_termios,
348eb07e3a9SKartik Rajput #ifdef CONFIG_CONSOLE_POLL
349eb07e3a9SKartik Rajput 	.poll_init = tegra_utc_poll_init,
350eb07e3a9SKartik Rajput 	.poll_get_char = tegra_utc_get_poll_char,
351eb07e3a9SKartik Rajput 	.poll_put_char = tegra_utc_put_poll_char,
352eb07e3a9SKartik Rajput #endif
353eb07e3a9SKartik Rajput };
354eb07e3a9SKartik Rajput 
355eb07e3a9SKartik Rajput #if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
356eb07e3a9SKartik Rajput #define TEGRA_UTC_DEFAULT_FIFO_THRESHOLD	4
357eb07e3a9SKartik Rajput #define TEGRA_UTC_EARLYCON_MAX_BURST_SIZE	128
358eb07e3a9SKartik Rajput 
tegra_utc_putc(struct uart_port * port,unsigned char c)359eb07e3a9SKartik Rajput static void tegra_utc_putc(struct uart_port *port, unsigned char c)
360eb07e3a9SKartik Rajput {
361eb07e3a9SKartik Rajput 	writel(c, port->membase + TEGRA_UTC_DATA);
362eb07e3a9SKartik Rajput }
363eb07e3a9SKartik Rajput 
tegra_utc_early_write(struct console * con,const char * s,unsigned int n)364eb07e3a9SKartik Rajput static void tegra_utc_early_write(struct console *con, const char *s, unsigned int n)
365eb07e3a9SKartik Rajput {
366eb07e3a9SKartik Rajput 	struct earlycon_device *dev = con->data;
367eb07e3a9SKartik Rajput 
368eb07e3a9SKartik Rajput 	while (n) {
369eb07e3a9SKartik Rajput 		u32 burst_size = TEGRA_UTC_EARLYCON_MAX_BURST_SIZE;
370eb07e3a9SKartik Rajput 
371eb07e3a9SKartik Rajput 		burst_size -= readl(dev->port.membase + TEGRA_UTC_FIFO_OCCUPANCY);
372eb07e3a9SKartik Rajput 		if (n < burst_size)
373eb07e3a9SKartik Rajput 			burst_size = n;
374eb07e3a9SKartik Rajput 
375eb07e3a9SKartik Rajput 		uart_console_write(&dev->port, s, burst_size, tegra_utc_putc);
376eb07e3a9SKartik Rajput 
377eb07e3a9SKartik Rajput 		n -= burst_size;
378eb07e3a9SKartik Rajput 		s += burst_size;
379eb07e3a9SKartik Rajput 	}
380eb07e3a9SKartik Rajput }
381eb07e3a9SKartik Rajput 
tegra_utc_early_console_setup(struct earlycon_device * device,const char * opt)382eb07e3a9SKartik Rajput static int __init tegra_utc_early_console_setup(struct earlycon_device *device, const char *opt)
383eb07e3a9SKartik Rajput {
384eb07e3a9SKartik Rajput 	if (!device->port.membase)
385eb07e3a9SKartik Rajput 		return -ENODEV;
386eb07e3a9SKartik Rajput 
387eb07e3a9SKartik Rajput 	/* Configure TX */
388eb07e3a9SKartik Rajput 	writel(TEGRA_UTC_COMMAND_FLUSH | TEGRA_UTC_COMMAND_RESET,
389eb07e3a9SKartik Rajput 		device->port.membase + TEGRA_UTC_COMMAND);
390eb07e3a9SKartik Rajput 	writel(TEGRA_UTC_DEFAULT_FIFO_THRESHOLD, device->port.membase + TEGRA_UTC_FIFO_THRESHOLD);
391eb07e3a9SKartik Rajput 
392eb07e3a9SKartik Rajput 	/* Clear and mask all the interrupts. */
393eb07e3a9SKartik Rajput 	writel(TEGRA_UTC_INTR_COMMON, device->port.membase + TEGRA_UTC_INTR_CLEAR);
394eb07e3a9SKartik Rajput 
395eb07e3a9SKartik Rajput 	writel(0x0, device->port.membase + TEGRA_UTC_INTR_MASK);
396eb07e3a9SKartik Rajput 	writel(0x0, device->port.membase + TEGRA_UTC_INTR_SET);
397eb07e3a9SKartik Rajput 
398eb07e3a9SKartik Rajput 	/* Enable TX. */
399eb07e3a9SKartik Rajput 	writel(TEGRA_UTC_ENABLE_CLIENT_ENABLE, device->port.membase + TEGRA_UTC_ENABLE);
400eb07e3a9SKartik Rajput 
401eb07e3a9SKartik Rajput 	device->con->write = tegra_utc_early_write;
402eb07e3a9SKartik Rajput 
403eb07e3a9SKartik Rajput 	return 0;
404eb07e3a9SKartik Rajput }
405eb07e3a9SKartik Rajput OF_EARLYCON_DECLARE(tegra_utc, "nvidia,tegra264-utc", tegra_utc_early_console_setup);
406eb07e3a9SKartik Rajput 
tegra_utc_console_putchar(struct uart_port * port,unsigned char ch)407eb07e3a9SKartik Rajput static void tegra_utc_console_putchar(struct uart_port *port, unsigned char ch)
408eb07e3a9SKartik Rajput {
409eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(port, struct tegra_utc_port, port);
410eb07e3a9SKartik Rajput 
411eb07e3a9SKartik Rajput 	tegra_utc_tx_writel(tup, ch, TEGRA_UTC_DATA);
412eb07e3a9SKartik Rajput }
413eb07e3a9SKartik Rajput 
tegra_utc_console_write_atomic(struct console * cons,struct nbcon_write_context * wctxt)414eb07e3a9SKartik Rajput static void tegra_utc_console_write_atomic(struct console *cons, struct nbcon_write_context *wctxt)
415eb07e3a9SKartik Rajput {
416eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
417eb07e3a9SKartik Rajput 	unsigned int len;
418eb07e3a9SKartik Rajput 	char *outbuf;
419eb07e3a9SKartik Rajput 
420eb07e3a9SKartik Rajput 	if (!nbcon_enter_unsafe(wctxt))
421eb07e3a9SKartik Rajput 		return;
422eb07e3a9SKartik Rajput 
423eb07e3a9SKartik Rajput 	outbuf = wctxt->outbuf;
424eb07e3a9SKartik Rajput 	len = wctxt->len;
425eb07e3a9SKartik Rajput 
426eb07e3a9SKartik Rajput 	while (len) {
427eb07e3a9SKartik Rajput 		u32 burst_size = tup->fifosize;
428eb07e3a9SKartik Rajput 
429eb07e3a9SKartik Rajput 		burst_size -= tegra_utc_tx_readl(tup, TEGRA_UTC_FIFO_OCCUPANCY);
430eb07e3a9SKartik Rajput 		if (len < burst_size)
431eb07e3a9SKartik Rajput 			burst_size = len;
432eb07e3a9SKartik Rajput 
433eb07e3a9SKartik Rajput 		uart_console_write(&tup->port, outbuf, burst_size, tegra_utc_console_putchar);
434eb07e3a9SKartik Rajput 
435eb07e3a9SKartik Rajput 		outbuf += burst_size;
436eb07e3a9SKartik Rajput 		len -= burst_size;
437*9d64c6aeSChen Ni 	}
438eb07e3a9SKartik Rajput 
439eb07e3a9SKartik Rajput 	nbcon_exit_unsafe(wctxt);
440eb07e3a9SKartik Rajput }
441eb07e3a9SKartik Rajput 
tegra_utc_console_write_thread(struct console * cons,struct nbcon_write_context * wctxt)442eb07e3a9SKartik Rajput static void tegra_utc_console_write_thread(struct console *cons, struct nbcon_write_context *wctxt)
443eb07e3a9SKartik Rajput {
444eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
445eb07e3a9SKartik Rajput 	unsigned int len = READ_ONCE(wctxt->len);
446eb07e3a9SKartik Rajput 	unsigned int i;
447eb07e3a9SKartik Rajput 	u32 val;
448eb07e3a9SKartik Rajput 
449eb07e3a9SKartik Rajput 	for (i = 0; i < len; i++) {
450eb07e3a9SKartik Rajput 		if (!nbcon_enter_unsafe(wctxt))
451eb07e3a9SKartik Rajput 			break;
452eb07e3a9SKartik Rajput 
453eb07e3a9SKartik Rajput 		read_poll_timeout_atomic(tegra_utc_tx_readl, val, !(val & TEGRA_UTC_FIFO_FULL),
454eb07e3a9SKartik Rajput 					 0, USEC_PER_SEC, false, tup, TEGRA_UTC_FIFO_STATUS);
455eb07e3a9SKartik Rajput 		uart_console_write(&tup->port, wctxt->outbuf + i, 1, tegra_utc_console_putchar);
456eb07e3a9SKartik Rajput 
457eb07e3a9SKartik Rajput 		if (!nbcon_exit_unsafe(wctxt))
458eb07e3a9SKartik Rajput 			break;
459eb07e3a9SKartik Rajput 	}
460eb07e3a9SKartik Rajput }
461eb07e3a9SKartik Rajput 
tegra_utc_console_device_lock(struct console * cons,unsigned long * flags)462eb07e3a9SKartik Rajput static void tegra_utc_console_device_lock(struct console *cons, unsigned long *flags)
463eb07e3a9SKartik Rajput {
464eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
465eb07e3a9SKartik Rajput 	struct uart_port *port = &tup->port;
466eb07e3a9SKartik Rajput 
467eb07e3a9SKartik Rajput 	__uart_port_lock_irqsave(port, flags);
468eb07e3a9SKartik Rajput }
469eb07e3a9SKartik Rajput 
tegra_utc_console_device_unlock(struct console * cons,unsigned long flags)470eb07e3a9SKartik Rajput static void tegra_utc_console_device_unlock(struct console *cons, unsigned long flags)
471eb07e3a9SKartik Rajput {
472eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
473eb07e3a9SKartik Rajput 	struct uart_port *port = &tup->port;
474eb07e3a9SKartik Rajput 
475eb07e3a9SKartik Rajput 	__uart_port_unlock_irqrestore(port, flags);
476eb07e3a9SKartik Rajput }
477eb07e3a9SKartik Rajput 
tegra_utc_console_setup(struct console * cons,char * options)478eb07e3a9SKartik Rajput static int tegra_utc_console_setup(struct console *cons, char *options)
479eb07e3a9SKartik Rajput {
480eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = container_of(cons, struct tegra_utc_port, console);
481eb07e3a9SKartik Rajput 
482eb07e3a9SKartik Rajput 	tegra_utc_init_tx(tup);
483eb07e3a9SKartik Rajput 
484eb07e3a9SKartik Rajput 	return 0;
485eb07e3a9SKartik Rajput }
486eb07e3a9SKartik Rajput #endif
487eb07e3a9SKartik Rajput 
488eb07e3a9SKartik Rajput static struct uart_driver tegra_utc_driver = {
489eb07e3a9SKartik Rajput 	.driver_name	= "tegra-utc",
490eb07e3a9SKartik Rajput 	.dev_name	= "ttyUTC",
491eb07e3a9SKartik Rajput 	.nr		= TEGRA_UTC_UART_NR,
492eb07e3a9SKartik Rajput };
493eb07e3a9SKartik Rajput 
tegra_utc_setup_port(struct device * dev,struct tegra_utc_port * tup)494eb07e3a9SKartik Rajput static int tegra_utc_setup_port(struct device *dev, struct tegra_utc_port *tup)
495eb07e3a9SKartik Rajput {
496eb07e3a9SKartik Rajput 	tup->port.dev			= dev;
497eb07e3a9SKartik Rajput 	tup->port.fifosize		= tup->fifosize;
498eb07e3a9SKartik Rajput 	tup->port.flags			= UPF_BOOT_AUTOCONF;
499eb07e3a9SKartik Rajput 	tup->port.iotype		= UPIO_MEM;
500eb07e3a9SKartik Rajput 	tup->port.ops			= &tegra_utc_uart_ops;
501eb07e3a9SKartik Rajput 	tup->port.type			= PORT_TEGRA_TCU;
502eb07e3a9SKartik Rajput 	tup->port.private_data		= tup;
503eb07e3a9SKartik Rajput 
504eb07e3a9SKartik Rajput #if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
505eb07e3a9SKartik Rajput 	strscpy(tup->console.name, "ttyUTC", sizeof(tup->console.name));
506eb07e3a9SKartik Rajput 	tup->console.write_atomic	= tegra_utc_console_write_atomic;
507eb07e3a9SKartik Rajput 	tup->console.write_thread	= tegra_utc_console_write_thread;
508eb07e3a9SKartik Rajput 	tup->console.device_lock	= tegra_utc_console_device_lock;
509eb07e3a9SKartik Rajput 	tup->console.device_unlock	= tegra_utc_console_device_unlock;
510eb07e3a9SKartik Rajput 	tup->console.device		= uart_console_device;
511eb07e3a9SKartik Rajput 	tup->console.setup		= tegra_utc_console_setup;
512eb07e3a9SKartik Rajput 	tup->console.flags		= CON_PRINTBUFFER | CON_NBCON;
513eb07e3a9SKartik Rajput 	tup->console.data		= &tegra_utc_driver;
514eb07e3a9SKartik Rajput #endif
515eb07e3a9SKartik Rajput 
516eb07e3a9SKartik Rajput 	return uart_read_port_properties(&tup->port);
517eb07e3a9SKartik Rajput }
518eb07e3a9SKartik Rajput 
tegra_utc_register_port(struct tegra_utc_port * tup)519eb07e3a9SKartik Rajput static int tegra_utc_register_port(struct tegra_utc_port *tup)
520eb07e3a9SKartik Rajput {
521eb07e3a9SKartik Rajput 	int ret;
522eb07e3a9SKartik Rajput 
523eb07e3a9SKartik Rajput 	ret = uart_add_one_port(&tegra_utc_driver, &tup->port);
524eb07e3a9SKartik Rajput 	if (ret)
525eb07e3a9SKartik Rajput 		return ret;
526eb07e3a9SKartik Rajput 
527eb07e3a9SKartik Rajput #if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
528eb07e3a9SKartik Rajput 	register_console(&tup->console);
529eb07e3a9SKartik Rajput #endif
530eb07e3a9SKartik Rajput 
531eb07e3a9SKartik Rajput 	return 0;
532eb07e3a9SKartik Rajput }
533eb07e3a9SKartik Rajput 
tegra_utc_probe(struct platform_device * pdev)534eb07e3a9SKartik Rajput static int tegra_utc_probe(struct platform_device *pdev)
535eb07e3a9SKartik Rajput {
536eb07e3a9SKartik Rajput 	const unsigned int *soc_fifosize;
537eb07e3a9SKartik Rajput 	struct device *dev = &pdev->dev;
538eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup;
539eb07e3a9SKartik Rajput 	int ret;
540eb07e3a9SKartik Rajput 
541eb07e3a9SKartik Rajput 	tup = devm_kzalloc(dev, sizeof(*tup), GFP_KERNEL);
542eb07e3a9SKartik Rajput 	if (!tup)
543eb07e3a9SKartik Rajput 		return -ENOMEM;
544eb07e3a9SKartik Rajput 
545eb07e3a9SKartik Rajput 	ret = device_property_read_u32(dev, "tx-threshold", &tup->tx_threshold);
546eb07e3a9SKartik Rajput 	if (ret)
547eb07e3a9SKartik Rajput 		return dev_err_probe(dev, ret, "missing %s property\n", "tx-threshold");
548eb07e3a9SKartik Rajput 
549eb07e3a9SKartik Rajput 	ret = device_property_read_u32(dev, "rx-threshold", &tup->rx_threshold);
550eb07e3a9SKartik Rajput 	if (ret)
551eb07e3a9SKartik Rajput 		return dev_err_probe(dev, ret, "missing %s property\n", "rx-threshold");
552eb07e3a9SKartik Rajput 
553eb07e3a9SKartik Rajput 	soc_fifosize = device_get_match_data(dev);
554eb07e3a9SKartik Rajput 	tup->fifosize = *soc_fifosize;
555eb07e3a9SKartik Rajput 
556eb07e3a9SKartik Rajput 	tup->tx_base = devm_platform_ioremap_resource_byname(pdev, "tx");
557eb07e3a9SKartik Rajput 	if (IS_ERR(tup->tx_base))
558eb07e3a9SKartik Rajput 		return PTR_ERR(tup->tx_base);
559eb07e3a9SKartik Rajput 
560eb07e3a9SKartik Rajput 	tup->rx_base = devm_platform_ioremap_resource_byname(pdev, "rx");
561eb07e3a9SKartik Rajput 	if (IS_ERR(tup->rx_base))
562eb07e3a9SKartik Rajput 		return PTR_ERR(tup->rx_base);
563eb07e3a9SKartik Rajput 
564eb07e3a9SKartik Rajput 	ret = tegra_utc_setup_port(dev, tup);
565eb07e3a9SKartik Rajput 	if (ret)
566eb07e3a9SKartik Rajput 		dev_err_probe(dev, ret, "failed to setup uart port\n");
567eb07e3a9SKartik Rajput 
568eb07e3a9SKartik Rajput 	platform_set_drvdata(pdev, tup);
569eb07e3a9SKartik Rajput 
570eb07e3a9SKartik Rajput 	return tegra_utc_register_port(tup);
571eb07e3a9SKartik Rajput }
572eb07e3a9SKartik Rajput 
tegra_utc_remove(struct platform_device * pdev)573eb07e3a9SKartik Rajput static void tegra_utc_remove(struct platform_device *pdev)
574eb07e3a9SKartik Rajput {
575eb07e3a9SKartik Rajput 	struct tegra_utc_port *tup = platform_get_drvdata(pdev);
576eb07e3a9SKartik Rajput 
577eb07e3a9SKartik Rajput #if IS_ENABLED(CONFIG_SERIAL_TEGRA_UTC_CONSOLE)
578eb07e3a9SKartik Rajput 	unregister_console(&tup->console);
579eb07e3a9SKartik Rajput #endif
580eb07e3a9SKartik Rajput 	uart_remove_one_port(&tegra_utc_driver, &tup->port);
581eb07e3a9SKartik Rajput }
582eb07e3a9SKartik Rajput 
583eb07e3a9SKartik Rajput static const unsigned int tegra264_utc_soc = 128;
584eb07e3a9SKartik Rajput 
585eb07e3a9SKartik Rajput static const struct of_device_id tegra_utc_of_match[] = {
586eb07e3a9SKartik Rajput 	{ .compatible = "nvidia,tegra264-utc", .data = &tegra264_utc_soc },
587eb07e3a9SKartik Rajput 	{}
588eb07e3a9SKartik Rajput };
589eb07e3a9SKartik Rajput MODULE_DEVICE_TABLE(of, tegra_utc_of_match);
590eb07e3a9SKartik Rajput 
591eb07e3a9SKartik Rajput static struct platform_driver tegra_utc_platform_driver = {
592eb07e3a9SKartik Rajput 	.probe = tegra_utc_probe,
593eb07e3a9SKartik Rajput 	.remove = tegra_utc_remove,
594eb07e3a9SKartik Rajput 	.driver = {
595eb07e3a9SKartik Rajput 		.name = "tegra-utc",
596eb07e3a9SKartik Rajput 		.of_match_table = tegra_utc_of_match,
597eb07e3a9SKartik Rajput 	},
598eb07e3a9SKartik Rajput };
599eb07e3a9SKartik Rajput 
tegra_utc_init(void)600eb07e3a9SKartik Rajput static int __init tegra_utc_init(void)
601eb07e3a9SKartik Rajput {
602eb07e3a9SKartik Rajput 	int ret;
603eb07e3a9SKartik Rajput 
604eb07e3a9SKartik Rajput 	ret = uart_register_driver(&tegra_utc_driver);
605eb07e3a9SKartik Rajput 	if (ret)
606eb07e3a9SKartik Rajput 		return ret;
607eb07e3a9SKartik Rajput 
608eb07e3a9SKartik Rajput 	ret = platform_driver_register(&tegra_utc_platform_driver);
609eb07e3a9SKartik Rajput 	if (ret)
610eb07e3a9SKartik Rajput 		uart_unregister_driver(&tegra_utc_driver);
611eb07e3a9SKartik Rajput 
612eb07e3a9SKartik Rajput 	return ret;
613eb07e3a9SKartik Rajput }
614eb07e3a9SKartik Rajput module_init(tegra_utc_init);
615eb07e3a9SKartik Rajput 
tegra_utc_exit(void)616eb07e3a9SKartik Rajput static void __exit tegra_utc_exit(void)
617eb07e3a9SKartik Rajput {
618eb07e3a9SKartik Rajput 	platform_driver_unregister(&tegra_utc_platform_driver);
619eb07e3a9SKartik Rajput 	uart_unregister_driver(&tegra_utc_driver);
620eb07e3a9SKartik Rajput }
621eb07e3a9SKartik Rajput module_exit(tegra_utc_exit);
622eb07e3a9SKartik Rajput 
623eb07e3a9SKartik Rajput MODULE_AUTHOR("Kartik Rajput <kkartik@nvidia.com>");
624eb07e3a9SKartik Rajput MODULE_DESCRIPTION("Tegra UART Trace Controller");
625eb07e3a9SKartik Rajput MODULE_LICENSE("GPL");
626