From: Domenico Andreoli <[email-masked]>

This driver is also taken from the existing Samsung's uncompress.h
header file.

Note how we use samsung specific struct samsung_decomp_console to
specify parameters on a per-SoC basis. The intent is to have a single
console data definition per SoC.

So we can have one line for each board file using the s5p6450:

DECOMP_CONSOLE_SOC(SOME6450, decomp_console_soc_s5p6450);

Signed-off-by: Domenico Andreoli <[email-masked]>

---
 arch/arm/mach-s5p64x0/mach-smdk6450.c |    4 +
 drivers/tty/serial/Makefile           |    2 
 drivers/tty/serial/samsung-decomp.c   |  140 ++++++++++++++++++++++++++++++++++
 3 files changed, 145 insertions(+), 1 deletion(-)

Index: b/drivers/tty/serial/Makefile
===================================================================
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -26,7 +26,7 @@ obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
 obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
 obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
 obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
-obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
+obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o samsung-decomp.o
 obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
 obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
 obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
Index: b/drivers/tty/serial/samsung-decomp.c
===================================================================
--- /dev/null
+++ b/drivers/tty/serial/samsung-decomp.c
@@ -0,0 +1,140 @@
+/*
+ * Decompressor support for Samsung UARTs
+ *
+ * Copyright (C) 2012 Domenico Andreoli <[email-masked]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/decompress/console.h>
+
+typedef unsigned int upf_t;	/* cannot include linux/serial_core.h */
+
+#include <mach/map.h>
+#include <mach/regs-gpio.h>
+#include <plat/regs-serial.h>
+
+static const char samsung_drvname[] __decomp_archdata = "samsung-uart";
+
+#define DECOMP_CONSOLE_SAMSUNG(_soc, _data) \
+	DECOMP_CONSOLE_DATA(decomp_console_soc_##_soc, samsung_drvname, _data);
+
+struct samsung_decomp_console {
+	unsigned long base;
+	unsigned int fifo_mask;
+	unsigned int fifo_max;
+};
+
+/* s5p6450 serial ports */
+static const struct samsung_decomp_console
+  __decomp_console_data_s5p6450[] __decomp_archdata = {
+	{ 0xec800000, },
+	{ 0xec800400, },
+	{ 0xec800800, },
+	{ 0xec800c00, },
+};
+DECOMP_CONSOLE_SAMSUNG(s5p6450, __decomp_console_data_s5p6450);
+
+static inline unsigned int uart_rd(unsigned long base, unsigned int reg)
+{
+	volatile unsigned int *p = (volatile unsigned int *) (base + reg);
+	return *p;
+}
+
+static inline void uart_wr(unsigned long base, unsigned int reg, unsigned int val)
+{
+	volatile unsigned int *p = (volatile unsigned int *) (base + reg);
+	*p = val;
+}
+
+#define samsung_decomp_console(_drv) ((struct samsung_decomp_console *) (_drv)->devdata)
+
+static int __decomp_arch samsung_decomp_probe(struct decomp_console_drv *drv)
+{
+	struct samsung_decomp_console *console = samsung_decomp_console(drv);
+	u32 fifocon;
+	int retries = 1000;
+
+	/* this driver doesn't support probing of the base address */
+	if (!console->base)
+		return -EINVAL;
+
+	/* Enable the UART FIFOs if they are not enabled and our
+	 * configuration says we should turn them on.
+	 */
+	if (console->fifo_mask && console->fifo_max &&
+	    IS_ENABLED(CONFIG_S3C_BOOT_UART_FORCE_FIFO)) {
+		fifocon = uart_rd(console->base, S3C2410_UFCON);
+
+		if (!(fifocon & S3C2410_UFCON_FIFOMODE)) {
+			fifocon |= S3C2410_UFCON_RESETBOTH;
+			uart_wr(console->base, S3C2410_UFCON, fifocon);
+
+			/* wait for fifo reset to complete */
+			while (retries--) {
+				fifocon = uart_rd(console->base, S3C2410_UFCON);
+				if (!(fifocon & S3C2410_UFCON_RESETBOTH))
+					break;
+				barrier();
+			}
+		}
+	}
+
+	return retries ? 0 : -EBUSY;
+}
+
+/* we can deal with the case the UARTs are being run
+ * in FIFO mode, so that we don't hold up our execution
+ * waiting for tx to happen...
+*/
+static void __decomp_arch samsung_decomp_putc(struct decomp_console_drv *drv, int ch)
+{
+	struct samsung_decomp_console *console = samsung_decomp_console(drv);
+
+	if (console->fifo_mask && console->fifo_max &&
+	    uart_rd(console->base, S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) {
+		while (1) {
+			int level = uart_rd(console->base, S3C2410_UFSTAT);
+			level &= console->fifo_mask;
+
+			if (level < console->fifo_max)
+				break;
+		}
+	} else {
+		/* not using fifos */
+		while ((uart_rd(console->base, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE)
+			barrier();
+	}
+
+	/* write byte to transmission register */
+	uart_wr(console->base, S3C2410_UTXH, ch);
+}
+
+static void __decomp_arch samsung_decomp_flush(struct decomp_console_drv *drv)
+{
+	struct samsung_decomp_console *console = samsung_decomp_console(drv);
+
+	/* FIXME: not using fifos */
+	while ((uart_rd(console->base, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE)
+		barrier();
+}
+
+static const char samsung_dt_compat[][16] __decomp_archdata = {
+	"samsung-uart",
+	"",
+};
+
+DECOMP_CONSOLE_START("ttySAC")
+	.probe = samsung_decomp_probe,
+	.putc = samsung_decomp_putc,
+	.flush = samsung_decomp_flush,
+	.dt_compat = samsung_dt_compat,
+DECOMP_CONSOLE_END
Index: b/arch/arm/mach-s5p64x0/mach-smdk6450.c
===================================================================
--- a/arch/arm/mach-s5p64x0/mach-smdk6450.c
+++ b/arch/arm/mach-s5p64x0/mach-smdk6450.c
@@ -26,6 +26,8 @@
 #include <linux/fb.h>
 #include <linux/mmc/host.h>
 
+#include <linux/decompress/console.h>
+
 #include <video/platform_lcd.h>
 
 #include <asm/hardware/vic.h>
@@ -287,6 +289,8 @@ static void __init smdk6450_machine_init
 	platform_add_devices(smdk6450_devices, ARRAY_SIZE(smdk6450_devices));
 }
 
+DECOMP_CONSOLE_SOC(SMDK6450, decomp_console_soc_s5p6450);
+
 MACHINE_START(SMDK6450, "SMDK6450")
 	/* Maintainer: Kukjin Kim <[email-masked]> */
 	.atag_offset	= 0x100,