xref: /linux/drivers/tty/serial/tegra-tcu.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
12d908b38SThierry Reding // SPDX-License-Identifier: GPL-2.0
22d908b38SThierry Reding /*
32d908b38SThierry Reding  * Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
42d908b38SThierry Reding  */
52d908b38SThierry Reding 
62d908b38SThierry Reding #include <linux/console.h>
72d908b38SThierry Reding #include <linux/mailbox_client.h>
82d908b38SThierry Reding #include <linux/module.h>
92d908b38SThierry Reding #include <linux/of.h>
102d908b38SThierry Reding #include <linux/platform_device.h>
112d908b38SThierry Reding #include <linux/serial.h>
122d908b38SThierry Reding #include <linux/serial_core.h>
132d908b38SThierry Reding #include <linux/slab.h>
142d908b38SThierry Reding #include <linux/tty.h>
152d908b38SThierry Reding #include <linux/tty_flip.h>
162d908b38SThierry Reding 
172d908b38SThierry Reding #define TCU_MBOX_BYTE(i, x)			((x) << (i * 8))
182d908b38SThierry Reding #define TCU_MBOX_BYTE_V(x, i)			(((x) >> (i * 8)) & 0xff)
192d908b38SThierry Reding #define TCU_MBOX_NUM_BYTES(x)			((x) << 24)
202d908b38SThierry Reding #define TCU_MBOX_NUM_BYTES_V(x)			(((x) >> 24) & 0x3)
212d908b38SThierry Reding 
222d908b38SThierry Reding struct tegra_tcu {
232d908b38SThierry Reding 	struct uart_driver driver;
242d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
252d908b38SThierry Reding 	struct console console;
262d908b38SThierry Reding #endif
272d908b38SThierry Reding 	struct uart_port port;
282d908b38SThierry Reding 
292d908b38SThierry Reding 	struct mbox_client tx_client, rx_client;
302d908b38SThierry Reding 	struct mbox_chan *tx, *rx;
312d908b38SThierry Reding };
322d908b38SThierry Reding 
332d908b38SThierry Reding static unsigned int tegra_tcu_uart_tx_empty(struct uart_port *port)
342d908b38SThierry Reding {
352d908b38SThierry Reding 	return TIOCSER_TEMT;
362d908b38SThierry Reding }
372d908b38SThierry Reding 
382d908b38SThierry Reding static void tegra_tcu_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
392d908b38SThierry Reding {
402d908b38SThierry Reding }
412d908b38SThierry Reding 
422d908b38SThierry Reding static unsigned int tegra_tcu_uart_get_mctrl(struct uart_port *port)
432d908b38SThierry Reding {
442d908b38SThierry Reding 	return 0;
452d908b38SThierry Reding }
462d908b38SThierry Reding 
472d908b38SThierry Reding static void tegra_tcu_uart_stop_tx(struct uart_port *port)
482d908b38SThierry Reding {
492d908b38SThierry Reding }
502d908b38SThierry Reding 
512d908b38SThierry Reding static void tegra_tcu_write_one(struct tegra_tcu *tcu, u32 value,
522d908b38SThierry Reding 				unsigned int count)
532d908b38SThierry Reding {
542d908b38SThierry Reding 	void *msg;
552d908b38SThierry Reding 
562d908b38SThierry Reding 	value |= TCU_MBOX_NUM_BYTES(count);
572d908b38SThierry Reding 	msg = (void *)(unsigned long)value;
582d908b38SThierry Reding 	mbox_send_message(tcu->tx, msg);
592d908b38SThierry Reding 	mbox_flush(tcu->tx, 1000);
602d908b38SThierry Reding }
612d908b38SThierry Reding 
622d908b38SThierry Reding static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s,
632d908b38SThierry Reding 			    unsigned int count)
642d908b38SThierry Reding {
652d908b38SThierry Reding 	unsigned int written = 0, i = 0;
662d908b38SThierry Reding 	bool insert_nl = false;
672d908b38SThierry Reding 	u32 value = 0;
682d908b38SThierry Reding 
692d908b38SThierry Reding 	while (i < count) {
702d908b38SThierry Reding 		if (insert_nl) {
712d908b38SThierry Reding 			value |= TCU_MBOX_BYTE(written++, '\n');
722d908b38SThierry Reding 			insert_nl = false;
732d908b38SThierry Reding 			i++;
742d908b38SThierry Reding 		} else if (s[i] == '\n') {
752d908b38SThierry Reding 			value |= TCU_MBOX_BYTE(written++, '\r');
762d908b38SThierry Reding 			insert_nl = true;
772d908b38SThierry Reding 		} else {
782d908b38SThierry Reding 			value |= TCU_MBOX_BYTE(written++, s[i++]);
792d908b38SThierry Reding 		}
802d908b38SThierry Reding 
812d908b38SThierry Reding 		if (written == 3) {
822d908b38SThierry Reding 			tegra_tcu_write_one(tcu, value, 3);
832d908b38SThierry Reding 			value = written = 0;
842d908b38SThierry Reding 		}
852d908b38SThierry Reding 	}
862d908b38SThierry Reding 
872d908b38SThierry Reding 	if (written)
882d908b38SThierry Reding 		tegra_tcu_write_one(tcu, value, written);
892d908b38SThierry Reding }
902d908b38SThierry Reding 
912d908b38SThierry Reding static void tegra_tcu_uart_start_tx(struct uart_port *port)
922d908b38SThierry Reding {
932d908b38SThierry Reding 	struct tegra_tcu *tcu = port->private_data;
94*1788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
95*1788cf6aSJiri Slaby (SUSE) 	unsigned char *tail;
96*1788cf6aSJiri Slaby (SUSE) 	unsigned int count;
972d908b38SThierry Reding 
982d908b38SThierry Reding 	for (;;) {
99*1788cf6aSJiri Slaby (SUSE) 		count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
100*1788cf6aSJiri Slaby (SUSE) 				UART_XMIT_SIZE);
1012d908b38SThierry Reding 		if (!count)
1022d908b38SThierry Reding 			break;
1032d908b38SThierry Reding 
104*1788cf6aSJiri Slaby (SUSE) 		tegra_tcu_write(tcu, tail, count);
1051d10cd4dSIlpo Järvinen 		uart_xmit_advance(port, count);
1062d908b38SThierry Reding 	}
1072d908b38SThierry Reding 
1082d908b38SThierry Reding 	uart_write_wakeup(port);
1092d908b38SThierry Reding }
1102d908b38SThierry Reding 
1112d908b38SThierry Reding static void tegra_tcu_uart_stop_rx(struct uart_port *port)
1122d908b38SThierry Reding {
1132d908b38SThierry Reding }
1142d908b38SThierry Reding 
1152d908b38SThierry Reding static void tegra_tcu_uart_break_ctl(struct uart_port *port, int ctl)
1162d908b38SThierry Reding {
1172d908b38SThierry Reding }
1182d908b38SThierry Reding 
1192d908b38SThierry Reding static int tegra_tcu_uart_startup(struct uart_port *port)
1202d908b38SThierry Reding {
1212d908b38SThierry Reding 	return 0;
1222d908b38SThierry Reding }
1232d908b38SThierry Reding 
1242d908b38SThierry Reding static void tegra_tcu_uart_shutdown(struct uart_port *port)
1252d908b38SThierry Reding {
1262d908b38SThierry Reding }
1272d908b38SThierry Reding 
1282d908b38SThierry Reding static void tegra_tcu_uart_set_termios(struct uart_port *port,
1292d908b38SThierry Reding 				       struct ktermios *new,
130bec5b814SIlpo Järvinen 				       const struct ktermios *old)
1312d908b38SThierry Reding {
1322d908b38SThierry Reding }
1332d908b38SThierry Reding 
1342d908b38SThierry Reding static const struct uart_ops tegra_tcu_uart_ops = {
1352d908b38SThierry Reding 	.tx_empty = tegra_tcu_uart_tx_empty,
1362d908b38SThierry Reding 	.set_mctrl = tegra_tcu_uart_set_mctrl,
1372d908b38SThierry Reding 	.get_mctrl = tegra_tcu_uart_get_mctrl,
1382d908b38SThierry Reding 	.stop_tx = tegra_tcu_uart_stop_tx,
1392d908b38SThierry Reding 	.start_tx = tegra_tcu_uart_start_tx,
1402d908b38SThierry Reding 	.stop_rx = tegra_tcu_uart_stop_rx,
1412d908b38SThierry Reding 	.break_ctl = tegra_tcu_uart_break_ctl,
1422d908b38SThierry Reding 	.startup = tegra_tcu_uart_startup,
1432d908b38SThierry Reding 	.shutdown = tegra_tcu_uart_shutdown,
1442d908b38SThierry Reding 	.set_termios = tegra_tcu_uart_set_termios,
1452d908b38SThierry Reding };
1462d908b38SThierry Reding 
1472d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
1482d908b38SThierry Reding static void tegra_tcu_console_write(struct console *cons, const char *s,
1492d908b38SThierry Reding 				    unsigned int count)
1502d908b38SThierry Reding {
1512d908b38SThierry Reding 	struct tegra_tcu *tcu = container_of(cons, struct tegra_tcu, console);
1522d908b38SThierry Reding 
1532d908b38SThierry Reding 	tegra_tcu_write(tcu, s, count);
1542d908b38SThierry Reding }
1552d908b38SThierry Reding 
1562d908b38SThierry Reding static int tegra_tcu_console_setup(struct console *cons, char *options)
1572d908b38SThierry Reding {
1582d908b38SThierry Reding 	return 0;
1592d908b38SThierry Reding }
1602d908b38SThierry Reding #endif
1612d908b38SThierry Reding 
1622d908b38SThierry Reding static void tegra_tcu_receive(struct mbox_client *cl, void *msg)
1632d908b38SThierry Reding {
1642d908b38SThierry Reding 	struct tegra_tcu *tcu = container_of(cl, struct tegra_tcu, rx_client);
1652d908b38SThierry Reding 	struct tty_port *port = &tcu->port.state->port;
1662d908b38SThierry Reding 	u32 value = (u32)(unsigned long)msg;
1672d908b38SThierry Reding 	unsigned int num_bytes, i;
1682d908b38SThierry Reding 
1692d908b38SThierry Reding 	num_bytes = TCU_MBOX_NUM_BYTES_V(value);
1702d908b38SThierry Reding 
1712d908b38SThierry Reding 	for (i = 0; i < num_bytes; i++)
1722d908b38SThierry Reding 		tty_insert_flip_char(port, TCU_MBOX_BYTE_V(value, i),
1732d908b38SThierry Reding 				     TTY_NORMAL);
1742d908b38SThierry Reding 
1752d908b38SThierry Reding 	tty_flip_buffer_push(port);
1762d908b38SThierry Reding }
1772d908b38SThierry Reding 
1782d908b38SThierry Reding static int tegra_tcu_probe(struct platform_device *pdev)
1792d908b38SThierry Reding {
1802d908b38SThierry Reding 	struct uart_port *port;
1812d908b38SThierry Reding 	struct tegra_tcu *tcu;
1822d908b38SThierry Reding 	int err;
1832d908b38SThierry Reding 
1842d908b38SThierry Reding 	tcu = devm_kzalloc(&pdev->dev, sizeof(*tcu), GFP_KERNEL);
1852d908b38SThierry Reding 	if (!tcu)
1862d908b38SThierry Reding 		return -ENOMEM;
1872d908b38SThierry Reding 
1882d908b38SThierry Reding 	tcu->tx_client.dev = &pdev->dev;
1892d908b38SThierry Reding 	tcu->rx_client.dev = &pdev->dev;
1902d908b38SThierry Reding 	tcu->rx_client.rx_callback = tegra_tcu_receive;
1912d908b38SThierry Reding 
1922d908b38SThierry Reding 	tcu->tx = mbox_request_channel_byname(&tcu->tx_client, "tx");
1932d908b38SThierry Reding 	if (IS_ERR(tcu->tx)) {
1942d908b38SThierry Reding 		err = PTR_ERR(tcu->tx);
1952d908b38SThierry Reding 		dev_err(&pdev->dev, "failed to get tx mailbox: %d\n", err);
1962d908b38SThierry Reding 		return err;
1972d908b38SThierry Reding 	}
1982d908b38SThierry Reding 
1992d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
2002d908b38SThierry Reding 	/* setup the console */
2012d908b38SThierry Reding 	strcpy(tcu->console.name, "ttyTCU");
2022d908b38SThierry Reding 	tcu->console.device = uart_console_device;
2032d908b38SThierry Reding 	tcu->console.flags = CON_PRINTBUFFER | CON_ANYTIME;
2042d908b38SThierry Reding 	tcu->console.index = -1;
2052d908b38SThierry Reding 	tcu->console.write = tegra_tcu_console_write;
2062d908b38SThierry Reding 	tcu->console.setup = tegra_tcu_console_setup;
2072d908b38SThierry Reding 	tcu->console.data = &tcu->driver;
2082d908b38SThierry Reding #endif
2092d908b38SThierry Reding 
2102d908b38SThierry Reding 	/* setup the driver */
2112d908b38SThierry Reding 	tcu->driver.owner = THIS_MODULE;
2122d908b38SThierry Reding 	tcu->driver.driver_name = "tegra-tcu";
2132d908b38SThierry Reding 	tcu->driver.dev_name = "ttyTCU";
2142d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
2152d908b38SThierry Reding 	tcu->driver.cons = &tcu->console;
2162d908b38SThierry Reding #endif
2172d908b38SThierry Reding 	tcu->driver.nr = 1;
2182d908b38SThierry Reding 
2192d908b38SThierry Reding 	err = uart_register_driver(&tcu->driver);
2202d908b38SThierry Reding 	if (err) {
2212d908b38SThierry Reding 		dev_err(&pdev->dev, "failed to register UART driver: %d\n",
2222d908b38SThierry Reding 			err);
223af9a1f61SMikko Perttunen 		goto free_tx;
2242d908b38SThierry Reding 	}
2252d908b38SThierry Reding 
2262d908b38SThierry Reding 	/* setup the port */
2272d908b38SThierry Reding 	port = &tcu->port;
2282d908b38SThierry Reding 	spin_lock_init(&port->lock);
2292d908b38SThierry Reding 	port->dev = &pdev->dev;
2302d908b38SThierry Reding 	port->type = PORT_TEGRA_TCU;
2312d908b38SThierry Reding 	port->ops = &tegra_tcu_uart_ops;
2322d908b38SThierry Reding 	port->fifosize = 1;
2332d908b38SThierry Reding 	port->iotype = UPIO_MEM;
2342d908b38SThierry Reding 	port->flags = UPF_BOOT_AUTOCONF;
2352d908b38SThierry Reding 	port->private_data = tcu;
2362d908b38SThierry Reding 
2372d908b38SThierry Reding 	err = uart_add_one_port(&tcu->driver, port);
2382d908b38SThierry Reding 	if (err) {
2392d908b38SThierry Reding 		dev_err(&pdev->dev, "failed to add UART port: %d\n", err);
2402d908b38SThierry Reding 		goto unregister_uart;
2412d908b38SThierry Reding 	}
2422d908b38SThierry Reding 
243af9a1f61SMikko Perttunen 	/*
244af9a1f61SMikko Perttunen 	 * Request RX channel after creating port to ensure tcu->port
245af9a1f61SMikko Perttunen 	 * is ready for any immediate incoming bytes.
246af9a1f61SMikko Perttunen 	 */
247af9a1f61SMikko Perttunen 	tcu->rx = mbox_request_channel_byname(&tcu->rx_client, "rx");
248af9a1f61SMikko Perttunen 	if (IS_ERR(tcu->rx)) {
249af9a1f61SMikko Perttunen 		err = PTR_ERR(tcu->rx);
250af9a1f61SMikko Perttunen 		dev_err(&pdev->dev, "failed to get rx mailbox: %d\n", err);
251af9a1f61SMikko Perttunen 		goto remove_uart_port;
252af9a1f61SMikko Perttunen 	}
253af9a1f61SMikko Perttunen 
2542d908b38SThierry Reding 	platform_set_drvdata(pdev, tcu);
2552d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
2562d908b38SThierry Reding 	register_console(&tcu->console);
2572d908b38SThierry Reding #endif
2582d908b38SThierry Reding 
2592d908b38SThierry Reding 	return 0;
2602d908b38SThierry Reding 
261af9a1f61SMikko Perttunen remove_uart_port:
262af9a1f61SMikko Perttunen 	uart_remove_one_port(&tcu->driver, &tcu->port);
2632d908b38SThierry Reding unregister_uart:
2642d908b38SThierry Reding 	uart_unregister_driver(&tcu->driver);
2652d908b38SThierry Reding free_tx:
2662d908b38SThierry Reding 	mbox_free_channel(tcu->tx);
2672d908b38SThierry Reding 
2682d908b38SThierry Reding 	return err;
2692d908b38SThierry Reding }
2702d908b38SThierry Reding 
2715e29d46fSUwe Kleine-König static void tegra_tcu_remove(struct platform_device *pdev)
2722d908b38SThierry Reding {
2732d908b38SThierry Reding 	struct tegra_tcu *tcu = platform_get_drvdata(pdev);
2742d908b38SThierry Reding 
2752d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
2762d908b38SThierry Reding 	unregister_console(&tcu->console);
2772d908b38SThierry Reding #endif
278af9a1f61SMikko Perttunen 	mbox_free_channel(tcu->rx);
2792d908b38SThierry Reding 	uart_remove_one_port(&tcu->driver, &tcu->port);
2802d908b38SThierry Reding 	uart_unregister_driver(&tcu->driver);
2812d908b38SThierry Reding 	mbox_free_channel(tcu->tx);
2822d908b38SThierry Reding }
2832d908b38SThierry Reding 
2842d908b38SThierry Reding static const struct of_device_id tegra_tcu_match[] = {
2852d908b38SThierry Reding 	{ .compatible = "nvidia,tegra194-tcu" },
2862d908b38SThierry Reding 	{ }
2872d908b38SThierry Reding };
2889e5313acSBixuan Cui MODULE_DEVICE_TABLE(of, tegra_tcu_match);
2892d908b38SThierry Reding 
2902d908b38SThierry Reding static struct platform_driver tegra_tcu_driver = {
2912d908b38SThierry Reding 	.driver = {
2922d908b38SThierry Reding 		.name = "tegra-tcu",
2932d908b38SThierry Reding 		.of_match_table = tegra_tcu_match,
2942d908b38SThierry Reding 	},
2952d908b38SThierry Reding 	.probe = tegra_tcu_probe,
2965e29d46fSUwe Kleine-König 	.remove_new = tegra_tcu_remove,
2972d908b38SThierry Reding };
2982d908b38SThierry Reding module_platform_driver(tegra_tcu_driver);
2992d908b38SThierry Reding 
3002d908b38SThierry Reding MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
3012d908b38SThierry Reding MODULE_LICENSE("GPL v2");
3022d908b38SThierry Reding MODULE_DESCRIPTION("NVIDIA Tegra Combined UART driver");
303