Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758224AbbEVPzq (ORCPT ); Fri, 22 May 2015 11:55:46 -0400 Received: from mga09.intel.com ([134.134.136.24]:27773 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757129AbbEVPzm (ORCPT ); Fri, 22 May 2015 11:55:42 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.13,476,1427785200"; d="scan'208";a="730301565" Date: Fri, 22 May 2015 09:06:59 -0700 From: Bin Gao To: Greg Kroah-Hartman Cc: Jiri Slaby , One Thousand Gnomes , linux-kernel@vger.kernel.org Subject: [PATCH v4 1/2] serial_core: add pci uart early console support Message-ID: <20150522160659.GA108447@worksta> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8004 Lines: 274 On some Intel Atom SoCs, the legacy IO port UART(0x3F8) is not available. Instead, a 8250 compatible PCI uart can be used as early console. This patch adds pci support to the 8250 early console driver uart8250. For example, to enable pci uart(00:21.3) as early console on these platforms, append the following line to the kernel command line (assume baud rate is 115200): earlyprintk=uart8250,pci32,0:24.2,115200n8 Signed-off-by: Bin Gao --- Changes in v4: - moved PCI_EARLY definition from arch/x86/Kconfig to drivers/pci/Kconfig - added 'earlyprintk' for x86 as alias to the early param 'earlycon' arch/x86/Kconfig | 1 + drivers/pci/Kconfig | 11 +++ drivers/tty/serial/earlycon.c | 9 +++ drivers/tty/serial/serial_core.c | 145 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 164 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 226d569..bdedd61 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -143,6 +143,7 @@ config X86 select ACPI_LEGACY_TABLES_LOOKUP if ACPI select X86_FEATURE_NAMES if PROC_FS select SRCU + select PCI_EARLY if PCI config INSTRUCTION_DECODER def_bool y diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 7a8f1c5..4f0f055 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -114,4 +114,15 @@ config PCI_LABEL def_bool y if (DMI || ACPI) select NLS +config PCI_EARLY + bool "Early PCI access" + depends on PCI + default n + help + This option indicates that a group of APIs are available (in + asm/pci-direct.h) so the kernel can access pci config registers + before the PCI subsystem is initialized. Any arch that supports + early pci APIs should enable this option which is required by + arch independent codes, e.g. uart8250 pci early console driver. + source "drivers/pci/host/Kconfig" diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 6dc471e..63ae60e 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -193,6 +193,15 @@ static int __init param_setup_earlycon(char *buf) } early_param("earlycon", param_setup_earlycon); +/* x86 uses "earlyprintk=xxx", so we keep the compatibility here */ +#ifdef CONFIG_X86 +static int __init param_setup_earlycon_x86(char *buf) +{ + return param_setup_earlycon(buf); +} +early_param("earlyprintk", param_setup_earlycon_x86); +#endif + int __init of_setup_earlycon(unsigned long addr, int (*setup)(struct earlycon_device *, const char *)) { diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0b7bb12..19ca2a0 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -34,10 +34,15 @@ #include #include #include +#include #include #include +#ifdef CONFIG_PCI_EARLY +#include +#endif + /* * This is used to lock changes in serial line configuration. */ @@ -1808,6 +1813,110 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) return ports + idx; } +static int parse_bdf(char *options, char **endp, char delimiter, u8 *val) +{ + char str[4]; /* max 3 chars, plus a NULL terminator */ + char *p = options; + int i = 0; + + while (*p) { + if (i >= 4) + return -EINVAL; + + if (*p == delimiter) { + str[i++] = 0; + if (endp) + *endp = p + 1; + return kstrtou8(str, 10, val); /* decimal, no hex */ + } + + str[i++] = *p++; + } + + return -EINVAL; +} + +#ifdef CONFIG_PCI_EARLY +/* + * The whole pci option from the command line is: pci[32],B:D.F[,options] + * Examples: + * pci,0:21.3,115200n8 + * pci32,0:21.3 + * Here pci32 means 8250 UART registers are 32-bit width(regshift = 2). + * pci means 8250 UART registers are 8-bit width(regshift = 0). + * B,D and F are bus, device and function, in decimal(not hex). + * The additional options(115200n8) would be parsed by the earlycon framework. + * + * @options: the pci options + * @phys: the pointer to return pci mem or io address + * return: <0: error + * 0: pci mem + * 1: pci io + */ +static int parse_pci_options(char *options, unsigned long *phys) +{ + u8 bus, dev, func; + char *endp; + u64 bar0; + u16 cmd; + int pci_io = 0; + + if (!early_pci_allowed()) { + pr_err("earlycon pci not available(early pci not allowed)\n"); + return -EINVAL; + } + + /* We come here with options=B:D.F[,options] */ + if (parse_bdf(options, &endp, ':', &bus)) + goto failed; + + if (parse_bdf(endp, &endp, '.', &dev)) + goto failed; + + if (parse_bdf(endp, &endp, ',', &func)) + goto failed; + + /* + * On these platforms class code in pci config is broken, + * so skip checking it. + */ + + bar0 = read_pci_config(bus, dev, func, PCI_BASE_ADDRESS_0); + + /* The BAR is IO or Memory? */ + if ((bar0 & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) + pci_io = 1; + + if ((bar0 & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == + PCI_BASE_ADDRESS_MEM_TYPE_64) + bar0 |= (u64)read_pci_config(bus, dev, func, + PCI_BASE_ADDRESS_0 + 4) << 32; + + *phys = bar0 & (pci_io ? PCI_BASE_ADDRESS_IO_MASK : + PCI_BASE_ADDRESS_MEM_MASK); + + /* Enable address decoding */ + cmd = read_pci_config_16(bus, dev, func, PCI_COMMAND); + write_pci_config_16(bus, dev, func, PCI_COMMAND, + cmd | (pci_io ? PCI_COMMAND_IO : PCI_COMMAND_MEMORY)); + + pr_info("Use 8250 uart at PCI 0000:%02u:%02u.%01u as early console\n", + bus, dev, func); + return pci_io; + +failed: + pr_err("Invalid earlycon pci parameters\n"); + return -EINVAL; +} + +#else +static int parse_pci_options(char *options, unsigned long *phys) +{ + pr_err("earlycon pci not available(need CONFIG_PCI_EARLY)\n"); + return -EINVAL; +} +#endif + /** * uart_parse_earlycon - Parse earlycon options * @p: ptr to 2nd field (ie., just beyond ',') @@ -1816,8 +1925,9 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * @options: ptr for field; NULL if not present (out) * * Decodes earlycon kernel command line parameters of the form - * earlycon=,io|mmio|mmio32,, + * earlycon=,io|mmio|mmio32|pci|pci32,, * console=,io|mmio|mmio32,, + * For pci/pci32, the format is B:D.F, e.g. 0:24.2 * * The optional form * earlycon=,0x, @@ -1829,12 +1939,23 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, char **options) { + int pci = 0, ret; + unsigned long phys; + if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; p += 5; } else if (strncmp(p, "mmio32,", 7) == 0) { *iotype = UPIO_MEM32; p += 7; + } else if (strncmp(p, "pci,", 4) == 0) { + pci = 1; + p += 4; + ret = parse_pci_options(p, &phys); + } else if (strncmp(p, "pci32,", 6) == 0) { + pci = 2; + p += 6; + ret = parse_pci_options(p, &phys); } else if (strncmp(p, "io,", 3) == 0) { *iotype = UPIO_PORT; p += 3; @@ -1844,7 +1965,27 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, return -EINVAL; } - *addr = simple_strtoul(p, NULL, 0); + if (pci) { + if (ret < 0) /* error */ + return ret; + + /* + * Once PCI mem/io is read from PCI BAR, we can reuse + * mmio/mmio32/io type to minimize code change. + */ + if (ret > 0) /* PCI io */ + *iotype = UPIO_PORT; + else { /* ret = 0: PCI mem */ + if (pci == 2) + *iotype = UPIO_MEM32; + else + *iotype = UPIO_MEM; + } + + *addr = phys; + } else + *addr = simple_strtoul(p, NULL, 0); + p = strchr(p, ','); if (p) p++; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/