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 <bin.gao@xxxxxxxxx> --- Changes in v5: - updated Documentation/kernel-parameters.txt. - moved earlyprintk= to patch 2/2 (requires x86 people's review). - rolled back to simple_strto* APIs. - seperate pci/pci32 format description. - minor error and debug message changes. - if/else statements in uart_parse_earlycon() were refactored to avoid logic steering locals. Changes in v4: - moved PCI_EARLY definition from arch/x86/Kconfig to drivers/pci/Kconfig - made earlycon= for all archs but earlyprintk= only for x86 by changing "#ifdef #else #endif" to "#if #endif". Changes in v3: - introduced CONFIG_EARLY_PCI to protect pci codes in serial_core.c. - added earlyprintk= as alia to earlycon= to keep x86 compatibility. changes in v2: - added the second patch (2/2) to remove existed pci early console support from arch/x86/kernel/early_printk.c. Documentation/kernel-parameters.txt | 15 +++++ arch/x86/Kconfig | 1 + drivers/pci/Kconfig | 11 ++++ drivers/tty/serial/serial_core.c | 106 +++++++++++++++++++++++++++++++++++- 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 61ab162..598606e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -969,6 +969,16 @@ bytes respectively. Such letter suffixes can also be entirely omitted. same format described for "console=ttyS<n>"; if unspecified, the h/w is not initialized. + uart8250,pci,<bus:dev.func>[,options] + uart8250,pci32,<bus:dev.func>[,options] + Start an early, polled-mode console on the 8250/16550 + UART at the specified PCI device (bus:dev.func). + The io or memory mmaped register width is either 8-bit + (pci) or 32-bit (pci32). + 'options' are specified in the same format described + for "console=ttyS<n>"; if unspecified, the h/w is not + initialized. + pl011,<addr> Start an early, polled-mode console on a pl011 serial port at the specified address. The pl011 serial port @@ -1009,6 +1019,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. earlyprintk=serial[,0x...[,baudrate]] earlyprintk=ttySn[,baudrate] earlyprintk=dbgp[debugController#] + earlyprintk=uart8250,pci,<bus:dev.fun>[,options] + earlyprintk=uart8250,pci32,<bus:dev.func>[,options] earlyprintk is useful when the kernel crashes before the normal console is initialized. It is not enabled by @@ -1037,6 +1049,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. The xen output can only be used by Xen PV guests. + The uart8250,pci and uart8250,pci32 output share the + same definition that is in earlycon= section. + edac_report= [HW,EDAC] Control how to report EDAC event Format: {"on" | "off" | "force"} on: enable EDAC to report H/W event. May be overridden 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/serial_core.c b/drivers/tty/serial/serial_core.c index 0b7bb12..5b21999 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -34,10 +34,15 @@ #include <linux/serial_core.h> #include <linux/delay.h> #include <linux/mutex.h> +#include <linux/pci_regs.h> #include <asm/irq.h> #include <asm/uaccess.h> +#ifdef CONFIG_PCI_EARLY +#include <asm/pci-direct.h> +#endif + /* * This is used to lock changes in serial line configuration. */ @@ -1808,6 +1813,85 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) return ports + idx; } +#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; + u64 bar0; + u16 cmd; + int pci_io = 0; + + if (!early_pci_allowed()) { + pr_err("earlycon: early pci not allowed.\n"); + return -EINVAL; + } + + /* We come here with options=B:D.F[,options] */ + if (*options == 0) + goto failed; + + bus = (u8)simple_strtoul(options, &options, 10); + if (*options != ':') + goto failed; + + dev = (u8)simple_strtoul(options + 1, &options, 10); + if (*options != '.') + goto failed; + + func = (u8)simple_strtoul(options + 1, NULL, 10); + + 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_debug("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 supported (requires CONFIG_PCI_EARLY=y)\n"); + return -EINVAL; +} +#endif + /** * uart_parse_earlycon - Parse earlycon options * @p: ptr to 2nd field (ie., just beyond '<name>,') @@ -1817,7 +1901,9 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * * Decodes earlycon kernel command line parameters of the form * earlycon=<name>,io|mmio|mmio32,<addr>,<options> + * earlycon=<name>,pci|pci32,<bus:dev.func>,<options> * console=<name>,io|mmio|mmio32,<addr>,<options> + * For pci|pci32, bus, dev and func are in decimal. * * The optional form * earlycon=<name>,0x<addr>,<options> @@ -1829,22 +1915,40 @@ 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 ret; + unsigned long phys; + if (strncmp(p, "mmio,", 5) == 0) { *iotype = UPIO_MEM; p += 5; + *addr = simple_strtoul(p, NULL, 0); } else if (strncmp(p, "mmio32,", 7) == 0) { *iotype = UPIO_MEM32; p += 7; + *addr = simple_strtoul(p, NULL, 0); + } else if (strncmp(p, "pci,", 4) == 0) { + p += 4; + ret = parse_pci_options(p, &phys); + if (ret < 0) + return ret; + *iotype = (ret > 0) ? UPIO_PORT : UPIO_MEM; + } else if (strncmp(p, "pci32,", 6) == 0) { + p += 6; + ret = parse_pci_options(p, &phys); + if (ret < 0) + return ret; + *iotype = (ret > 0) ? UPIO_PORT : UPIO_MEM32; } else if (strncmp(p, "io,", 3) == 0) { *iotype = UPIO_PORT; p += 3; + *addr = simple_strtoul(p, NULL, 0); } else if (strncmp(p, "0x", 2) == 0) { *iotype = UPIO_MEM; + *addr = simple_strtoul(p, NULL, 0); } else { return -EINVAL; } - *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-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html