DesignWare SPI UART is used as one of the debug ports on Low Power Intel Architecture (LPIA) platforms. This patch is introduced to support this debugging console reported by ACPI DBGP/DBG2. The original MID SPI early console stuff is also refined to co-exist with the new ACPI usage model. To use this facility on LPIA platforms, you need to enable the following kernel configurations: CONFIG_EARLY_PRINTK_ACPI=y CONFIG_EARLY_PRINTK_INTEL_MID_SPI=y Then you need to append the following kernel parameter to the kernel command line in the boot loader configuration file: earlyprintk=acpi Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx> Reviewed-by: Feng Tang <feng.tang@xxxxxxxxx> --- Documentation/kernel-parameters.txt | 1 + arch/x86/Kconfig.debug | 23 +++ arch/x86/include/asm/mrst.h | 2 +- arch/x86/kernel/early_printk.c | 12 +- arch/x86/platform/mrst/early_printk_mrst.c | 186 +---------------------- drivers/platform/x86/Makefile | 2 + drivers/platform/x86/early/Makefile | 5 + drivers/platform/x86/early/intel_mid_spi.c | 220 ++++++++++++++++++++++++++++ include/acpi/actbl2.h | 1 + include/linux/intel_mid_early.h | 12 ++ 10 files changed, 283 insertions(+), 181 deletions(-) create mode 100644 drivers/platform/x86/early/Makefile create mode 100644 drivers/platform/x86/early/intel_mid_spi.c create mode 100644 include/linux/intel_mid_early.h diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f656765..003ffd9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -762,6 +762,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. earlyprintk=vga earlyprintk=serial[,ttySn[,baudrate]] earlyprintk=ttySn[,baudrate] + earlyprintk=mrst earlyprintk=dbgp[debugController#] earlyprintk=acpi[debugController#] diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 5778082..4a26e2d 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -43,9 +43,32 @@ config EARLY_PRINTK with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. +config EARLY_PRINTK_INTEL_MID_SPI + bool "Early printk for Intel MID SPI UART port" + depends on EARLY_PRINTK + ---help--- + Write kernel log output directly into the MID SPI UART debug port. + + Intel MID platforms are using DesignWare SPI UART as its debug + console. This option does not introduce actual early console into + the kernel binary, but is required by a real early console + implementation (EARLY_PRINTK_INTEL_MID or EARLY_PRINTK_ACPI). + You should normally N here unless you need to do kernel booting + development. + config EARLY_PRINTK_INTEL_MID bool "Early printk for Intel MID platform support" depends on EARLY_PRINTK && X86_INTEL_MID + select EARLY_PRINTK_INTEL_MID_SPI + ---help--- + Write kernel log output directly into the MID SPI UART debug port. + + Intel MID platforms are always equipped with SPI debug ports and + USB OTG debug ports. To enable these debugging facilities, you + need to pass "earlyprintk=mrst" parameter to the kernel through + boot loaders. Please see "Documentation/kernel-parameter.txt" for + details. You should normally N here unless you need to do kernel + booting development. config EARLY_PRINTK_DBGP bool "Early printk via EHCI debug port" diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index fc18bf3..8ab0655 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -12,6 +12,7 @@ #define _ASM_X86_MRST_H #include <linux/sfi.h> +#include <linux/intel_mid_early.h> extern int pci_mrst_init(void); extern int __init sfi_parse_mrtc(struct sfi_table_header *table); @@ -63,7 +64,6 @@ extern enum mrst_timer_options mrst_timer_options; #define SFI_MTMR_MAX_NUM 8 #define SFI_MRTC_MAX 8 -extern struct console early_mrst_console; extern void mrst_early_console_init(void); extern struct console early_hsu_console; diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index bf5b596..9b5ee96 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -205,6 +205,16 @@ static inline void early_console_register(struct console *con, int keep_early) int __init __acpi_early_console_start(struct acpi_debug_port *info) { +#ifdef CONFIG_EARLY_PRINTK_INTEL_MID_SPI + if (info->port_type == ACPI_DBG2_SERIAL_PORT + && info->port_subtype == ACPI_DBG2_INTEL_MID_SPI + && info->register_count > 0) { + mid_spi_early_console_init((u32)(info->registers[0].address)); + early_console_register(&mid_spi_early_console, + acpi_early_console_keep(info)); + } +#endif + return 0; } #endif @@ -256,7 +266,7 @@ static int __init setup_early_printk(char *buf) #ifdef CONFIG_EARLY_PRINTK_INTEL_MID if (!strncmp(buf, "mrst", 4)) { mrst_early_console_init(); - early_console_register(&early_mrst_console, keep); + early_console_register(&mid_spi_early_console, keep); } if (!strncmp(buf, "hsu", 3)) { diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/mrst/early_printk_mrst.c index 028454f..7afbf18 100644 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ b/arch/x86/platform/mrst/early_printk_mrst.c @@ -29,82 +29,8 @@ #include <asm/pgtable.h> #include <asm/mrst.h> -#define MRST_SPI_TIMEOUT 0x200000 #define MRST_REGBASE_SPI0 0xff128000 #define MRST_REGBASE_SPI1 0xff128400 -#define MRST_CLK_SPI0_REG 0xff11d86c - -/* Bit fields in CTRLR0 */ -#define SPI_DFS_OFFSET 0 - -#define SPI_FRF_OFFSET 4 -#define SPI_FRF_SPI 0x0 -#define SPI_FRF_SSP 0x1 -#define SPI_FRF_MICROWIRE 0x2 -#define SPI_FRF_RESV 0x3 - -#define SPI_MODE_OFFSET 6 -#define SPI_SCPH_OFFSET 6 -#define SPI_SCOL_OFFSET 7 -#define SPI_TMOD_OFFSET 8 -#define SPI_TMOD_TR 0x0 /* xmit & recv */ -#define SPI_TMOD_TO 0x1 /* xmit only */ -#define SPI_TMOD_RO 0x2 /* recv only */ -#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ - -#define SPI_SLVOE_OFFSET 10 -#define SPI_SRL_OFFSET 11 -#define SPI_CFS_OFFSET 12 - -/* Bit fields in SR, 7 bits */ -#define SR_MASK 0x7f /* cover 7 bits */ -#define SR_BUSY (1 << 0) -#define SR_TF_NOT_FULL (1 << 1) -#define SR_TF_EMPT (1 << 2) -#define SR_RF_NOT_EMPT (1 << 3) -#define SR_RF_FULL (1 << 4) -#define SR_TX_ERR (1 << 5) -#define SR_DCOL (1 << 6) - -struct dw_spi_reg { - u32 ctrl0; - u32 ctrl1; - u32 ssienr; - u32 mwcr; - u32 ser; - u32 baudr; - u32 txfltr; - u32 rxfltr; - u32 txflr; - u32 rxflr; - u32 sr; - u32 imr; - u32 isr; - u32 risr; - u32 txoicr; - u32 rxoicr; - u32 rxuicr; - u32 msticr; - u32 icr; - u32 dmacr; - u32 dmatdlr; - u32 dmardlr; - u32 idr; - u32 version; - - /* Currently operates as 32 bits, though only the low 16 bits matter */ - u32 dr; -} __packed; - -#define dw_readl(dw, name) __raw_readl(&(dw)->name) -#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) - -/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ -static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; - -static u32 *pclk_spi0; -/* Always contains an accessible address, start with 0 */ -static struct dw_spi_reg *pspi; static struct kmsg_dumper dw_dumper; static int dumper_registered; @@ -116,77 +42,21 @@ static void dw_kmsg_dump(struct kmsg_dumper *dumper, size_t len; /* When run to this, we'd better re-init the HW */ - mrst_early_console_init(); + mid_spi_early_console_reset(); while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) - early_mrst_console.write(&early_mrst_console, line, len); -} - -/* Set the ratio rate to 115200, 8n1, IRQ disabled */ -static void max3110_write_config(void) -{ - u16 config; - - config = 0xc001; - dw_writel(pspi, dr, config); -} - -/* Translate char to a eligible word and send to max3110 */ -static void max3110_write_data(char c) -{ - u16 data; - - data = 0x8000 | c; - dw_writel(pspi, dr, data); + mid_spi_early_console.write(&mid_spi_early_console, line, len); } void mrst_early_console_init(void) { - u32 ctrlr0 = 0; - u32 spi0_cdiv; - u32 freq; /* Freqency info only need be searched once */ - - /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ - pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - MRST_CLK_SPI0_REG); - spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; - freq = 100000000 / (spi0_cdiv + 1); + /* Default use SPI0 register for mrst */ + unsigned long spi_paddr = MRST_REGBASE_SPI0; + /* Detect Penwell and use SPI1 */ if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) - mrst_spi_paddr = MRST_REGBASE_SPI1; - - pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, - mrst_spi_paddr); - - /* Disable SPI controller */ - dw_writel(pspi, ssienr, 0); - - /* Set control param, 8 bits, transmit only mode */ - ctrlr0 = dw_readl(pspi, ctrl0); - - ctrlr0 &= 0xfcc0; - ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) - | (SPI_TMOD_TO << SPI_TMOD_OFFSET); - dw_writel(pspi, ctrl0, ctrlr0); - - /* - * Change the spi0 clk to comply with 115200 bps, use 100000 to - * calculate the clk dividor to make the clock a little slower - * than real baud rate. - */ - dw_writel(pspi, baudr, freq/100000); - - /* Disable all INT for early phase */ - dw_writel(pspi, imr, 0x0); - - /* Set the cs to spi-uart */ - dw_writel(pspi, ser, 0x2); - - /* Enable the HW, the last step for HW init */ - dw_writel(pspi, ssienr, 0x1); - - /* Set the default configuration */ - max3110_write_config(); + spi_paddr = MRST_REGBASE_SPI1; + mid_spi_early_console_init(spi_paddr); /* Register the kmsg dumper */ if (!dumper_registered) { @@ -196,48 +66,6 @@ void mrst_early_console_init(void) } } -/* Slave select should be called in the read/write function */ -static void early_mrst_spi_putc(char c) -{ - unsigned int timeout; - u32 sr; - - timeout = MRST_SPI_TIMEOUT; - /* Early putc needs to make sure the TX FIFO is not full */ - while (--timeout) { - sr = dw_readl(pspi, sr); - if (!(sr & SR_TF_NOT_FULL)) - cpu_relax(); - else - break; - } - - if (!timeout) - pr_warning("MRST earlycon: timed out\n"); - else - max3110_write_data(c); -} - -/* Early SPI only uses polling mode */ -static void early_mrst_spi_write(struct console *con, const char *str, unsigned n) -{ - int i; - - for (i = 0; i < n && *str; i++) { - if (*str == '\n') - early_mrst_spi_putc('\r'); - early_mrst_spi_putc(*str); - str++; - } -} - -struct console early_mrst_console = { - .name = "earlymrst", - .write = early_mrst_spi_write, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - /* * Following is the early console based on Medfield HSU (High * Speed UART) device. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index bf7e4f9..6e4ff46 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -50,3 +50,5 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o + +obj-$(CONFIG_EARLY_PRINTK) += early/ diff --git a/drivers/platform/x86/early/Makefile b/drivers/platform/x86/early/Makefile new file mode 100644 index 0000000..5d9408c --- /dev/null +++ b/drivers/platform/x86/early/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for linux/drivers/platform/x86/early +# x86 Platform-Specific Early Printk Support +# +obj-$(CONFIG_EARLY_PRINTK_INTEL_MID_SPI) += intel_mid_spi.o diff --git a/drivers/platform/x86/early/intel_mid_spi.c b/drivers/platform/x86/early/intel_mid_spi.c new file mode 100644 index 0000000..fa5a1a2 --- /dev/null +++ b/drivers/platform/x86/early/intel_mid_spi.c @@ -0,0 +1,220 @@ +/* + * intel_mid_spi.c - SPI UART early consoles for Intel MID platforms + * + * Copyright (c) 2008-2012, Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +/* + * This file is originally implemented by Feng Tang <feng.tang@xxxxxxxxx>. + * It is moved here to co-exist with CONFIG_EARLY_PRINTK_ACPI. + * Any questions you should contact the original author. + */ + +#include <linux/serial_reg.h> +#include <linux/serial_mfd.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/intel_mid_early.h> +#include <asm/fixmap.h> + +#define DW_SPI_TIMEOUT 0x200000 +#define DW_SPI0_CLK_REG 0xff11d86c + +/* Bit fields in CTRLR0 */ +#define SPI_DFS_OFFSET 0 + +#define SPI_FRF_OFFSET 4 +#define SPI_FRF_SPI 0x0 +#define SPI_FRF_SSP 0x1 +#define SPI_FRF_MICROWIRE 0x2 +#define SPI_FRF_RESV 0x3 + +#define SPI_MODE_OFFSET 6 +#define SPI_SCPH_OFFSET 6 +#define SPI_SCOL_OFFSET 7 +#define SPI_TMOD_OFFSET 8 +#define SPI_TMOD_TR 0x0 /* xmit & recv */ +#define SPI_TMOD_TO 0x1 /* xmit only */ +#define SPI_TMOD_RO 0x2 /* recv only */ +#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ + +#define SPI_SLVOE_OFFSET 10 +#define SPI_SRL_OFFSET 11 +#define SPI_CFS_OFFSET 12 + +/* Bit fields in SR, 7 bits */ +#define SR_MASK 0x7f /* cover 7 bits */ +#define SR_BUSY (1 << 0) +#define SR_TF_NOT_FULL (1 << 1) +#define SR_TF_EMPT (1 << 2) +#define SR_RF_NOT_EMPT (1 << 3) +#define SR_RF_FULL (1 << 4) +#define SR_TX_ERR (1 << 5) +#define SR_DCOL (1 << 6) + +struct dw_spi_reg { + u32 ctrl0; + u32 ctrl1; + u32 ssienr; + u32 mwcr; + u32 ser; + u32 baudr; + u32 txfltr; + u32 rxfltr; + u32 txflr; + u32 rxflr; + u32 sr; + u32 imr; + u32 isr; + u32 risr; + u32 txoicr; + u32 rxoicr; + u32 rxuicr; + u32 msticr; + u32 icr; + u32 dmacr; + u32 dmatdlr; + u32 dmardlr; + u32 idr; + u32 version; + + /* Currently operates as 32 bits, though only the low 16 bits matter */ + u32 dr; +} __packed; + +#define dw_readl(dw, name) __raw_readl(&(dw)->name) +#define dw_writel(dw, name, val) __raw_writel((val), &(dw)->name) + +static u32 *pclk_spi0; +/* Always contains an accessible address, start with 0 */ +static struct dw_spi_reg *pspi; +static int dw_spi_initialized; +static u32 dw_spi_regbase; + +/* Translate char to a eligible word and send to max3110 */ +static void max3110_write_data(char c) +{ + u16 data; + + data = 0x8000 | c; + dw_writel(pspi, dr, data); +} + +/* Set the ratio rate to 115200, 8n1, IRQ disabled */ +static void max3110_write_config(void) +{ + u16 config; + + config = 0xc001; + dw_writel(pspi, dr, config); +} + +int mid_spi_early_console_reset(void) +{ + u32 ctrlr0 = 0; + u32 spi0_cdiv; + u32 freq; /* Freqency info only need be searched once */ + + /* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ + pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, + DW_SPI0_CLK_REG); + spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; + freq = 100000000 / (spi0_cdiv + 1); + + pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, + dw_spi_regbase); + + /* Disable SPI controller */ + dw_writel(pspi, ssienr, 0); + + /* Set control param, 8 bits, transmit only mode */ + ctrlr0 = dw_readl(pspi, ctrl0); + + ctrlr0 &= 0xfcc0; + ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) + | (SPI_TMOD_TO << SPI_TMOD_OFFSET); + dw_writel(pspi, ctrl0, ctrlr0); + + /* + * Change the spi0 clk to comply with 115200 bps, use 100000 to + * calculate the clk dividor to make the clock a little slower + * than real baud rate. + */ + dw_writel(pspi, baudr, freq/100000); + + /* Disable all INT for early phase */ + dw_writel(pspi, imr, 0x0); + + /* Set the cs to spi-uart */ + dw_writel(pspi, ser, 0x2); + + /* Enable the HW, the last step for HW init */ + dw_writel(pspi, ssienr, 0x1); + + /* Set the default configuration */ + max3110_write_config(); + + return 0; +} +EXPORT_SYMBOL(mid_spi_early_console_reset); + +/* Slave select should be called in the read/write function */ +static void dw_early_spi_putc(char c) +{ + unsigned int timeout; + u32 sr; + + timeout = DW_SPI_TIMEOUT; + /* Early putc needs to make sure the TX FIFO is not full */ + while (--timeout) { + sr = dw_readl(pspi, sr); + if (!(sr & SR_TF_NOT_FULL)) + cpu_relax(); + else + break; + } + + if (!timeout) + pr_warn("mid-spi-early: timed out\n"); + else + max3110_write_data(c); +} + +/* Early SPI only uses polling mode */ +static void dw_early_spi_write(struct console *con, + const char *str, unsigned n) +{ + int i; + + for (i = 0; i < n && *str; i++) { + if (*str == '\n') + dw_early_spi_putc('\r'); + dw_early_spi_putc(*str); + str++; + } +} + +struct console mid_spi_early_console = { + .name = "earlymidspi", + .write = dw_early_spi_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +void __init mid_spi_early_console_init(u32 spi_paddr) +{ + if (dw_spi_initialized) + return; + dw_spi_regbase = spi_paddr; + mid_spi_early_console_reset(); + dw_spi_initialized = 1; +} + diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 1b2b356..ffa9d62 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -341,6 +341,7 @@ struct acpi_dbg2_device { #define ACPI_DBG2_16550_COMPATIBLE 0x0000 #define ACPI_DBG2_16550_SUBSET 0x0001 +#define ACPI_DBG2_INTEL_MID_SPI 0x0002 #define ACPI_DBG2_1394_STANDARD 0x0000 diff --git a/include/linux/intel_mid_early.h b/include/linux/intel_mid_early.h new file mode 100644 index 0000000..edba232 --- /dev/null +++ b/include/linux/intel_mid_early.h @@ -0,0 +1,12 @@ +#ifndef LINUX_INTEL_MID_EARLY_H +#define LINUX_INTEL_MID_EARLY_H + +#include <linux/console.h> + +extern struct console mid_spi_early_console; + +extern void mid_spi_early_console_init(u32 spi_paddr); +extern int mid_spi_early_console_reset(void); + +#endif /* LINUX_INTEL_MID_EARLY_H */ + -- 1.7.10 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html