Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Early serial console for 8250/16550 devices
  4 *
  5 * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
  6 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
  7 *
  8 * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
  9 * and on early_printk.c by Andi Kleen.
 10 *
 11 * This is for use before the serial driver has initialized, in
 12 * particular, before the UARTs have been discovered and named.
 13 * Instead of specifying the console device as, e.g., "ttyS0",
 14 * we locate the device directly by its MMIO or I/O port address.
 15 *
 16 * The user can specify the device directly, e.g.,
 17 *	earlycon=uart8250,io,0x3f8,9600n8
 18 *	earlycon=uart8250,mmio,0xff5e0000,115200n8
 19 *	earlycon=uart8250,mmio32,0xff5e0000,115200n8
 20 * or
 21 *	console=uart8250,io,0x3f8,9600n8
 22 *	console=uart8250,mmio,0xff5e0000,115200n8
 23 *	console=uart8250,mmio32,0xff5e0000,115200n8
 24 */
 25
 26#include <linux/tty.h>
 27#include <linux/init.h>
 28#include <linux/console.h>
 29#include <linux/of.h>
 30#include <linux/of_device.h>
 31#include <linux/serial_reg.h>
 32#include <linux/serial.h>
 33#include <linux/serial_8250.h>
 34#include <asm/io.h>
 35#include <asm/serial.h>
 36
 37static unsigned int serial8250_early_in(struct uart_port *port, int offset)
 38{
 39	int reg_offset = offset;
 40	offset <<= port->regshift;
 41
 42	switch (port->iotype) {
 43	case UPIO_MEM:
 44		return readb(port->membase + offset);
 45	case UPIO_MEM16:
 46		return readw(port->membase + offset);
 47	case UPIO_MEM32:
 48		return readl(port->membase + offset);
 49	case UPIO_MEM32BE:
 50		return ioread32be(port->membase + offset);
 51	case UPIO_PORT:
 52		return inb(port->iobase + offset);
 53	case UPIO_AU:
 54		return port->serial_in(port, reg_offset);
 55	default:
 56		return 0;
 57	}
 58}
 59
 60static void serial8250_early_out(struct uart_port *port, int offset, int value)
 61{
 62	int reg_offset = offset;
 63	offset <<= port->regshift;
 64
 65	switch (port->iotype) {
 66	case UPIO_MEM:
 67		writeb(value, port->membase + offset);
 68		break;
 69	case UPIO_MEM16:
 70		writew(value, port->membase + offset);
 71		break;
 72	case UPIO_MEM32:
 73		writel(value, port->membase + offset);
 74		break;
 75	case UPIO_MEM32BE:
 76		iowrite32be(value, port->membase + offset);
 77		break;
 78	case UPIO_PORT:
 79		outb(value, port->iobase + offset);
 80		break;
 81	case UPIO_AU:
 82		port->serial_out(port, reg_offset, value);
 83		break;
 84	}
 85}
 86
 87#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 88
 89static void serial_putc(struct uart_port *port, int c)
 90{
 91	unsigned int status;
 92
 93	serial8250_early_out(port, UART_TX, c);
 94
 95	for (;;) {
 96		status = serial8250_early_in(port, UART_LSR);
 97		if ((status & BOTH_EMPTY) == BOTH_EMPTY)
 98			break;
 99		cpu_relax();
100	}
101}
102
103static void early_serial8250_write(struct console *console,
104					const char *s, unsigned int count)
105{
106	struct earlycon_device *device = console->data;
107	struct uart_port *port = &device->port;
108
109	uart_console_write(port, s, count, serial_putc);
110}
111
112static void __init init_port(struct earlycon_device *device)
113{
114	struct uart_port *port = &device->port;
115	unsigned int divisor;
116	unsigned char c;
117	unsigned int ier;
118
119	serial8250_early_out(port, UART_LCR, 0x3);	/* 8n1 */
120	ier = serial8250_early_in(port, UART_IER);
121	serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
122	serial8250_early_out(port, UART_FCR, 0);	/* no fifo */
123	serial8250_early_out(port, UART_MCR, 0x3);	/* DTR + RTS */
124
125	if (port->uartclk) {
126		divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
127		c = serial8250_early_in(port, UART_LCR);
128		serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
129		serial8250_early_out(port, UART_DLL, divisor & 0xff);
130		serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
131		serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
132	}
133}
134
135int __init early_serial8250_setup(struct earlycon_device *device,
136					 const char *options)
137{
138	if (!(device->port.membase || device->port.iobase))
139		return -ENODEV;
140
141	if (!device->baud) {
142		struct uart_port *port = &device->port;
143		unsigned int ier;
144
145		/* assume the device was initialized, only mask interrupts */
146		ier = serial8250_early_in(port, UART_IER);
147		serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
148	} else
149		init_port(device);
150
151	device->con->write = early_serial8250_write;
152	return 0;
153}
154EARLYCON_DECLARE(uart8250, early_serial8250_setup);
155EARLYCON_DECLARE(uart, early_serial8250_setup);
156OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
157OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
158OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);
159OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);
160
161#ifdef CONFIG_SERIAL_8250_OMAP
162
163static int __init early_omap8250_setup(struct earlycon_device *device,
164				       const char *options)
165{
166	struct uart_port *port = &device->port;
167
168	if (!(device->port.membase || device->port.iobase))
169		return -ENODEV;
170
171	port->regshift = 2;
172	device->con->write = early_serial8250_write;
173	return 0;
174}
175
176OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup);
177OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
178OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
179
180#endif
181
182#ifdef CONFIG_SERIAL_8250_RT288X
183
184unsigned int au_serial_in(struct uart_port *p, int offset);
185void au_serial_out(struct uart_port *p, int offset, int value);
186
187static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
188{
189	dev->port.serial_in = au_serial_in;
190	dev->port.serial_out = au_serial_out;
191	dev->port.iotype = UPIO_AU;
192	dev->con->write = early_serial8250_write;
193	return 0;
194}
195OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
196
197#endif