brintos

brintos / linux-shallow public Read only

0
0
Text · 15.8 KiB · fb082ee Raw
610 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2018 Socionext Inc.4 */5 6#include <linux/clk.h>7#include <linux/console.h>8#include <linux/module.h>9#include <linux/of_irq.h>10#include <linux/platform_device.h>11#include <linux/serial_core.h>12#include <linux/tty.h>13#include <linux/tty_flip.h>14 15#define USIO_NAME		"mlb-usio-uart"16#define USIO_UART_DEV_NAME	"ttyUSI"17 18static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];19 20#define RX	021#define TX	122static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];23 24#define MLB_USIO_REG_SMR		025#define MLB_USIO_REG_SCR		126#define MLB_USIO_REG_ESCR		227#define MLB_USIO_REG_SSR		328#define MLB_USIO_REG_DR			429#define MLB_USIO_REG_BGR		630#define MLB_USIO_REG_FCR		1231#define MLB_USIO_REG_FBYTE		1432 33#define MLB_USIO_SMR_SOE		BIT(0)34#define MLB_USIO_SMR_SBL		BIT(3)35#define MLB_USIO_SCR_TXE		BIT(0)36#define MLB_USIO_SCR_RXE		BIT(1)37#define MLB_USIO_SCR_TBIE		BIT(2)38#define MLB_USIO_SCR_TIE		BIT(3)39#define MLB_USIO_SCR_RIE		BIT(4)40#define MLB_USIO_SCR_UPCL		BIT(7)41#define MLB_USIO_ESCR_L_8BIT		042#define MLB_USIO_ESCR_L_5BIT		143#define MLB_USIO_ESCR_L_6BIT		244#define MLB_USIO_ESCR_L_7BIT		345#define MLB_USIO_ESCR_P			BIT(3)46#define MLB_USIO_ESCR_PEN		BIT(4)47#define MLB_USIO_ESCR_FLWEN		BIT(7)48#define MLB_USIO_SSR_TBI		BIT(0)49#define MLB_USIO_SSR_TDRE		BIT(1)50#define MLB_USIO_SSR_RDRF		BIT(2)51#define MLB_USIO_SSR_ORE		BIT(3)52#define MLB_USIO_SSR_FRE		BIT(4)53#define MLB_USIO_SSR_PE			BIT(5)54#define MLB_USIO_SSR_REC		BIT(7)55#define MLB_USIO_SSR_BRK		BIT(8)56#define MLB_USIO_FCR_FE1		BIT(0)57#define MLB_USIO_FCR_FE2		BIT(1)58#define MLB_USIO_FCR_FCL1		BIT(2)59#define MLB_USIO_FCR_FCL2		BIT(3)60#define MLB_USIO_FCR_FSET		BIT(4)61#define MLB_USIO_FCR_FTIE		BIT(9)62#define MLB_USIO_FCR_FDRQ		BIT(10)63#define MLB_USIO_FCR_FRIIE		BIT(11)64 65static void mlb_usio_stop_tx(struct uart_port *port)66{67	writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,68	       port->membase + MLB_USIO_REG_FCR);69	writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,70	       port->membase + MLB_USIO_REG_SCR);71}72 73static void mlb_usio_tx_chars(struct uart_port *port)74{75	struct tty_port *tport = &port->state->port;76	int count;77 78	writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,79	       port->membase + MLB_USIO_REG_FCR);80	writeb(readb(port->membase + MLB_USIO_REG_SCR) &81	       ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),82	       port->membase + MLB_USIO_REG_SCR);83 84	if (port->x_char) {85		writew(port->x_char, port->membase + MLB_USIO_REG_DR);86		port->icount.tx++;87		port->x_char = 0;88		return;89	}90	if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {91		mlb_usio_stop_tx(port);92		return;93	}94 95	count = port->fifosize -96		(readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);97 98	do {99		unsigned char ch;100 101		if (!uart_fifo_get(port, &ch))102			break;103 104		writew(ch, port->membase + MLB_USIO_REG_DR);105		port->icount.tx++;106	} while (--count > 0);107 108	writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,109	       port->membase + MLB_USIO_REG_FCR);110 111	writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,112	       port->membase + MLB_USIO_REG_SCR);113 114	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)115		uart_write_wakeup(port);116 117	if (kfifo_is_empty(&tport->xmit_fifo))118		mlb_usio_stop_tx(port);119}120 121static void mlb_usio_start_tx(struct uart_port *port)122{123	u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);124 125	writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);126	if (!(fcr & MLB_USIO_FCR_FDRQ))127		return;128 129	writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,130	       port->membase + MLB_USIO_REG_SCR);131 132	if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)133		mlb_usio_tx_chars(port);134}135 136static void mlb_usio_stop_rx(struct uart_port *port)137{138	writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,139	       port->membase + MLB_USIO_REG_SCR);140}141 142static void mlb_usio_enable_ms(struct uart_port *port)143{144	writeb(readb(port->membase + MLB_USIO_REG_SCR) |145	       MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,146	       port->membase + MLB_USIO_REG_SCR);147}148 149static void mlb_usio_rx_chars(struct uart_port *port)150{151	struct tty_port *ttyport = &port->state->port;152	u8 flag = 0, ch = 0;153	u8 status;154	int max_count = 2;155 156	while (max_count--) {157		status = readb(port->membase + MLB_USIO_REG_SSR);158 159		if (!(status & MLB_USIO_SSR_RDRF))160			break;161 162		if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |163				MLB_USIO_SSR_PE))) {164			ch = readw(port->membase + MLB_USIO_REG_DR);165			flag = TTY_NORMAL;166			port->icount.rx++;167			if (uart_handle_sysrq_char(port, ch))168				continue;169			uart_insert_char(port, status, MLB_USIO_SSR_ORE,170					 ch, flag);171			continue;172		}173		if (status & MLB_USIO_SSR_PE)174			port->icount.parity++;175		if (status & MLB_USIO_SSR_ORE)176			port->icount.overrun++;177		status &= port->read_status_mask;178		if (status & MLB_USIO_SSR_BRK) {179			flag = TTY_BREAK;180			ch = 0;181		} else182			if (status & MLB_USIO_SSR_PE) {183				flag = TTY_PARITY;184				ch = 0;185			} else186				if (status & MLB_USIO_SSR_FRE) {187					flag = TTY_FRAME;188					ch = 0;189				}190		if (flag)191			uart_insert_char(port, status, MLB_USIO_SSR_ORE,192					 ch, flag);193 194		writeb(readb(port->membase + MLB_USIO_REG_SSR) |195				MLB_USIO_SSR_REC,196				port->membase + MLB_USIO_REG_SSR);197 198		max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;199		writew(readw(port->membase + MLB_USIO_REG_FCR) |200		       MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,201		port->membase + MLB_USIO_REG_FCR);202	}203 204	tty_flip_buffer_push(ttyport);205}206 207static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)208{209	struct uart_port *port = dev_id;210 211	uart_port_lock(port);212	mlb_usio_rx_chars(port);213	uart_port_unlock(port);214 215	return IRQ_HANDLED;216}217 218static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)219{220	struct uart_port *port = dev_id;221 222	uart_port_lock(port);223	if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)224		mlb_usio_tx_chars(port);225	uart_port_unlock(port);226 227	return IRQ_HANDLED;228}229 230static unsigned int mlb_usio_tx_empty(struct uart_port *port)231{232	return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?233		TIOCSER_TEMT : 0;234}235 236static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)237{238}239 240static unsigned int mlb_usio_get_mctrl(struct uart_port *port)241{242	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;243 244}245 246static void mlb_usio_break_ctl(struct uart_port *port, int break_state)247{248}249 250static int mlb_usio_startup(struct uart_port *port)251{252	const char *portname = to_platform_device(port->dev)->name;253	unsigned long flags;254	int ret, index = port->line;255	unsigned char  escr;256 257	ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,258				0, portname, port);259	if (ret)260		return ret;261	ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,262				0, portname, port);263	if (ret) {264		free_irq(mlb_usio_irq[index][RX], port);265		return ret;266	}267 268	escr = readb(port->membase + MLB_USIO_REG_ESCR);269	if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))270		escr |= MLB_USIO_ESCR_FLWEN;271	uart_port_lock_irqsave(port, &flags);272	writeb(0, port->membase + MLB_USIO_REG_SCR);273	writeb(escr, port->membase + MLB_USIO_REG_ESCR);274	writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);275	writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);276	writew(0, port->membase + MLB_USIO_REG_FCR);277	writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,278	       port->membase + MLB_USIO_REG_FCR);279	writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,280	       port->membase + MLB_USIO_REG_FCR);281	writew(0, port->membase + MLB_USIO_REG_FBYTE);282	writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);283 284	writeb(MLB_USIO_SCR_TXE  | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |285	       MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);286	uart_port_unlock_irqrestore(port, flags);287 288	return 0;289}290 291static void mlb_usio_shutdown(struct uart_port *port)292{293	int index = port->line;294 295	free_irq(mlb_usio_irq[index][RX], port);296	free_irq(mlb_usio_irq[index][TX], port);297}298 299static void mlb_usio_set_termios(struct uart_port *port,300				 struct ktermios *termios,301				 const struct ktermios *old)302{303	unsigned int escr, smr = MLB_USIO_SMR_SOE;304	unsigned long flags, baud, quot;305 306	switch (termios->c_cflag & CSIZE) {307	case CS5:308		escr = MLB_USIO_ESCR_L_5BIT;309		break;310	case CS6:311		escr = MLB_USIO_ESCR_L_6BIT;312		break;313	case CS7:314		escr = MLB_USIO_ESCR_L_7BIT;315		break;316	case CS8:317	default:318		escr = MLB_USIO_ESCR_L_8BIT;319		break;320	}321 322	if (termios->c_cflag & CSTOPB)323		smr |= MLB_USIO_SMR_SBL;324 325	if (termios->c_cflag & PARENB) {326		escr |= MLB_USIO_ESCR_PEN;327		if (termios->c_cflag & PARODD)328			escr |= MLB_USIO_ESCR_P;329	}330	/* Set hard flow control */331	if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||332			(termios->c_cflag & CRTSCTS))333		escr |= MLB_USIO_ESCR_FLWEN;334 335	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);336	if (baud > 1)337		quot = port->uartclk / baud - 1;338	else339		quot = 0;340 341	uart_port_lock_irqsave(port, &flags);342	uart_update_timeout(port, termios->c_cflag, baud);343	port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |344				 MLB_USIO_SSR_TDRE;345	if (termios->c_iflag & INPCK)346		port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;347 348	port->ignore_status_mask = 0;349	if (termios->c_iflag & IGNPAR)350		port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;351	if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))352		port->ignore_status_mask |= MLB_USIO_SSR_ORE;353	if ((termios->c_cflag & CREAD) == 0)354		port->ignore_status_mask |= MLB_USIO_SSR_RDRF;355 356	writeb(0, port->membase + MLB_USIO_REG_SCR);357	writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);358	writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);359	writew(0, port->membase + MLB_USIO_REG_FCR);360	writeb(smr, port->membase + MLB_USIO_REG_SMR);361	writeb(escr, port->membase + MLB_USIO_REG_ESCR);362	writew(quot, port->membase + MLB_USIO_REG_BGR);363	writew(0, port->membase + MLB_USIO_REG_FCR);364	writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |365	       MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,366	       port->membase + MLB_USIO_REG_FCR);367	writew(0, port->membase + MLB_USIO_REG_FBYTE);368	writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);369	writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |370	       MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);371	uart_port_unlock_irqrestore(port, flags);372}373 374static const char *mlb_usio_type(struct uart_port *port)375{376	return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);377}378 379static void mlb_usio_config_port(struct uart_port *port, int flags)380{381	if (flags & UART_CONFIG_TYPE)382		port->type = PORT_MLB_USIO;383}384 385static const struct uart_ops mlb_usio_ops = {386	.tx_empty	= mlb_usio_tx_empty,387	.set_mctrl	= mlb_usio_set_mctrl,388	.get_mctrl	= mlb_usio_get_mctrl,389	.stop_tx	= mlb_usio_stop_tx,390	.start_tx	= mlb_usio_start_tx,391	.stop_rx	= mlb_usio_stop_rx,392	.enable_ms	= mlb_usio_enable_ms,393	.break_ctl	= mlb_usio_break_ctl,394	.startup	= mlb_usio_startup,395	.shutdown	= mlb_usio_shutdown,396	.set_termios	= mlb_usio_set_termios,397	.type		= mlb_usio_type,398	.config_port	= mlb_usio_config_port,399};400 401#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE402 403static void mlb_usio_console_putchar(struct uart_port *port, unsigned char c)404{405	while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))406		cpu_relax();407 408	writew(c, port->membase + MLB_USIO_REG_DR);409}410 411static void mlb_usio_console_write(struct console *co, const char *s,412			       unsigned int count)413{414	struct uart_port *port = &mlb_usio_ports[co->index];415 416	uart_console_write(port, s, count, mlb_usio_console_putchar);417}418 419static int __init mlb_usio_console_setup(struct console *co, char *options)420{421	struct uart_port *port;422	int baud = 115200;423	int parity = 'n';424	int flow = 'n';425	int bits = 8;426 427	if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)428		return -ENODEV;429 430	port = &mlb_usio_ports[co->index];431	if (!port->membase)432		return -ENODEV;433 434 435	if (options)436		uart_parse_options(options, &baud, &parity, &bits, &flow);437 438	if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))439		flow = 'r';440 441	return uart_set_options(port, co, baud, parity, bits, flow);442}443 444 445static struct uart_driver mlb_usio_uart_driver;446static struct console mlb_usio_console = {447	.name   = USIO_UART_DEV_NAME,448	.write  = mlb_usio_console_write,449	.device = uart_console_device,450	.setup  = mlb_usio_console_setup,451	.flags  = CON_PRINTBUFFER,452	.index  = -1,453	.data   = &mlb_usio_uart_driver,454};455 456static int __init mlb_usio_console_init(void)457{458	register_console(&mlb_usio_console);459	return 0;460}461console_initcall(mlb_usio_console_init);462 463 464static void mlb_usio_early_console_write(struct console *co, const char *s,465					u_int count)466{467	struct earlycon_device *dev = co->data;468 469	uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);470}471 472static int __init mlb_usio_early_console_setup(struct earlycon_device *device,473						const char *opt)474{475	if (!device->port.membase)476		return -ENODEV;477	device->con->write = mlb_usio_early_console_write;478	return 0;479}480 481OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",482			mlb_usio_early_console_setup);483 484#define USIO_CONSOLE	(&mlb_usio_console)485#else486#define USIO_CONSOLE	NULL487#endif488 489static struct  uart_driver mlb_usio_uart_driver = {490	.owner		= THIS_MODULE,491	.driver_name	= USIO_NAME,492	.dev_name	= USIO_UART_DEV_NAME,493	.cons           = USIO_CONSOLE,494	.nr		= CONFIG_SERIAL_MILBEAUT_USIO_PORTS,495};496 497static int mlb_usio_probe(struct platform_device *pdev)498{499	struct clk *clk = devm_clk_get(&pdev->dev, NULL);500	struct uart_port *port;501	struct resource *res;502	int index = 0;503	int ret;504 505	if (IS_ERR(clk)) {506		dev_err(&pdev->dev, "Missing clock\n");507		return PTR_ERR(clk);508	}509	ret = clk_prepare_enable(clk);510	if (ret) {511		dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);512		return ret;513	}514	of_property_read_u32(pdev->dev.of_node, "index", &index);515	port = &mlb_usio_ports[index];516 517	port->private_data = (void *)clk;518	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);519	if (res == NULL) {520		dev_err(&pdev->dev, "Missing regs\n");521		ret = -ENODEV;522		goto failed;523	}524	port->membase = devm_ioremap(&pdev->dev, res->start,525				resource_size(res));526 527	ret = platform_get_irq_byname(pdev, "rx");528	mlb_usio_irq[index][RX] = ret;529 530	ret = platform_get_irq_byname(pdev, "tx");531	mlb_usio_irq[index][TX] = ret;532 533	port->irq = mlb_usio_irq[index][RX];534	port->uartclk = clk_get_rate(clk);535	port->fifosize = 128;536	port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE);537	port->iotype = UPIO_MEM32;538	port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;539	port->line = index;540	port->ops = &mlb_usio_ops;541	port->dev = &pdev->dev;542 543	ret = uart_add_one_port(&mlb_usio_uart_driver, port);544	if (ret) {545		dev_err(&pdev->dev, "Adding port failed: %d\n", ret);546		goto failed;547	}548	return 0;549 550failed:551	clk_disable_unprepare(clk);552 553	return ret;554}555 556static void mlb_usio_remove(struct platform_device *pdev)557{558	struct uart_port *port = &mlb_usio_ports[pdev->id];559	struct clk *clk = port->private_data;560 561	uart_remove_one_port(&mlb_usio_uart_driver, port);562	clk_disable_unprepare(clk);563}564 565static const struct of_device_id mlb_usio_dt_ids[] = {566	{ .compatible = "socionext,milbeaut-usio-uart" },567	{ /* sentinel */ }568};569MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);570 571static struct platform_driver mlb_usio_driver = {572	.probe          = mlb_usio_probe,573	.remove_new     = mlb_usio_remove,574	.driver         = {575		.name   = USIO_NAME,576		.of_match_table = mlb_usio_dt_ids,577	},578};579 580static int __init mlb_usio_init(void)581{582	int ret = uart_register_driver(&mlb_usio_uart_driver);583 584	if (ret) {585		pr_err("%s: uart registration failed: %d\n", __func__, ret);586		return ret;587	}588	ret = platform_driver_register(&mlb_usio_driver);589	if (ret) {590		uart_unregister_driver(&mlb_usio_uart_driver);591		pr_err("%s: drv registration failed: %d\n", __func__, ret);592		return ret;593	}594 595	return 0;596}597 598static void __exit mlb_usio_exit(void)599{600	platform_driver_unregister(&mlb_usio_driver);601	uart_unregister_driver(&mlb_usio_uart_driver);602}603 604module_init(mlb_usio_init);605module_exit(mlb_usio_exit);606 607MODULE_AUTHOR("SOCIONEXT");608MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");609MODULE_LICENSE("GPL");610