brintos

brintos / linux-shallow public Read only

0
0
Text · 13.3 KiB · 30425a3 Raw
562 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  Driver for CLPS711x serial ports4 *5 *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.6 *7 *  Copyright 1999 ARM Limited8 *  Copyright (C) 2000 Deep Blue Solutions Ltd.9 */10 11#include <linux/module.h>12#include <linux/device.h>13#include <linux/console.h>14#include <linux/serial_core.h>15#include <linux/serial.h>16#include <linux/clk.h>17#include <linux/io.h>18#include <linux/tty.h>19#include <linux/tty_flip.h>20#include <linux/ioport.h>21#include <linux/of.h>22#include <linux/platform_device.h>23#include <linux/regmap.h>24 25#include <linux/mfd/syscon.h>26#include <linux/mfd/syscon/clps711x.h>27 28#include "serial_mctrl_gpio.h"29 30#define UART_CLPS711X_DEVNAME	"ttyCL"31#define UART_CLPS711X_NR	232#define UART_CLPS711X_MAJOR	20433#define UART_CLPS711X_MINOR	4034 35#define UARTDR_OFFSET		(0x00)36#define UBRLCR_OFFSET		(0x40)37 38#define UARTDR_FRMERR		(1 << 8)39#define UARTDR_PARERR		(1 << 9)40#define UARTDR_OVERR		(1 << 10)41 42#define UBRLCR_BAUD_MASK	((1 << 12) - 1)43#define UBRLCR_BREAK		(1 << 12)44#define UBRLCR_PRTEN		(1 << 13)45#define UBRLCR_EVENPRT		(1 << 14)46#define UBRLCR_XSTOP		(1 << 15)47#define UBRLCR_FIFOEN		(1 << 16)48#define UBRLCR_WRDLEN5		(0 << 17)49#define UBRLCR_WRDLEN6		(1 << 17)50#define UBRLCR_WRDLEN7		(2 << 17)51#define UBRLCR_WRDLEN8		(3 << 17)52#define UBRLCR_WRDLEN_MASK	(3 << 17)53 54struct clps711x_port {55	struct uart_port	port;56	unsigned int		tx_enabled;57	int			rx_irq;58	struct regmap		*syscon;59	struct mctrl_gpios	*gpios;60};61 62static struct uart_driver clps711x_uart = {63	.owner		= THIS_MODULE,64	.driver_name	= UART_CLPS711X_DEVNAME,65	.dev_name	= UART_CLPS711X_DEVNAME,66	.major		= UART_CLPS711X_MAJOR,67	.minor		= UART_CLPS711X_MINOR,68	.nr		= UART_CLPS711X_NR,69};70 71static void uart_clps711x_stop_tx(struct uart_port *port)72{73	struct clps711x_port *s = dev_get_drvdata(port->dev);74 75	if (s->tx_enabled) {76		disable_irq(port->irq);77		s->tx_enabled = 0;78	}79}80 81static void uart_clps711x_start_tx(struct uart_port *port)82{83	struct clps711x_port *s = dev_get_drvdata(port->dev);84 85	if (!s->tx_enabled) {86		s->tx_enabled = 1;87		enable_irq(port->irq);88	}89}90 91static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)92{93	struct uart_port *port = dev_id;94	struct clps711x_port *s = dev_get_drvdata(port->dev);95	unsigned int status;96	u16 ch;97	u8 flg;98 99	for (;;) {100		u32 sysflg = 0;101 102		regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);103		if (sysflg & SYSFLG_URXFE)104			break;105 106		ch = readw(port->membase + UARTDR_OFFSET);107		status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);108		ch &= 0xff;109 110		port->icount.rx++;111		flg = TTY_NORMAL;112 113		if (unlikely(status)) {114			if (status & UARTDR_PARERR)115				port->icount.parity++;116			else if (status & UARTDR_FRMERR)117				port->icount.frame++;118			else if (status & UARTDR_OVERR)119				port->icount.overrun++;120 121			status &= port->read_status_mask;122 123			if (status & UARTDR_PARERR)124				flg = TTY_PARITY;125			else if (status & UARTDR_FRMERR)126				flg = TTY_FRAME;127			else if (status & UARTDR_OVERR)128				flg = TTY_OVERRUN;129		}130 131		if (uart_handle_sysrq_char(port, ch))132			continue;133 134		if (status & port->ignore_status_mask)135			continue;136 137		uart_insert_char(port, status, UARTDR_OVERR, ch, flg);138	}139 140	tty_flip_buffer_push(&port->state->port);141 142	return IRQ_HANDLED;143}144 145static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)146{147	struct uart_port *port = dev_id;148	struct clps711x_port *s = dev_get_drvdata(port->dev);149	struct tty_port *tport = &port->state->port;150	unsigned char c;151 152	if (port->x_char) {153		writew(port->x_char, port->membase + UARTDR_OFFSET);154		port->icount.tx++;155		port->x_char = 0;156		return IRQ_HANDLED;157	}158 159	if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {160		if (s->tx_enabled) {161			disable_irq_nosync(port->irq);162			s->tx_enabled = 0;163		}164		return IRQ_HANDLED;165	}166 167	while (uart_fifo_get(port, &c)) {168		u32 sysflg = 0;169 170		writew(c, port->membase + UARTDR_OFFSET);171 172		regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);173		if (sysflg & SYSFLG_UTXFF)174			break;175	}176 177	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)178		uart_write_wakeup(port);179 180	return IRQ_HANDLED;181}182 183static unsigned int uart_clps711x_tx_empty(struct uart_port *port)184{185	struct clps711x_port *s = dev_get_drvdata(port->dev);186	u32 sysflg = 0;187 188	regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);189 190	return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT;191}192 193static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)194{195	unsigned int result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;196	struct clps711x_port *s = dev_get_drvdata(port->dev);197 198	return mctrl_gpio_get(s->gpios, &result);199}200 201static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)202{203	struct clps711x_port *s = dev_get_drvdata(port->dev);204 205	mctrl_gpio_set(s->gpios, mctrl);206}207 208static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)209{210	unsigned int ubrlcr;211 212	ubrlcr = readl(port->membase + UBRLCR_OFFSET);213	if (break_state)214		ubrlcr |= UBRLCR_BREAK;215	else216		ubrlcr &= ~UBRLCR_BREAK;217	writel(ubrlcr, port->membase + UBRLCR_OFFSET);218}219 220static void uart_clps711x_set_ldisc(struct uart_port *port,221				    struct ktermios *termios)222{223	if (!port->line) {224		struct clps711x_port *s = dev_get_drvdata(port->dev);225 226		regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN,227				   (termios->c_line == N_IRDA) ? SYSCON1_SIREN : 0);228	}229}230 231static int uart_clps711x_startup(struct uart_port *port)232{233	struct clps711x_port *s = dev_get_drvdata(port->dev);234 235	/* Disable break */236	writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK,237	       port->membase + UBRLCR_OFFSET);238 239	/* Enable the port */240	return regmap_update_bits(s->syscon, SYSCON_OFFSET,241				  SYSCON_UARTEN, SYSCON_UARTEN);242}243 244static void uart_clps711x_shutdown(struct uart_port *port)245{246	struct clps711x_port *s = dev_get_drvdata(port->dev);247 248	/* Disable the port */249	regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);250}251 252static void uart_clps711x_set_termios(struct uart_port *port,253				      struct ktermios *termios,254				      const struct ktermios *old)255{256	u32 ubrlcr;257	unsigned int baud, quot;258 259	/* Mask termios capabilities we don't support */260	termios->c_cflag &= ~CMSPAR;261	termios->c_iflag &= ~(BRKINT | IGNBRK);262 263	/* Ask the core to calculate the divisor for us */264	baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,265						      port->uartclk / 16);266	quot = uart_get_divisor(port, baud);267 268	switch (termios->c_cflag & CSIZE) {269	case CS5:270		ubrlcr = UBRLCR_WRDLEN5;271		break;272	case CS6:273		ubrlcr = UBRLCR_WRDLEN6;274		break;275	case CS7:276		ubrlcr = UBRLCR_WRDLEN7;277		break;278	case CS8:279	default:280		ubrlcr = UBRLCR_WRDLEN8;281		break;282	}283 284	if (termios->c_cflag & CSTOPB)285		ubrlcr |= UBRLCR_XSTOP;286 287	if (termios->c_cflag & PARENB) {288		ubrlcr |= UBRLCR_PRTEN;289		if (!(termios->c_cflag & PARODD))290			ubrlcr |= UBRLCR_EVENPRT;291	}292 293	/* Enable FIFO */294	ubrlcr |= UBRLCR_FIFOEN;295 296	/* Set read status mask */297	port->read_status_mask = UARTDR_OVERR;298	if (termios->c_iflag & INPCK)299		port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;300 301	/* Set status ignore mask */302	port->ignore_status_mask = 0;303	if (!(termios->c_cflag & CREAD))304		port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |305					    UARTDR_FRMERR;306 307	uart_update_timeout(port, termios->c_cflag, baud);308 309	writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET);310}311 312static const char *uart_clps711x_type(struct uart_port *port)313{314	return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;315}316 317static void uart_clps711x_config_port(struct uart_port *port, int flags)318{319	if (flags & UART_CONFIG_TYPE)320		port->type = PORT_CLPS711X;321}322 323static void uart_clps711x_nop_void(struct uart_port *port)324{325}326 327static int uart_clps711x_nop_int(struct uart_port *port)328{329	return 0;330}331 332static const struct uart_ops uart_clps711x_ops = {333	.tx_empty	= uart_clps711x_tx_empty,334	.set_mctrl	= uart_clps711x_set_mctrl,335	.get_mctrl	= uart_clps711x_get_mctrl,336	.stop_tx	= uart_clps711x_stop_tx,337	.start_tx	= uart_clps711x_start_tx,338	.stop_rx	= uart_clps711x_nop_void,339	.break_ctl	= uart_clps711x_break_ctl,340	.set_ldisc	= uart_clps711x_set_ldisc,341	.startup	= uart_clps711x_startup,342	.shutdown	= uart_clps711x_shutdown,343	.set_termios	= uart_clps711x_set_termios,344	.type		= uart_clps711x_type,345	.config_port	= uart_clps711x_config_port,346	.release_port	= uart_clps711x_nop_void,347	.request_port	= uart_clps711x_nop_int,348};349 350#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE351static void uart_clps711x_console_putchar(struct uart_port *port, unsigned char ch)352{353	struct clps711x_port *s = dev_get_drvdata(port->dev);354	u32 sysflg = 0;355 356	/* Wait for FIFO is not full */357	do {358		regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);359	} while (sysflg & SYSFLG_UTXFF);360 361	writew(ch, port->membase + UARTDR_OFFSET);362}363 364static void uart_clps711x_console_write(struct console *co, const char *c,365					unsigned n)366{367	struct uart_port *port = clps711x_uart.state[co->index].uart_port;368	struct clps711x_port *s = dev_get_drvdata(port->dev);369	u32 sysflg = 0;370 371	uart_console_write(port, c, n, uart_clps711x_console_putchar);372 373	/* Wait for transmitter to become empty */374	do {375		regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);376	} while (sysflg & SYSFLG_UBUSY);377}378 379static int uart_clps711x_console_setup(struct console *co, char *options)380{381	int baud = 38400, bits = 8, parity = 'n', flow = 'n';382	int ret, index = co->index;383	struct clps711x_port *s;384	struct uart_port *port;385	unsigned int quot;386	u32 ubrlcr;387 388	if (index < 0 || index >= UART_CLPS711X_NR)389		return -EINVAL;390 391	port = clps711x_uart.state[index].uart_port;392	if (!port)393		return -ENODEV;394 395	s = dev_get_drvdata(port->dev);396 397	if (!options) {398		u32 syscon = 0;399 400		regmap_read(s->syscon, SYSCON_OFFSET, &syscon);401		if (syscon & SYSCON_UARTEN) {402			ubrlcr = readl(port->membase + UBRLCR_OFFSET);403 404			if (ubrlcr & UBRLCR_PRTEN) {405				if (ubrlcr & UBRLCR_EVENPRT)406					parity = 'e';407				else408					parity = 'o';409			}410 411			if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)412				bits = 7;413 414			quot = ubrlcr & UBRLCR_BAUD_MASK;415			baud = port->uartclk / (16 * (quot + 1));416		}417	} else418		uart_parse_options(options, &baud, &parity, &bits, &flow);419 420	ret = uart_set_options(port, co, baud, parity, bits, flow);421	if (ret)422		return ret;423 424	return regmap_update_bits(s->syscon, SYSCON_OFFSET,425				  SYSCON_UARTEN, SYSCON_UARTEN);426}427 428static struct console clps711x_console = {429	.name	= UART_CLPS711X_DEVNAME,430	.device	= uart_console_device,431	.write	= uart_clps711x_console_write,432	.setup	= uart_clps711x_console_setup,433	.flags	= CON_PRINTBUFFER,434	.index	= -1,435};436#endif437 438static int uart_clps711x_probe(struct platform_device *pdev)439{440	struct device_node *np = pdev->dev.of_node;441	struct clps711x_port *s;442	struct resource *res;443	struct clk *uart_clk;444	int irq, ret;445 446	s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);447	if (!s)448		return -ENOMEM;449 450	uart_clk = devm_clk_get(&pdev->dev, NULL);451	if (IS_ERR(uart_clk))452		return PTR_ERR(uart_clk);453 454	s->port.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);455	if (IS_ERR(s->port.membase))456		return PTR_ERR(s->port.membase);457 458	irq = platform_get_irq(pdev, 0);459	if (irq < 0)460		return irq;461	s->port.irq = irq;462 463	s->rx_irq = platform_get_irq(pdev, 1);464	if (s->rx_irq < 0)465		return s->rx_irq;466 467	s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");468	if (IS_ERR(s->syscon))469		return PTR_ERR(s->syscon);470 471	s->port.line		= of_alias_get_id(np, "serial");472	s->port.dev		= &pdev->dev;473	s->port.iotype		= UPIO_MEM32;474	s->port.mapbase		= res->start;475	s->port.type		= PORT_CLPS711X;476	s->port.fifosize	= 16;477	s->port.has_sysrq	= IS_ENABLED(CONFIG_SERIAL_CLPS711X_CONSOLE);478	s->port.flags		= UPF_SKIP_TEST | UPF_FIXED_TYPE;479	s->port.uartclk		= clk_get_rate(uart_clk);480	s->port.ops		= &uart_clps711x_ops;481 482	platform_set_drvdata(pdev, s);483 484	s->gpios = mctrl_gpio_init_noauto(&pdev->dev, 0);485	if (IS_ERR(s->gpios))486	    return PTR_ERR(s->gpios);487 488	ret = uart_add_one_port(&clps711x_uart, &s->port);489	if (ret)490		return ret;491 492	/* Disable port */493	if (!uart_console(&s->port))494		regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);495 496	s->tx_enabled = 1;497 498	ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0,499			       dev_name(&pdev->dev), &s->port);500	if (ret) {501		uart_remove_one_port(&clps711x_uart, &s->port);502		return ret;503	}504 505	ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0,506			       dev_name(&pdev->dev), &s->port);507	if (ret)508		uart_remove_one_port(&clps711x_uart, &s->port);509 510	return ret;511}512 513static void uart_clps711x_remove(struct platform_device *pdev)514{515	struct clps711x_port *s = platform_get_drvdata(pdev);516 517	uart_remove_one_port(&clps711x_uart, &s->port);518}519 520static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {521	{ .compatible = "cirrus,ep7209-uart", },522	{ }523};524MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);525 526static struct platform_driver clps711x_uart_platform = {527	.driver = {528		.name		= "clps711x-uart",529		.of_match_table	= of_match_ptr(clps711x_uart_dt_ids),530	},531	.probe	= uart_clps711x_probe,532	.remove_new = uart_clps711x_remove,533};534 535static int __init uart_clps711x_init(void)536{537	int ret;538 539#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE540	clps711x_uart.cons = &clps711x_console;541	clps711x_console.data = &clps711x_uart;542#endif543 544	ret = uart_register_driver(&clps711x_uart);545	if (ret)546		return ret;547 548	return platform_driver_register(&clps711x_uart_platform);549}550module_init(uart_clps711x_init);551 552static void __exit uart_clps711x_exit(void)553{554	platform_driver_unregister(&clps711x_uart_platform);555	uart_unregister_driver(&clps711x_uart);556}557module_exit(uart_clps711x_exit);558 559MODULE_AUTHOR("Deep Blue Solutions Ltd");560MODULE_DESCRIPTION("CLPS711X serial driver");561MODULE_LICENSE("GPL");562