brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 6176083 Raw
206 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Early serial console for 8250/16550 devices4 *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, in12 * 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,9600n818 *	earlycon=uart8250,mmio,0xff5e0000,115200n819 *	earlycon=uart8250,mmio32,0xff5e0000,115200n820 * or21 *	console=uart8250,io,0x3f8,9600n822 *	console=uart8250,mmio,0xff5e0000,115200n823 *	console=uart8250,mmio32,0xff5e0000,115200n824 */25 26#include <linux/tty.h>27#include <linux/init.h>28#include <linux/console.h>29#include <linux/of.h>30#include <linux/serial_reg.h>31#include <linux/serial.h>32#include <linux/serial_8250.h>33#include <asm/io.h>34#include <asm/serial.h>35 36static unsigned int serial8250_early_in(struct uart_port *port, int offset)37{38	offset <<= port->regshift;39 40	switch (port->iotype) {41	case UPIO_MEM:42		return readb(port->membase + offset);43	case UPIO_MEM16:44		return readw(port->membase + offset);45	case UPIO_MEM32:46		return readl(port->membase + offset);47	case UPIO_MEM32BE:48		return ioread32be(port->membase + offset);49	case UPIO_PORT:50		return inb(port->iobase + offset);51	default:52		return 0;53	}54}55 56static void serial8250_early_out(struct uart_port *port, int offset, int value)57{58	offset <<= port->regshift;59 60	switch (port->iotype) {61	case UPIO_MEM:62		writeb(value, port->membase + offset);63		break;64	case UPIO_MEM16:65		writew(value, port->membase + offset);66		break;67	case UPIO_MEM32:68		writel(value, port->membase + offset);69		break;70	case UPIO_MEM32BE:71		iowrite32be(value, port->membase + offset);72		break;73	case UPIO_PORT:74		outb(value, port->iobase + offset);75		break;76	}77}78 79static void serial_putc(struct uart_port *port, unsigned char c)80{81	unsigned int status;82 83	serial8250_early_out(port, UART_TX, c);84 85	for (;;) {86		status = serial8250_early_in(port, UART_LSR);87		if (uart_lsr_tx_empty(status))88			break;89		cpu_relax();90	}91}92 93static void early_serial8250_write(struct console *console,94					const char *s, unsigned int count)95{96	struct earlycon_device *device = console->data;97	struct uart_port *port = &device->port;98 99	uart_console_write(port, s, count, serial_putc);100}101 102#ifdef CONFIG_CONSOLE_POLL103static int early_serial8250_read(struct console *console,104				 char *s, unsigned int count)105{106	struct earlycon_device *device = console->data;107	struct uart_port *port = &device->port;108	unsigned int status;109	int num_read = 0;110 111	while (num_read < count) {112		status = serial8250_early_in(port, UART_LSR);113		if (!(status & UART_LSR_DR))114			break;115		s[num_read++] = serial8250_early_in(port, UART_RX);116	}117 118	return num_read;119}120#else121#define early_serial8250_read NULL122#endif123 124static void __init init_port(struct earlycon_device *device)125{126	struct uart_port *port = &device->port;127	unsigned int divisor;128	unsigned char c;129	unsigned int ier;130 131	serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8);		/* 8n1 */132	ier = serial8250_early_in(port, UART_IER);133	serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */134	serial8250_early_out(port, UART_FCR, 0);	/* no fifo */135	serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);136 137	if (port->uartclk) {138		divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);139		c = serial8250_early_in(port, UART_LCR);140		serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);141		serial8250_early_out(port, UART_DLL, divisor & 0xff);142		serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);143		serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);144	}145}146 147int __init early_serial8250_setup(struct earlycon_device *device,148					 const char *options)149{150	if (!(device->port.membase || device->port.iobase))151		return -ENODEV;152 153	if (!device->baud) {154		struct uart_port *port = &device->port;155		unsigned int ier;156 157		/* assume the device was initialized, only mask interrupts */158		ier = serial8250_early_in(port, UART_IER);159		serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);160	} else161		init_port(device);162 163	device->con->write = early_serial8250_write;164	device->con->read = early_serial8250_read;165	return 0;166}167EARLYCON_DECLARE(uart8250, early_serial8250_setup);168EARLYCON_DECLARE(uart, early_serial8250_setup);169OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);170OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);171OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);172OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);173 174static int __init early_serial8250_rs2_setup(struct earlycon_device *device,175					     const char *options)176{177	device->port.regshift = 2;178 179	return early_serial8250_setup(device, options);180}181OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup);182OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup);183OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup);184 185#ifdef CONFIG_SERIAL_8250_OMAP186 187static int __init early_omap8250_setup(struct earlycon_device *device,188				       const char *options)189{190	struct uart_port *port = &device->port;191 192	if (!(device->port.membase || device->port.iobase))193		return -ENODEV;194 195	port->regshift = 2;196	device->con->write = early_serial8250_write;197	return 0;198}199 200OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup);201OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);202OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);203OF_EARLYCON_DECLARE(omap8250, "ti,am654-uart", early_omap8250_setup);204 205#endif206