Here we have a more complex driver, the samsung-uart. It saves some code but could save some more. Surely it could be smarter but now is just WIP. It shows how to use the driver data on the memory provided by the decompressor. It also shows code built ad hoc for a given platform, indeed this driver is not ready for a true generic kernel but this can be worked out later in the privacy of the platform subtree. This implementation is granted to break your Samsung platform but at least it builds with mini2440, s3c6400, s5p64x0, s5pc100, s5pv210, exynos4 and spear6xx defconfigs. I could not test any. This patch exists only to test the strength of the Earlycon's design but I can improve it if such design is accepted. Signed-off-by: Domenico Andreoli --- arch/arm/mach-exynos/include/mach/uncompress.h | 23 +-- arch/arm/mach-s3c24xx/include/mach/uncompress.h | 38 +---- arch/arm/mach-s3c64xx/include/mach/uncompress.h | 24 ++- arch/arm/mach-s5p64x0/include/mach/uncompress.h | 144 +------------------- arch/arm/mach-s5pc100/include/mach/uncompress.h | 17 +- arch/arm/mach-s5pv210/include/mach/uncompress.h | 11 +- arch/arm/mach-spear6xx/include/mach/uncompress.h | 5 + arch/arm/plat-samsung/include/plat/uncompress.h | 106 +-------------- drivers/tty/serial/Makefile | 2 drivers/tty/serial/samsung-earlycon.c | 167 +++++++++++++++++++++++ 10 files changed, 239 insertions(+), 298 deletions(-) Index: b/arch/arm/plat-samsung/include/plat/uncompress.h =================================================================== --- a/arch/arm/plat-samsung/include/plat/uncompress.h +++ b/arch/arm/plat-samsung/include/plat/uncompress.h @@ -14,84 +14,12 @@ #ifndef __ASM_PLAT_UNCOMPRESS_H #define __ASM_PLAT_UNCOMPRESS_H -typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ - -/* uart setup */ - -unsigned int fifo_mask; -unsigned int fifo_max; - -/* forward declerations */ - -static void arch_detect_cpu(void); - -/* defines for UART registers */ - -#include #include /* working in physical space... */ #undef S3C2410_WDOGREG #define S3C2410_WDOGREG(x) ((S3C24XX_PA_WATCHDOG + (x))) -/* how many bytes we allow into the FIFO at a time in FIFO mode */ -#define FIFO_MAX (14) - -#ifdef S3C_PA_UART -#define uart_base S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT) -#endif - -static __inline__ void -uart_wr(unsigned int reg, unsigned int val) -{ - volatile unsigned int *ptr; - - ptr = (volatile unsigned int *)(reg + uart_base); - *ptr = val; -} - -static __inline__ unsigned int -uart_rd(unsigned int reg) -{ - volatile unsigned int *ptr; - - ptr = (volatile unsigned int *)(reg + uart_base); - return *ptr; -} - -/* 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 putc(int ch) -{ - if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) { - int level; - - while (1) { - level = uart_rd(S3C2410_UFSTAT); - level &= fifo_mask; - - if (level < fifo_max) - break; - } - - } else { - /* not using fifos */ - - while ((uart_rd(S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE) - barrier(); - } - - /* write byte to transmission register */ - uart_wr(S3C2410_UTXH, ch); -} - -static inline void flush(void) -{ -} - #define __raw_writel(d, ad) \ do { \ *((volatile unsigned int __force *)(ad)) = (d); \ @@ -142,45 +70,23 @@ static void arch_decomp_error(const char #define arch_error arch_decomp_error #endif -#ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO -static inline void arch_enable_uart_fifo(void) -{ - u32 fifocon = uart_rd(S3C2410_UFCON); - - if (!(fifocon & S3C2410_UFCON_FIFOMODE)) { - fifocon |= S3C2410_UFCON_RESETBOTH; - uart_wr(S3C2410_UFCON, fifocon); - - /* wait for fifo reset to complete */ - while (1) { - fifocon = uart_rd(S3C2410_UFCON); - if (!(fifocon & S3C2410_UFCON_RESETBOTH)) - break; - } - } -} -#else -#define arch_enable_uart_fifo() do { } while(0) -#endif - - static void arch_decomp_setup(void) { + unsigned long uart_base; + /* we may need to setup the uart(s) here if we are not running * on an BAST... the BAST will have left the uarts configured * after calling linux. */ - arch_detect_cpu(); arch_decomp_wdog_start(); - /* Enable the UART FIFOs if they where not enabled and our - * configuration says we should turn them on. - */ - - arch_enable_uart_fifo(); + uart_base = get_uart_base(); + if (uart_base) + setup_earlycon("samsung-uart", uart_base); } +#define EARLYCON_STATIC_SETUP #endif /* __ASM_PLAT_UNCOMPRESS_H */ 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-earlycon.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-earlycon.c =================================================================== --- /dev/null +++ b/drivers/tty/serial/samsung-earlycon.c @@ -0,0 +1,167 @@ +/* + * Earlycon driver for Samsung UARTs + * + * Copyright (C) 2012 Domenico Andreoli + * + * 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 +#include + +typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ + +#include +#include +#include + +struct uart_drvdata { + unsigned int fifo_mask; + unsigned int fifo_max; +}; + +#define uart_wr(_base, _reg, _val) *((volatile unsigned int *)((_base) + (_reg))) = (_val) +#define uart_rd(_base, _reg) *((volatile unsigned int *)((_base) + (_reg))) + +#if defined(CONFIG_ARCH_S3C24XX) + +static int __earlyconinit is_arm926(void) +{ + unsigned int cpuid; + + asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (cpuid)); + + return ((cpuid & 0xff0) == 0x260); +} + +static int __earlyconinit samsung_earlycon_config(struct uart_drvdata *drvdata) +{ + unsigned int cpuid; + + cpuid = *((volatile unsigned int *)S3C2410_GSTATUS1); + cpuid &= S3C2410_GSTATUS1_IDMASK; + + if (RELOC_FUN(is_arm926)() || cpuid == S3C2410_GSTATUS1_2440 || + cpuid == S3C2410_GSTATUS1_2442 || + cpuid == S3C2410_GSTATUS1_2416 || + cpuid == S3C2410_GSTATUS1_2450) { + drvdata->fifo_mask = S3C2440_UFSTAT_TXMASK; + drvdata->fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; + } else { + drvdata->fifo_mask = S3C2410_UFSTAT_TXMASK; + drvdata->fifo_max = 15 << S3C2410_UFSTAT_TXSHIFT; + } + + return 0; +} + +#elif defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_ARCH_S5PC100) + +static int __earlyconinit samsung_earlycon_config(struct uart_drvdata *drvdata) +{ + drvdata->fifo_mask = S3C2440_UFSTAT_TXMASK; + drvdata->fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; + return 0; +} + +#elif defined(CONFIG_ARCH_ARCH_S5PV210) || defined(CONFIG_ARCH_ARCH_EXYNOS) + +static int __earlyconinit samsung_earlycon_config(struct uart_drvdata *drvdata) +{ + drvdata->fifo_mask = S5PV210_UFSTAT_TXMASK; + drvdata->fifo_max = 15 << S5PV210_UFSTAT_TXSHIFT; + return 0; +} + +#else + +static int __earlyconinit samsung_earlycon_config(struct uart_drvdata *drvdata) +{ + return -1; +} + +#endif + +static int __earlyconinit samsung_earlycon_probe(struct earlycon_drv *drv) +{ + struct uart_drvdata *drvdata = (struct uart_drvdata *) drv->data; + u32 fifocon; + int retries = 1000; + + /* this driver doesn't support probing of the base address */ + if (!drv->base) + return -EINVAL; + + if (RELOC_FUN(samsung_earlycon_config)(drvdata)) + return -EINVAL; + + /* Enable the UART FIFOs if they are not enabled and our + * configuration says we should turn them on. + */ + if (IS_ENABLED(CONFIG_S3C_BOOT_UART_FORCE_FIFO)) { + fifocon = uart_rd(drv->base, S3C2410_UFCON); + + if (!(fifocon & S3C2410_UFCON_FIFOMODE)) { + fifocon |= S3C2410_UFCON_RESETBOTH; + uart_wr(drv->base, S3C2410_UFCON, fifocon); + + /* wait for fifo reset to complete */ + while (retries--) { + fifocon = uart_rd(drv->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 __earlyconinit samsung_earlycon_putc(struct earlycon_drv *drv, int ch) +{ + struct uart_drvdata *drvdata = (struct uart_drvdata *) drv->data; + + if (drvdata && uart_rd(drv->base, S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) { + int level; + + while (1) { + level = uart_rd(drv->base, S3C2410_UFSTAT); + level &= drvdata->fifo_mask; + + if (level < drvdata->fifo_max) + break; + } + + } else { + /* not using fifos */ + while ((uart_rd(drv->base, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE) + barrier(); + } + + /* write byte to transmission register */ + uart_wr(drv->base, S3C2410_UTXH, ch); +} + +static void __earlyconinit samsung_earlycon_flush(struct earlycon_drv *drv) +{ + /* TODO */ +} + +EARLYCON_START("samsung-uart") + .probe = samsung_earlycon_probe, + .putc = samsung_earlycon_putc, + .flush = samsung_earlycon_flush, +EARLYCON_END Index: b/arch/arm/mach-s3c24xx/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-s3c24xx/include/mach/uncompress.h +++ b/arch/arm/mach-s3c24xx/include/mach/uncompress.h @@ -14,41 +14,21 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H -#include #include -/* working in physical space... */ -#undef S3C2410_GPIOREG -#define S3C2410_GPIOREG(x) ((S3C24XX_PA_GPIO + (x))) +typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ -#include +#include -static inline int is_arm926(void) +static inline unsigned long get_uart_base(void) { - unsigned int cpuid; - - asm volatile ("mrc p15, 0, %0, c1, c0, 0" : "=r" (cpuid)); - - return ((cpuid & 0xff0) == 0x260); +#ifdef S3C_PA_UART + return S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); +#else + return 0; +#endif } -static void arch_detect_cpu(void) -{ - unsigned int cpuid; - - cpuid = *((volatile unsigned int *)S3C2410_GSTATUS1); - cpuid &= S3C2410_GSTATUS1_IDMASK; - - if (is_arm926() || cpuid == S3C2410_GSTATUS1_2440 || - cpuid == S3C2410_GSTATUS1_2442 || - cpuid == S3C2410_GSTATUS1_2416 || - cpuid == S3C2410_GSTATUS1_2450) { - fifo_mask = S3C2440_UFSTAT_TXMASK; - fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; - } else { - fifo_mask = S3C2410_UFSTAT_TXMASK; - fifo_max = 15 << S3C2410_UFSTAT_TXSHIFT; - } -} +#include #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-s3c64xx/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-s3c64xx/include/mach/uncompress.h +++ b/arch/arm/mach-s3c64xx/include/mach/uncompress.h @@ -16,13 +16,27 @@ #define __ASM_ARCH_UNCOMPRESS_H #include -#include -static void arch_detect_cpu(void) +typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ + +#include + +#ifdef S3C_PA_UART + +static inline unsigned long get_uart_base(void) { - /* we do not need to do any cpu detection here at the moment. */ - fifo_mask = S3C2440_UFSTAT_TXMASK; - fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; + return S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); } +#else + +static inline unsigned long get_uart_base(void) +{ + return 0; +} + +#endif + +#include + #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-s5p64x0/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-s5p64x0/include/mach/uncompress.h +++ b/arch/arm/mach-s5p64x0/include/mach/uncompress.h @@ -15,38 +15,15 @@ #include -/* - * cannot use commonly - * because uart base of S5P6440 and S5P6450 is different - */ - -typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ - -/* uart setup */ - -unsigned int fifo_mask; -unsigned int fifo_max; - -/* forward declerations */ - -static void arch_detect_cpu(void); - -/* defines for UART registers */ - -#include #include /* working in physical space... */ #undef S3C2410_WDOGREG #define S3C2410_WDOGREG(x) ((S3C24XX_PA_WATCHDOG + (x))) -/* how many bytes we allow into the FIFO at a time in FIFO mode */ -#define FIFO_MAX (14) - -unsigned long uart_base; - -static __inline__ void get_uart_base(void) +static inline unsigned long get_uart_base(void) { + unsigned long uart_base; unsigned int chipid; chipid = *(const volatile unsigned int __force *) 0xE0100118; @@ -57,58 +34,8 @@ static __inline__ void get_uart_base(voi uart_base += 0xEC800000; else uart_base += 0xEC000000; -} - -static __inline__ void uart_wr(unsigned int reg, unsigned int val) -{ - volatile unsigned int *ptr; - - get_uart_base(); - ptr = (volatile unsigned int *)(reg + uart_base); - *ptr = val; -} - -static __inline__ unsigned int uart_rd(unsigned int reg) -{ - volatile unsigned int *ptr; - - get_uart_base(); - ptr = (volatile unsigned int *)(reg + uart_base); - return *ptr; -} - -/* - * 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 putc(int ch) -{ - if (uart_rd(S3C2410_UFCON) & S3C2410_UFCON_FIFOMODE) { - int level; - while (1) { - level = uart_rd(S3C2410_UFSTAT); - level &= fifo_mask; - - if (level < fifo_max) - break; - } - - } else { - /* not using fifos */ - - while ((uart_rd(S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE) != S3C2410_UTRSTAT_TXE) - barrier(); - } - - /* write byte to transmission register */ - uart_wr(S3C2410_UTXH, ch); -} - -static inline void flush(void) -{ + return uart_base; } #define __raw_writel(d, ad) \ @@ -144,69 +71,6 @@ static void arch_decomp_wdog_start(void) #define arch_decomp_wdog() #endif -#ifdef CONFIG_S3C_BOOT_ERROR_RESET - -static void arch_decomp_error(const char *x) -{ - putstr("\n\n"); - putstr(x); - putstr("\n\n -- System resetting\n"); - - __raw_writel(0x4000, S3C2410_WTDAT); - __raw_writel(0x4000, S3C2410_WTCNT); - __raw_writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x40), S3C2410_WTCON); - - while(1); -} - -#define arch_error arch_decomp_error -#endif - -#ifdef CONFIG_S3C_BOOT_UART_FORCE_FIFO -static inline void arch_enable_uart_fifo(void) -{ - u32 fifocon = uart_rd(S3C2410_UFCON); - - if (!(fifocon & S3C2410_UFCON_FIFOMODE)) { - fifocon |= S3C2410_UFCON_RESETBOTH; - uart_wr(S3C2410_UFCON, fifocon); - - /* wait for fifo reset to complete */ - while (1) { - fifocon = uart_rd(S3C2410_UFCON); - if (!(fifocon & S3C2410_UFCON_RESETBOTH)) - break; - } - } -} -#else -#define arch_enable_uart_fifo() do { } while(0) -#endif - -static void arch_decomp_setup(void) -{ - /* - * we may need to setup the uart(s) here if we are not running - * on an BAST... the BAST will have left the uarts configured - * after calling linux. - */ - - arch_detect_cpu(); - arch_decomp_wdog_start(); - - /* - * Enable the UART FIFOs if they where not enabled and our - * configuration says we should turn them on. - */ - - arch_enable_uart_fifo(); -} - - - -static void arch_detect_cpu(void) -{ - /* we do not need to do any cpu detection here at the moment. */ -} +#include #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-exynos/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-exynos/include/mach/uncompress.h +++ b/arch/arm/mach-exynos/include/mach/uncompress.h @@ -12,21 +12,16 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H __FILE__ -#include - #include -volatile u8 *uart_base; - -#include - static unsigned int __raw_readl(unsigned int ptr) { return *((volatile unsigned int *)ptr); } -static void arch_detect_cpu(void) +static unsigned long get_uart_base(void) { + unsigned long uart_base; u32 chip_id = __raw_readl(EXYNOS_PA_CHIPID); /* @@ -38,15 +33,13 @@ static void arch_detect_cpu(void) chip_id &= 0xf; if (chip_id == 0x5) - uart_base = (volatile u8 *)EXYNOS5_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); + uart_base = EXYNOS5_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); else - uart_base = (volatile u8 *)EXYNOS4_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); + uart_base = EXYNOS4_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); - /* - * For preventing FIFO overrun or infinite loop of UART console, - * fifo_max should be the minimum fifo size of all of the UART channels - */ - fifo_mask = S5PV210_UFSTAT_TXMASK; - fifo_max = 15 << S5PV210_UFSTAT_TXSHIFT; + return uart_base; } + +#include + #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-s5pv210/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-s5pv210/include/mach/uncompress.h +++ b/arch/arm/mach-s5pv210/include/mach/uncompress.h @@ -14,11 +14,16 @@ #define __ASM_ARCH_UNCOMPRESS_H #include -#include -static void arch_detect_cpu(void) +static inline unsigned long get_uart_base(void) { - /* we do not need to do any cpu detection here at the moment. */ +#ifdef S3C_PA_UART + return S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); +#else + return 0; +#endif } +#include + #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-s5pc100/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-s5pc100/include/mach/uncompress.h +++ b/arch/arm/mach-s5pc100/include/mach/uncompress.h @@ -16,13 +16,20 @@ #define __ASM_ARCH_UNCOMPRESS_H #include -#include -static void arch_detect_cpu(void) +typedef unsigned int upf_t; /* cannot include linux/serial_core.h */ + +#include + +static inline unsigned long get_uart_base(void) { - /* we do not need to do any cpu detection here at the moment. */ - fifo_mask = S3C2440_UFSTAT_TXMASK; - fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT; +#ifdef S3C_PA_UART + return S3C_PA_UART + (S3C_UART_OFFSET * CONFIG_S3C_LOWLEVEL_UART_PORT); +#else + return 0; +#endif } +#include + #endif /* __ASM_ARCH_UNCOMPRESS_H */ Index: b/arch/arm/mach-spear6xx/include/mach/uncompress.h =================================================================== --- a/arch/arm/mach-spear6xx/include/mach/uncompress.h +++ b/arch/arm/mach-spear6xx/include/mach/uncompress.h @@ -14,6 +14,11 @@ #ifndef __MACH_UNCOMPRESS_H #define __MACH_UNCOMPRESS_H +static inline unsigned long get_uart_base(void) +{ + return 0; +} + #include #endif /* __MACH_UNCOMPRESS_H */