1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
29bef3d41SPaul Gortmaker /*
39bef3d41SPaul Gortmaker  * Early serial console for 8250/16550 devices
49bef3d41SPaul Gortmaker  *
59bef3d41SPaul Gortmaker  * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
69bef3d41SPaul Gortmaker  *	Bjorn Helgaas <bjorn.helgaas@hp.com>
79bef3d41SPaul Gortmaker  *
89bef3d41SPaul Gortmaker  * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
99bef3d41SPaul Gortmaker  * and on early_printk.c by Andi Kleen.
109bef3d41SPaul Gortmaker  *
119bef3d41SPaul Gortmaker  * This is for use before the serial driver has initialized, in
129bef3d41SPaul Gortmaker  * particular, before the UARTs have been discovered and named.
139bef3d41SPaul Gortmaker  * Instead of specifying the console device as, e.g., "ttyS0",
149bef3d41SPaul Gortmaker  * we locate the device directly by its MMIO or I/O port address.
159bef3d41SPaul Gortmaker  *
169bef3d41SPaul Gortmaker  * The user can specify the device directly, e.g.,
179bef3d41SPaul Gortmaker  *	earlycon=uart8250,io,0x3f8,9600n8
189bef3d41SPaul Gortmaker  *	earlycon=uart8250,mmio,0xff5e0000,115200n8
199bef3d41SPaul Gortmaker  *	earlycon=uart8250,mmio32,0xff5e0000,115200n8
209bef3d41SPaul Gortmaker  * or
219bef3d41SPaul Gortmaker  *	console=uart8250,io,0x3f8,9600n8
229bef3d41SPaul Gortmaker  *	console=uart8250,mmio,0xff5e0000,115200n8
239bef3d41SPaul Gortmaker  *	console=uart8250,mmio32,0xff5e0000,115200n8
249bef3d41SPaul Gortmaker  */
259bef3d41SPaul Gortmaker 
269bef3d41SPaul Gortmaker #include <linux/tty.h>
279bef3d41SPaul Gortmaker #include <linux/init.h>
289bef3d41SPaul Gortmaker #include <linux/console.h>
29d05f1570SScott Wood #include <linux/of.h>
309bef3d41SPaul Gortmaker #include <linux/serial_reg.h>
319bef3d41SPaul Gortmaker #include <linux/serial.h>
329bef3d41SPaul Gortmaker #include <linux/serial_8250.h>
339bef3d41SPaul Gortmaker #include <asm/io.h>
349bef3d41SPaul Gortmaker #include <asm/serial.h>
359bef3d41SPaul Gortmaker 
serial8250_early_in(struct uart_port * port,int offset)36bdc4704bSJeffy Chen static unsigned int serial8250_early_in(struct uart_port *port, int offset)
379bef3d41SPaul Gortmaker {
38dc6b576bSPeter Hurley 	offset <<= port->regshift;
39dc6b576bSPeter Hurley 
409bef3d41SPaul Gortmaker 	switch (port->iotype) {
419bef3d41SPaul Gortmaker 	case UPIO_MEM:
429bef3d41SPaul Gortmaker 		return readb(port->membase + offset);
43bd94c407SMasahiro Yamada 	case UPIO_MEM16:
44dc6b576bSPeter Hurley 		return readw(port->membase + offset);
459bef3d41SPaul Gortmaker 	case UPIO_MEM32:
46dc6b576bSPeter Hurley 		return readl(port->membase + offset);
47c627f2ceSKevin Cernekee 	case UPIO_MEM32BE:
48dc6b576bSPeter Hurley 		return ioread32be(port->membase + offset);
499bef3d41SPaul Gortmaker 	case UPIO_PORT:
509bef3d41SPaul Gortmaker 		return inb(port->iobase + offset);
519bef3d41SPaul Gortmaker 	default:
529bef3d41SPaul Gortmaker 		return 0;
539bef3d41SPaul Gortmaker 	}
549bef3d41SPaul Gortmaker }
559bef3d41SPaul Gortmaker 
serial8250_early_out(struct uart_port * port,int offset,int value)56bdc4704bSJeffy Chen static void serial8250_early_out(struct uart_port *port, int offset, int value)
579bef3d41SPaul Gortmaker {
58dc6b576bSPeter Hurley 	offset <<= port->regshift;
59dc6b576bSPeter Hurley 
609bef3d41SPaul Gortmaker 	switch (port->iotype) {
619bef3d41SPaul Gortmaker 	case UPIO_MEM:
629bef3d41SPaul Gortmaker 		writeb(value, port->membase + offset);
639bef3d41SPaul Gortmaker 		break;
64bd94c407SMasahiro Yamada 	case UPIO_MEM16:
65dc6b576bSPeter Hurley 		writew(value, port->membase + offset);
66bd94c407SMasahiro Yamada 		break;
679bef3d41SPaul Gortmaker 	case UPIO_MEM32:
68dc6b576bSPeter Hurley 		writel(value, port->membase + offset);
699bef3d41SPaul Gortmaker 		break;
70c627f2ceSKevin Cernekee 	case UPIO_MEM32BE:
71dc6b576bSPeter Hurley 		iowrite32be(value, port->membase + offset);
72c627f2ceSKevin Cernekee 		break;
739bef3d41SPaul Gortmaker 	case UPIO_PORT:
749bef3d41SPaul Gortmaker 		outb(value, port->iobase + offset);
759bef3d41SPaul Gortmaker 		break;
769bef3d41SPaul Gortmaker 	}
779bef3d41SPaul Gortmaker }
789bef3d41SPaul Gortmaker 
serial_putc(struct uart_port * port,unsigned char c)793f8bab17SJiri Slaby static void serial_putc(struct uart_port *port, unsigned char c)
809bef3d41SPaul Gortmaker {
819bef3d41SPaul Gortmaker 	unsigned int status;
829bef3d41SPaul Gortmaker 
83004e2ed5SMasahiro Yamada 	serial8250_early_out(port, UART_TX, c);
84004e2ed5SMasahiro Yamada 
859bef3d41SPaul Gortmaker 	for (;;) {
86ed71871bSNoam Camus 		status = serial8250_early_in(port, UART_LSR);
8734619de1SIlpo Järvinen 		if (uart_lsr_tx_empty(status))
88004e2ed5SMasahiro Yamada 			break;
899bef3d41SPaul Gortmaker 		cpu_relax();
909bef3d41SPaul Gortmaker 	}
919bef3d41SPaul Gortmaker }
929bef3d41SPaul Gortmaker 
early_serial8250_write(struct console * console,const char * s,unsigned int count)93bdc4704bSJeffy Chen static void early_serial8250_write(struct console *console,
949bef3d41SPaul Gortmaker 					const char *s, unsigned int count)
959bef3d41SPaul Gortmaker {
96d0d654ceSPeter Hurley 	struct earlycon_device *device = console->data;
97d0d654ceSPeter Hurley 	struct uart_port *port = &device->port;
989bef3d41SPaul Gortmaker 
999bef3d41SPaul Gortmaker 	uart_console_write(port, s, count, serial_putc);
1009bef3d41SPaul Gortmaker }
1019bef3d41SPaul Gortmaker 
102c5e7467dSDouglas Anderson #ifdef CONFIG_CONSOLE_POLL
early_serial8250_read(struct console * console,char * s,unsigned int count)103c5e7467dSDouglas Anderson static int early_serial8250_read(struct console *console,
104c5e7467dSDouglas Anderson 				 char *s, unsigned int count)
105c5e7467dSDouglas Anderson {
106c5e7467dSDouglas Anderson 	struct earlycon_device *device = console->data;
107c5e7467dSDouglas Anderson 	struct uart_port *port = &device->port;
108c5e7467dSDouglas Anderson 	unsigned int status;
109c5e7467dSDouglas Anderson 	int num_read = 0;
110c5e7467dSDouglas Anderson 
111c5e7467dSDouglas Anderson 	while (num_read < count) {
112c5e7467dSDouglas Anderson 		status = serial8250_early_in(port, UART_LSR);
113c5e7467dSDouglas Anderson 		if (!(status & UART_LSR_DR))
114c5e7467dSDouglas Anderson 			break;
115c5e7467dSDouglas Anderson 		s[num_read++] = serial8250_early_in(port, UART_RX);
116c5e7467dSDouglas Anderson 	}
117c5e7467dSDouglas Anderson 
118c5e7467dSDouglas Anderson 	return num_read;
119c5e7467dSDouglas Anderson }
120c5e7467dSDouglas Anderson #else
121c5e7467dSDouglas Anderson #define early_serial8250_read NULL
122c5e7467dSDouglas Anderson #endif
123c5e7467dSDouglas Anderson 
init_port(struct earlycon_device * device)124d2fd6810SRob Herring static void __init init_port(struct earlycon_device *device)
1259bef3d41SPaul Gortmaker {
1269bef3d41SPaul Gortmaker 	struct uart_port *port = &device->port;
1279bef3d41SPaul Gortmaker 	unsigned int divisor;
1289bef3d41SPaul Gortmaker 	unsigned char c;
129a4c639b0SRob Herring 	unsigned int ier;
1309bef3d41SPaul Gortmaker 
1318573b2ebSIlpo Järvinen 	serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8);		/* 8n1 */
132a4c639b0SRob Herring 	ier = serial8250_early_in(port, UART_IER);
133a4c639b0SRob Herring 	serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
134ed71871bSNoam Camus 	serial8250_early_out(port, UART_FCR, 0);	/* no fifo */
1358573b2ebSIlpo Järvinen 	serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
1369bef3d41SPaul Gortmaker 
137700ad553SMichal Simek 	if (port->uartclk) {
138b15d5380SAlexey Brodkin 		divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
139ed71871bSNoam Camus 		c = serial8250_early_in(port, UART_LCR);
140ed71871bSNoam Camus 		serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
141ed71871bSNoam Camus 		serial8250_early_out(port, UART_DLL, divisor & 0xff);
142ed71871bSNoam Camus 		serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
143ed71871bSNoam Camus 		serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
1449bef3d41SPaul Gortmaker 	}
1450ff3ab70SMatt Redfearn }
1469bef3d41SPaul Gortmaker 
early_serial8250_setup(struct earlycon_device * device,const char * options)1471c5841e8SEddie Huang int __init early_serial8250_setup(struct earlycon_device *device,
148d2fd6810SRob Herring 					 const char *options)
1499bef3d41SPaul Gortmaker {
150d2fd6810SRob Herring 	if (!(device->port.membase || device->port.iobase))
151cd385e9aSPeter Hurley 		return -ENODEV;
1529bef3d41SPaul Gortmaker 
15360efcf04SRob Herring 	if (!device->baud) {
1540e3e143eSPeter Hurley 		struct uart_port *port = &device->port;
1550e3e143eSPeter Hurley 		unsigned int ier;
1560e3e143eSPeter Hurley 
1570e3e143eSPeter Hurley 		/* assume the device was initialized, only mask interrupts */
1580e3e143eSPeter Hurley 		ier = serial8250_early_in(port, UART_IER);
1590e3e143eSPeter Hurley 		serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
1600e3e143eSPeter Hurley 	} else
1619bef3d41SPaul Gortmaker 		init_port(device);
162d2fd6810SRob Herring 
163d2fd6810SRob Herring 	device->con->write = early_serial8250_write;
164c5e7467dSDouglas Anderson 	device->con->read = early_serial8250_read;
1659bef3d41SPaul Gortmaker 	return 0;
1669bef3d41SPaul Gortmaker }
167d2fd6810SRob Herring EARLYCON_DECLARE(uart8250, early_serial8250_setup);
168d2fd6810SRob Herring EARLYCON_DECLARE(uart, early_serial8250_setup);
169d05f1570SScott Wood OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
170d05f1570SScott Wood OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
1710ab556c2SJon Hunter OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);
17201e4d273SKefeng Wang OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);
17375d611bfSPeter Hurley 
17475d611bfSPeter Hurley #ifdef CONFIG_SERIAL_8250_OMAP
17575d611bfSPeter Hurley 
early_omap8250_setup(struct earlycon_device * device,const char * options)17675d611bfSPeter Hurley static int __init early_omap8250_setup(struct earlycon_device *device,
17775d611bfSPeter Hurley 				       const char *options)
17875d611bfSPeter Hurley {
17975d611bfSPeter Hurley 	struct uart_port *port = &device->port;
18075d611bfSPeter Hurley 
18175d611bfSPeter Hurley 	if (!(device->port.membase || device->port.iobase))
18275d611bfSPeter Hurley 		return -ENODEV;
18375d611bfSPeter Hurley 
18475d611bfSPeter Hurley 	port->regshift = 2;
18575d611bfSPeter Hurley 	device->con->write = early_serial8250_write;
18675d611bfSPeter Hurley 	return 0;
18775d611bfSPeter Hurley }
18875d611bfSPeter Hurley 
18975d611bfSPeter Hurley OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup);
19075d611bfSPeter Hurley OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
19175d611bfSPeter Hurley OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
192*22fdcaafSRonald Wahl OF_EARLYCON_DECLARE(omap8250, "ti,am654-uart", early_omap8250_setup);
19375d611bfSPeter Hurley 
19475d611bfSPeter Hurley #endif
195