earlycon.c 5.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Copyright (C) 2014 Linaro Ltd.
 * Author: Rob Herring <robh@kernel.org>
 *
 * Based on 8250 earlycon:
 * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
 *	Bjorn Helgaas <bjorn.helgaas@hp.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
13 14 15

#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt

16 17 18 19 20
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/serial_core.h>
R
Rob Herring 已提交
21
#include <linux/sizes.h>
22 23 24 25 26 27 28 29

#ifdef CONFIG_FIX_EARLYCON_MEM
#include <asm/fixmap.h>
#endif

#include <asm/serial.h>

static struct console early_con = {
30
	.name =		"uart",		/* fixed up at earlycon registration */
31
	.flags =	CON_PRINTBUFFER | CON_BOOT,
32
	.index =	0,
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
};

static struct earlycon_device early_console_dev = {
	.con = &early_con,
};

static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
{
	void __iomem *base;
#ifdef CONFIG_FIX_EARLYCON_MEM
	set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
	base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
	base += paddr & ~PAGE_MASK;
#else
	base = ioremap(paddr, size);
#endif
	if (!base)
		pr_err("%s: Couldn't map 0x%llx\n", __func__,
		       (unsigned long long)paddr);

	return base;
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
static void __init earlycon_init(struct earlycon_device *device,
				 const char *name)
{
	struct console *earlycon = device->con;
	const char *s;
	size_t len;

	/* scan backwards from end of string for first non-numeral */
	for (s = name + strlen(name);
	     s > name && s[-1] >= '0' && s[-1] <= '9';
	     s--)
		;
	if (*s)
		earlycon->index = simple_strtoul(s, NULL, 10);
	len = s - name;
	strlcpy(earlycon->name, name, min(len + 1, sizeof(earlycon->name)));
	earlycon->data = &early_console_dev;
}

75
static int __init parse_options(struct earlycon_device *device, char *options)
76 77
{
	struct uart_port *port = &device->port;
78
	int length;
79 80
	unsigned long addr;

81 82
	if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
		return -EINVAL;
83

84
	switch (port->iotype) {
85 86 87 88 89 90 91
	case UPIO_MEM:
		port->mapbase = addr;
		break;
	case UPIO_MEM16:
		port->regshift = 1;
		port->mapbase = addr;
		break;
92
	case UPIO_MEM32:
93
	case UPIO_MEM32BE:
94
		port->regshift = 2;
95
		port->mapbase = addr;
96 97
		break;
	case UPIO_PORT:
98
		port->iobase = addr;
99 100
		break;
	default:
101 102 103 104
		return -EINVAL;
	}

	if (options) {
105
		device->baud = simple_strtoul(options, NULL, 0);
106 107 108 109 110
		length = min(strcspn(options, " ") + 1,
			     (size_t)(sizeof(device->options)));
		strlcpy(device->options, options, length);
	}

111 112
	if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 ||
	    port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE)
113
		pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
114
			(port->iotype == UPIO_MEM) ? "" :
115
			(port->iotype == UPIO_MEM16) ? "16" :
116
			(port->iotype == UPIO_MEM32) ? "32" : "32be",
117 118 119 120 121 122 123 124 125 126
			(unsigned long long)port->mapbase,
			device->options);
	else
		pr_info("Early serial console at I/O port 0x%lx (options '%s')\n",
			port->iobase,
			device->options);

	return 0;
}

127
static int __init register_earlycon(char *buf, const struct earlycon_id *match)
128 129 130 131 132
{
	int err;
	struct uart_port *port = &early_console_dev.port;

	/* On parsing error, pass the options buf to the setup function */
133
	if (buf && !parse_options(&early_console_dev, buf))
134 135
		buf = NULL;

136
	spin_lock_init(&port->lock);
137
	port->uartclk = BASE_BAUD * 16;
138 139 140
	if (port->mapbase)
		port->membase = earlycon_map(port->mapbase, 64);

141
	earlycon_init(&early_console_dev, match->name);
142
	err = match->setup(&early_console_dev, buf);
143 144 145 146 147 148 149 150
	if (err < 0)
		return err;
	if (!early_console_dev.con->write)
		return -ENODEV;

	register_console(early_console_dev.con);
	return 0;
}
R
Rob Herring 已提交
151

152 153 154 155 156 157
/**
 *	setup_earlycon - match and register earlycon console
 *	@buf:	earlycon param string
 *
 *	Registers the earlycon console matching the earlycon specified
 *	in the param string @buf. Acceptable param strings are of the form
158
 *	   <name>,io|mmio|mmio32|mmio32be,<addr>,<options>
159 160 161 162 163 164 165 166 167 168 169 170
 *	   <name>,0x<addr>,<options>
 *	   <name>,<options>
 *	   <name>
 *
 *	Only for the third form does the earlycon setup() method receive the
 *	<options> string in the 'options' parameter; all other forms set
 *	the parameter to NULL.
 *
 *	Returns 0 if an attempt to register the earlycon was made,
 *	otherwise negative error code
 */
int __init setup_earlycon(char *buf)
171
{
172
	const struct earlycon_id *match;
173

174 175
	if (!buf || !buf[0])
		return -EINVAL;
176

177 178
	if (early_con.flags & CON_ENABLED)
		return -EALREADY;
179

180
	for (match = __earlycon_table; match < __earlycon_table_end; match++) {
181
		size_t len = strlen(match->name);
182

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
		if (strncmp(buf, match->name, len))
			continue;

		if (buf[len]) {
			if (buf[len] != ',')
				continue;
			buf += len + 1;
		} else
			buf = NULL;

		return register_earlycon(buf, match);
	}

	return -ENOENT;
}

/* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon(char *buf)
{
	int err;

	/*
	 * Just 'earlycon' is a valid param for devicetree earlycons;
	 * don't generate a warning from parse_early_params() in that case
	 */
	if (!buf || !buf[0])
		return 0;

	err = setup_earlycon(buf);
P
Peter Hurley 已提交
212 213
	if (err == -ENOENT || err == -EALREADY)
		return 0;
214
	return err;
215
}
216
early_param("earlycon", param_setup_earlycon);
217

218 219
#ifdef CONFIG_OF_EARLY_FLATTREE

R
Rob Herring 已提交
220
int __init of_setup_earlycon(unsigned long addr,
221
			     const struct earlycon_id *match)
R
Rob Herring 已提交
222 223 224 225
{
	int err;
	struct uart_port *port = &early_console_dev.port;

226
	spin_lock_init(&port->lock);
R
Rob Herring 已提交
227 228 229 230 231
	port->iotype = UPIO_MEM;
	port->mapbase = addr;
	port->uartclk = BASE_BAUD * 16;
	port->membase = earlycon_map(addr, SZ_4K);

232 233
	earlycon_init(&early_console_dev, match->name);
	err = match->setup(&early_console_dev, NULL);
R
Rob Herring 已提交
234 235 236 237 238 239 240 241 242
	if (err < 0)
		return err;
	if (!early_console_dev.con->write)
		return -ENODEV;


	register_console(early_console_dev.con);
	return 0;
}
243 244

#endif /* CONFIG_OF_EARLY_FLATTREE */