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.zh...@intel.com>
Reviewed-by: Feng Tang <feng.t...@intel.com>
---
 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.t...@intel.com>.
+ * 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-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to