Message ID | 1395436128-11244-5-git-send-email-robherring2@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c > new file mode 100644 > index 0000000..241757a > --- /dev/null > +++ b/drivers/tty/serial/earlycon.c [...] > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) > +{ > + void __iomem *base; > +#ifdef CONFIG_FIX_EARLYCON_MEM > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); > + base += paddr & ~PAGE_MASK; > +#else > + base = ioremap_nocache(paddr, size); > +#endif Just curious why not set_fixmap_io (and plain ioremap)? > +static int __init parse_options(struct earlycon_device *device, > + char *options) > +{ [...] > + if (port->mapbase) > + port->membase = earlycon_map(port->mapbase, 64); I would move the earlycon_map() call in setup_earlycon() (personal preference, parse_options() implies just parsing the options rather than having additional side-effects).
On Monday 24 March 2014 11:22:03 Catalin Marinas wrote: > On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: > > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c > > new file mode 100644 > > index 0000000..241757a > > --- /dev/null > > +++ b/drivers/tty/serial/earlycon.c > [...] > > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) > > +{ > > + void __iomem *base; > > +#ifdef CONFIG_FIX_EARLYCON_MEM > > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); > > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); > > + base += paddr & ~PAGE_MASK; > > +#else > > + base = ioremap_nocache(paddr, size); > > +#endif > > Just curious why not set_fixmap_io (and plain ioremap)? Good point. Note that ioremap_nocache() is the same as ioremap() on *all* architectures. Arnd
On Mon, Mar 24, 2014 at 6:29 AM, Arnd Bergmann <arnd@arndb.de> wrote: > On Monday 24 March 2014 11:22:03 Catalin Marinas wrote: >> On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: >> > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c >> > new file mode 100644 >> > index 0000000..241757a >> > --- /dev/null >> > +++ b/drivers/tty/serial/earlycon.c >> [...] >> > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) >> > +{ >> > + void __iomem *base; >> > +#ifdef CONFIG_FIX_EARLYCON_MEM >> > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); >> > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); >> > + base += paddr & ~PAGE_MASK; >> > +#else >> > + base = ioremap_nocache(paddr, size); >> > +#endif >> >> Just curious why not set_fixmap_io (and plain ioremap)? > > Good point. Note that ioremap_nocache() is the same as ioremap() > on *all* architectures. I investigated this before adding this to arm64. set_fixmap_io and set_fixmap_nocache are not the same settings on x86. Whether the mapping type really matters on x86 or not, I don't know. So I added the nocache variant to arm64 to avoid a change to x86. Rob
On Monday 24 March 2014 08:36:46 Rob Herring wrote: > On Mon, Mar 24, 2014 at 6:29 AM, Arnd Bergmann <arnd@arndb.de> wrote: > > On Monday 24 March 2014 11:22:03 Catalin Marinas wrote: > >> On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: > >> > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c > >> > new file mode 100644 > >> > index 0000000..241757a > >> > --- /dev/null > >> > +++ b/drivers/tty/serial/earlycon.c > >> [...] > >> > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) > >> > +{ > >> > + void __iomem *base; > >> > +#ifdef CONFIG_FIX_EARLYCON_MEM > >> > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); > >> > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); > >> > + base += paddr & ~PAGE_MASK; > >> > +#else > >> > + base = ioremap_nocache(paddr, size); > >> > +#endif > >> > >> Just curious why not set_fixmap_io (and plain ioremap)? > > > > Good point. Note that ioremap_nocache() is the same as ioremap() > > on *all* architectures. > > I investigated this before adding this to arm64. set_fixmap_io and > set_fixmap_nocache are not the same settings on x86. Whether the > mapping type really matters on x86 or not, I don't know. So I added > the nocache variant to arm64 to avoid a change to x86. My best guess is that it's an x86 bug. ioremap() always uses an uncached mapping on x86, so it's strange to see early_ioremap() and set_fixmap_io() use a cached mapping. It probably doesn't matter as long as the mtrr is set up to treat all MMIO registers as non-cacheable, but I think there should not be a difference. Arnd
On Mon, Mar 24, 2014 at 10:42 AM, Arnd Bergmann <arnd@arndb.de> wrote: > On Monday 24 March 2014 08:36:46 Rob Herring wrote: >> On Mon, Mar 24, 2014 at 6:29 AM, Arnd Bergmann <arnd@arndb.de> wrote: >> > On Monday 24 March 2014 11:22:03 Catalin Marinas wrote: >> >> On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: >> >> > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c >> >> > new file mode 100644 >> >> > index 0000000..241757a >> >> > --- /dev/null >> >> > +++ b/drivers/tty/serial/earlycon.c >> >> [...] >> >> > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) >> >> > +{ >> >> > + void __iomem *base; >> >> > +#ifdef CONFIG_FIX_EARLYCON_MEM >> >> > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); >> >> > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); >> >> > + base += paddr & ~PAGE_MASK; >> >> > +#else >> >> > + base = ioremap_nocache(paddr, size); >> >> > +#endif >> >> >> >> Just curious why not set_fixmap_io (and plain ioremap)? >> > >> > Good point. Note that ioremap_nocache() is the same as ioremap() >> > on *all* architectures. Did you mean "not the same"? Why are there 2 flavors if they are always the same. >> I investigated this before adding this to arm64. set_fixmap_io and >> set_fixmap_nocache are not the same settings on x86. Whether the >> mapping type really matters on x86 or not, I don't know. So I added >> the nocache variant to arm64 to avoid a change to x86. > > My best guess is that it's an x86 bug. ioremap() always uses an uncached > mapping on x86, so it's strange to see early_ioremap() and set_fixmap_io() > use a cached mapping. It probably doesn't matter as long as the mtrr is > set up to treat all MMIO registers as non-cacheable, but I think there > should not be a difference. At some point it was believed to be needed on the 8250 driver. Perhaps Alan can comment since the commit message tells us nothing: commit 6f441fe99814f64315b8c11890744230b990c460 Author: Alan Cox <alan@lxorguk.ukuu.org.uk> Date: Thu May 1 04:34:59 2008 -0700 8250: switch 8250 drivers to use _nocache ioremaps Signed-off-by: Alan Cox <alan@redhat.com> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Rob
> At some point it was believed to be needed on the 8250 driver. Perhaps > Alan can comment since the commit message tells us nothing: Don't remember sorry - I guess at the time someone had ioremap on their plaform acting as cached. It was a long time ago 8)
On Thursday 17 April 2014, Rob Herring wrote: > On Mon, Mar 24, 2014 at 10:42 AM, Arnd Bergmann <arnd@arndb.de> wrote: > > On Monday 24 March 2014 08:36:46 Rob Herring wrote: > >> On Mon, Mar 24, 2014 at 6:29 AM, Arnd Bergmann <arnd@arndb.de> wrote: > >> > On Monday 24 March 2014 11:22:03 Catalin Marinas wrote: > >> >> On Fri, Mar 21, 2014 at 09:08:44PM +0000, Rob Herring wrote: > >> >> > diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c > >> >> > new file mode 100644 > >> >> > index 0000000..241757a > >> >> > --- /dev/null > >> >> > +++ b/drivers/tty/serial/earlycon.c > >> >> [...] > >> >> > +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) > >> >> > +{ > >> >> > + void __iomem *base; > >> >> > +#ifdef CONFIG_FIX_EARLYCON_MEM > >> >> > + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); > >> >> > + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); > >> >> > + base += paddr & ~PAGE_MASK; > >> >> > +#else > >> >> > + base = ioremap_nocache(paddr, size); > >> >> > +#endif > >> >> > >> >> Just curious why not set_fixmap_io (and plain ioremap)? > >> > > >> > Good point. Note that ioremap_nocache() is the same as ioremap() > >> > on *all* architectures. > > Did you mean "not the same"? No > Why are there 2 flavors if they are > always the same. I assume it's just a mistake someone made a long time ago and nobody ever changed it because they didn't want to break obscure code. x86 seems to be a bit odd here, because is has both page-table and MTRR method of setting up whether an access can be cached or not. This may have led to the introduction of the _nocache variant, when the regular ioremap was already uncached on all other architectures but cached on x86. Arnd
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2e6d8dd..1685189 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -7,6 +7,13 @@ if TTY menu "Serial drivers" depends on HAS_IOMEM +config SERIAL_EARLYCON + bool "Early console for serial ports" + help + Support for early consoles with the earlycon parameter. This enables + the console before standard serial driver is probed. The console is + enabled when early_param is processed. + source "drivers/tty/serial/8250/Kconfig" comment "Non-8250 serial port support" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 3680854..8af1415 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -5,6 +5,8 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o +obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o + # These Sparc drivers have to appear before others such as 8250 # which share ttySx minor node space. Otherwise console device # names change and other unplesantries. diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c new file mode 100644 index 0000000..241757a --- /dev/null +++ b/drivers/tty/serial/earlycon.c @@ -0,0 +1,148 @@ +/* + * 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. + */ +#include <linux/console.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/serial_core.h> + +#include <asm/fixmap.h> +#include <asm/serial.h> + +static struct console early_con = { + .name = "earlycon", + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +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_nocache(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + base += paddr & ~PAGE_MASK; +#else + base = ioremap_nocache(paddr, size); +#endif + if (!base) + pr_err("%s: Couldn't map 0x%llx\n", __func__, + (unsigned long long)paddr); + + return base; +} + +static int __init parse_options(struct earlycon_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length, ret; + unsigned long addr; + + if (!options) + return -ENODEV; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + options += mmio ? 5 : 7; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + if (mmio32) + port->regshift = 2; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + options += 3; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->iobase = addr; + mmio = 0; + } else if (!strncmp(options, "0x", 2)) { + port->iotype = UPIO_MEM; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + } else { + return -EINVAL; + } + + if (port->mapbase) + port->membase = earlycon_map(port->mapbase, 64); + + port->uartclk = BASE_BAUD * 16; + + options = strchr(options, ','); + if (options) { + options++; + ret = kstrtouint(options, 0, &device->baud); + if (ret) + return ret; + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); + strlcpy(device->options, options, length); + } + + if (mmio || mmio32) + pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (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; +} + +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + size_t len; + + if (!buf || !match || !setup) + return -EINVAL; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + if (buf[len] && (buf[len] != ',')) + return 0; + + buf += len + 1; + + err = parse_options(&early_console_dev, buf); + /* On parsing error, pass the options buf to the setup function */ + if (!err) + buf = NULL; + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, buf); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + register_console(early_console_dev.con); + return 0; +} diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index f729be9..7a15b5b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -285,6 +285,22 @@ static inline int uart_poll_timeout(struct uart_port *port) /* * Console helpers. */ +struct earlycon_device { + struct console *con; + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; +}; +int setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)); + +#define EARLYCON_DECLARE(name, func) \ +static int __init name ## _setup_earlycon(char *buf) \ +{ \ + return setup_earlycon(buf, __stringify(name), func); \ +} \ +early_param("earlycon", name ## _setup_earlycon); + struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); void uart_parse_options(char *options, int *baud, int *parity, int *bits,