brintos

brintos / linux-shallow public Read only

0
0
Text · 18.2 KiB · eabbf8a Raw
805 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  Driver for AMBA 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 * This is a generic driver for ARM AMBA-type serial ports.  They11 * have a lot of 16550-like features, but are not register compatible.12 * Note that although they do have CTS, DCD and DSR inputs, they do13 * not have an RI input, nor do they have DTR or RTS outputs.  If14 * required, these have to be supplied via some other means (eg, GPIO)15 * and hooked into this driver.16 */17 18#include <linux/module.h>19#include <linux/ioport.h>20#include <linux/init.h>21#include <linux/console.h>22#include <linux/sysrq.h>23#include <linux/device.h>24#include <linux/tty.h>25#include <linux/tty_flip.h>26#include <linux/serial_core.h>27#include <linux/serial.h>28#include <linux/amba/bus.h>29#include <linux/amba/serial.h>30#include <linux/clk.h>31#include <linux/slab.h>32#include <linux/io.h>33 34#define UART_NR		835 36#define SERIAL_AMBA_MAJOR	20437#define SERIAL_AMBA_MINOR	1638#define SERIAL_AMBA_NR		UART_NR39 40#define AMBA_ISR_PASS_LIMIT	25641 42#define UART_RX_DATA(s)		(((s) & UART01x_FR_RXFE) == 0)43#define UART_TX_READY(s)	(((s) & UART01x_FR_TXFF) == 0)44 45#define UART_DUMMY_RSR_RX	25646#define UART_PORT_SIZE		6447 48/*49 * We wrap our port structure around the generic uart_port.50 */51struct uart_amba_port {52	struct uart_port	port;53	struct clk		*clk;54	struct amba_device	*dev;55	struct amba_pl010_data	*data;56	unsigned int		old_status;57};58 59static void pl010_stop_tx(struct uart_port *port)60{61	struct uart_amba_port *uap =62		container_of(port, struct uart_amba_port, port);63	unsigned int cr;64 65	cr = readb(uap->port.membase + UART010_CR);66	cr &= ~UART010_CR_TIE;67	writel(cr, uap->port.membase + UART010_CR);68}69 70static void pl010_start_tx(struct uart_port *port)71{72	struct uart_amba_port *uap =73		container_of(port, struct uart_amba_port, port);74	unsigned int cr;75 76	cr = readb(uap->port.membase + UART010_CR);77	cr |= UART010_CR_TIE;78	writel(cr, uap->port.membase + UART010_CR);79}80 81static void pl010_stop_rx(struct uart_port *port)82{83	struct uart_amba_port *uap =84		container_of(port, struct uart_amba_port, port);85	unsigned int cr;86 87	cr = readb(uap->port.membase + UART010_CR);88	cr &= ~(UART010_CR_RIE | UART010_CR_RTIE);89	writel(cr, uap->port.membase + UART010_CR);90}91 92static void pl010_disable_ms(struct uart_port *port)93{94	struct uart_amba_port *uap = (struct uart_amba_port *)port;95	unsigned int cr;96 97	cr = readb(uap->port.membase + UART010_CR);98	cr &= ~UART010_CR_MSIE;99	writel(cr, uap->port.membase + UART010_CR);100}101 102static void pl010_enable_ms(struct uart_port *port)103{104	struct uart_amba_port *uap =105		container_of(port, struct uart_amba_port, port);106	unsigned int cr;107 108	cr = readb(uap->port.membase + UART010_CR);109	cr |= UART010_CR_MSIE;110	writel(cr, uap->port.membase + UART010_CR);111}112 113static void pl010_rx_chars(struct uart_port *port)114{115	unsigned int status, rsr, max_count = 256;116	u8 ch, flag;117 118	status = readb(port->membase + UART01x_FR);119	while (UART_RX_DATA(status) && max_count--) {120		ch = readb(port->membase + UART01x_DR);121		flag = TTY_NORMAL;122 123		port->icount.rx++;124 125		/*126		 * Note that the error handling code is127		 * out of the main execution path128		 */129		rsr = readb(port->membase + UART01x_RSR) | UART_DUMMY_RSR_RX;130		if (unlikely(rsr & UART01x_RSR_ANY)) {131			writel(0, port->membase + UART01x_ECR);132 133			if (rsr & UART01x_RSR_BE) {134				rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);135				port->icount.brk++;136				if (uart_handle_break(port))137					goto ignore_char;138			} else if (rsr & UART01x_RSR_PE)139				port->icount.parity++;140			else if (rsr & UART01x_RSR_FE)141				port->icount.frame++;142			if (rsr & UART01x_RSR_OE)143				port->icount.overrun++;144 145			rsr &= port->read_status_mask;146 147			if (rsr & UART01x_RSR_BE)148				flag = TTY_BREAK;149			else if (rsr & UART01x_RSR_PE)150				flag = TTY_PARITY;151			else if (rsr & UART01x_RSR_FE)152				flag = TTY_FRAME;153		}154 155		if (uart_handle_sysrq_char(port, ch))156			goto ignore_char;157 158		uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag);159 160	ignore_char:161		status = readb(port->membase + UART01x_FR);162	}163	tty_flip_buffer_push(&port->state->port);164}165 166static void pl010_tx_chars(struct uart_port *port)167{168	u8 ch;169 170	uart_port_tx_limited(port, ch, port->fifosize >> 1,171		true,172		writel(ch, port->membase + UART01x_DR),173		({}));174}175 176static void pl010_modem_status(struct uart_amba_port *uap)177{178	struct uart_port *port = &uap->port;179	unsigned int status, delta;180 181	writel(0, port->membase + UART010_ICR);182 183	status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;184 185	delta = status ^ uap->old_status;186	uap->old_status = status;187 188	if (!delta)189		return;190 191	if (delta & UART01x_FR_DCD)192		uart_handle_dcd_change(port, status & UART01x_FR_DCD);193 194	if (delta & UART01x_FR_DSR)195		port->icount.dsr++;196 197	if (delta & UART01x_FR_CTS)198		uart_handle_cts_change(port, status & UART01x_FR_CTS);199 200	wake_up_interruptible(&port->state->port.delta_msr_wait);201}202 203static irqreturn_t pl010_int(int irq, void *dev_id)204{205	struct uart_amba_port *uap = dev_id;206	struct uart_port *port = &uap->port;207	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;208	int handled = 0;209 210	uart_port_lock(port);211 212	status = readb(port->membase + UART010_IIR);213	if (status) {214		do {215			if (status & (UART010_IIR_RTIS | UART010_IIR_RIS))216				pl010_rx_chars(port);217			if (status & UART010_IIR_MIS)218				pl010_modem_status(uap);219			if (status & UART010_IIR_TIS)220				pl010_tx_chars(port);221 222			if (pass_counter-- == 0)223				break;224 225			status = readb(port->membase + UART010_IIR);226		} while (status & (UART010_IIR_RTIS | UART010_IIR_RIS |227				   UART010_IIR_TIS));228		handled = 1;229	}230 231	uart_port_unlock(port);232 233	return IRQ_RETVAL(handled);234}235 236static unsigned int pl010_tx_empty(struct uart_port *port)237{238	unsigned int status = readb(port->membase + UART01x_FR);239 240	return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT;241}242 243static unsigned int pl010_get_mctrl(struct uart_port *port)244{245	unsigned int result = 0;246	unsigned int status;247 248	status = readb(port->membase + UART01x_FR);249	if (status & UART01x_FR_DCD)250		result |= TIOCM_CAR;251	if (status & UART01x_FR_DSR)252		result |= TIOCM_DSR;253	if (status & UART01x_FR_CTS)254		result |= TIOCM_CTS;255 256	return result;257}258 259static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl)260{261	struct uart_amba_port *uap =262		container_of(port, struct uart_amba_port, port);263 264	if (uap->data)265		uap->data->set_mctrl(uap->dev, port->membase, mctrl);266}267 268static void pl010_break_ctl(struct uart_port *port, int break_state)269{270	unsigned long flags;271	unsigned int lcr_h;272 273	uart_port_lock_irqsave(port, &flags);274	lcr_h = readb(port->membase + UART010_LCRH);275	if (break_state == -1)276		lcr_h |= UART01x_LCRH_BRK;277	else278		lcr_h &= ~UART01x_LCRH_BRK;279	writel(lcr_h, port->membase + UART010_LCRH);280	uart_port_unlock_irqrestore(port, flags);281}282 283static int pl010_startup(struct uart_port *port)284{285	struct uart_amba_port *uap =286		container_of(port, struct uart_amba_port, port);287	int retval;288 289	/*290	 * Try to enable the clock producer.291	 */292	retval = clk_prepare_enable(uap->clk);293	if (retval)294		goto out;295 296	port->uartclk = clk_get_rate(uap->clk);297 298	/*299	 * Allocate the IRQ300	 */301	retval = request_irq(port->irq, pl010_int, 0, "uart-pl010", uap);302	if (retval)303		goto clk_dis;304 305	/*306	 * initialise the old status of the modem signals307	 */308	uap->old_status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;309 310	/*311	 * Finally, enable interrupts312	 */313	writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE,314	       port->membase + UART010_CR);315 316	return 0;317 318 clk_dis:319	clk_disable_unprepare(uap->clk);320 out:321	return retval;322}323 324static void pl010_shutdown(struct uart_port *port)325{326	struct uart_amba_port *uap =327		container_of(port, struct uart_amba_port, port);328 329	/*330	 * Free the interrupt331	 */332	free_irq(port->irq, uap);333 334	/*335	 * disable all interrupts, disable the port336	 */337	writel(0, port->membase + UART010_CR);338 339	/* disable break condition and fifos */340	writel(readb(port->membase + UART010_LCRH) &341		~(UART01x_LCRH_BRK | UART01x_LCRH_FEN),342	       port->membase + UART010_LCRH);343 344	/*345	 * Shut down the clock producer346	 */347	clk_disable_unprepare(uap->clk);348}349 350static void351pl010_set_termios(struct uart_port *port, struct ktermios *termios,352		  const struct ktermios *old)353{354	unsigned int lcr_h, old_cr;355	unsigned long flags;356	unsigned int baud, quot;357 358	/*359	 * Ask the core to calculate the divisor for us.360	 */361	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);362	quot = uart_get_divisor(port, baud);363 364	switch (termios->c_cflag & CSIZE) {365	case CS5:366		lcr_h = UART01x_LCRH_WLEN_5;367		break;368	case CS6:369		lcr_h = UART01x_LCRH_WLEN_6;370		break;371	case CS7:372		lcr_h = UART01x_LCRH_WLEN_7;373		break;374	default: // CS8375		lcr_h = UART01x_LCRH_WLEN_8;376		break;377	}378	if (termios->c_cflag & CSTOPB)379		lcr_h |= UART01x_LCRH_STP2;380	if (termios->c_cflag & PARENB) {381		lcr_h |= UART01x_LCRH_PEN;382		if (!(termios->c_cflag & PARODD))383			lcr_h |= UART01x_LCRH_EPS;384	}385	if (port->fifosize > 1)386		lcr_h |= UART01x_LCRH_FEN;387 388	uart_port_lock_irqsave(port, &flags);389 390	/*391	 * Update the per-port timeout.392	 */393	uart_update_timeout(port, termios->c_cflag, baud);394 395	port->read_status_mask = UART01x_RSR_OE;396	if (termios->c_iflag & INPCK)397		port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;398	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))399		port->read_status_mask |= UART01x_RSR_BE;400 401	/*402	 * Characters to ignore403	 */404	port->ignore_status_mask = 0;405	if (termios->c_iflag & IGNPAR)406		port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;407	if (termios->c_iflag & IGNBRK) {408		port->ignore_status_mask |= UART01x_RSR_BE;409		/*410		 * If we're ignoring parity and break indicators,411		 * ignore overruns too (for real raw support).412		 */413		if (termios->c_iflag & IGNPAR)414			port->ignore_status_mask |= UART01x_RSR_OE;415	}416 417	/*418	 * Ignore all characters if CREAD is not set.419	 */420	if ((termios->c_cflag & CREAD) == 0)421		port->ignore_status_mask |= UART_DUMMY_RSR_RX;422 423	old_cr = readb(port->membase + UART010_CR) & ~UART010_CR_MSIE;424 425	if (UART_ENABLE_MS(port, termios->c_cflag))426		old_cr |= UART010_CR_MSIE;427 428	/* Set baud rate */429	quot -= 1;430	writel((quot & 0xf00) >> 8, port->membase + UART010_LCRM);431	writel(quot & 0xff, port->membase + UART010_LCRL);432 433	/*434	 * ----------v----------v----------v----------v-----435	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L436	 * ----------^----------^----------^----------^-----437	 */438	writel(lcr_h, port->membase + UART010_LCRH);439	writel(old_cr, port->membase + UART010_CR);440 441	uart_port_unlock_irqrestore(port, flags);442}443 444static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios)445{446	if (termios->c_line == N_PPS) {447		port->flags |= UPF_HARDPPS_CD;448		uart_port_lock_irq(port);449		pl010_enable_ms(port);450		uart_port_unlock_irq(port);451	} else {452		port->flags &= ~UPF_HARDPPS_CD;453		if (!UART_ENABLE_MS(port, termios->c_cflag)) {454			uart_port_lock_irq(port);455			pl010_disable_ms(port);456			uart_port_unlock_irq(port);457		}458	}459}460 461static const char *pl010_type(struct uart_port *port)462{463	return port->type == PORT_AMBA ? "AMBA" : NULL;464}465 466/*467 * Release the memory region(s) being used by 'port'468 */469static void pl010_release_port(struct uart_port *port)470{471	release_mem_region(port->mapbase, UART_PORT_SIZE);472}473 474/*475 * Request the memory region(s) being used by 'port'476 */477static int pl010_request_port(struct uart_port *port)478{479	return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010")480			!= NULL ? 0 : -EBUSY;481}482 483/*484 * Configure/autoconfigure the port.485 */486static void pl010_config_port(struct uart_port *port, int flags)487{488	if (flags & UART_CONFIG_TYPE) {489		port->type = PORT_AMBA;490		pl010_request_port(port);491	}492}493 494/*495 * verify the new serial_struct (for TIOCSSERIAL).496 */497static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)498{499	int ret = 0;500	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)501		ret = -EINVAL;502	if (ser->irq < 0 || ser->irq >= nr_irqs)503		ret = -EINVAL;504	if (ser->baud_base < 9600)505		ret = -EINVAL;506	return ret;507}508 509static const struct uart_ops amba_pl010_pops = {510	.tx_empty	= pl010_tx_empty,511	.set_mctrl	= pl010_set_mctrl,512	.get_mctrl	= pl010_get_mctrl,513	.stop_tx	= pl010_stop_tx,514	.start_tx	= pl010_start_tx,515	.stop_rx	= pl010_stop_rx,516	.enable_ms	= pl010_enable_ms,517	.break_ctl	= pl010_break_ctl,518	.startup	= pl010_startup,519	.shutdown	= pl010_shutdown,520	.set_termios	= pl010_set_termios,521	.set_ldisc	= pl010_set_ldisc,522	.type		= pl010_type,523	.release_port	= pl010_release_port,524	.request_port	= pl010_request_port,525	.config_port	= pl010_config_port,526	.verify_port	= pl010_verify_port,527};528 529static struct uart_amba_port *amba_ports[UART_NR];530 531#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE532 533static void pl010_console_putchar(struct uart_port *port, unsigned char ch)534{535	unsigned int status;536 537	do {538		status = readb(port->membase + UART01x_FR);539		barrier();540	} while (!UART_TX_READY(status));541	writel(ch, port->membase + UART01x_DR);542}543 544static void545pl010_console_write(struct console *co, const char *s, unsigned int count)546{547	struct uart_amba_port *uap = amba_ports[co->index];548	struct uart_port *port = &uap->port;549	unsigned int status, old_cr;550 551	clk_enable(uap->clk);552 553	/*554	 *	First save the CR then disable the interrupts555	 */556	old_cr = readb(port->membase + UART010_CR);557	writel(UART01x_CR_UARTEN, port->membase + UART010_CR);558 559	uart_console_write(port, s, count, pl010_console_putchar);560 561	/*562	 *	Finally, wait for transmitter to become empty563	 *	and restore the TCR564	 */565	do {566		status = readb(port->membase + UART01x_FR);567		barrier();568	} while (status & UART01x_FR_BUSY);569	writel(old_cr, port->membase + UART010_CR);570 571	clk_disable(uap->clk);572}573 574static void __init575pl010_console_get_options(struct uart_amba_port *uap, int *baud,576			     int *parity, int *bits)577{578	if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) {579		unsigned int lcr_h, quot;580		lcr_h = readb(uap->port.membase + UART010_LCRH);581 582		*parity = 'n';583		if (lcr_h & UART01x_LCRH_PEN) {584			if (lcr_h & UART01x_LCRH_EPS)585				*parity = 'e';586			else587				*parity = 'o';588		}589 590		if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)591			*bits = 7;592		else593			*bits = 8;594 595		quot = readb(uap->port.membase + UART010_LCRL) |596		       readb(uap->port.membase + UART010_LCRM) << 8;597		*baud = uap->port.uartclk / (16 * (quot + 1));598	}599}600 601static int __init pl010_console_setup(struct console *co, char *options)602{603	struct uart_amba_port *uap;604	int baud = 38400;605	int bits = 8;606	int parity = 'n';607	int flow = 'n';608	int ret;609 610	/*611	 * Check whether an invalid uart number has been specified, and612	 * if so, search for the first available port that does have613	 * console support.614	 */615	if (co->index >= UART_NR)616		co->index = 0;617	uap = amba_ports[co->index];618	if (!uap)619		return -ENODEV;620 621	ret = clk_prepare(uap->clk);622	if (ret)623		return ret;624 625	uap->port.uartclk = clk_get_rate(uap->clk);626 627	if (options)628		uart_parse_options(options, &baud, &parity, &bits, &flow);629	else630		pl010_console_get_options(uap, &baud, &parity, &bits);631 632	return uart_set_options(&uap->port, co, baud, parity, bits, flow);633}634 635static struct uart_driver amba_reg;636static struct console amba_console = {637	.name		= "ttyAM",638	.write		= pl010_console_write,639	.device		= uart_console_device,640	.setup		= pl010_console_setup,641	.flags		= CON_PRINTBUFFER,642	.index		= -1,643	.data		= &amba_reg,644};645 646#define AMBA_CONSOLE	&amba_console647#else648#define AMBA_CONSOLE	NULL649#endif650 651static DEFINE_MUTEX(amba_reg_lock);652static struct uart_driver amba_reg = {653	.owner			= THIS_MODULE,654	.driver_name		= "ttyAM",655	.dev_name		= "ttyAM",656	.major			= SERIAL_AMBA_MAJOR,657	.minor			= SERIAL_AMBA_MINOR,658	.nr			= UART_NR,659	.cons			= AMBA_CONSOLE,660};661 662static int pl010_probe(struct amba_device *dev, const struct amba_id *id)663{664	struct uart_amba_port *uap;665	void __iomem *base;666	int i, ret;667 668	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)669		if (amba_ports[i] == NULL)670			break;671 672	if (i == ARRAY_SIZE(amba_ports))673		return -EBUSY;674 675	uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port),676			   GFP_KERNEL);677	if (!uap)678		return -ENOMEM;679 680	base = devm_ioremap(&dev->dev, dev->res.start,681			    resource_size(&dev->res));682	if (!base)683		return -ENOMEM;684 685	uap->clk = devm_clk_get(&dev->dev, NULL);686	if (IS_ERR(uap->clk))687		return PTR_ERR(uap->clk);688 689	uap->port.dev = &dev->dev;690	uap->port.mapbase = dev->res.start;691	uap->port.membase = base;692	uap->port.iotype = UPIO_MEM;693	uap->port.irq = dev->irq[0];694	uap->port.fifosize = 16;695	uap->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_AMBA_PL010_CONSOLE);696	uap->port.ops = &amba_pl010_pops;697	uap->port.flags = UPF_BOOT_AUTOCONF;698	uap->port.line = i;699	uap->dev = dev;700	uap->data = dev_get_platdata(&dev->dev);701 702	amba_ports[i] = uap;703 704	amba_set_drvdata(dev, uap);705 706	mutex_lock(&amba_reg_lock);707	if (!amba_reg.state) {708		ret = uart_register_driver(&amba_reg);709		if (ret < 0) {710			mutex_unlock(&amba_reg_lock);711			dev_err(uap->port.dev,712				"Failed to register AMBA-PL010 driver\n");713			return ret;714		}715	}716	mutex_unlock(&amba_reg_lock);717 718	ret = uart_add_one_port(&amba_reg, &uap->port);719	if (ret)720		amba_ports[i] = NULL;721 722	return ret;723}724 725static void pl010_remove(struct amba_device *dev)726{727	struct uart_amba_port *uap = amba_get_drvdata(dev);728	int i;729	bool busy = false;730 731	uart_remove_one_port(&amba_reg, &uap->port);732 733	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)734		if (amba_ports[i] == uap)735			amba_ports[i] = NULL;736		else if (amba_ports[i])737			busy = true;738 739	if (!busy)740		uart_unregister_driver(&amba_reg);741}742 743#ifdef CONFIG_PM_SLEEP744static int pl010_suspend(struct device *dev)745{746	struct uart_amba_port *uap = dev_get_drvdata(dev);747 748	if (uap)749		uart_suspend_port(&amba_reg, &uap->port);750 751	return 0;752}753 754static int pl010_resume(struct device *dev)755{756	struct uart_amba_port *uap = dev_get_drvdata(dev);757 758	if (uap)759		uart_resume_port(&amba_reg, &uap->port);760 761	return 0;762}763#endif764 765static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume);766 767static const struct amba_id pl010_ids[] = {768	{769		.id	= 0x00041010,770		.mask	= 0x000fffff,771	},772	{ 0, 0 },773};774 775MODULE_DEVICE_TABLE(amba, pl010_ids);776 777static struct amba_driver pl010_driver = {778	.drv = {779		.name	= "uart-pl010",780		.pm	= &pl010_dev_pm_ops,781	},782	.id_table	= pl010_ids,783	.probe		= pl010_probe,784	.remove		= pl010_remove,785};786 787static int __init pl010_init(void)788{789	printk(KERN_INFO "Serial: AMBA driver\n");790 791	return  amba_driver_register(&pl010_driver);792}793 794static void __exit pl010_exit(void)795{796	amba_driver_unregister(&pl010_driver);797}798 799module_init(pl010_init);800module_exit(pl010_exit);801 802MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");803MODULE_DESCRIPTION("ARM AMBA serial port driver");804MODULE_LICENSE("GPL");805