brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 4de0c97 Raw
519 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the serial port on the 21285 StrongArm-110 core logic chip.4 *5 * Based on drivers/char/serial.c6 */7#include <linux/module.h>8#include <linux/tty.h>9#include <linux/ioport.h>10#include <linux/init.h>11#include <linux/console.h>12#include <linux/device.h>13#include <linux/tty_flip.h>14#include <linux/serial_core.h>15#include <linux/serial.h>16#include <linux/io.h>17 18#include <asm/irq.h>19#include <asm/mach-types.h>20#include <asm/system_info.h>21#include <asm/hardware/dec21285.h>22#include <mach/hardware.h>23 24#define BAUD_BASE		(mem_fclk_21285/64)25 26#define SERIAL_21285_NAME	"ttyFB"27#define SERIAL_21285_MAJOR	20428#define SERIAL_21285_MINOR	429 30#define RXSTAT_DUMMY_READ	0x8000000031#define RXSTAT_FRAME		(1 << 0)32#define RXSTAT_PARITY		(1 << 1)33#define RXSTAT_OVERRUN		(1 << 2)34#define RXSTAT_ANYERR		(RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)35 36#define H_UBRLCR_BREAK		(1 << 0)37#define H_UBRLCR_PARENB		(1 << 1)38#define H_UBRLCR_PAREVN		(1 << 2)39#define H_UBRLCR_STOPB		(1 << 3)40#define H_UBRLCR_FIFO		(1 << 4)41 42static const char serial21285_name[] = "Footbridge UART";43 44/*45 * We only need 2 bits of data, so instead of creating a whole structure for46 * this, use bits of the private_data pointer of the uart port structure.47 */48#define tx_enabled_bit	049#define rx_enabled_bit	150 51static bool is_enabled(struct uart_port *port, int bit)52{53	unsigned long *private_data = (unsigned long *)&port->private_data;54 55	if (test_bit(bit, private_data))56		return true;57	return false;58}59 60static void enable(struct uart_port *port, int bit)61{62	unsigned long *private_data = (unsigned long *)&port->private_data;63 64	set_bit(bit, private_data);65}66 67static void disable(struct uart_port *port, int bit)68{69	unsigned long *private_data = (unsigned long *)&port->private_data;70 71	clear_bit(bit, private_data);72}73 74#define is_tx_enabled(port)	is_enabled(port, tx_enabled_bit)75#define tx_enable(port)		enable(port, tx_enabled_bit)76#define tx_disable(port)	disable(port, tx_enabled_bit)77 78#define is_rx_enabled(port)	is_enabled(port, rx_enabled_bit)79#define rx_enable(port)		enable(port, rx_enabled_bit)80#define rx_disable(port)	disable(port, rx_enabled_bit)81 82/*83 * The documented expression for selecting the divisor is:84 *  BAUD_BASE / baud - 185 * However, typically BAUD_BASE is not divisible by baud, so86 * we want to select the divisor that gives us the minimum87 * error.  Therefore, we want:88 *  int(BAUD_BASE / baud - 0.5) ->89 *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->90 *  int((BAUD_BASE - (baud >> 1)) / baud)91 */92 93static void serial21285_stop_tx(struct uart_port *port)94{95	if (is_tx_enabled(port)) {96		disable_irq_nosync(IRQ_CONTX);97		tx_disable(port);98	}99}100 101static void serial21285_start_tx(struct uart_port *port)102{103	if (!is_tx_enabled(port)) {104		enable_irq(IRQ_CONTX);105		tx_enable(port);106	}107}108 109static void serial21285_stop_rx(struct uart_port *port)110{111	if (is_rx_enabled(port)) {112		disable_irq_nosync(IRQ_CONRX);113		rx_disable(port);114	}115}116 117static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)118{119	struct uart_port *port = dev_id;120	unsigned int status, rxs, max_count = 256;121	u8 ch, flag;122 123	status = *CSR_UARTFLG;124	while (!(status & 0x10) && max_count--) {125		ch = *CSR_UARTDR;126		flag = TTY_NORMAL;127		port->icount.rx++;128 129		rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;130		if (unlikely(rxs & RXSTAT_ANYERR)) {131			if (rxs & RXSTAT_PARITY)132				port->icount.parity++;133			else if (rxs & RXSTAT_FRAME)134				port->icount.frame++;135			if (rxs & RXSTAT_OVERRUN)136				port->icount.overrun++;137 138			rxs &= port->read_status_mask;139 140			if (rxs & RXSTAT_PARITY)141				flag = TTY_PARITY;142			else if (rxs & RXSTAT_FRAME)143				flag = TTY_FRAME;144		}145 146		uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);147 148		status = *CSR_UARTFLG;149	}150	tty_flip_buffer_push(&port->state->port);151 152	return IRQ_HANDLED;153}154 155static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)156{157	struct uart_port *port = dev_id;158	u8 ch;159 160	uart_port_tx_limited(port, ch, 256,161		!(*CSR_UARTFLG & 0x20),162		*CSR_UARTDR = ch,163		({}));164 165	return IRQ_HANDLED;166}167 168static unsigned int serial21285_tx_empty(struct uart_port *port)169{170	return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;171}172 173/* no modem control lines */174static unsigned int serial21285_get_mctrl(struct uart_port *port)175{176	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;177}178 179static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)180{181}182 183static void serial21285_break_ctl(struct uart_port *port, int break_state)184{185	unsigned long flags;186	unsigned int h_lcr;187 188	uart_port_lock_irqsave(port, &flags);189	h_lcr = *CSR_H_UBRLCR;190	if (break_state)191		h_lcr |= H_UBRLCR_BREAK;192	else193		h_lcr &= ~H_UBRLCR_BREAK;194	*CSR_H_UBRLCR = h_lcr;195	uart_port_unlock_irqrestore(port, flags);196}197 198static int serial21285_startup(struct uart_port *port)199{200	int ret;201 202	tx_enable(port);203	rx_enable(port);204 205	ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,206			  serial21285_name, port);207	if (ret == 0) {208		ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,209				  serial21285_name, port);210		if (ret)211			free_irq(IRQ_CONRX, port);212	}213 214	return ret;215}216 217static void serial21285_shutdown(struct uart_port *port)218{219	free_irq(IRQ_CONTX, port);220	free_irq(IRQ_CONRX, port);221}222 223static void224serial21285_set_termios(struct uart_port *port, struct ktermios *termios,225			const struct ktermios *old)226{227	unsigned long flags;228	unsigned int baud, quot, h_lcr, b;229 230	/*231	 * We don't support modem control lines.232	 */233	termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);234	termios->c_cflag |= CLOCAL;235 236	/*237	 * We don't support BREAK character recognition.238	 */239	termios->c_iflag &= ~(IGNBRK | BRKINT);240 241	/*242	 * Ask the core to calculate the divisor for us.243	 */244	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 245	quot = uart_get_divisor(port, baud);246	b = port->uartclk / (16 * quot);247	tty_termios_encode_baud_rate(termios, b, b);248 249	switch (termios->c_cflag & CSIZE) {250	case CS5:251		h_lcr = 0x00;252		break;253	case CS6:254		h_lcr = 0x20;255		break;256	case CS7:257		h_lcr = 0x40;258		break;259	default: /* CS8 */260		h_lcr = 0x60;261		break;262	}263 264	if (termios->c_cflag & CSTOPB)265		h_lcr |= H_UBRLCR_STOPB;266	if (termios->c_cflag & PARENB) {267		h_lcr |= H_UBRLCR_PARENB;268		if (!(termios->c_cflag & PARODD))269			h_lcr |= H_UBRLCR_PAREVN;270	}271 272	if (port->fifosize)273		h_lcr |= H_UBRLCR_FIFO;274 275	uart_port_lock_irqsave(port, &flags);276 277	/*278	 * Update the per-port timeout.279	 */280	uart_update_timeout(port, termios->c_cflag, baud);281 282	/*283	 * Which character status flags are we interested in?284	 */285	port->read_status_mask = RXSTAT_OVERRUN;286	if (termios->c_iflag & INPCK)287		port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;288 289	/*290	 * Which character status flags should we ignore?291	 */292	port->ignore_status_mask = 0;293	if (termios->c_iflag & IGNPAR)294		port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;295	if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)296		port->ignore_status_mask |= RXSTAT_OVERRUN;297 298	/*299	 * Ignore all characters if CREAD is not set.300	 */301	if ((termios->c_cflag & CREAD) == 0)302		port->ignore_status_mask |= RXSTAT_DUMMY_READ;303 304	quot -= 1;305 306	*CSR_UARTCON = 0;307	*CSR_L_UBRLCR = quot & 0xff;308	*CSR_M_UBRLCR = (quot >> 8) & 0x0f;309	*CSR_H_UBRLCR = h_lcr;310	*CSR_UARTCON = 1;311 312	uart_port_unlock_irqrestore(port, flags);313}314 315static const char *serial21285_type(struct uart_port *port)316{317	return port->type == PORT_21285 ? "DC21285" : NULL;318}319 320static void serial21285_release_port(struct uart_port *port)321{322	release_mem_region(port->mapbase, 32);323}324 325static int serial21285_request_port(struct uart_port *port)326{327	return request_mem_region(port->mapbase, 32, serial21285_name)328			 != NULL ? 0 : -EBUSY;329}330 331static void serial21285_config_port(struct uart_port *port, int flags)332{333	if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)334		port->type = PORT_21285;335}336 337/*338 * verify the new serial_struct (for TIOCSSERIAL).339 */340static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)341{342	int ret = 0;343	if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)344		ret = -EINVAL;345	if (ser->irq <= 0)346		ret = -EINVAL;347	if (ser->baud_base != port->uartclk / 16)348		ret = -EINVAL;349	return ret;350}351 352static const struct uart_ops serial21285_ops = {353	.tx_empty	= serial21285_tx_empty,354	.get_mctrl	= serial21285_get_mctrl,355	.set_mctrl	= serial21285_set_mctrl,356	.stop_tx	= serial21285_stop_tx,357	.start_tx	= serial21285_start_tx,358	.stop_rx	= serial21285_stop_rx,359	.break_ctl	= serial21285_break_ctl,360	.startup	= serial21285_startup,361	.shutdown	= serial21285_shutdown,362	.set_termios	= serial21285_set_termios,363	.type		= serial21285_type,364	.release_port	= serial21285_release_port,365	.request_port	= serial21285_request_port,366	.config_port	= serial21285_config_port,367	.verify_port	= serial21285_verify_port,368};369 370static struct uart_port serial21285_port = {371	.mapbase	= 0x42000160,372	.iotype		= UPIO_MEM,373	.irq		= 0,374	.fifosize	= 16,375	.ops		= &serial21285_ops,376	.flags		= UPF_BOOT_AUTOCONF,377};378 379static void serial21285_setup_ports(void)380{381	serial21285_port.uartclk = mem_fclk_21285 / 4;382}383 384#ifdef CONFIG_SERIAL_21285_CONSOLE385static void serial21285_console_putchar(struct uart_port *port, unsigned char ch)386{387	while (*CSR_UARTFLG & 0x20)388		barrier();389	*CSR_UARTDR = ch;390}391 392static void393serial21285_console_write(struct console *co, const char *s,394			  unsigned int count)395{396	uart_console_write(&serial21285_port, s, count, serial21285_console_putchar);397}398 399static void __init400serial21285_get_options(struct uart_port *port, int *baud,401			int *parity, int *bits)402{403	if (*CSR_UARTCON == 1) {404		unsigned int tmp;405 406		tmp = *CSR_H_UBRLCR;407		switch (tmp & 0x60) {408		case 0x00:409			*bits = 5;410			break;411		case 0x20:412			*bits = 6;413			break;414		case 0x40:415			*bits = 7;416			break;417		default:418		case 0x60:419			*bits = 8;420			break;421		}422 423		if (tmp & H_UBRLCR_PARENB) {424			*parity = 'o';425			if (tmp & H_UBRLCR_PAREVN)426				*parity = 'e';427		}428 429		tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);430 431		*baud = port->uartclk / (16 * (tmp + 1));432	}433}434 435static int __init serial21285_console_setup(struct console *co, char *options)436{437	struct uart_port *port = &serial21285_port;438	int baud = 9600;439	int bits = 8;440	int parity = 'n';441	int flow = 'n';442 443	/*444	 * Check whether an invalid uart number has been specified, and445	 * if so, search for the first available port that does have446	 * console support.447	 */448	if (options)449		uart_parse_options(options, &baud, &parity, &bits, &flow);450	else451		serial21285_get_options(port, &baud, &parity, &bits);452 453	return uart_set_options(port, co, baud, parity, bits, flow);454}455 456static struct uart_driver serial21285_reg;457 458static struct console serial21285_console =459{460	.name		= SERIAL_21285_NAME,461	.write		= serial21285_console_write,462	.device		= uart_console_device,463	.setup		= serial21285_console_setup,464	.flags		= CON_PRINTBUFFER,465	.index		= -1,466	.data		= &serial21285_reg,467};468 469static int __init rs285_console_init(void)470{471	serial21285_setup_ports();472	register_console(&serial21285_console);473	return 0;474}475console_initcall(rs285_console_init);476 477#define SERIAL_21285_CONSOLE	&serial21285_console478#else479#define SERIAL_21285_CONSOLE	NULL480#endif481 482static struct uart_driver serial21285_reg = {483	.owner			= THIS_MODULE,484	.driver_name		= "ttyFB",485	.dev_name		= "ttyFB",486	.major			= SERIAL_21285_MAJOR,487	.minor			= SERIAL_21285_MINOR,488	.nr			= 1,489	.cons			= SERIAL_21285_CONSOLE,490};491 492static int __init serial21285_init(void)493{494	int ret;495 496	printk(KERN_INFO "Serial: 21285 driver\n");497 498	serial21285_setup_ports();499 500	ret = uart_register_driver(&serial21285_reg);501	if (ret == 0)502		uart_add_one_port(&serial21285_reg, &serial21285_port);503 504	return ret;505}506 507static void __exit serial21285_exit(void)508{509	uart_remove_one_port(&serial21285_reg, &serial21285_port);510	uart_unregister_driver(&serial21285_reg);511}512 513module_init(serial21285_init);514module_exit(serial21285_exit);515 516MODULE_LICENSE("GPL");517MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver");518MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);519