From c275dfefebb95c1ffdca630a65055a850b83adc7 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 19 Dec 2015 19:38:55 -0700 Subject: dm: core: Provide uclass_find_device_by_phandle() only when needed This function cannot be used unless support is enabled for device tree control. Adjust the code to reflect that. Signed-off-by: Simon Glass diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 1af0947..36bcf19 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -278,6 +278,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node, return -ENODEV; } +#if CONFIG_IS_ENABLED(OF_CONTROL) static int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, @@ -308,6 +309,7 @@ static int uclass_find_device_by_phandle(enum uclass_id id, return -ENODEV; } +#endif int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) @@ -374,6 +376,7 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, return uclass_get_device_tail(dev, ret, devp); } +#if CONFIG_IS_ENABLED(OF_CONTROL) int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) { @@ -384,6 +387,7 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, ret = uclass_find_device_by_phandle(id, parent, name, &dev); return uclass_get_device_tail(dev, ret, devp); } +#endif int uclass_first_device(enum uclass_id id, struct udevice **devp) { -- cgit v0.10.2 From 28a6c01187d30f0e7b7dbe6e4e198e76ce6246f1 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:04 +0530 Subject: arm: omap-common: do not build timer when CONFIG_TIMER defined To prepare timer driver to DM/DT conversion do not build the exiting timer driver when CONFIG_TIMER is defined. But since omap's SPL doesn't support DM yet so built timer driver only for SPL build when CONFIG_TIMER is defined. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/cpu/armv7/omap-common/Makefile b/arch/arm/cpu/armv7/omap-common/Makefile index 464a5d1..87a7ac0 100644 --- a/arch/arm/cpu/armv7/omap-common/Makefile +++ b/arch/arm/cpu/armv7/omap-common/Makefile @@ -6,7 +6,13 @@ # obj-y := reset.o +ifeq ($(CONFIG_TIMER),) obj-y += timer.o +else +ifdef CONFIG_SPL_BUILD +obj-y += timer.o +endif +endif obj-y += utils.o ifneq ($(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),) -- cgit v0.10.2 From c8336975381d481829e3e6a81c38cd118826ca03 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Sat, 16 Jan 2016 21:33:58 +0530 Subject: dm: timer: uclass: add timer init in uclass driver to add timer device Adding timer init function in timer-uclass driver to create and initialize the timer device on platforms where u-boot,dm-pre-reloc is not used. Since there will be multiple timer devices in the system, adding a tick-timer node in chosen node to know which timer device to be used as tick timer in u-boot. Signed-off-by: Mugunthan V N diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt new file mode 100644 index 0000000..bf9a30a --- /dev/null +++ b/doc/device-tree-bindings/chosen.txt @@ -0,0 +1,43 @@ +The chosen node +--------------- +The chosen node does not represent a real device, but serves as a place +for passing data like which serial device to used to print the logs etc + + +stdout-path property +-------------------- +Device trees may specify the device to be used for boot console output +with a stdout-path property under /chosen. + +Example +------- +/ { + chosen { + stdout-path = "/serial@f00:115200"; + }; + + serial@f00 { + compatible = "vendor,some-uart"; + reg = <0xf00 0x10>; + }; +}; + +tick-timer property +------------------- +In a system there are multiple timers, specify which timer to be used +as the tick-timer. Earlier it was hardcoded in the timer driver now +since device tree has all the timer nodes. Specify which timer to be +used as tick timer. + +Example +------- +/ { + chosen { + tick-timer = "/timer2@f00"; + }; + + timer2@f00 { + compatible = "vendor,some-timer"; + reg = <0xf00 0x10>; + }; +}; diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index aca421b..0480178 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include @@ -56,6 +58,48 @@ u64 timer_conv_64(u32 count) return ((u64)gd->timebase_h << 32) | gd->timebase_l; } +int notrace dm_timer_init(void) +{ + const void *blob = gd->fdt_blob; + struct udevice *dev = NULL; + int node; + int ret; + + if (gd->timer) + return 0; + + /* Check for a chosen timer to be used for tick */ + node = fdtdec_get_chosen_node(blob, "tick-timer"); + if (node < 0) { + /* No chosen timer, trying first available timer */ + ret = uclass_first_device(UCLASS_TIMER, &dev); + if (ret) + return ret; + if (!dev) + return -ENODEV; + } else { + if (uclass_get_device_by_of_offset(UCLASS_TIMER, node, &dev)) { + /* + * If the timer is not marked to be bound before + * relocation, bind it anyway. + */ + if (node > 0 && + !lists_bind_fdt(gd->dm_root, blob, node, &dev)) { + ret = device_probe(dev); + if (ret) + return ret; + } + } + } + + if (dev) { + gd->timer = dev; + return 0; + } + + return -ENODEV; +} + UCLASS_DRIVER(timer) = { .id = UCLASS_TIMER, .name = "timer", diff --git a/include/timer.h b/include/timer.h index 7fee17e..f14725c 100644 --- a/include/timer.h +++ b/include/timer.h @@ -8,6 +8,15 @@ #define _TIMER_H_ /* + * dm_timer_init - initialize a timer for time keeping. On success + * initializes gd->timer so that lib/timer can use it for future + * referrence. + * + * @return - 0 on success or error number + */ +int dm_timer_init(void); + +/* * timer_conv_64 - convert 32-bit counter value to 64-bit * * @count: 32-bit counter value diff --git a/lib/time.c b/lib/time.c index f37a662..e9f6861 100644 --- a/lib/time.c +++ b/lib/time.c @@ -41,23 +41,6 @@ extern unsigned long __weak timer_read_counter(void); #endif #ifdef CONFIG_TIMER -static int notrace dm_timer_init(void) -{ - struct udevice *dev; - int ret; - - if (!gd->timer) { - ret = uclass_first_device(UCLASS_TIMER, &dev); - if (ret) - return ret; - if (!dev) - return -ENODEV; - gd->timer = dev; - } - - return 0; -} - ulong notrace get_tbclk(void) { int ret; -- cgit v0.10.2 From a5d801130cc9f4fc1cf2b710ac7560759b2e1c95 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:06 +0530 Subject: dm: timer: uclass: Add flag to control sequence numbering Like SPI and I2C, timer devices also have multiple chip instances. This patch adds the flag 'DM_UC_FLAG_SEQ_ALIAS' in timer_uclass driver to control device sequence numbering. Signed-off-by: Mugunthan V N Reviewed-by: Simon Glass Reviewed-by: Bin Meng Reviewed-by: Tom Rini Reviewed-by: Simon Glass diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index 0480178..b1f4868 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -104,5 +104,6 @@ UCLASS_DRIVER(timer) = { .id = UCLASS_TIMER, .name = "timer", .pre_probe = timer_pre_probe, + .flags = DM_UC_FLAG_SEQ_ALIAS, .per_device_auto_alloc_size = sizeof(struct timer_dev_priv), }; -- cgit v0.10.2 From dadf31375acd4edaf3c7596dc129152244939e50 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:07 +0530 Subject: drivers: timer: omap_timer: add timer driver for omap devices based on dm Adding a timer driver for omap devices based on driver model and device tree. Signed-off-by: Mugunthan V N diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 2b10d2b..ff65a73 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -30,4 +30,10 @@ config X86_TSC_TIMER help Select this to enable Time-Stamp Counter (TSC) timer for x86. +config OMAP_TIMER + bool "Omap timer support" + depends on TIMER + help + Select this to enable an timer for Omap devices. + endmenu diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index fe954ec..f351fbb 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_TIMER) += timer-uclass.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o +obj-$(CONFIG_OMAP_TIMER) += omap-timer.o diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c new file mode 100644 index 0000000..3bb38c5 --- /dev/null +++ b/drivers/timer/omap-timer.c @@ -0,0 +1,108 @@ +/* + * TI OMAP timer driver + * + * Copyright (C) 2015, Texas Instruments, Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Timer register bits */ +#define TCLR_START BIT(0) /* Start=1 */ +#define TCLR_AUTO_RELOAD BIT(1) /* Auto reload */ +#define TCLR_PRE_EN BIT(5) /* Pre-scaler enable */ +#define TCLR_PTV_SHIFT (2) /* Pre-scaler shift value */ + +#define TIMER_CLOCK (V_SCLK / (2 << CONFIG_SYS_PTV)) + +struct omap_gptimer_regs { + unsigned int tidr; /* offset 0x00 */ + unsigned char res1[12]; + unsigned int tiocp_cfg; /* offset 0x10 */ + unsigned char res2[12]; + unsigned int tier; /* offset 0x20 */ + unsigned int tistatr; /* offset 0x24 */ + unsigned int tistat; /* offset 0x28 */ + unsigned int tisr; /* offset 0x2c */ + unsigned int tcicr; /* offset 0x30 */ + unsigned int twer; /* offset 0x34 */ + unsigned int tclr; /* offset 0x38 */ + unsigned int tcrr; /* offset 0x3c */ + unsigned int tldr; /* offset 0x40 */ + unsigned int ttgr; /* offset 0x44 */ + unsigned int twpc; /* offset 0x48 */ + unsigned int tmar; /* offset 0x4c */ + unsigned int tcar1; /* offset 0x50 */ + unsigned int tscir; /* offset 0x54 */ + unsigned int tcar2; /* offset 0x58 */ +}; + +/* Omap Timer Priv */ +struct omap_timer_priv { + struct omap_gptimer_regs *regs; +}; + +static int omap_timer_get_count(struct udevice *dev, u64 *count) +{ + struct omap_timer_priv *priv = dev_get_priv(dev); + + *count = readl(&priv->regs->tcrr); + + return 0; +} + +static int omap_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct omap_timer_priv *priv = dev_get_priv(dev); + + uc_priv->clock_rate = TIMER_CLOCK; + + /* start the counter ticking up, reload value on overflow */ + writel(0, &priv->regs->tldr); + /* enable timer */ + writel((CONFIG_SYS_PTV << 2) | TCLR_PRE_EN | TCLR_AUTO_RELOAD | + TCLR_START, &priv->regs->tclr); + + return 0; +} + +static int omap_timer_ofdata_to_platdata(struct udevice *dev) +{ + struct omap_timer_priv *priv = dev_get_priv(dev); + + priv->regs = (struct omap_gptimer_regs *)dev_get_addr(dev); + + return 0; +} + + +static const struct timer_ops omap_timer_ops = { + .get_count = omap_timer_get_count, +}; + +static const struct udevice_id omap_timer_ids[] = { + { .compatible = "ti,am335x-timer" }, + { .compatible = "ti,am4372-timer" }, + { .compatible = "ti,omap5430-timer" }, + {} +}; + +U_BOOT_DRIVER(omap_timer) = { + .name = "omap_timer", + .id = UCLASS_TIMER, + .of_match = omap_timer_ids, + .ofdata_to_platdata = omap_timer_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct omap_timer_priv), + .probe = omap_timer_probe, + .ops = &omap_timer_ops, + .flags = DM_FLAG_PRE_RELOC, +}; -- cgit v0.10.2 From 1ce32ba7ae287ca6155876da7d662a4ea318db76 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:08 +0530 Subject: am43xx_evm: timer: do not define CONFIG_TIMER for spl Since OMAP's spl doesn't support DM currently, do not define CONFIG_TIMER for spl build. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/include/configs/am43xx_evm.h b/include/configs/am43xx_evm.h index de7538f..c3867ef 100644 --- a/include/configs/am43xx_evm.h +++ b/include/configs/am43xx_evm.h @@ -144,6 +144,7 @@ #undef CONFIG_DM_MMC #undef CONFIG_DM_SPI #undef CONFIG_DM_SPI_FLASH +#undef CONFIG_TIMER #endif #ifndef CONFIG_SPL_BUILD -- cgit v0.10.2 From 34afc6148ad5bf9e53fcc1c7a313c4a70a57dca3 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:09 +0530 Subject: arm: dts: am437x-sk-evm: add tick-timer to chosen node Specify which timer to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/am437x-sk-evm.dts b/arch/arm/dts/am437x-sk-evm.dts index 89feaf3..260edb9 100644 --- a/arch/arm/dts/am437x-sk-evm.dts +++ b/arch/arm/dts/am437x-sk-evm.dts @@ -26,6 +26,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; backlight { -- cgit v0.10.2 From fb912c6f95e67e16a59971c959d5554bcbc06b0b Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:10 +0530 Subject: defconfig: am437x_sk_evm: enable timer driver model Enable timer driver model for am437x_sk_evm as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/am437x_sk_evm_defconfig b/configs/am437x_sk_evm_defconfig index 8f78eeb..9eb41f9 100644 --- a/configs/am437x_sk_evm_defconfig +++ b/configs/am437x_sk_evm_defconfig @@ -21,3 +21,5 @@ CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From ff9e612651074aa2317fdb607b337928994be725 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:11 +0530 Subject: arm: dts: am437x-gp-evm: add tick-timer to chosen node Specify which timer to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/am437x-gp-evm.dts b/arch/arm/dts/am437x-gp-evm.dts index b5f0b4e..8e23b96 100644 --- a/arch/arm/dts/am437x-gp-evm.dts +++ b/arch/arm/dts/am437x-gp-evm.dts @@ -26,6 +26,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; vmmcsd_fixed: fixedregulator-sd { -- cgit v0.10.2 From 49d3fe52e2866903dc7ac4485d453f7b7ae2688f Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:12 +0530 Subject: defconfig: am437x_gp_evm: enable timer driver model Enable timer driver model for am437x_gp_evm as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/am437x_gp_evm_defconfig b/configs/am437x_gp_evm_defconfig index 7155c98..1d79ba19 100644 --- a/configs/am437x_gp_evm_defconfig +++ b/configs/am437x_gp_evm_defconfig @@ -18,3 +18,5 @@ CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From ac3507dc5063ce189830f725bf0149df22cf7910 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:13 +0530 Subject: am335x_evm: timer: do not define CONFIG_TIMER for spl Since OMAP's spl doesn't support DM currently, do not define CONFIG_TIMER for spl build. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/include/configs/am335x_evm.h b/include/configs/am335x_evm.h index cf6a606..6ebe0b3 100644 --- a/include/configs/am335x_evm.h +++ b/include/configs/am335x_evm.h @@ -357,6 +357,7 @@ */ #ifdef CONFIG_SPL_BUILD #undef CONFIG_DM_MMC +#undef CONFIG_TIMER #endif #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_USBETH_SUPPORT) -- cgit v0.10.2 From c7bd870462231d2b0ea2b093b5e10c6f1f6d5f55 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:14 +0530 Subject: arm: dts: am335x-boneblack: add tick-timer to chosen node Specify which timer to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/am335x-boneblack.dts b/arch/arm/dts/am335x-boneblack.dts index 679248a..27ebe4a 100644 --- a/arch/arm/dts/am335x-boneblack.dts +++ b/arch/arm/dts/am335x-boneblack.dts @@ -15,6 +15,7 @@ compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; chosen { stdout-path = &uart0; + tick-timer = &timer2; }; }; -- cgit v0.10.2 From 3f8e517135a35bb48a86738d909089807f6ae082 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:15 +0530 Subject: defconfig: am335x_boneblack_vboot: enable timer driver model Enable timer driver model for am335x_boneblack_vboot as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/am335x_boneblack_vboot_defconfig b/configs/am335x_boneblack_vboot_defconfig index 5dcb942..060aa1c 100644 --- a/configs/am335x_boneblack_vboot_defconfig +++ b/configs/am335x_boneblack_vboot_defconfig @@ -22,3 +22,5 @@ CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_SYS_NS16550=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From d3e25aed73143b98e3f27908f2d951345d8c617b Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:16 +0530 Subject: arm: dts: am335x-evm: add tick-timer to chosen node Specify timer2 to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/am335x-evm.dts b/arch/arm/dts/am335x-evm.dts index e1c5d4f..c0bc2af 100644 --- a/arch/arm/dts/am335x-evm.dts +++ b/arch/arm/dts/am335x-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; cpus { -- cgit v0.10.2 From 00dffc9abe9b039900cab43722a9017ef5355de2 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:17 +0530 Subject: defconfig: am335x_gp_evm: enable timer driver model Enable timer driver model for am335x_gp_evm as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/am335x_gp_evm_defconfig b/configs/am335x_gp_evm_defconfig index 74d9ffb..49461e2 100644 --- a/configs/am335x_gp_evm_defconfig +++ b/configs/am335x_gp_evm_defconfig @@ -16,3 +16,5 @@ CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_SYS_NS16550=y CONFIG_RSA=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From 30a0cdb65162cd9705f167b7efd9fbb087857f6a Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:18 +0530 Subject: ti_omap5_common: timer: do not define CONFIG_TIMER for spl Since OMAP's spl doesn't support DM currently, do not define CONFIG_TIMER for spl build. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/include/configs/ti_omap5_common.h b/include/configs/ti_omap5_common.h index 2d492f8..d164e6a 100644 --- a/include/configs/ti_omap5_common.h +++ b/include/configs/ti_omap5_common.h @@ -164,6 +164,7 @@ */ #ifdef CONFIG_SPL_BUILD #undef CONFIG_DM_MMC +#undef CONFIG_TIMER #endif #endif /* __CONFIG_TI_OMAP5_COMMON_H */ -- cgit v0.10.2 From 6c3401ad862428d6865eb0bf232804e37bd2735d Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:19 +0530 Subject: arm: dts: dra72-evm: add tick-timer to chosen node Specify timer2 to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/dra72-evm.dts b/arch/arm/dts/dra72-evm.dts index a62550f..fc2d167 100644 --- a/arch/arm/dts/dra72-evm.dts +++ b/arch/arm/dts/dra72-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart1; + tick-timer = &timer2; }; memory { -- cgit v0.10.2 From 09bdea0188118341fc44893d4caeed151b5ac584 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:20 +0530 Subject: defconfig: dra72_evm: enable timer driver model Enable timer driver model for dra72_evm_defconfig as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/dra72_evm_defconfig b/configs/dra72_evm_defconfig index b57ecca..32d1dc1 100644 --- a/configs/dra72_evm_defconfig +++ b/configs/dra72_evm_defconfig @@ -22,3 +22,5 @@ CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From 87a2127f9f5b3d6a8e14b64be0dbaf888079fa30 Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:21 +0530 Subject: arm: dts: dra7-evm: add tick-timer to chosen node Specify timer2 to be used as tick-timer in chosen node. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/arch/arm/dts/dra7-evm.dts b/arch/arm/dts/dra7-evm.dts index 797d411..242fd53 100644 --- a/arch/arm/dts/dra7-evm.dts +++ b/arch/arm/dts/dra7-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart1; + tick-timer = &timer2; }; memory { -- cgit v0.10.2 From 9ab401ae0eb0386e300f24262c3b9178892769aa Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Thu, 24 Dec 2015 16:08:22 +0530 Subject: defconfig: dra74_evm: enable timer driver model Enable timer driver model for dra74_evm_defconfig as omap-timer supports driver model. Signed-off-by: Mugunthan V N Reviewed-by: Tom Rini diff --git a/configs/dra74_evm_defconfig b/configs/dra74_evm_defconfig index 6e5a705..9946261 100644 --- a/configs/dra74_evm_defconfig +++ b/configs/dra74_evm_defconfig @@ -21,3 +21,5 @@ CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y -- cgit v0.10.2 From 8e31681c5a5a2fd6bbdf751bcc900a4481e2fa29 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 29 Dec 2015 05:22:46 -0700 Subject: tiny-printf: Avoid printing NULL strings Add a check for NULL strings to avoid printing junk to the console. Signed-off-by: Simon Glass Reviewed-by: Stefan Roese diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index 403b134..efe5c25 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -108,8 +108,10 @@ int vprintf(const char *fmt, va_list va) w--; while (w-- > 0) putc(lz ? '0' : ' '); - while ((ch = *p++)) - putc(ch); + if (p) { + while ((ch = *p++)) + putc(ch); + } } } -- cgit v0.10.2 From 4dc5259ac7f54f4856ee9cf5ff01cfd4f67c82a6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 29 Dec 2015 05:22:48 -0700 Subject: dm: gpio: Allow the uclass to work without printf() For SPL we don't really need sprintf() and with tiny-printf this is not available. Allow this to be dropped in SPL when using tiny-printf. Signed-off-by: Simon Glass diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 4cce11f..3ed4d89 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -154,6 +154,7 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label) static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) { +#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF) va_list args; char buf[40]; @@ -161,6 +162,9 @@ static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return dm_gpio_request(desc, buf); +#else + return dm_gpio_request(desc, fmt); +#endif } /** @@ -199,6 +203,7 @@ int gpio_request(unsigned gpio, const char *label) */ int gpio_requestf(unsigned gpio, const char *fmt, ...) { +#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF) va_list args; char buf[40]; @@ -206,6 +211,9 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...) vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return gpio_request(gpio, buf); +#else + return gpio_request(gpio, fmt); +#endif } int _dm_gpio_free(struct udevice *dev, uint offset) -- cgit v0.10.2 From 4a1db6d8ab4d5e4565bda079710a028ada12ddbe Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 29 Dec 2015 05:22:49 -0700 Subject: dm: mmc: Try to honour the sequence order At present we add driver-model MMC devices in the order we find them. The 'alias' order is not honoured. It is difficult to fix this for the case where we have holes in the sequence. But for the common case where the devices are numbered from 0 without any gaps, we can add the devices to the internal data structures in this order. Signed-off-by: Simon Glass diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e6028d5..ede5d6e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1780,18 +1780,28 @@ static int mmc_probe(bd_t *bis) #elif defined(CONFIG_DM_MMC) static int mmc_probe(bd_t *bis) { - int ret; + int ret, i; struct uclass *uc; - struct udevice *m; + struct udevice *dev; ret = uclass_get(UCLASS_MMC, &uc); if (ret) return ret; - uclass_foreach_dev(m, uc) { - ret = device_probe(m); + /* + * Try to add them in sequence order. Really with driver model we + * should allow holes, but the current MMC list does not allow that. + * So if we request 0, 1, 3 we will get 0, 1, 2. + */ + for (i = 0; ; i++) { + ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev); + if (ret == -ENODEV) + break; + } + uclass_foreach_dev(dev, uc) { + ret = device_probe(dev); if (ret) - printf("%s - probe failed: %d\n", m->name, ret); + printf("%s - probe failed: %d\n", dev->name, ret); } return 0; -- cgit v0.10.2 From 4fbd258e69373a60cdf24c9a64065b486f8748c9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 29 Dec 2015 05:22:50 -0700 Subject: i2c: Correct command return values We should not return a -ve error code from command functions. Instead, return CMD_RET_FAILURE. This avoids the "exit not allowed from main input shell" error messages from the hush shell. Signed-off-by: Simon Glass Acked-by: Heiko Schocher diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 552c875..b3bb644 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -1809,7 +1809,8 @@ static int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, if (ret) printf("Failure changing bus number (%d)\n", ret); } - return ret; + + return ret ? CMD_RET_FAILURE : 0; } #endif /* defined(CONFIG_SYS_I2C) */ @@ -1852,7 +1853,8 @@ static int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char * const if (ret) printf("Failure changing bus speed (%d)\n", ret); } - return ret; + + return ret ? CMD_RET_FAILURE : 0; } /** -- cgit v0.10.2 From 5589a8182957054f8ac4ec3d91492710e718e44d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 29 Dec 2015 05:22:52 -0700 Subject: pinctrl: Avoid binding all pinconfig nodes before relocation This can create a large number of pinctrl devices. It chews up early malloc() memory and takes time. Only bind those which are marked as needed before relocation. Signed-off-by: Simon Glass diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index b5fdcd1..c42b312 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -111,12 +111,16 @@ static int pinconfig_post_bind(struct udevice *dev) { const void *fdt = gd->fdt_blob; int offset = dev->of_offset; + bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); const char *name; int ret; for (offset = fdt_first_subnode(fdt, offset); offset > 0; offset = fdt_next_subnode(fdt, offset)) { + if (pre_reloc_only && + !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL)) + continue; /* * If this node has "compatible" property, this is not * a pin configuration node, but a normal device. skip. -- cgit v0.10.2 From 74b1320ae535b9dfe6fb7a86e05a8787e503f59c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 5 Jan 2016 09:30:57 -0700 Subject: tiny-printf: Always print zeroes At present this does not print zero values in numeric format (hex and decimal). Add a special case for this. Signed-off-by: Simon Glass Reviewed-by: Stefan Roese Reviewed-by: Tom Rini diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index efe5c25..a06abed 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -82,13 +82,21 @@ int vprintf(const char *fmt, va_list va) num = -(int)num; out('-'); } - for (div = 1000000000; div; div /= 10) - div_out(&num, div); + if (!num) { + out_dgt(0); + } else { + for (div = 1000000000; div; div /= 10) + div_out(&num, div); + } break; case 'x': num = va_arg(va, unsigned int); - for (div = 0x10000000; div; div /= 0x10) - div_out(&num, div); + if (!num) { + out_dgt(0); + } else { + for (div = 0x10000000; div; div /= 0x10) + div_out(&num, div); + } break; case 'c': out((char)(va_arg(va, int))); -- cgit v0.10.2 From 72b335e91d41ff5dfecdea57d3397f368bb488f5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 5 Jan 2016 09:30:58 -0700 Subject: sandbox: Support the bmp command Adjust this command to use map_sysmem() correctly so that it works on sandbox. Signed-off-by: Simon Glass Reviewed-by: Tom Rini diff --git a/common/cmd_bmp.c b/common/cmd_bmp.c index cb1f071..f04b7d4 100644 --- a/common/cmd_bmp.c +++ b/common/cmd_bmp.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -57,7 +58,8 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, /* align to 32-bit-aligned-address + 2 */ bmp = (struct bmp_image *)((((unsigned int)dst + 1) & ~3) + 2); - if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, (uchar *)addr, &len) != 0) { + if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), + &len) != 0) { free(dst); return NULL; } @@ -187,7 +189,7 @@ U_BOOT_CMD( */ static int bmp_info(ulong addr) { - struct bmp_image *bmp = (struct bmp_image *)addr; + struct bmp_image *bmp = (struct bmp_image *)map_sysmem(addr, 0); void *bmp_alloc_addr = NULL; unsigned long len; @@ -224,7 +226,7 @@ static int bmp_info(ulong addr) int bmp_display(ulong addr, int x, int y) { int ret; - struct bmp_image *bmp = (struct bmp_image *)addr; + struct bmp_image *bmp = map_sysmem(addr, 0); void *bmp_alloc_addr = NULL; unsigned long len; @@ -236,11 +238,12 @@ int bmp_display(ulong addr, int x, int y) printf("There is no valid bmp file at the given address\n"); return 1; } + addr = map_to_sysmem(bmp); #if defined(CONFIG_LCD) - ret = lcd_display_bitmap((ulong)bmp, x, y); + ret = lcd_display_bitmap(addr, x, y); #elif defined(CONFIG_VIDEO) - ret = video_display_bitmap((unsigned long)bmp, x, y); + ret = video_display_bitmap(addr, x, y); #else # error bmp_display() requires CONFIG_LCD or CONFIG_VIDEO #endif -- cgit v0.10.2 From 20af3c0a0034b885cd269cb7abdc2d933d82a723 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 5 Jan 2016 09:30:59 -0700 Subject: dm: core: Call uclass post_bind() after the driver's bind() method At present the uclass's post_bind() method is called before the driver's bind() method. This means that the uclass cannot use any of the information set up by the driver. Move it later in the sequence to permit this. This is an ordering change which is always fairly major in nature. The main impact is that devices which have children will not see them appear in their bind() method. From what I can see, existing drivers do not look at their children in the bind() method, so this should be safe. Conceptually this change seems to result in a 'more correct' ordering, since the uclass (which is broader than the device) gets the last word. Signed-off-by: Simon Glass Reviewed-by: Tom Rini diff --git a/drivers/core/device.c b/drivers/core/device.c index 818d03f..1e5584a 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -135,6 +135,11 @@ int device_bind(struct udevice *parent, const struct driver *drv, if (ret) goto fail_child_post_bind; } + if (uc->uc_drv->post_bind) { + ret = uc->uc_drv->post_bind(dev); + if (ret) + goto fail_uclass_post_bind; + } if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); @@ -145,6 +150,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, return 0; +fail_uclass_post_bind: + /* There is no child unbind() method, so no clean-up required */ fail_child_post_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (drv->unbind && drv->unbind(dev)) { diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 36bcf19..e1acefe 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -430,11 +430,6 @@ int uclass_bind_device(struct udevice *dev) goto err; } } - if (uc->uc_drv->post_bind) { - ret = uc->uc_drv->post_bind(dev); - if (ret) - goto err; - } return 0; err: -- cgit v0.10.2 From cf2045283284a3226889b66541c456eb7ca869e9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 5 Jan 2016 09:31:00 -0700 Subject: bzip2: Support compression for sandbox Add the missing code to allow bzip2 compression to be used. This is useful for sandbox tests. These files are taken from the bzip2 1.0.6 release. The license text is copied to the top of each file as is done with other bzip2 files in U-Boot. The only other change is to squash a compiler warning with nBytes. Signed-off-by: Simon Glass Reviewed-by: Tom Rini diff --git a/include/bzlib.h b/include/bzlib.h index 2d864d5..19314f8 100644 --- a/include/bzlib.h +++ b/include/bzlib.h @@ -68,7 +68,10 @@ /* Configure for U-Boot environment */ #define BZ_NO_STDIO + +#ifndef CONFIG_SANDBOX #define BZ_NO_COMPRESS +#endif /* End of configuration for U-Boot environment */ #ifdef __cplusplus diff --git a/lib/bzip2/Makefile b/lib/bzip2/Makefile index f0b81ad..585d776 100644 --- a/lib/bzip2/Makefile +++ b/lib/bzip2/Makefile @@ -4,3 +4,4 @@ obj-y += bzlib.o bzlib_crctable.o bzlib_decompress.o \ bzlib_randtable.o bzlib_huffman.o +obj-$(CONFIG_SANDBOX) += bzlib_compress.o bzlib_blocksort.o diff --git a/lib/bzip2/bzlib_blocksort.c b/lib/bzip2/bzlib_blocksort.c new file mode 100644 index 0000000..2785521 --- /dev/null +++ b/lib/bzip2/bzlib_blocksort.c @@ -0,0 +1,1134 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2002 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@acm.org + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/lib/bzip2/bzlib_compress.c b/lib/bzip2/bzlib_compress.c new file mode 100644 index 0000000..c8da1c7 --- /dev/null +++ b/lib/bzip2/bzlib_compress.c @@ -0,0 +1,714 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2002 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@acm.org + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); + else /* squash compiler 'used but not set' warning */ + nBytes = nBytes; +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ -- cgit v0.10.2 From 0a7edce0ef26d9a8c11e9c097e0ed12cf334e27d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 6 Jan 2016 10:33:03 -0700 Subject: dm: timer: refuse timers with zero clock_rate If a timer has a zero clock_rate, get_tbclk() will return zero for it, which will cause tick_to_time() to perform a division-by-zero, which will crash U-Boot. Signed-off-by: Stephen Warren Acked-by: Simon Glass diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index b1f4868..83d1a35 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -49,6 +49,16 @@ static int timer_pre_probe(struct udevice *dev) return 0; } +static int timer_post_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->clock_rate) + return -EINVAL; + + return 0; +} + u64 timer_conv_64(u32 count) { /* increment tbh if tbl has rolled over */ @@ -105,5 +115,6 @@ UCLASS_DRIVER(timer) = { .name = "timer", .pre_probe = timer_pre_probe, .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_probe = timer_post_probe, .per_device_auto_alloc_size = sizeof(struct timer_dev_priv), }; -- cgit v0.10.2 From bb883f824c6ba4e6f530f3214afd9e492604a873 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 6 Jan 2016 10:33:04 -0700 Subject: timer: sandbox: work without device tree A default invocation of sandbox U-Boot apparently uses no device tree, which means that no timer is registers, which in turn means that the sleep shell command hangs. Fix the sandbox timer code to register a device when there's no DT, just like e.g. the sandbox reset driver does. When there's no DT, the DM uclass can't initialize clock_rate from DT, so set a default value in the timer code instead. Signed-off-by: Stephen Warren Reviewed-by: Bin Meng Acked-by: Simon Glass diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index 00a9944..a8da936 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -27,6 +27,11 @@ static int sandbox_timer_get_count(struct udevice *dev, u64 *count) static int sandbox_timer_probe(struct udevice *dev) { + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->clock_rate) + uc_priv->clock_rate = 1000000; + return 0; } @@ -47,3 +52,8 @@ U_BOOT_DRIVER(sandbox_timer) = { .ops = &sandbox_timer_ops, .flags = DM_FLAG_PRE_RELOC, }; + +/* This is here in case we don't have a device tree */ +U_BOOT_DEVICE(sandbox_timer_non_fdt) = { + .name = "sandbox_timer", +}; -- cgit v0.10.2 From 4ef7909970bbed5e911f3d5e3d27ecedb80cec0e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 7 Jan 2016 15:16:08 -0700 Subject: usb: Define USB_MAX_STOR_DEV in only one place We can define this in the header file and use it in usb_storage.c. There is no need to define it twice. Remove the #define from usb_storage.c. Signed-off-by: Simon Glass Reviewed-by: Marek Vasut diff --git a/common/usb_storage.c b/common/usb_storage.c index 4fdb55f..8737cf7 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -65,7 +65,6 @@ static const unsigned char us_direction[256/8] = { static ccb usb_ccb __attribute__((aligned(ARCH_DMA_MINALIGN))); static __u32 CBWTag; -#define USB_MAX_STOR_DEV 7 static int usb_max_devs; /* number of highest available usb device */ static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; -- cgit v0.10.2 From 813858186625b23c9dac287bfe6c0c6a2b4f6686 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 12 Jan 2016 16:36:38 +0900 Subject: clk: move Kconfig options into sub-menu Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 890f22f..9fcde39 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -1,3 +1,5 @@ +menu "Clock" + config CLK bool "Enable clock driver support" depends on DM @@ -17,3 +19,5 @@ config SPL_CLK SPL, enable this option. It might provide a cleaner interface to setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. + +endmenu -- cgit v0.10.2 From 8bdf9cfda04896c3ff6d704f398395776b69cba8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 13 Jan 2016 13:16:08 +0900 Subject: clk: fix comments in include/clk.h The comment about get_periph_rate() is the same as that of set_periph_rate(). I am fixing typos here and there while I am in this file. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/include/clk.h b/include/clk.h index 254ad2b..f244301 100644 --- a/include/clk.h +++ b/include/clk.h @@ -29,19 +29,19 @@ struct clk_ops { ulong (*set_rate)(struct udevice *dev, ulong rate); /** - * clk_set_periph_rate() - Set clock rate for a peripheral - * - * @dev: Device to adjust (UCLASS_CLK) - * @rate: New clock rate in Hz - * @return new clock rate in Hz, or -ve error code - */ + * get_periph_rate() - Get clock rate for a peripheral + * + * @dev: Device to check (UCLASS_CLK) + * @periph: Peripheral ID to check + * @return clock rate in Hz, or -ve error code + */ ulong (*get_periph_rate)(struct udevice *dev, int periph); /** - * clk_set_periph_rate() - Set current clock rate for a peripheral + * set_periph_rate() - Set current clock rate for a peripheral * * @dev: Device to update (UCLASS_CLK) - * @periph: Peripheral ID to cupdate + * @periph: Peripheral ID to update * @return new clock rate in Hz, or -ve error code */ ulong (*set_periph_rate)(struct udevice *dev, int periph, ulong rate); @@ -58,7 +58,7 @@ struct clk_ops { ulong clk_get_rate(struct udevice *dev); /** - * set_rate() - Set current clock rate + * clk_set_rate() - Set current clock rate * * @dev: Device to adjust * @rate: New clock rate in Hz @@ -78,7 +78,7 @@ ulong clk_get_periph_rate(struct udevice *dev, int periph); * clk_set_periph_rate() - Set current clock rate for a peripheral * * @dev: Device to update (UCLASS_CLK) - * @periph: Peripheral ID to cupdate + * @periph: Peripheral ID to update * @return new clock rate in Hz, or -ve error code */ ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate); -- cgit v0.10.2 From ad1cf785853eef60e48053ead4fbb47dad840d55 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 13 Jan 2016 13:16:09 +0900 Subject: clk: add needed include and declaration to include/clk.h This header uses ulong, so it needs to include . Likewise, "struct udevice" must be declared before it is used. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/include/clk.h b/include/clk.h index f244301..371784a 100644 --- a/include/clk.h +++ b/include/clk.h @@ -8,6 +8,10 @@ #ifndef _CLK_H_ #define _CLK_H_ +#include + +struct udevice; + int soc_clk_dump(void); struct clk_ops { -- cgit v0.10.2 From 9e52126f347c1de39f8fe2c62d6486cf77f4af3a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 13 Jan 2016 13:16:10 +0900 Subject: clk: add static qualifier to local functions Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/clk/clk_rk3036.c b/drivers/clk/clk_rk3036.c index 6c802b6..f650810 100644 --- a/drivers/clk/clk_rk3036.c +++ b/drivers/clk/clk_rk3036.c @@ -314,7 +314,7 @@ static ulong rk3036_clk_set_rate(struct udevice *dev, ulong rate) return 0; } -ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3036_clk_priv *priv = dev_get_priv(dev); ulong new_rate; diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index 54d4930..0172ad1 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -508,7 +508,7 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate, return rockchip_spi_get_clk(cru, clk_general_rate, periph); } -ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3288_clk_priv *priv = dev_get_priv(dev); ulong new_rate; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index 058225a..367130f 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -32,7 +32,7 @@ static ulong sandbox_clk_set_rate(struct udevice *dev, ulong rate) return 0; } -ulong sandbox_get_periph_rate(struct udevice *dev, int periph) +static ulong sandbox_get_periph_rate(struct udevice *dev, int periph) { struct sandbox_clk_priv *priv = dev_get_priv(dev); @@ -41,7 +41,8 @@ ulong sandbox_get_periph_rate(struct udevice *dev, int periph) return priv->periph_rate[periph]; } -ulong sandbox_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong sandbox_set_periph_rate(struct udevice *dev, int periph, + ulong rate) { struct sandbox_clk_priv *priv = dev_get_priv(dev); ulong old_rate; -- cgit v0.10.2 From f0e075162f815f9cb769c7bb9003e0ad3b829811 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 13 Jan 2016 13:16:12 +0900 Subject: clk: add API to enable clock The most basic thing for clock is to enable it, but it is missing in this uclass. Signed-off-by: Masahiro Yamada Acked-by: Simon Glass diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 73dfd7d..19f6f07 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -32,6 +32,16 @@ ulong clk_set_rate(struct udevice *dev, ulong rate) return ops->set_rate(dev, rate); } +int clk_enable(struct udevice *dev, int periph) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->enable) + return -ENOSYS; + + return ops->enable(dev, periph); +} + ulong clk_get_periph_rate(struct udevice *dev, int periph) { struct clk_ops *ops = clk_get_ops(dev); diff --git a/include/clk.h b/include/clk.h index 371784a..941808a 100644 --- a/include/clk.h +++ b/include/clk.h @@ -33,6 +33,15 @@ struct clk_ops { ulong (*set_rate)(struct udevice *dev, ulong rate); /** + * enable() - Enable the clock for a peripheral + * + * @dev: clock provider + * @periph: Peripheral ID to enable + * @return zero on success, or -ve error code + */ + int (*enable)(struct udevice *dev, int periph); + + /** * get_periph_rate() - Get clock rate for a peripheral * * @dev: Device to check (UCLASS_CLK) @@ -71,6 +80,15 @@ ulong clk_get_rate(struct udevice *dev); ulong clk_set_rate(struct udevice *dev, ulong rate); /** + * clk_enable() - Enable the clock for a peripheral + * + * @dev: clock provider + * @periph: Peripheral ID to enable + * @return zero on success, or -ve error code + */ +int clk_enable(struct udevice *dev, int periph); + +/** * clk_get_periph_rate() - Get current clock rate for a peripheral * * @dev: Device to check (UCLASS_CLK) -- cgit v0.10.2 From 27067a46c56cdaa0f8a2ef433f0aed4376834c44 Mon Sep 17 00:00:00 2001 From: Mateusz Kulikowski Date: Thu, 14 Jan 2016 20:37:41 +0100 Subject: patman: Add --thread option Add option to create threaded series of patches. With it, it will be possible to create patch threads like this: [PATCH 0/10] Add support for time travel [PATCH 1/10] Add Flux Capacitor driver [PATCH 2/10] Add Mr. Fusion driver (...) Internally it will call git send-email with --thread option Signed-off-by: Mateusz Kulikowski Acked-by: Simon Glass diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 9e739d8..5f1b4f6 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -328,7 +328,7 @@ def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): return result def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, - self_only=False, alias=None, in_reply_to=None): + self_only=False, alias=None, in_reply_to=None, thread=False): """Email a patch series. Args: @@ -342,6 +342,8 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, self_only: True to just email to yourself as a test in_reply_to: If set we'll pass this to git as --in-reply-to. Should be a message ID that this is in reply to. + thread: True to add --thread to git send-email (make + all patches reply to cover-letter or first patch in series) Returns: Git command that was/would be run @@ -400,6 +402,8 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, cmd = ['git', 'send-email', '--annotate'] if in_reply_to: cmd.append('--in-reply-to="%s"' % in_reply_to) + if thread: + cmd.append('--thread') cmd += to cmd += cc diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 6fe8fe0..d05c5ff 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -61,6 +61,8 @@ parser.add_option('--no-check', action='store_false', dest='check_patch', help="Don't check for patch compliance") parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('-T', '--thread', action='store_true', dest='thread', + default=False, help='Create patches as a single thread') parser.usage += """ @@ -161,7 +163,7 @@ else: if its_a_go: cmd = gitutil.EmailPatches(series, cover_fname, args, options.dry_run, not options.ignore_bad_tags, cc_file, - in_reply_to=options.in_reply_to) + in_reply_to=options.in_reply_to, thread=options.thread) else: print col.Color(col.RED, "Not sending emails due to errors/warnings") -- cgit v0.10.2 From d201506cca782c54309b488170623094f252aab5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Jan 2016 11:15:24 -0700 Subject: test/py: Implement pytest infrastructure This tool aims to test U-Boot by executing U-Boot shell commands using the console interface. A single top-level script exists to execute or attach to the U-Boot console, run the entire script of tests against it, and summarize the results. Advantages of this approach are: - Testing is performed in the same way a user or script would interact with U-Boot; there can be no disconnect. - There is no need to write or embed test-related code into U-Boot itself. It is asserted that writing test-related code in Python is simpler and more flexible that writing it all in C. - It is reasonably simple to interact with U-Boot in this way. A few simple tests are provided as examples. Soon, we should convert as many as possible of the other tests in test/* and test/cmd_ut.c too. The hook scripts, relay control utilities, and udev rules I use for my own HW setup are published at https://github.com/swarren/uboot-test-hooks. See README.md for more details! Signed-off-by: Stephen Warren Signed-off-by: Stephen Warren Tested-by: Michal Simek Tested-by: Simon Glass Acked-by: Simon Glass #v3 diff --git a/test/py/.gitignore b/test/py/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/test/py/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/test/py/README.md b/test/py/README.md new file mode 100644 index 0000000..8036299 --- /dev/null +++ b/test/py/README.md @@ -0,0 +1,300 @@ +# U-Boot pytest suite + +## Introduction + +This tool aims to test U-Boot by executing U-Boot shell commands using the +console interface. A single top-level script exists to execute or attach to the +U-Boot console, run the entire script of tests against it, and summarize the +results. Advantages of this approach are: + +- Testing is performed in the same way a user or script would interact with + U-Boot; there can be no disconnect. +- There is no need to write or embed test-related code into U-Boot itself. + It is asserted that writing test-related code in Python is simpler and more + flexible that writing it all in C. +- It is reasonably simple to interact with U-Boot in this way. + +## Requirements + +The test suite is implemented using pytest. Interaction with the U-Boot console +involves executing some binary and interacting with its stdin/stdout. You will +need to implement various "hook" scripts that are called by the test suite at +the appropriate time. + +On Debian or Debian-like distributions, the following packages are required. +Similar package names should exist in other distributions. + +| Package | Version tested (Ubuntu 14.04) | +| -------------- | ----------------------------- | +| python | 2.7.5-5ubuntu3 | +| python-pytest | 2.5.1-1 | + +The test script supports either: + +- Executing a sandbox port of U-Boot on the local machine as a sub-process, + and interacting with it over stdin/stdout. +- Executing an external "hook" scripts to flash a U-Boot binary onto a + physical board, attach to the board's console stream, and reset the board. + Further details are described later. + +### Using `virtualenv` to provide requirements + +Older distributions (e.g. Ubuntu 10.04) may not provide all the required +packages, or may provide versions that are too old to run the test suite. One +can use the Python `virtualenv` script to locally install more up-to-date +versions of the required packages without interfering with the OS installation. +For example: + +```bash +$ cd /path/to/u-boot +$ sudo apt-get install python python-virtualenv +$ virtualenv venv +$ . ./venv/bin/activate +$ pip install pytest +``` + +## Testing sandbox + +To run the testsuite on the sandbox port (U-Boot built as a native user-space +application), simply execute: + +``` +./test/py/test.py --bd sandbox --build +``` + +The `--bd` option tells the test suite which board type is being tested. This +lets the test suite know which features the board has, and hence exactly what +can be tested. + +The `--build` option tells U-Boot to compile U-Boot. Alternatively, you may +omit this option and build U-Boot yourself, in whatever way you choose, before +running the test script. + +The test script will attach to U-Boot, execute all valid tests for the board, +then print a summary of the test process. A complete log of the test session +will be written to `${build_dir}/test-log.html`. This is best viewed in a web +browser, but may be read directly as plain text, perhaps with the aid of the +`html2text` utility. + +## Command-line options + +- `--board-type`, `--bd`, `-B` set the type of the board to be tested. For + example, `sandbox` or `seaboard`. +- `--board-identity`, `--id` set the identity of the board to be tested. + This allows differentiation between multiple instances of the same type of + physical board that are attached to the same host machine. This parameter is + not interpreted by the test script in any way, but rather is simply passed + to the hook scripts described below, and may be used in any site-specific + way deemed necessary. +- `--build` indicates that the test script should compile U-Boot itself + before running the tests. If using this option, make sure that any + environment variables required by the build process are already set, such as + `$CROSS_COMPILE`. +- `--build-dir` sets the directory containing the compiled U-Boot binaries. + If omitted, this is `${source_dir}/build-${board_type}`. +- `--result-dir` sets the directory to write results, such as log files, + into. If omitted, the build directory is used. +- `--persistent-data-dir` sets the directory used to store persistent test + data. This is test data that may be re-used across test runs, such as file- + system images. + +`pytest` also implements a number of its own command-line options. Please see +`pytest` documentation for complete details. Execute `py.test --version` for +a brief summary. Note that U-Boot's test.py script passes all command-line +arguments directly to `pytest` for processing. + +## Testing real hardware + +The tools and techniques used to interact with real hardware will vary +radically between different host and target systems, and the whims of the user. +For this reason, the test suite does not attempt to directly interact with real +hardware in any way. Rather, it executes a standardized set of "hook" scripts +via `$PATH`. These scripts implement certain actions on behalf of the test +suite. This keeps the test suite simple and isolated from system variances +unrelated to U-Boot features. + +### Hook scripts + +#### Environment variables + +The following environment variables are set when running hook scripts: + +- `UBOOT_BOARD_TYPE` the board type being tested. +- `UBOOT_BOARD_IDENTITY` the board identity being tested, or `na` if none was + specified. +- `UBOOT_SOURCE_DIR` the U-Boot source directory. +- `UBOOT_TEST_PY_DIR` the full path to `test/py/` in the source directory. +- `UBOOT_BUILD_DIR` the U-Boot build directory. +- `UBOOT_RESULT_DIR` the test result directory. +- `UBOOT_PERSISTENT_DATA_DIR` the test peristent data directory. + +#### `u-boot-test-console` + +This script provides access to the U-Boot console. The script's stdin/stdout +should be connected to the board's console. This process should continue to run +indefinitely, until killed. The test suite will run this script in parallel +with all other hooks. + +This script may be implemented e.g. by exec()ing `cu`, `kermit`, `conmux`, etc. + +If you are able to run U-Boot under a hardware simulator such as qemu, then +you would likely spawn that simulator from this script. However, note that +`u-boot-test-reset` may be called multiple times per test script run, and must +cause U-Boot to start execution from scratch each time. Hopefully your +simulator includes a virtual reset button! If not, you can launch the +simulator from `u-boot-test-reset` instead, while arranging for this console +process to always communicate with the current simulator instance. + +#### `u-boot-test-flash` + +Prior to running the test suite against a board, some arrangement must be made +so that the board executes the particular U-Boot binary to be tested. Often, +this involves writing the U-Boot binary to the board's flash ROM. The test +suite calls this hook script for that purpose. + +This script should perform the entire flashing process synchronously; the +script should only exit once flashing is complete, and a board reset will +cause the newly flashed U-Boot binary to be executed. + +It is conceivable that this script will do nothing. This might be useful in +the following cases: + +- Some other process has already written the desired U-Boot binary into the + board's flash prior to running the test suite. +- The board allows U-Boot to be downloaded directly into RAM, and executed + from there. Use of this feature will reduce wear on the board's flash, so + may be preferable if available, and if cold boot testing of U-Boot is not + required. If this feature is used, the `u-boot-test-reset` script should + peform this download, since the board could conceivably be reset multiple + times in a single test run. + +It is up to the user to determine if those situations exist, and to code this +hook script appropriately. + +This script will typically be implemented by calling out to some SoC- or +board-specific vendor flashing utility. + +#### `u-boot-test-reset` + +Whenever the test suite needs to reset the target board, this script is +executed. This is guaranteed to happen at least once, prior to executing the +first test function. If any test fails, the test infra-structure will execute +this script again to restore U-Boot to an operational state before running the +next test function. + +This script will likely be implemented by communicating with some form of +relay or electronic switch attached to the board's reset signal. + +The semantics of this script require that when it is executed, U-Boot will +start running from scratch. If the U-Boot binary to be tested has been written +to flash, pulsing the board's reset signal is likely all this script need do. +However, in some scenarios, this script may perform other actions. For +example, it may call out to some SoC- or board-specific vendor utility in order +to download the U-Boot binary directly into RAM and execute it. This would +avoid the need for `u-boot-test-flash` to actually write U-Boot to flash, thus +saving wear on the flash chip(s). + +### Board-type-specific configuration + +Each board has a different configuration and behaviour. Many of these +differences can be automatically detected by parsing the `.config` file in the +build directory. However, some differences can't yet be handled automatically. + +For each board, an optional Python module `u_boot_board_${board_type}` may exist +to provide board-specific information to the test script. Any global value +defined in these modules is available for use by any test function. The data +contained in these scripts must be purely derived from U-Boot source code. +Hence, these configuration files are part of the U-Boot source tree too. + +### Execution environment configuration + +Each user's hardware setup may enable testing different subsets of the features +implemented by a particular board's configuration of U-Boot. For example, a +U-Boot configuration may support USB device mode and USB Mass Storage, but this +can only be tested if a USB cable is connected between the board and the host +machine running the test script. + +For each board, optional Python modules `u_boot_boardenv_${board_type}` and +`u_boot_boardenv_${board_type}_${board_identity}` may exist to provide +board-specific and board-identity-specific information to the test script. Any +global value defined in these modules is available for use by any test +function. The data contained in these is specific to a particular user's +hardware configuration. Hence, these configuration files are not part of the +U-Boot source tree, and should be installed outside of the source tree. Users +should set `$PYTHONPATH` prior to running the test script to allow these +modules to be loaded. + +### Board module parameter usage + +The test scripts rely on the following variables being defined by the board +module: + +- None at present. + +### U-Boot `.config` feature usage + +The test scripts rely on various U-Boot `.config` features, either directly in +order to test those features, or indirectly in order to query information from +the running U-Boot instance in order to test other features. + +One example is that testing of the `md` command requires knowledge of a RAM +address to use for the test. This data is parsed from the output of the +`bdinfo` command, and hence relies on CONFIG_CMD_BDI being enabled. + +For a complete list of dependencies, please search the test scripts for +instances of: + +- `buildconfig.get(...` +- `@pytest.mark.buildconfigspec(...` + +### Complete invocation example + +Assuming that you have installed the hook scripts into $HOME/ubtest/bin, and +any required environment configuration Python modules into $HOME/ubtest/py, +then you would likely invoke the test script as follows: + +If U-Boot has already been built: + +```bash +PATH=$HOME/ubtest/bin:$PATH \ + PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ + ./test/py/test.py --bd seaboard +``` + +If you want the test script to compile U-Boot for you too, then you likely +need to set `$CROSS_COMPILE` to allow this, and invoke the test script as +follow: + +```bash +CROSS_COMPILE=arm-none-eabi- \ + PATH=$HOME/ubtest/bin:$PATH \ + PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ + ./test/py/test.py --bd seaboard --build +``` + +## Writing tests + +Please refer to the pytest documentation for details of writing pytest tests. +Details specific to the U-Boot test suite are described below. + +A test fixture named `u_boot_console` should be used by each test function. This +provides the means to interact with the U-Boot console, and retrieve board and +environment configuration information. + +The function `u_boot_console.run_command()` executes a shell command on the +U-Boot console, and returns all output from that command. This allows +validation or interpretation of the command output. This function validates +that certain strings are not seen on the U-Boot console. These include shell +error messages and the U-Boot sign-on message (in order to detect unexpected +board resets). See the source of `u_boot_console_base.py` for a complete list of +"bad" strings. Some test scenarios are expected to trigger these strings. Use +`u_boot_console.disable_check()` to temporarily disable checking for specific +strings. See `test_unknown_cmd.py` for an example. + +Board- and board-environment configuration values may be accessed as sub-fields +of the `u_boot_console.config` object, for example +`u_boot_console.config.ram_base`. + +Build configuration values (from `.config`) may be accessed via the dictionary +`u_boot_console.config.buildconfig`, with keys equal to the Kconfig variable +names. diff --git a/test/py/conftest.py b/test/py/conftest.py new file mode 100644 index 0000000..e1674df --- /dev/null +++ b/test/py/conftest.py @@ -0,0 +1,422 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Implementation of pytest run-time hook functions. These are invoked by +# pytest at certain points during operation, e.g. startup, for each executed +# test, at shutdown etc. These hooks perform functions such as: +# - Parsing custom command-line options. +# - Pullilng in user-specified board configuration. +# - Creating the U-Boot console test fixture. +# - Creating the HTML log file. +# - Monitoring each test's results. +# - Implementing custom pytest markers. + +import atexit +import errno +import os +import os.path +import pexpect +import pytest +from _pytest.runner import runtestprotocol +import ConfigParser +import StringIO +import sys + +# Globals: The HTML log file, and the connection to the U-Boot console. +log = None +console = None + +def mkdir_p(path): + '''Create a directory path. + + This includes creating any intermediate/parent directories. Any errors + caused due to already extant directories are ignored. + + Args: + path: The directory path to create. + + Returns: + Nothing. + ''' + + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +def pytest_addoption(parser): + '''pytest hook: Add custom command-line options to the cmdline parser. + + Args: + parser: The pytest command-line parser. + + Returns: + Nothing. + ''' + + parser.addoption('--build-dir', default=None, + help='U-Boot build directory (O=)') + parser.addoption('--result-dir', default=None, + help='U-Boot test result/tmp directory') + parser.addoption('--persistent-data-dir', default=None, + help='U-Boot test persistent generated data directory') + parser.addoption('--board-type', '--bd', '-B', default='sandbox', + help='U-Boot board type') + parser.addoption('--board-identity', '--id', default='na', + help='U-Boot board identity/instance') + parser.addoption('--build', default=False, action='store_true', + help='Compile U-Boot before running tests') + +def pytest_configure(config): + '''pytest hook: Perform custom initialization at startup time. + + Args: + config: The pytest configuration. + + Returns: + Nothing. + ''' + + global log + global console + global ubconfig + + test_py_dir = os.path.dirname(os.path.abspath(__file__)) + source_dir = os.path.dirname(os.path.dirname(test_py_dir)) + + board_type = config.getoption('board_type') + board_type_filename = board_type.replace('-', '_') + + board_identity = config.getoption('board_identity') + board_identity_filename = board_identity.replace('-', '_') + + build_dir = config.getoption('build_dir') + if not build_dir: + build_dir = source_dir + '/build-' + board_type + mkdir_p(build_dir) + + result_dir = config.getoption('result_dir') + if not result_dir: + result_dir = build_dir + mkdir_p(result_dir) + + persistent_data_dir = config.getoption('persistent_data_dir') + if not persistent_data_dir: + persistent_data_dir = build_dir + '/persistent-data' + mkdir_p(persistent_data_dir) + + import multiplexed_log + log = multiplexed_log.Logfile(result_dir + '/test-log.html') + + if config.getoption('build'): + if build_dir != source_dir: + o_opt = 'O=%s' % build_dir + else: + o_opt = '' + cmds = ( + ['make', o_opt, '-s', board_type + '_defconfig'], + ['make', o_opt, '-s', '-j8'], + ) + runner = log.get_runner('make', sys.stdout) + for cmd in cmds: + runner.run(cmd, cwd=source_dir) + runner.close() + + class ArbitraryAttributeContainer(object): + pass + + ubconfig = ArbitraryAttributeContainer() + ubconfig.brd = dict() + ubconfig.env = dict() + + modules = [ + (ubconfig.brd, 'u_boot_board_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' + + board_identity_filename), + ] + for (dict_to_fill, module_name) in modules: + try: + module = __import__(module_name) + except ImportError: + continue + dict_to_fill.update(module.__dict__) + + ubconfig.buildconfig = dict() + + for conf_file in ('.config', 'include/autoconf.mk'): + dot_config = build_dir + '/' + conf_file + if not os.path.exists(dot_config): + raise Exception(conf_file + ' does not exist; ' + + 'try passing --build option?') + + with open(dot_config, 'rt') as f: + ini_str = '[root]\n' + f.read() + ini_sio = StringIO.StringIO(ini_str) + parser = ConfigParser.RawConfigParser() + parser.readfp(ini_sio) + ubconfig.buildconfig.update(parser.items('root')) + + ubconfig.test_py_dir = test_py_dir + ubconfig.source_dir = source_dir + ubconfig.build_dir = build_dir + ubconfig.result_dir = result_dir + ubconfig.persistent_data_dir = persistent_data_dir + ubconfig.board_type = board_type + ubconfig.board_identity = board_identity + + env_vars = ( + 'board_type', + 'board_identity', + 'source_dir', + 'test_py_dir', + 'build_dir', + 'result_dir', + 'persistent_data_dir', + ) + for v in env_vars: + os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v) + + if board_type == 'sandbox': + import u_boot_console_sandbox + console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig) + else: + import u_boot_console_exec_attach + console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) + +def pytest_generate_tests(metafunc): + '''pytest hook: parameterize test functions based on custom rules. + + If a test function takes parameter(s) (fixture names) of the form brd__xxx + or env__xxx, the brd and env configuration dictionaries are consulted to + find the list of values to use for those parameters, and the test is + parametrized so that it runs once for each combination of values. + + Args: + metafunc: The pytest test function. + + Returns: + Nothing. + ''' + + subconfigs = { + 'brd': console.config.brd, + 'env': console.config.env, + } + for fn in metafunc.fixturenames: + parts = fn.split('__') + if len(parts) < 2: + continue + if parts[0] not in subconfigs: + continue + subconfig = subconfigs[parts[0]] + vals = [] + val = subconfig.get(fn, []) + # If that exact name is a key in the data source: + if val: + # ... use the dict value as a single parameter value. + vals = (val, ) + else: + # ... otherwise, see if there's a key that contains a list of + # values to use instead. + vals = subconfig.get(fn + 's', []) + metafunc.parametrize(fn, vals) + +@pytest.fixture(scope='session') +def u_boot_console(request): + '''Generate the value of a test's u_boot_console fixture. + + Args: + request: The pytest request. + + Returns: + The fixture value. + ''' + + return console + +tests_not_run = set() +tests_failed = set() +tests_skipped = set() +tests_passed = set() + +def pytest_itemcollected(item): + '''pytest hook: Called once for each test found during collection. + + This enables our custom result analysis code to see the list of all tests + that should eventually be run. + + Args: + item: The item that was collected. + + Returns: + Nothing. + ''' + + tests_not_run.add(item.name) + +def cleanup(): + '''Clean up all global state. + + Executed (via atexit) once the entire test process is complete. This + includes logging the status of all tests, and the identity of any failed + or skipped tests. + + Args: + None. + + Returns: + Nothing. + ''' + + if console: + console.close() + if log: + log.status_pass('%d passed' % len(tests_passed)) + if tests_skipped: + log.status_skipped('%d skipped' % len(tests_skipped)) + for test in tests_skipped: + log.status_skipped('... ' + test) + if tests_failed: + log.status_fail('%d failed' % len(tests_failed)) + for test in tests_failed: + log.status_fail('... ' + test) + if tests_not_run: + log.status_fail('%d not run' % len(tests_not_run)) + for test in tests_not_run: + log.status_fail('... ' + test) + log.close() +atexit.register(cleanup) + +def setup_boardspec(item): + '''Process any 'boardspec' marker for a test. + + Such a marker lists the set of board types that a test does/doesn't + support. If tests are being executed on an unsupported board, the test is + marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + mark = item.get_marker('boardspec') + if not mark: + return + required_boards = [] + for board in mark.args: + if board.startswith('!'): + if ubconfig.board_type == board[1:]: + pytest.skip('board not supported') + return + else: + required_boards.append(board) + if required_boards and ubconfig.board_type not in required_boards: + pytest.skip('board not supported') + +def setup_buildconfigspec(item): + '''Process any 'buildconfigspec' marker for a test. + + Such a marker lists some U-Boot configuration feature that the test + requires. If tests are being executed on an U-Boot build that doesn't + have the required feature, the test is marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + mark = item.get_marker('buildconfigspec') + if not mark: + return + for option in mark.args: + if not ubconfig.buildconfig.get('config_' + option.lower(), None): + pytest.skip('.config feature not enabled') + +def pytest_runtest_setup(item): + '''pytest hook: Configure (set up) a test item. + + Called once for each test to perform any custom configuration. This hook + is used to skip the test if certain conditions apply. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + log.start_section(item.name) + setup_boardspec(item) + setup_buildconfigspec(item) + +def pytest_runtest_protocol(item, nextitem): + '''pytest hook: Called to execute a test. + + This hook wraps the standard pytest runtestprotocol() function in order + to acquire visibility into, and record, each test function's result. + + Args: + item: The pytest test item to execute. + nextitem: The pytest test item that will be executed after this one. + + Returns: + A list of pytest reports (test result data). + ''' + + reports = runtestprotocol(item, nextitem=nextitem) + failed = None + skipped = None + for report in reports: + if report.outcome == 'failed': + failed = report + break + if report.outcome == 'skipped': + if not skipped: + skipped = report + + if failed: + tests_failed.add(item.name) + elif skipped: + tests_skipped.add(item.name) + else: + tests_passed.add(item.name) + tests_not_run.remove(item.name) + + try: + if failed: + msg = 'FAILED:\n' + str(failed.longrepr) + log.status_fail(msg) + elif skipped: + msg = 'SKIPPED:\n' + str(skipped.longrepr) + log.status_skipped(msg) + else: + log.status_pass('OK') + except: + # If something went wrong with logging, it's better to let the test + # process continue, which may report other exceptions that triggered + # the logging issue (e.g. console.log wasn't created). Hence, just + # squash the exception. If the test setup failed due to e.g. syntax + # error somewhere else, this won't be seen. However, once that issue + # is fixed, if this exception still exists, it will then be logged as + # part of the test's stdout. + import traceback + print 'Exception occurred while logging runtest status:' + traceback.print_exc() + # FIXME: Can we force a test failure here? + + log.end_section(item.name) + + if failed: + console.cleanup_spawn() + + return reports diff --git a/test/py/multiplexed_log.css b/test/py/multiplexed_log.css new file mode 100644 index 0000000..50f7b90 --- /dev/null +++ b/test/py/multiplexed_log.css @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015 Stephen Warren + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * This provides pretty formatting of the HTML log file, e.g. + * - colored bars beside/above log sections for easily parsed delineation. + * - color highlighting of various messages. + */ + +body { + background-color: black; + color: #ffffff; +} + +pre { + margin-top: 0px; + margin-bottom: 0px; +} + +.implicit { + color: #808080; +} + +.section { + border-style: solid; + border-color: #303030; + border-width: 0px 0px 0px 5px; + padding-left: 5px +} + +.section-header { + background-color: #303030; + margin-left: -5px; + margin-top: 5px; +} + +.section-trailer { + display: none; +} + +.stream { + border-style: solid; + border-color: #303030; + border-width: 0px 0px 0px 5px; + padding-left: 5px +} + +.stream-header { + background-color: #303030; + margin-left: -5px; + margin-top: 5px; +} + +.stream-trailer { + display: none; +} + +.error { + color: #ff0000 +} + +.warning { + color: #ffff00 +} + +.info { + color: #808080 +} + +.action { + color: #8080ff +} + +.status-pass { + color: #00ff00 +} + +.status-skipped { + color: #ffff00 +} + +.status-fail { + color: #ff0000 +} diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py new file mode 100644 index 0000000..48f2b51 --- /dev/null +++ b/test/py/multiplexed_log.py @@ -0,0 +1,515 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Generate an HTML-formatted log file containing multiple streams of data, +# each represented in a well-delineated/-structured fashion. + +import cgi +import os.path +import shutil +import subprocess + +mod_dir = os.path.dirname(os.path.abspath(__file__)) + +class LogfileStream(object): + '''A file-like object used to write a single logical stream of data into + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.''' + + def __init__(self, logfile, name, chained_file): + '''Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream. + chained_file: The file-like object to which all stream data should be + logged to in addition to logfile. Can be None. + + Returns: + Nothing. + ''' + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + + def close(self): + '''Dummy function so that this class is "file-like". + + Args: + None. + + Returns: + Nothing. + ''' + + pass + + def write(self, data, implicit=False): + '''Write data to the log stream. + + Args: + data: The data to write tot he file. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + ''' + + self.logfile.write(self, data, implicit) + if self.chained_file: + self.chained_file.write(data) + + def flush(self): + '''Flush the log stream, to ensure correct log interleaving. + + Args: + None. + + Returns: + Nothing. + ''' + + self.logfile.flush() + if self.chained_file: + self.chained_file.flush() + +class RunAndLog(object): + '''A utility object used to execute sub-processes and log their output to + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.''' + + def __init__(self, logfile, name, chained_file): + '''Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream or sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + Nothing. + ''' + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + + def close(self): + '''Clean up any resources managed by this object.''' + pass + + def run(self, cmd, cwd=None): + '''Run a command as a sub-process, and log the results. + + Args: + cmd: The command to execute. + cwd: The directory to run the command in. Can be None to use the + current directory. + + Returns: + Nothing. + ''' + + msg = "+" + " ".join(cmd) + "\n" + if self.chained_file: + self.chained_file.write(msg) + self.logfile.write(self, msg) + + try: + p = subprocess.Popen(cmd, cwd=cwd, + stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (stdout, stderr) = p.communicate() + output = '' + if stdout: + if stderr: + output += 'stdout:\n' + output += stdout + if stderr: + if stdout: + output += 'stderr:\n' + output += stderr + exit_status = p.returncode + exception = None + except subprocess.CalledProcessError as cpe: + output = cpe.output + exit_status = cpe.returncode + exception = cpe + except Exception as e: + output = '' + exit_status = 0 + exception = e + if output and not output.endswith('\n'): + output += '\n' + if exit_status and not exception: + exception = Exception('Exit code: ' + str(exit_status)) + if exception: + output += str(exception) + '\n' + self.logfile.write(self, output) + if self.chained_file: + self.chained_file.write(output) + if exception: + raise exception + +class SectionCtxMgr(object): + '''A context manager for Python's "with" statement, which allows a certain + portion of test code to be logged to a separate section of the log file. + Objects of this type should be created by factory functions in the Logfile + class rather than directly.''' + + def __init__(self, log, marker): + '''Initialize a new object. + + Args: + log: The Logfile object to log to. + marker: The name of the nested log section. + + Returns: + Nothing. + ''' + + self.log = log + self.marker = marker + + def __enter__(self): + self.log.start_section(self.marker) + + def __exit__(self, extype, value, traceback): + self.log.end_section(self.marker) + +class Logfile(object): + '''Generates an HTML-formatted log file containing multiple streams of + data, each represented in a well-delineated/-structured fashion.''' + + def __init__(self, fn): + '''Initialize a new object. + + Args: + fn: The filename to write to. + + Returns: + Nothing. + ''' + + self.f = open(fn, "wt") + self.last_stream = None + self.blocks = [] + self.cur_evt = 1 + shutil.copy(mod_dir + "/multiplexed_log.css", os.path.dirname(fn)) + self.f.write("""\ + + + + + + +""") + + def close(self): + '''Close the log file. + + After calling this function, no more data may be written to the log. + + Args: + None. + + Returns: + Nothing. + ''' + + self.f.write("""\ + + + +""") + self.f.close() + + # The set of characters that should be represented as hexadecimal codes in + # the log file. + _nonprint = ("%" + "".join(chr(c) for c in range(0, 32) if c not in (9, 10)) + + "".join(chr(c) for c in range(127, 256))) + + def _escape(self, data): + '''Render data format suitable for inclusion in an HTML document. + + This includes HTML-escaping certain characters, and translating + control characters to a hexadecimal representation. + + Args: + data: The raw string data to be escaped. + + Returns: + An escaped version of the data. + ''' + + data = data.replace(chr(13), "") + data = "".join((c in self._nonprint) and ("%%%02x" % ord(c)) or + c for c in data) + data = cgi.escape(data) + return data + + def _terminate_stream(self): + '''Write HTML to the log file to terminate the current stream's data. + + Args: + None. + + Returns: + Nothing. + ''' + + self.cur_evt += 1 + if not self.last_stream: + return + self.f.write("\n") + self.f.write("
End stream: " + + self.last_stream.name + "
\n") + self.f.write("\n") + self.last_stream = None + + def _note(self, note_type, msg): + '''Write a note or one-off message to the log file. + + Args: + note_type: The type of note. This must be a value supported by the + accompanying multiplexed_log.css. + msg: The note/message to log. + + Returns: + Nothing. + ''' + + self._terminate_stream() + self.f.write("
\n
")
+        self.f.write(self._escape(msg))
+        self.f.write("\n
\n") + + def start_section(self, marker): + '''Begin a new nested section in the log file. + + Args: + marker: The name of the section that is starting. + + Returns: + Nothing. + ''' + + self._terminate_stream() + self.blocks.append(marker) + blk_path = "/".join(self.blocks) + self.f.write("
\n") + self.f.write("
Section: " + blk_path + "
\n") + + def end_section(self, marker): + '''Terminate the current nested section in the log file. + + This function validates proper nesting of start_section() and + end_section() calls. If a mismatch is found, an exception is raised. + + Args: + marker: The name of the section that is ending. + + Returns: + Nothing. + ''' + + if (not self.blocks) or (marker != self.blocks[-1]): + raise Exception("Block nesting mismatch: \"%s\" \"%s\"" % + (marker, "/".join(self.blocks))) + self._terminate_stream() + blk_path = "/".join(self.blocks) + self.f.write("
End section: " + blk_path + "
\n") + self.f.write("
\n") + self.blocks.pop() + + def section(self, marker): + '''Create a temporary section in the log file. + + This function creates a context manager for Python's "with" statement, + which allows a certain portion of test code to be logged to a separate + section of the log file. + + Usage: + with log.section("somename"): + some test code + + Args: + marker: The name of the nested section. + + Returns: + A context manager object. + ''' + + return SectionCtxMgr(self, marker) + + def error(self, msg): + '''Write an error note to the log file. + + Args: + msg: A message describing the error. + + Returns: + Nothing. + ''' + + self._note("error", msg) + + def warning(self, msg): + '''Write an warning note to the log file. + + Args: + msg: A message describing the warning. + + Returns: + Nothing. + ''' + + self._note("warning", msg) + + def info(self, msg): + '''Write an informational note to the log file. + + Args: + msg: An informational message. + + Returns: + Nothing. + ''' + + self._note("info", msg) + + def action(self, msg): + '''Write an action note to the log file. + + Args: + msg: A message describing the action that is being logged. + + Returns: + Nothing. + ''' + + self._note("action", msg) + + def status_pass(self, msg): + '''Write a note to the log file describing test(s) which passed. + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-pass", msg) + + def status_skipped(self, msg): + '''Write a note to the log file describing skipped test(s). + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-skipped", msg) + + def status_fail(self, msg): + '''Write a note to the log file describing failed test(s). + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-fail", msg) + + def get_stream(self, name, chained_file=None): + '''Create an object to log a single stream's data into the log file. + + This creates a "file-like" object that can be written to in order to + write a single stream's data to the log file. The implementation will + handle any required interleaving of data (from multiple streams) in + the log, in a way that makes it obvious which stream each bit of data + came from. + + Args: + name: The name of the stream. + chained_file: The file-like object to which all stream data should + be logged to in addition to this log. Can be None. + + Returns: + A file-like object. + ''' + + return LogfileStream(self, name, chained_file) + + def get_runner(self, name, chained_file=None): + '''Create an object that executes processes and logs their output. + + Args: + name: The name of this sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + A RunAndLog object. + ''' + + return RunAndLog(self, name, chained_file) + + def write(self, stream, data, implicit=False): + '''Write stream data into the log file. + + This function should only be used by instances of LogfileStream or + RunAndLog. + + Args: + stream: The stream whose data is being logged. + data: The data to log. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + ''' + + if stream != self.last_stream: + self._terminate_stream() + self.f.write("
\n" % stream.name) + self.f.write("
Stream: " + stream.name + "
\n") + self.f.write("
")
+        if implicit:
+            self.f.write("")
+        self.f.write(self._escape(data))
+        if implicit:
+            self.f.write("")
+        self.last_stream = stream
+
+    def flush(self):
+        '''Flush the log stream, to ensure correct log interleaving.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.f.flush()
diff --git a/test/py/pytest.ini b/test/py/pytest.ini
new file mode 100644
index 0000000..67e514f
--- /dev/null
+++ b/test/py/pytest.ini
@@ -0,0 +1,11 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Static configuration data for pytest. pytest reads this at startup time.
+
+[pytest]
+markers =
+    boardspec: U-Boot: Describes the set of boards a test can/can't run on.
+    buildconfigspec: U-Boot: Describes Kconfig/config-header constraints.
diff --git a/test/py/test.py b/test/py/test.py
new file mode 100755
index 0000000..9c23898
--- /dev/null
+++ b/test/py/test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Wrapper script to invoke pytest with the directory name that contains the
+# U-Boot tests.
+
+import os
+import os.path
+import sys
+
+# Get rid of argv[0]
+sys.argv.pop(0)
+
+# argv; py.test test_directory_name user-supplied-arguments
+args = ["py.test", os.path.dirname(__file__) + "/tests"]
+args.extend(sys.argv)
+
+try:
+    os.execvp("py.test", args)
+except:
+    # Log full details of any exception for detailed analysis
+    import traceback
+    traceback.print_exc()
+    # Hint to the user that they likely simply haven't installed the required
+    # dependencies.
+    print >>sys.stderr, """
+exec(py.test) failed; perhaps you are missing some dependencies?
+See test/py/README.md for the list."""
diff --git a/test/py/tests/test_000_version.py b/test/py/tests/test_000_version.py
new file mode 100644
index 0000000..d262f05
--- /dev/null
+++ b/test/py/tests/test_000_version.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# pytest runs tests the order of their module path, which is related to the
+# filename containing the test. This file is named such that it is sorted
+# first, simply as a very basic sanity check of the functionality of the U-Boot
+# command prompt.
+
+def test_version(u_boot_console):
+    '''Test that the "version" command prints the U-Boot version.'''
+
+    # "version" prints the U-Boot sign-on message. This is usually considered
+    # an error, so that any unexpected reboot causes an error. Here, this
+    # error detection is disabled since the sign-on message is expected.
+    with u_boot_console.disable_check('main_signon'):
+        response = u_boot_console.run_command('version')
+    # Ensure "version" printed what we expected.
+    u_boot_console.validate_version_string_in_text(response)
diff --git a/test/py/tests/test_help.py b/test/py/tests/test_help.py
new file mode 100644
index 0000000..894f3b5
--- /dev/null
+++ b/test/py/tests/test_help.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+def test_help(u_boot_console):
+    '''Test that the "help" command can be executed.'''
+
+    u_boot_console.run_command('help')
diff --git a/test/py/tests/test_unknown_cmd.py b/test/py/tests/test_unknown_cmd.py
new file mode 100644
index 0000000..2de93e0
--- /dev/null
+++ b/test/py/tests/test_unknown_cmd.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+def test_unknown_command(u_boot_console):
+    '''Test that executing an unknown command causes U-Boot to print an
+    error.'''
+
+    # The "unknown command" error is actively expected here,
+    # so error detection for it is disabled.
+    with u_boot_console.disable_check('unknown_command'):
+        response = u_boot_console.run_command('non_existent_cmd')
+    assert('Unknown command \'non_existent_cmd\' - try \'help\'' in response)
diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py
new file mode 100644
index 0000000..520f9a9
--- /dev/null
+++ b/test/py/u_boot_console_base.py
@@ -0,0 +1,360 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Common logic to interact with U-Boot via the console. This class provides
+# the interface that tests use to execute U-Boot shell commands and wait for
+# their results. Sub-classes exist to perform board-type-specific setup
+# operations, such as spawning a sub-process for Sandbox, or attaching to the
+# serial console of real hardware.
+
+import multiplexed_log
+import os
+import pytest
+import re
+import sys
+
+# Regexes for text we expect U-Boot to send to the console.
+pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)')
+pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}-[^\r\n]*)')
+pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
+pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
+pattern_error_notification = re.compile('## Error: ')
+
+class ConsoleDisableCheck(object):
+    '''Context manager (for Python's with statement) that temporarily disables
+    the specified console output error check. This is useful when deliberately
+    executing a command that is known to trigger one of the error checks, in
+    order to test that the error condition is actually raised. This class is
+    used internally by ConsoleBase::disable_check(); it is not intended for
+    direct usage.'''
+
+    def __init__(self, console, check_type):
+        self.console = console
+        self.check_type = check_type
+
+    def __enter__(self):
+        self.console.disable_check_count[self.check_type] += 1
+
+    def __exit__(self, extype, value, traceback):
+        self.console.disable_check_count[self.check_type] -= 1
+
+class ConsoleBase(object):
+    '''The interface through which test functions interact with the U-Boot
+    console. This primarily involves executing shell commands, capturing their
+    results, and checking for common error conditions. Some common utilities
+    are also provided too.'''
+
+    def __init__(self, log, config, max_fifo_fill):
+        '''Initialize a U-Boot console connection.
+
+        Can only usefully be called by sub-classes.
+
+        Args:
+            log: A mulptiplex_log.Logfile object, to which the U-Boot output
+                will be logged.
+            config: A configuration data structure, as built by conftest.py.
+            max_fifo_fill: The maximum number of characters to send to U-Boot
+                command-line before waiting for U-Boot to echo the characters
+                back. For UART-based HW without HW flow control, this value
+                should be set less than the UART RX FIFO size to avoid
+                overflow, assuming that U-Boot can't keep up with full-rate
+                traffic at the baud rate.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.log = log
+        self.config = config
+        self.max_fifo_fill = max_fifo_fill
+
+        self.logstream = self.log.get_stream('console', sys.stdout)
+
+        # Array slice removes leading/trailing quotes
+        self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
+        self.prompt_escaped = re.escape(self.prompt)
+        self.p = None
+        self.disable_check_count = {
+            'spl_signon': 0,
+            'main_signon': 0,
+            'unknown_command': 0,
+            'error_notification': 0,
+        }
+
+        self.at_prompt = False
+        self.at_prompt_logevt = None
+        self.ram_base = None
+
+    def close(self):
+        '''Terminate the connection to the U-Boot console.
+
+        This function is only useful once all interaction with U-Boot is
+        complete. Once this function is called, data cannot be sent to or
+        received from U-Boot.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        if self.p:
+            self.p.close()
+        self.logstream.close()
+
+    def run_command(self, cmd, wait_for_echo=True, send_nl=True,
+            wait_for_prompt=True):
+        '''Execute a command via the U-Boot console.
+
+        The command is always sent to U-Boot.
+
+        U-Boot echoes any command back to its output, and this function
+        typically waits for that to occur. The wait can be disabled by setting
+        wait_for_echo=False, which is useful e.g. when sending CTRL-C to
+        interrupt a long-running command such as "ums".
+
+        Command execution is typically triggered by sending a newline
+        character. This can be disabled by setting send_nl=False, which is
+        also useful when sending CTRL-C.
+
+        This function typically waits for the command to finish executing, and
+        returns the console output that it generated. This can be disabled by
+        setting wait_for_prompt=False, which is useful when invoking a long-
+        running command such as "ums".
+
+        Args:
+            cmd: The command to send.
+            wait_for_each: Boolean indicating whether to wait for U-Boot to
+                echo the command text back to its output.
+            send_nl: Boolean indicating whether to send a newline character
+                after the command string.
+            wait_for_prompt: Boolean indicating whether to wait for the
+                command prompt to be sent by U-Boot. This typically occurs
+                immediately after the command has been executed.
+
+        Returns:
+            If wait_for_prompt == False:
+                Nothing.
+            Else:
+                The output from U-Boot during command execution. In other
+                words, the text U-Boot emitted between the point it echod the
+                command string and emitted the subsequent command prompts.
+        '''
+
+        self.ensure_spawned()
+
+        if self.at_prompt and \
+                self.at_prompt_logevt != self.logstream.logfile.cur_evt:
+            self.logstream.write(self.prompt, implicit=True)
+
+        bad_patterns = []
+        bad_pattern_ids = []
+        if (self.disable_check_count['spl_signon'] == 0 and
+                self.u_boot_spl_signon):
+            bad_patterns.append(self.u_boot_spl_signon_escaped)
+            bad_pattern_ids.append('SPL signon')
+        if self.disable_check_count['main_signon'] == 0:
+            bad_patterns.append(self.u_boot_main_signon_escaped)
+            bad_pattern_ids.append('U-Boot main signon')
+        if self.disable_check_count['unknown_command'] == 0:
+            bad_patterns.append(pattern_unknown_command)
+            bad_pattern_ids.append('Unknown command')
+        if self.disable_check_count['error_notification'] == 0:
+            bad_patterns.append(pattern_error_notification)
+            bad_pattern_ids.append('Error notification')
+        try:
+            self.at_prompt = False
+            if send_nl:
+                cmd += '\n'
+            while cmd:
+                # Limit max outstanding data, so UART FIFOs don't overflow
+                chunk = cmd[:self.max_fifo_fill]
+                cmd = cmd[self.max_fifo_fill:]
+                self.p.send(chunk)
+                if not wait_for_echo:
+                    continue
+                chunk = re.escape(chunk)
+                chunk = chunk.replace('\\\n', '[\r\n]')
+                m = self.p.expect([chunk] + bad_patterns)
+                if m != 0:
+                    self.at_prompt = False
+                    raise Exception('Bad pattern found on console: ' +
+                                    bad_pattern_ids[m - 1])
+            if not wait_for_prompt:
+                return
+            m = self.p.expect([self.prompt_escaped] + bad_patterns)
+            if m != 0:
+                self.at_prompt = False
+                raise Exception('Bad pattern found on console: ' +
+                                bad_pattern_ids[m - 1])
+            self.at_prompt = True
+            self.at_prompt_logevt = self.logstream.logfile.cur_evt
+            # Only strip \r\n; space/TAB might be significant if testing
+            # indentation.
+            return self.p.before.strip('\r\n')
+        except Exception as ex:
+            self.log.error(str(ex))
+            self.cleanup_spawn()
+            raise
+
+    def ctrlc(self):
+        '''Send a CTRL-C character to U-Boot.
+
+        This is useful in order to stop execution of long-running synchronous
+        commands such as "ums".
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.run_command(chr(3), wait_for_echo=False, send_nl=False)
+
+    def ensure_spawned(self):
+        '''Ensure a connection to a correctly running U-Boot instance.
+
+        This may require spawning a new Sandbox process or resetting target
+        hardware, as defined by the implementation sub-class.
+
+        This is an internal function and should not be called directly.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        if self.p:
+            return
+        try:
+            self.at_prompt = False
+            self.log.action('Starting U-Boot')
+            self.p = self.get_spawn()
+            # Real targets can take a long time to scroll large amounts of
+            # text if LCD is enabled. This value may need tweaking in the
+            # future, possibly per-test to be optimal. This works for 'help'
+            # on board 'seaboard'.
+            self.p.timeout = 30000
+            self.p.logfile_read = self.logstream
+            if self.config.buildconfig.get('CONFIG_SPL', False) == 'y':
+                self.p.expect([pattern_u_boot_spl_signon])
+                self.u_boot_spl_signon = self.p.after
+                self.u_boot_spl_signon_escaped = re.escape(self.p.after)
+            else:
+                self.u_boot_spl_signon = None
+            self.p.expect([pattern_u_boot_main_signon])
+            self.u_boot_main_signon = self.p.after
+            self.u_boot_main_signon_escaped = re.escape(self.p.after)
+            build_idx = self.u_boot_main_signon.find(', Build:')
+            if build_idx == -1:
+                self.u_boot_version_string = self.u_boot_main_signon
+            else:
+                self.u_boot_version_string = self.u_boot_main_signon[:build_idx]
+            while True:
+                match = self.p.expect([self.prompt_escaped,
+                                       pattern_stop_autoboot_prompt])
+                if match == 1:
+                    self.p.send(chr(3)) # CTRL-C
+                    continue
+                break
+            self.at_prompt = True
+            self.at_prompt_logevt = self.logstream.logfile.cur_evt
+        except Exception as ex:
+            self.log.error(str(ex))
+            self.cleanup_spawn()
+            raise
+
+    def cleanup_spawn(self):
+        '''Shut down all interaction with the U-Boot instance.
+
+        This is used when an error is detected prior to re-establishing a
+        connection with a fresh U-Boot instance.
+
+        This is an internal function and should not be called directly.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        try:
+            if self.p:
+                self.p.close()
+        except:
+            pass
+        self.p = None
+
+    def validate_version_string_in_text(self, text):
+        '''Assert that a command's output includes the U-Boot signon message.
+
+        This is primarily useful for validating the "version" command without
+        duplicating the signon text regex in a test function.
+
+        Args:
+            text: The command output text to check.
+
+        Returns:
+            Nothing. An exception is raised if the validation fails.
+        '''
+
+        assert(self.u_boot_version_string in text)
+
+    def disable_check(self, check_type):
+        '''Temporarily disable an error check of U-Boot's output.
+
+        Create a new context manager (for use with the "with" statement) which
+        temporarily disables a particular console output error check.
+
+        Args:
+            check_type: The type of error-check to disable. Valid values may
+            be found in self.disable_check_count above.
+
+        Returns:
+            A context manager object.
+        '''
+
+        return ConsoleDisableCheck(self, check_type)
+
+    def find_ram_base(self):
+        '''Find the running U-Boot's RAM location.
+
+        Probe the running U-Boot to determine the address of the first bank
+        of RAM. This is useful for tests that test reading/writing RAM, or
+        load/save files that aren't associated with some standard address
+        typically represented in an environment variable such as
+        ${kernel_addr_r}. The value is cached so that it only needs to be
+        actively read once.
+
+        Args:
+            None.
+
+        Returns:
+            The address of U-Boot's first RAM bank, as an integer.
+        '''
+
+        if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
+            pytest.skip('bdinfo command not supported')
+        if self.ram_base == -1:
+            pytest.skip('Previously failed to find RAM bank start')
+        if self.ram_base is not None:
+            return self.ram_base
+
+        with self.log.section('find_ram_base'):
+            response = self.run_command('bdinfo')
+            for l in response.split('\n'):
+                if '-> start' in l:
+                    self.ram_base = int(l.split('=')[1].strip(), 16)
+                    break
+            if self.ram_base is None:
+                self.ram_base = -1
+                raise Exception('Failed to find RAM bank start in `bdinfo`')
+
+        return self.ram_base
diff --git a/test/py/u_boot_console_exec_attach.py b/test/py/u_boot_console_exec_attach.py
new file mode 100644
index 0000000..0ca9e7c
--- /dev/null
+++ b/test/py/u_boot_console_exec_attach.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Logic to interact with U-Boot running on real hardware, typically via a
+# physical serial port.
+
+import sys
+from u_boot_spawn import Spawn
+from u_boot_console_base import ConsoleBase
+
+class ConsoleExecAttach(ConsoleBase):
+    '''Represents a physical connection to a U-Boot console, typically via a
+    serial port. This implementation executes a sub-process to attach to the
+    console, expecting that the stdin/out of the sub-process will be forwarded
+    to/from the physical hardware. This approach isolates the test infra-
+    structure from the user-/installation-specific details of how to
+    communicate with, and the identity of, serial ports etc.'''
+
+    def __init__(self, log, config):
+        '''Initialize a U-Boot console connection.
+
+        Args:
+            log: A multiplexed_log.Logfile instance.
+            config: A "configuration" object as defined in conftest.py.
+
+        Returns:
+            Nothing.
+        '''
+
+        # The max_fifo_fill value might need tweaking per-board/-SoC?
+        # 1 would be safe anywhere, but is very slow (a pexpect issue?).
+        # 16 is a common FIFO size.
+        # HW flow control would mean this could be infinite.
+        super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16)
+
+        self.log.action('Flashing U-Boot')
+        cmd = ['u-boot-test-flash', config.board_type, config.board_identity]
+        runner = self.log.get_runner(cmd[0], sys.stdout)
+        runner.run(cmd)
+        runner.close()
+
+    def get_spawn(self):
+        '''Connect to a fresh U-Boot instance.
+
+        The target board is reset, so that U-Boot begins running from scratch.
+
+        Args:
+            None.
+
+        Returns:
+            A u_boot_spawn.Spawn object that is attached to U-Boot.
+        '''
+
+        args = [self.config.board_type, self.config.board_identity]
+        s = Spawn(['u-boot-test-console'] + args)
+
+        self.log.action('Resetting board')
+        cmd = ['u-boot-test-reset'] + args
+        runner = self.log.get_runner(cmd[0], sys.stdout)
+        runner.run(cmd)
+        runner.close()
+
+        return s
diff --git a/test/py/u_boot_console_sandbox.py b/test/py/u_boot_console_sandbox.py
new file mode 100644
index 0000000..88b137e
--- /dev/null
+++ b/test/py/u_boot_console_sandbox.py
@@ -0,0 +1,79 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Logic to interact with the sandbox port of U-Boot, running as a sub-process.
+
+import time
+from u_boot_spawn import Spawn
+from u_boot_console_base import ConsoleBase
+
+class ConsoleSandbox(ConsoleBase):
+    '''Represents a connection to a sandbox U-Boot console, executed as a sub-
+    process.'''
+
+    def __init__(self, log, config):
+        '''Initialize a U-Boot console connection.
+
+        Args:
+            log: A multiplexed_log.Logfile instance.
+            config: A "configuration" object as defined in conftest.py.
+
+        Returns:
+            Nothing.
+        '''
+
+        super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
+
+    def get_spawn(self):
+        '''Connect to a fresh U-Boot instance.
+
+        A new sandbox process is created, so that U-Boot begins running from
+        scratch.
+
+        Args:
+            None.
+
+        Returns:
+            A u_boot_spawn.Spawn object that is attached to U-Boot.
+        '''
+
+        return Spawn([self.config.build_dir + '/u-boot'])
+
+    def kill(self, sig):
+        '''Send a specific Unix signal to the sandbox process.
+
+        Args:
+            sig: The Unix signal to send to the process.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.ensure_spawned()
+        self.log.action('kill %d' % sig)
+        self.p.kill(sig)
+
+    def validate_exited(self):
+        '''Determine whether the sandbox process has exited.
+
+        If required, this function waits a reasonable time for the process to
+        exit.
+
+        Args:
+            None.
+
+        Returns:
+            Boolean indicating whether the process has exited.
+        '''
+
+        p = self.p
+        self.p = None
+        for i in xrange(100):
+            ret = not p.isalive()
+            if ret:
+                break
+            time.sleep(0.1)
+        p.close()
+        return ret
diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py
new file mode 100644
index 0000000..1baee63
--- /dev/null
+++ b/test/py/u_boot_spawn.py
@@ -0,0 +1,174 @@
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Logic to spawn a sub-process and interact with its stdio.
+
+import os
+import re
+import pty
+import signal
+import select
+import time
+
+class Timeout(Exception):
+    '''An exception sub-class that indicates that a timeout occurred.'''
+    pass
+
+class Spawn(object):
+    '''Represents the stdio of a freshly created sub-process. Commands may be
+    sent to the process, and responses waited for.
+    '''
+
+    def __init__(self, args):
+        '''Spawn (fork/exec) the sub-process.
+
+        Args:
+            args: array of processs arguments. argv[0] is the command to execute.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.waited = False
+        self.buf = ''
+        self.logfile_read = None
+        self.before = ''
+        self.after = ''
+        self.timeout = None
+
+        (self.pid, self.fd) = pty.fork()
+        if self.pid == 0:
+            try:
+                # For some reason, SIGHUP is set to SIG_IGN at this point when
+                # run under "go" (www.go.cd). Perhaps this happens under any
+                # background (non-interactive) system?
+                signal.signal(signal.SIGHUP, signal.SIG_DFL)
+                os.execvp(args[0], args)
+            except:
+                print 'CHILD EXECEPTION:'
+                import traceback
+                traceback.print_exc()
+            finally:
+                os._exit(255)
+
+        self.poll = select.poll()
+        self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL)
+
+    def kill(self, sig):
+        '''Send unix signal "sig" to the child process.
+
+        Args:
+            sig: The signal number to send.
+
+        Returns:
+            Nothing.
+        '''
+
+        os.kill(self.pid, sig)
+
+    def isalive(self):
+        '''Determine whether the child process is still running.
+
+        Args:
+            None.
+
+        Returns:
+            Boolean indicating whether process is alive.
+        '''
+
+        if self.waited:
+            return False
+
+        w = os.waitpid(self.pid, os.WNOHANG)
+        if w[0] == 0:
+            return True
+
+        self.waited = True
+        return False
+
+    def send(self, data):
+        '''Send data to the sub-process's stdin.
+
+        Args:
+            data: The data to send to the process.
+
+        Returns:
+            Nothing.
+        '''
+
+        os.write(self.fd, data)
+
+    def expect(self, patterns):
+        '''Wait for the sub-process to emit specific data.
+
+        This function waits for the process to emit one pattern from the
+        supplied list of patterns, or for a timeout to occur.
+
+        Args:
+            patterns: A list of strings or regex objects that we expect to
+                see in the sub-process' stdout.
+
+        Returns:
+            The index within the patterns array of the pattern the process
+            emitted.
+
+        Notable exceptions:
+            Timeout, if the process did not emit any of the patterns within
+            the expected time.
+        '''
+
+        for pi in xrange(len(patterns)):
+            if type(patterns[pi]) == type(''):
+                patterns[pi] = re.compile(patterns[pi])
+
+        try:
+            while True:
+                earliest_m = None
+                earliest_pi = None
+                for pi in xrange(len(patterns)):
+                    pattern = patterns[pi]
+                    m = pattern.search(self.buf)
+                    if not m:
+                        continue
+                    if earliest_m and m.start() > earliest_m.start():
+                        continue
+                    earliest_m = m
+                    earliest_pi = pi
+                if earliest_m:
+                    pos = earliest_m.start()
+                    posafter = earliest_m.end() + 1
+                    self.before = self.buf[:pos]
+                    self.after = self.buf[pos:posafter]
+                    self.buf = self.buf[posafter:]
+                    return earliest_pi
+                events = self.poll.poll(self.timeout)
+                if not events:
+                    raise Timeout()
+                c = os.read(self.fd, 1024)
+                if not c:
+                    raise EOFError()
+                if self.logfile_read:
+                    self.logfile_read.write(c)
+                self.buf += c
+        finally:
+            if self.logfile_read:
+                self.logfile_read.flush()
+
+    def close(self):
+        '''Close the stdio connection to the sub-process.
+
+        This also waits a reasonable time for the sub-process to stop running.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        os.close(self.fd)
+        for i in xrange(100):
+            if not self.isalive():
+                break
+            time.sleep(0.1)
-- 
cgit v0.10.2


From 472040d5cbd1bbf9f5f325508b37feb7f129c125 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:25 -0700
Subject: test/py: test that sandbox exits when asked

Test the sandbox port's implementation of the reset command and SIGHUP
handling. These should both cause the U-Boot process to exit gracefully.

Signed-off-by: Stephen Warren 
Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/py/tests/test_sandbox_exit.py b/test/py/tests/test_sandbox_exit.py
new file mode 100644
index 0000000..2aa8eb4
--- /dev/null
+++ b/test/py/tests/test_sandbox_exit.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+import pytest
+import signal
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('reset')
+def test_reset(u_boot_console):
+    '''Test that the "reset" command exits sandbox process.'''
+
+    u_boot_console.run_command('reset', wait_for_prompt=False)
+    assert(u_boot_console.validate_exited())
+    u_boot_console.ensure_spawned()
+
+@pytest.mark.boardspec('sandbox')
+def test_ctrl_c(u_boot_console):
+    '''Test that sending SIGINT to sandbox causes it to exit.'''
+
+    u_boot_console.kill(signal.SIGINT)
+    assert(u_boot_console.validate_exited())
+    u_boot_console.ensure_spawned()
-- 
cgit v0.10.2


From 5ab097abad7d6d2b6da2d0423b95803950831cc6 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:26 -0700
Subject: test/py: add test of setenv/printenv/echo

This tests basic environment variable functionality.

Signed-off-by: Stephen Warren 
Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/command_ut.c b/test/command_ut.c
index 926573a..c086abe 100644
--- a/test/command_ut.c
+++ b/test/command_ut.c
@@ -20,10 +20,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	printf("%s: Testing commands\n", __func__);
 	run_command("env default -f -a", 0);
 
-	/* run a single command */
-	run_command("setenv single 1", 0);
-	assert(!strcmp("1", getenv("single")));
-
 	/* make sure that compound statements work */
 #ifdef CONFIG_SYS_HUSH_PARSER
 	run_command("if test -n ${single} ; then setenv check 1; fi", 0);
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
new file mode 100644
index 0000000..a3e8dd3
--- /dev/null
+++ b/test/py/tests/test_env.py
@@ -0,0 +1,221 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test operation of shell commands relating to environment variables.
+
+import pytest
+
+# FIXME: This might be useful for other tests;
+# perhaps refactor it into ConsoleBase or some other state object?
+class StateTestEnv(object):
+    '''Container that represents the state of all U-Boot environment variables.
+    This enables quick determination of existant/non-existant variable
+    names.
+    '''
+
+    def __init__(self, u_boot_console):
+        '''Initialize a new StateTestEnv object.
+
+        Args:
+            u_boot_console: A U-Boot console.
+
+        Returns:
+            Nothing.
+        '''
+
+        self.u_boot_console = u_boot_console
+        self.get_env()
+        self.set_var = self.get_non_existent_var()
+
+    def get_env(self):
+        '''Read all current environment variables from U-Boot.
+
+        Args:
+            None.
+
+        Returns:
+            Nothing.
+        '''
+
+        response = self.u_boot_console.run_command('printenv')
+        self.env = {}
+        for l in response.splitlines():
+            if not '=' in l:
+                continue
+            (var, value) = l.strip().split('=', 1)
+            self.env[var] = value
+
+    def get_existent_var(self):
+        '''Return the name of an environment variable that exists.
+
+        Args:
+            None.
+
+        Returns:
+            The name of an environment variable.
+        '''
+
+        for var in self.env:
+            return var
+
+    def get_non_existent_var(self):
+        '''Return the name of an environment variable that does not exist.
+
+        Args:
+            None.
+
+        Returns:
+            The name of an environment variable.
+        '''
+
+        n = 0
+        while True:
+            var = 'test_env_' + str(n)
+            if var not in self.env:
+                return var
+            n += 1
+
+@pytest.fixture(scope='module')
+def state_test_env(u_boot_console):
+    '''pytest fixture to provide a StateTestEnv object to tests.'''
+
+    return StateTestEnv(u_boot_console)
+
+def unset_var(state_test_env, var):
+    '''Unset an environment variable.
+
+    This both executes a U-Boot shell command and updates a StateTestEnv
+    object.
+
+    Args:
+        state_test_env: The StateTestEnv object to updata.
+        var: The variable name to unset.
+
+    Returns:
+        Nothing.
+    '''
+
+    state_test_env.u_boot_console.run_command('setenv %s' % var)
+    if var in state_test_env.env:
+        del state_test_env.env[var]
+
+def set_var(state_test_env, var, value):
+    '''Set an environment variable.
+
+    This both executes a U-Boot shell command and updates a StateTestEnv
+    object.
+
+    Args:
+        state_test_env: The StateTestEnv object to updata.
+        var: The variable name to set.
+        value: The value to set the variable to.
+
+    Returns:
+        Nothing.
+    '''
+
+    state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value))
+    state_test_env.env[var] = value
+
+def validate_empty(state_test_env, var):
+    '''Validate that a variable is not set, using U-Boot shell commands.
+
+    Args:
+        var: The variable name to test.
+
+    Returns:
+        Nothing.
+    '''
+
+    response = state_test_env.u_boot_console.run_command('echo $%s' % var)
+    assert response == ''
+
+def validate_set(state_test_env, var, value):
+    '''Validate that a variable is set, using U-Boot shell commands.
+
+    Args:
+        var: The variable name to test.
+        value: The value the variable is expected to have.
+
+    Returns:
+        Nothing.
+    '''
+
+    # echo does not preserve leading, internal, or trailing whitespace in the
+    # value. printenv does, and hence allows more complete testing.
+    response = state_test_env.u_boot_console.run_command('printenv %s' % var)
+    assert response == ('%s=%s' % (var, value))
+
+def test_env_echo_exists(state_test_env):
+    '''Test echoing a variable that exists.'''
+
+    var = state_test_env.get_existent_var()
+    value = state_test_env.env[var]
+    validate_set(state_test_env, var, value)
+
+def test_env_echo_non_existent(state_test_env):
+    '''Test echoing a variable that doesn't exist.'''
+
+    var = state_test_env.set_var
+    validate_empty(state_test_env, var)
+
+def test_env_printenv_non_existent(state_test_env):
+    '''Test printenv error message for non-existant variables.'''
+
+    var = state_test_env.set_var
+    c = state_test_env.u_boot_console
+    with c.disable_check('error_notification'):
+        response = c.run_command('printenv %s' % var)
+    assert(response == '## Error: "%s" not defined' % var)
+
+def test_env_unset_non_existent(state_test_env):
+    '''Test unsetting a nonexistent variable.'''
+
+    var = state_test_env.get_non_existent_var()
+    unset_var(state_test_env, var)
+    validate_empty(state_test_env, var)
+
+def test_env_set_non_existent(state_test_env):
+    '''Test set a non-existant variable.'''
+
+    var = state_test_env.set_var
+    value = 'foo'
+    set_var(state_test_env, var, value)
+    validate_set(state_test_env, var, value)
+
+def test_env_set_existing(state_test_env):
+    '''Test setting an existant variable.'''
+
+    var = state_test_env.set_var
+    value = 'bar'
+    set_var(state_test_env, var, value)
+    validate_set(state_test_env, var, value)
+
+def test_env_unset_existing(state_test_env):
+    '''Test unsetting a variable.'''
+
+    var = state_test_env.set_var
+    unset_var(state_test_env, var)
+    validate_empty(state_test_env, var)
+
+def test_env_expansion_spaces(state_test_env):
+    '''Test expanding a variable that contains a space in its value.'''
+
+    var_space = None
+    var_test = None
+    try:
+        var_space = state_test_env.get_non_existent_var()
+        set_var(state_test_env, var_space, ' ')
+
+        var_test = state_test_env.get_non_existent_var()
+        value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals()
+        set_var(state_test_env, var_test, value)
+        value = ' 1   2 '
+        validate_set(state_test_env, var_test, value)
+    finally:
+        if var_space:
+            unset_var(state_test_env, var_space)
+        if var_test:
+            unset_var(state_test_env, var_test)
-- 
cgit v0.10.2


From 98cee89b55a561917648196a94be7e80455a9ac9 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:27 -0700
Subject: test/py: test the md/mw commands

This tests whether md/mw work, and affect each-other.

Command repeat is also tested.

test/cmd_repeat.sh is removed, since the new Python-based test does
everything it used to.

Signed-off-by: Stephen Warren 
Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/cmd_repeat.sh b/test/cmd_repeat.sh
deleted file mode 100755
index 990e799..0000000
--- a/test/cmd_repeat.sh
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-# Test for U-Boot cli including command repeat
-
-BASE="$(dirname $0)"
-. $BASE/common.sh
-
-run_test() {
-	./${OUTPUT_DIR}/u-boot <${tmp}
-check_results ${tmp}
-rm ${tmp}
-echo "Test passed"
diff --git a/test/py/tests/test_md.py b/test/py/tests/test_md.py
new file mode 100644
index 0000000..94603c7
--- /dev/null
+++ b/test/py/tests/test_md.py
@@ -0,0 +1,36 @@
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md(u_boot_console):
+    '''Test that md reads memory as expected, and that memory can be modified
+    using the mw command.'''
+
+    ram_base = u_boot_console.find_ram_base()
+    addr = '%08x' % ram_base
+    val = 'a5f09876'
+    expected_response = addr + ': ' + val
+    u_boot_console.run_command('mw ' + addr + ' 0 10')
+    response = u_boot_console.run_command('md ' + addr + ' 10')
+    assert(not (expected_response in response))
+    u_boot_console.run_command('mw ' + addr + ' ' + val)
+    response = u_boot_console.run_command('md ' + addr + ' 10')
+    assert(expected_response in response)
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md_repeat(u_boot_console):
+    '''Test command repeat (via executing an empty command) operates correctly
+    for "md"; the command must repeat and dump an incrementing address.'''
+
+    ram_base = u_boot_console.find_ram_base()
+    addr_base = '%08x' % ram_base
+    words = 0x10
+    addr_repeat = '%08x' % (ram_base + (words * 4))
+    u_boot_console.run_command('md %s %x' % (addr_base, words))
+    response = u_boot_console.run_command('')
+    expected_response = addr_repeat + ': '
+    assert(expected_response in response)
-- 
cgit v0.10.2


From 8b86c609b8600c03c72557b7002b490a856902e0 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:28 -0700
Subject: test/py: add test of basic shell functionality

This tests whether the following features of the U-Boot shell:
- Execution of a directly entered command.
- Compound commands (; delimiter).
- Quoting of arguments containing spaces.
- Executing commands from environment variables.

Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/command_ut.c b/test/command_ut.c
index c086abe..43bd2c1 100644
--- a/test/command_ut.c
+++ b/test/command_ut.c
@@ -27,10 +27,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	run_command("setenv check", 0);
 #endif
 
-	/* commands separated by ; */
-	run_command_list("setenv list 1; setenv list ${list}1", -1, 0);
-	assert(!strcmp("11", getenv("list")));
-
 	/* commands separated by \n */
 	run_command_list("setenv list 1\n setenv list ${list}1", -1, 0);
 	assert(!strcmp("11", getenv("list")));
@@ -39,11 +35,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	run_command_list("setenv list 1${list}\n", -1, 0);
 	assert(!strcmp("111", getenv("list")));
 
-	/* three commands in a row */
-	run_command_list("setenv list 1\n setenv list ${list}2; "
-		"setenv list ${list}3", -1, 0);
-	assert(!strcmp("123", getenv("list")));
-
 	/* a command string with \0 in it. Stuff after \0 should be ignored */
 	run_command("setenv list", 0);
 	run_command_list(test_cmd, sizeof(test_cmd), 0);
@@ -62,13 +53,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	assert(run_command_list("false", -1, 0) == 1);
 	assert(run_command_list("echo", -1, 0) == 0);
 
-	run_command("setenv foo 'setenv monty 1; setenv python 2'", 0);
-	run_command("run foo", 0);
-	assert(getenv("monty") != NULL);
-	assert(!strcmp("1", getenv("monty")));
-	assert(getenv("python") != NULL);
-	assert(!strcmp("2", getenv("python")));
-
 #ifdef CONFIG_SYS_HUSH_PARSER
 	run_command("setenv foo 'setenv black 1\nsetenv adder 2'", 0);
 	run_command("run foo", 0);
diff --git a/test/py/tests/test_shell_basics.py b/test/py/tests/test_shell_basics.py
new file mode 100644
index 0000000..719ce61
--- /dev/null
+++ b/test/py/tests/test_shell_basics.py
@@ -0,0 +1,42 @@
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test basic shell functionality, such as commands separate by semi-colons.
+
+def test_shell_execute(u_boot_console):
+    '''Test any shell command.'''
+
+    response = u_boot_console.run_command('echo hello')
+    assert response.strip() == 'hello'
+
+def test_shell_semicolon_two(u_boot_console):
+    '''Test two shell commands separate by a semi-colon.'''
+
+    cmd = 'echo hello; echo world'
+    response = u_boot_console.run_command(cmd)
+    # This validation method ignores the exact whitespace between the strings
+    assert response.index('hello') < response.index('world')
+
+def test_shell_semicolon_three(u_boot_console):
+    '''Test three shell commands separate by a semi-colon, with variable
+    expansion dependencies between them.'''
+
+    cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
+        'echo ${list}'
+    response = u_boot_console.run_command(cmd)
+    assert response.strip() == '123'
+    u_boot_console.run_command('setenv list')
+
+def test_shell_run(u_boot_console):
+    '''Test the "run" shell command.'''
+
+    u_boot_console.run_command('setenv foo \"setenv monty 1; setenv python 2\"')
+    u_boot_console.run_command('run foo')
+    response = u_boot_console.run_command('echo $monty')
+    assert response.strip() == '1'
+    response = u_boot_console.run_command('echo $python')
+    assert response.strip() == '2'
+    u_boot_console.run_command('setenv foo')
+    u_boot_console.run_command('setenv monty')
+    u_boot_console.run_command('setenv python')
-- 
cgit v0.10.2


From cc156f3fc0b2a8cb6dbb529ada54ba1a902e2019 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:29 -0700
Subject: test/py: test the shell if command

Migrate all most tests from command_ut.c into the Python test system.
This allows the tests to be run against any U-Boot binary that supports
the if command (i.e. where hush is enabled) without requiring that
binary to be permanently bloated with the code from command_ut.

Some tests in command_ut.c can only be executed from C code, since they
test internal (more unit-level) features of various U-Boot APIs. The
migrated tests can all operate directly from the U-Boot console.

Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/command_ut.c b/test/command_ut.c
index 43bd2c1..54bf62b 100644
--- a/test/command_ut.c
+++ b/test/command_ut.c
@@ -7,9 +7,6 @@
 #define DEBUG
 
 #include 
-#ifdef CONFIG_SANDBOX
-#include 
-#endif
 
 static const char test_cmd[] = "setenv list 1\n setenv list ${list}2; "
 		"setenv list ${list}3\0"
@@ -20,13 +17,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	printf("%s: Testing commands\n", __func__);
 	run_command("env default -f -a", 0);
 
-	/* make sure that compound statements work */
-#ifdef CONFIG_SYS_HUSH_PARSER
-	run_command("if test -n ${single} ; then setenv check 1; fi", 0);
-	assert(!strcmp("1", getenv("check")));
-	run_command("setenv check", 0);
-#endif
-
 	/* commands separated by \n */
 	run_command_list("setenv list 1\n setenv list ${list}1", -1, 0);
 	assert(!strcmp("11", getenv("list")));
@@ -60,112 +50,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 	assert(!strcmp("1", getenv("black")));
 	assert(getenv("adder") != NULL);
 	assert(!strcmp("2", getenv("adder")));
-
-	/* Test the 'test' command */
-
-#define HUSH_TEST(name, expr, expected_result) \
-	run_command("if test " expr " ; then " \
-			"setenv " #name "_" #expected_result " y; else " \
-			"setenv " #name "_" #expected_result " n; fi", 0); \
-	assert(!strcmp(#expected_result, getenv(#name "_" #expected_result))); \
-	setenv(#name "_" #expected_result, NULL);
-
-	/* Basic operators */
-	HUSH_TEST(streq, "aaa = aaa", y);
-	HUSH_TEST(streq, "aaa = bbb", n);
-
-	HUSH_TEST(strneq, "aaa != bbb", y);
-	HUSH_TEST(strneq, "aaa != aaa", n);
-
-	HUSH_TEST(strlt, "aaa < bbb", y);
-	HUSH_TEST(strlt, "bbb < aaa", n);
-
-	HUSH_TEST(strgt, "bbb > aaa", y);
-	HUSH_TEST(strgt, "aaa > bbb", n);
-
-	HUSH_TEST(eq, "123 -eq 123", y);
-	HUSH_TEST(eq, "123 -eq 456", n);
-
-	HUSH_TEST(ne, "123 -ne 456", y);
-	HUSH_TEST(ne, "123 -ne 123", n);
-
-	HUSH_TEST(lt, "123 -lt 456", y);
-	HUSH_TEST(lt_eq, "123 -lt 123", n);
-	HUSH_TEST(lt, "456 -lt 123", n);
-
-	HUSH_TEST(le, "123 -le 456", y);
-	HUSH_TEST(le_eq, "123 -le 123", y);
-	HUSH_TEST(le, "456 -le 123", n);
-
-	HUSH_TEST(gt, "456 -gt 123", y);
-	HUSH_TEST(gt_eq, "123 -gt 123", n);
-	HUSH_TEST(gt, "123 -gt 456", n);
-
-	HUSH_TEST(ge, "456 -ge 123", y);
-	HUSH_TEST(ge_eq, "123 -ge 123", y);
-	HUSH_TEST(ge, "123 -ge 456", n);
-
-	HUSH_TEST(z, "-z \"\"", y);
-	HUSH_TEST(z, "-z \"aaa\"", n);
-
-	HUSH_TEST(n, "-n \"aaa\"", y);
-	HUSH_TEST(n, "-n \"\"", n);
-
-	/* Inversion of simple tests */
-	HUSH_TEST(streq_inv, "! aaa = aaa", n);
-	HUSH_TEST(streq_inv, "! aaa = bbb", y);
-
-	HUSH_TEST(streq_inv_inv, "! ! aaa = aaa", y);
-	HUSH_TEST(streq_inv_inv, "! ! aaa = bbb", n);
-
-	/* Binary operators */
-	HUSH_TEST(or_0_0, "aaa != aaa -o bbb != bbb", n);
-	HUSH_TEST(or_0_1, "aaa != aaa -o bbb = bbb", y);
-	HUSH_TEST(or_1_0, "aaa = aaa -o bbb != bbb", y);
-	HUSH_TEST(or_1_1, "aaa = aaa -o bbb = bbb", y);
-
-	HUSH_TEST(and_0_0, "aaa != aaa -a bbb != bbb", n);
-	HUSH_TEST(and_0_1, "aaa != aaa -a bbb = bbb", n);
-	HUSH_TEST(and_1_0, "aaa = aaa -a bbb != bbb", n);
-	HUSH_TEST(and_1_1, "aaa = aaa -a bbb = bbb", y);
-
-	/* Inversion within binary operators */
-	HUSH_TEST(or_0_0_inv, "! aaa != aaa -o ! bbb != bbb", y);
-	HUSH_TEST(or_0_1_inv, "! aaa != aaa -o ! bbb = bbb", y);
-	HUSH_TEST(or_1_0_inv, "! aaa = aaa -o ! bbb != bbb", y);
-	HUSH_TEST(or_1_1_inv, "! aaa = aaa -o ! bbb = bbb", n);
-
-	HUSH_TEST(or_0_0_inv_inv, "! ! aaa != aaa -o ! ! bbb != bbb", n);
-	HUSH_TEST(or_0_1_inv_inv, "! ! aaa != aaa -o ! ! bbb = bbb", y);
-	HUSH_TEST(or_1_0_inv_inv, "! ! aaa = aaa -o ! ! bbb != bbb", y);
-	HUSH_TEST(or_1_1_inv_inv, "! ! aaa = aaa -o ! ! bbb = bbb", y);
-
-	setenv("ut_var_nonexistent", NULL);
-	setenv("ut_var_exists", "1");
-	HUSH_TEST(z_varexp_quoted, "-z \"$ut_var_nonexistent\"", y);
-	HUSH_TEST(z_varexp_quoted, "-z \"$ut_var_exists\"", n);
-	setenv("ut_var_exists", NULL);
-
-	run_command("setenv ut_var_space \" \"", 0);
-	assert(!strcmp(getenv("ut_var_space"), " "));
-	run_command("setenv ut_var_test $ut_var_space", 0);
-	assert(!getenv("ut_var_test"));
-	run_command("setenv ut_var_test \"$ut_var_space\"", 0);
-	assert(!strcmp(getenv("ut_var_test"), " "));
-	run_command("setenv ut_var_test \" 1${ut_var_space}${ut_var_space} 2 \"", 0);
-	assert(!strcmp(getenv("ut_var_test"), " 1   2 "));
-	setenv("ut_var_space", NULL);
-	setenv("ut_var_test", NULL);
-
-#ifdef CONFIG_SANDBOX
-	/* File existence */
-	HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", n);
-	run_command("sb save hostfs - creating_this_file_breaks_uboot_unit_test 0 1", 0);
-	HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", y);
-	/* Perhaps this could be replaced by an "rm" shell command one day */
-	assert(!os_unlink("creating_this_file_breaks_uboot_unit_test"));
-	HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", n);
-#endif
 #endif
 
 	assert(run_command("", 0) == 0);
diff --git a/test/py/tests/test_hush_if_test.py b/test/py/tests/test_hush_if_test.py
new file mode 100644
index 0000000..cf4c3ae
--- /dev/null
+++ b/test/py/tests/test_hush_if_test.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test operation of the "if" shell command.
+
+import os
+import os.path
+import pytest
+
+# The list of "if test" conditions to test.
+subtests = (
+    # Base if functionality.
+
+    ('true', True),
+    ('false', False),
+
+    # Basic operators.
+
+    ('test aaa = aaa', True),
+    ('test aaa = bbb', False),
+
+    ('test aaa != bbb', True),
+    ('test aaa != aaa', False),
+
+    ('test aaa < bbb', True),
+    ('test bbb < aaa', False),
+
+    ('test bbb > aaa', True),
+    ('test aaa > bbb', False),
+
+    ('test 123 -eq 123', True),
+    ('test 123 -eq 456', False),
+
+    ('test 123 -ne 456', True),
+    ('test 123 -ne 123', False),
+
+    ('test 123 -lt 456', True),
+    ('test 123 -lt 123', False),
+    ('test 456 -lt 123', False),
+
+    ('test 123 -le 456', True),
+    ('test 123 -le 123', True),
+    ('test 456 -le 123', False),
+
+    ('test 456 -gt 123', True),
+    ('test 123 -gt 123', False),
+    ('test 123 -gt 456', False),
+
+    ('test 456 -ge 123', True),
+    ('test 123 -ge 123', True),
+    ('test 123 -ge 456', False),
+
+    ('test -z ""', True),
+    ('test -z "aaa"', False),
+
+    ('test -n "aaa"', True),
+    ('test -n ""', False),
+
+    # Inversion of simple tests.
+
+    ('test ! aaa = aaa', False),
+    ('test ! aaa = bbb', True),
+    ('test ! ! aaa = aaa', True),
+    ('test ! ! aaa = bbb', False),
+
+    # Binary operators.
+
+    ('test aaa != aaa -o bbb != bbb', False),
+    ('test aaa != aaa -o bbb = bbb', True),
+    ('test aaa = aaa -o bbb != bbb', True),
+    ('test aaa = aaa -o bbb = bbb', True),
+
+    ('test aaa != aaa -a bbb != bbb', False),
+    ('test aaa != aaa -a bbb = bbb', False),
+    ('test aaa = aaa -a bbb != bbb', False),
+    ('test aaa = aaa -a bbb = bbb', True),
+
+    # Inversion within binary operators.
+
+    ('test ! aaa != aaa -o ! bbb != bbb', True),
+    ('test ! aaa != aaa -o ! bbb = bbb', True),
+    ('test ! aaa = aaa -o ! bbb != bbb', True),
+    ('test ! aaa = aaa -o ! bbb = bbb', False),
+
+    ('test ! ! aaa != aaa -o ! ! bbb != bbb', False),
+    ('test ! ! aaa != aaa -o ! ! bbb = bbb', True),
+    ('test ! ! aaa = aaa -o ! ! bbb != bbb', True),
+    ('test ! ! aaa = aaa -o ! ! bbb = bbb', True),
+
+    # -z operator.
+
+    ('test -z "$ut_var_nonexistent"', True),
+    ('test -z "$ut_var_exists"', False),
+)
+
+def exec_hush_if(u_boot_console, expr, result):
+    '''Execute a shell "if" command, and validate its result.'''
+
+    cmd = 'if ' + expr + '; then echo true; else echo false; fi'
+    response = u_boot_console.run_command(cmd)
+    assert response.strip() == str(result).lower()
+
+@pytest.mark.buildconfigspec('sys_hush_parser')
+def test_hush_if_test_setup(u_boot_console):
+    '''Set up environment variables used during the "if" tests.'''
+
+    u_boot_console.run_command('setenv ut_var_nonexistent')
+    u_boot_console.run_command('setenv ut_var_exists 1')
+
+@pytest.mark.buildconfigspec('sys_hush_parser')
+@pytest.mark.parametrize('expr,result', subtests)
+def test_hush_if_test(u_boot_console, expr, result):
+    '''Test a single "if test" condition.'''
+
+    exec_hush_if(u_boot_console, expr, result)
+
+@pytest.mark.buildconfigspec('sys_hush_parser')
+def test_hush_if_test_teardown(u_boot_console):
+    '''Clean up environment variables used during the "if" tests.'''
+
+    u_boot_console.run_command('setenv ut_var_exists')
+
+@pytest.mark.buildconfigspec('sys_hush_parser')
+# We might test this on real filesystems via UMS, DFU, 'save', etc.
+# Of those, only UMS currently allows file removal though.
+@pytest.mark.boardspec('sandbox')
+def test_hush_if_test_host_file_exists(u_boot_console):
+    '''Test the "if test -e" shell command.'''
+
+    test_file = u_boot_console.config.result_dir + \
+        '/creating_this_file_breaks_u_boot_tests'
+
+    try:
+        os.unlink(test_file)
+    except:
+        pass
+    assert not os.path.exists(test_file)
+
+    expr = 'test -e hostfs - ' + test_file
+    exec_hush_if(u_boot_console, expr, False)
+
+    try:
+        with file(test_file, 'wb'):
+            pass
+        assert os.path.exists(test_file)
+
+        expr = 'test -e hostfs - ' + test_file
+        exec_hush_if(u_boot_console, expr, True)
+    finally:
+        os.unlink(test_file)
+
+    expr = 'test -e hostfs - ' + test_file
+    exec_hush_if(u_boot_console, expr, False)
-- 
cgit v0.10.2


From 3045d7f043d440498dc327f160c274d17a633c95 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:30 -0700
Subject: test/py: test the ums command

This test invokes the "ums" command in U-Boot, and validates that a USB
storage device is enumerated on the test host system, and can be read
from.

Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/py/tests/test_ums.py b/test/py/tests/test_ums.py
new file mode 100644
index 0000000..a137221
--- /dev/null
+++ b/test/py/tests/test_ums.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Test U-Boot's "ums" command. At present, this test only ensures that a UMS
+# device can be enumerated by the host/test machine. In the future, this test
+# should be enhanced to validate disk IO.
+
+import os
+import pytest
+import time
+
+'''
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+env__usb_dev_ports = (
+    {'tgt_usb_ctlr': '0', 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0'},
+)
+
+env__block_devs = (
+    {'type': 'mmc', 'id': '0'}, # eMMC; always present
+    {'type': 'mmc', 'id': '1'}, # SD card; present since I plugged one in
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+'''
+
+def open_ums_device(host_ums_dev_node):
+    '''Attempt to open a device node, returning either the opened file handle,
+    or None on any error.'''
+
+    try:
+        return open(host_ums_dev_node, 'rb')
+    except:
+        return None
+
+def wait_for_ums_device(host_ums_dev_node):
+    '''Continually attempt to open the device node exported by the "ums"
+    command, and either return the opened file handle, or raise an exception
+    after a timeout.'''
+
+    for i in xrange(100):
+        fh = open_ums_device(host_ums_dev_node)
+        if fh:
+            return fh
+        time.sleep(0.1)
+    raise Exception('UMS device did not appear')
+
+def wait_for_ums_device_gone(host_ums_dev_node):
+    '''Continually attempt to open the device node exported by the "ums"
+    command, and either return once the device has disappeared, or raise an
+    exception if it does not before a timeout occurs.'''
+
+    for i in xrange(100):
+        fh = open_ums_device(host_ums_dev_node)
+        if not fh:
+            return
+        fh.close()
+        time.sleep(0.1)
+    raise Exception('UMS device did not disappear')
+
+@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
+def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
+    '''Test the "ums" command; the host system must be able to enumerate a UMS
+    device when "ums" is running, and this device must disappear when "ums" is
+    aborted.'''
+
+    tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
+    host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
+
+    # We're interested in testing USB device mode on each port, not the cross-
+    # product of that with each device. So, just pick the first entry in the
+    # device list here. We'll test each block device somewhere else.
+    tgt_dev_type = env__block_devs[0]['type']
+    tgt_dev_id = env__block_devs[0]['id']
+
+    cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
+    u_boot_console.run_command('ums 0 mmc 0', wait_for_prompt=False)
+    fh = wait_for_ums_device(host_ums_dev_node)
+    fh.read(4096)
+    fh.close()
+    u_boot_console.ctrlc()
+    wait_for_ums_device_gone(host_ums_dev_node)
-- 
cgit v0.10.2


From 1c8b4d5f4f6a8bad12b2b690a04f96731410bb73 Mon Sep 17 00:00:00 2001
From: Stephen Warren 
Date: Fri, 15 Jan 2016 11:15:31 -0700
Subject: test/py: add a test for the sleep command

Execute "sleep", and validate that it sleeps for approximately the correct
amount of time.

Signed-off-by: Stephen Warren 
Acked-by: Simon Glass 

diff --git a/test/py/tests/test_sleep.py b/test/py/tests/test_sleep.py
new file mode 100644
index 0000000..64f1ddf
--- /dev/null
+++ b/test/py/tests/test_sleep.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+import pytest
+import time
+
+def test_sleep(u_boot_console):
+    '''Test the sleep command, and validate that it sleeps for approximately
+    the correct amount of time.'''
+
+    # Do this before we time anything, to make sure U-Boot is already running.
+    # Otherwise, the system boot time is included in the time measurement.
+    u_boot_console.ensure_spawned()
+
+    # 3s isn't too long, but is enough to cross a few second boundaries.
+    sleep_time = 3
+    tstart = time.time()
+    u_boot_console.run_command('sleep %d' % sleep_time)
+    tend = time.time()
+    elapsed = tend - tstart
+    delta_to_expected = abs(elapsed - sleep_time)
+    # 0.25s margin is hopefully enough to account for any system overhead.
+    assert delta_to_expected < 0.25
-- 
cgit v0.10.2


From 9f29aeb89de8199ebaea9995dbb30ea5fa2f1085 Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:38:58 -0800
Subject: Revert "fdt_support: Add multi-serial support for stdout fixup"

This reverts commit 3e303f748cf57fb23e8ec95ab7eac0074be50e2b.

The fix up in the /aliases node does not work under the following
scenarios:
- Not every non-DM serial driver was written to have a driver name
  that conforms the format of "serial%d" or "eserial%d".
- With driver model serial, the stdio_devices[] stores the serial
  device node name in the device tree.

Signed-off-by: Bin Meng 
Acked-by: Simon Glass 

diff --git a/common/fdt_support.c b/common/fdt_support.c
index 09f9237..75d0858 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -131,18 +131,6 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
 			      OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1);
 }
 #elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX)
-static void fdt_fill_multisername(char *sername, size_t maxlen)
-{
-	const char *outname = stdio_devices[stdout]->name;
-
-	if (strcmp(outname, "serial") > 0)
-		strncpy(sername, outname, maxlen);
-
-	/* eserial? */
-	if (strcmp(outname + 1, "serial") > 0)
-		strncpy(sername, outname + 1, maxlen);
-}
-
 static int fdt_fixup_stdout(void *fdt, int chosenoff)
 {
 	int err;
@@ -152,9 +140,7 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff)
 	int len;
 	char tmp[256]; /* long enough */
 
-	fdt_fill_multisername(sername, sizeof(sername) - 1);
-	if (!sername[0])
-		sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
+	sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
 
 	aliasoff = fdt_path_offset(fdt, "/aliases");
 	if (aliasoff < 0) {
-- 
cgit v0.10.2


From f833cd6279666e1c2de6fc271bfd5d451003ed3b Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:38:59 -0800
Subject: arm: ls1021atwr: Convert to driver model and enable serial support

Convert ls1021atwr_nor to driver model support. As a start, enable
ns16550 serial port driver.

Signed-off-by: Bin Meng 
Acked-by: Simon Glass 

diff --git a/arch/arm/dts/ls1021a-twr.dts b/arch/arm/dts/ls1021a-twr.dts
index 6ccd332..aead13f 100644
--- a/arch/arm/dts/ls1021a-twr.dts
+++ b/arch/arm/dts/ls1021a-twr.dts
@@ -19,6 +19,10 @@
 		spi0 = &qspi;
 		spi1 = &dspi1;
 	};
+
+	chosen {
+		stdout-path = &uart0;
+	};
 };
 
 &qspi {
diff --git a/arch/arm/dts/ls1021a.dtsi b/arch/arm/dts/ls1021a.dtsi
index 7fadd7c..ee0e554 100644
--- a/arch/arm/dts/ls1021a.dtsi
+++ b/arch/arm/dts/ls1021a.dtsi
@@ -218,7 +218,6 @@
 			compatible = "fsl,16550-FIFO64", "ns16550a";
 			reg = <0x21c0500 0x100>;
 			interrupts = ;
-			clock-frequency = <0>;
 			fifo-size = <15>;
 			status = "disabled";
 		};
@@ -227,7 +226,6 @@
 			compatible = "fsl,16550-FIFO64", "ns16550a";
 			reg = <0x21c0600 0x100>;
 			interrupts = ;
-			clock-frequency = <0>;
 			fifo-size = <15>;
 			status = "disabled";
 		};
@@ -236,7 +234,6 @@
 			compatible = "fsl,16550-FIFO64", "ns16550a";
 			reg = <0x21d0500 0x100>;
 			interrupts = ;
-			clock-frequency = <0>;
 			fifo-size = <15>;
 			status = "disabled";
 		};
@@ -245,7 +242,6 @@
 			compatible = "fsl,16550-FIFO64", "ns16550a";
 			reg = <0x21d0600 0x100>;
 			interrupts = ;
-			clock-frequency = <0>;
 			fifo-size = <15>;
 			status = "disabled";
 		};
diff --git a/configs/ls1021atwr_nor_defconfig b/configs/ls1021atwr_nor_defconfig
index aa874fd..2b79443 100644
--- a/configs/ls1021atwr_nor_defconfig
+++ b/configs/ls1021atwr_nor_defconfig
@@ -1,6 +1,10 @@
 CONFIG_ARM=y
 CONFIG_TARGET_LS1021ATWR=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr"
 # CONFIG_CMD_SETEXPR is not set
+CONFIG_OF_CONTROL=y
+CONFIG_DM=y
 CONFIG_NETDEVICES=y
 CONFIG_E1000=y
 CONFIG_SYS_NS16550=y
diff --git a/include/configs/ls1021atwr.h b/include/configs/ls1021atwr.h
index c12ba3a..bbef2a7 100644
--- a/include/configs/ls1021atwr.h
+++ b/include/configs/ls1021atwr.h
@@ -271,7 +271,9 @@
 #else
 #define CONFIG_CONS_INDEX		1
 #define CONFIG_SYS_NS16550_SERIAL
+#ifndef CONFIG_DM_SERIAL
 #define CONFIG_SYS_NS16550_REG_SIZE	1
+#endif
 #define CONFIG_SYS_NS16550_CLK		get_serial_clock()
 #endif
 
-- 
cgit v0.10.2


From 5ed07cf53ccd47be65b5bcfef7432a75bfbfa65e Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:00 -0800
Subject: serial: lpuart: Move CONFIG_FSL_LPUART to Kconfig

LPUART is seen on Freescale VF610 and QorIQ Layerscape devices.
Create a Kconfig option and move it to defconfig for all boards
that have this serial driver.

Signed-off-by: Bin Meng 
Reviewed-by: Simon Glass 
Acked-by: Stefan Agner 
Acked-by: Simon Glass 

diff --git a/configs/colibri_vf_defconfig b/configs/colibri_vf_defconfig
index f8441e3..45917c8 100644
--- a/configs/colibri_vf_defconfig
+++ b/configs/colibri_vf_defconfig
@@ -8,3 +8,4 @@ CONFIG_CMD_GPIO=y
 CONFIG_DM=y
 CONFIG_NAND_VF610_NFC=y
 CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/colibri_vf_dtb_defconfig b/configs/colibri_vf_dtb_defconfig
index 3596cec..b1a843a 100644
--- a/configs/colibri_vf_dtb_defconfig
+++ b/configs/colibri_vf_dtb_defconfig
@@ -11,3 +11,4 @@ CONFIG_OF_CONTROL=y
 CONFIG_DM=y
 CONFIG_NAND_VF610_NFC=y
 CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/ls1021aqds_ddr4_nor_lpuart_defconfig b/configs/ls1021aqds_ddr4_nor_lpuart_defconfig
index 68bd117..44b2a0d 100644
--- a/configs/ls1021aqds_ddr4_nor_lpuart_defconfig
+++ b/configs/ls1021aqds_ddr4_nor_lpuart_defconfig
@@ -4,3 +4,4 @@ CONFIG_SYS_EXTRA_OPTIONS="SYS_FSL_DDR4,LPUART"
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_NETDEVICES=y
 CONFIG_E1000=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/ls1021aqds_nor_lpuart_defconfig b/configs/ls1021aqds_nor_lpuart_defconfig
index b2f6832..1186af2 100644
--- a/configs/ls1021aqds_nor_lpuart_defconfig
+++ b/configs/ls1021aqds_nor_lpuart_defconfig
@@ -4,3 +4,4 @@ CONFIG_SYS_EXTRA_OPTIONS="LPUART"
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_NETDEVICES=y
 CONFIG_E1000=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/ls1021atwr_nor_lpuart_defconfig b/configs/ls1021atwr_nor_lpuart_defconfig
index d7afca9..58cd61f 100644
--- a/configs/ls1021atwr_nor_lpuart_defconfig
+++ b/configs/ls1021atwr_nor_lpuart_defconfig
@@ -4,3 +4,4 @@ CONFIG_SYS_EXTRA_OPTIONS="LPUART"
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_NETDEVICES=y
 CONFIG_E1000=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/pcm052_defconfig b/configs/pcm052_defconfig
index 9125645..26ab733 100644
--- a/configs/pcm052_defconfig
+++ b/configs/pcm052_defconfig
@@ -3,3 +3,4 @@ CONFIG_TARGET_PCM052=y
 CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/phytec/pcm052/imximage.cfg,ENV_IS_IN_NAND"
 CONFIG_NAND_VF610_NFC=y
 CONFIG_SYS_NAND_BUSWIDTH_16BIT=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig
index dc8df5c..d51c93b 100644
--- a/configs/vf610twr_defconfig
+++ b/configs/vf610twr_defconfig
@@ -6,3 +6,4 @@ CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_I
 CONFIG_NAND_VF610_NFC=y
 CONFIG_SYS_NAND_BUSWIDTH_16BIT=y
 CONFIG_SPI_FLASH=y
+CONFIG_FSL_LPUART=y
diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig
index 98880f3..299fa8f 100644
--- a/configs/vf610twr_nand_defconfig
+++ b/configs/vf610twr_nand_defconfig
@@ -6,3 +6,4 @@ CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_I
 CONFIG_NAND_VF610_NFC=y
 CONFIG_SYS_NAND_BUSWIDTH_16BIT=y
 CONFIG_SPI_FLASH=y
+CONFIG_FSL_LPUART=y
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 92084a2..83068cf 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -228,6 +228,12 @@ config ALTERA_UART
 	  Select this to enable an UART for Altera devices. Please find
 	  details on the "Embedded Peripherals IP User Guide" of Altera.
 
+config FSL_LPUART
+	bool "Freescale LPUART support"
+	help
+	  Select this to enable a Low Power UART for Freescale VF610 and
+	  QorIQ Layerscape devices.
+
 config SYS_NS16550
 	bool "NS16550 UART or compatible"
 	help
diff --git a/include/configs/colibri_vf.h b/include/configs/colibri_vf.h
index 708c79a..5aed3a5 100644
--- a/include/configs/colibri_vf.h
+++ b/include/configs/colibri_vf.h
@@ -36,7 +36,6 @@
 
 #define CONFIG_BOARD_EARLY_INIT_F
 
-#define CONFIG_FSL_LPUART
 #define LPUART_BASE			UART0_BASE
 
 /* Allow to overwrite serial and ethaddr */
diff --git a/include/configs/ls1021aqds.h b/include/configs/ls1021aqds.h
index 2e8dbc7..e8b1eca 100644
--- a/include/configs/ls1021aqds.h
+++ b/include/configs/ls1021aqds.h
@@ -371,7 +371,6 @@ unsigned long get_board_ddr_clk(void);
  * Serial Port
  */
 #ifdef CONFIG_LPUART
-#define CONFIG_FSL_LPUART
 #define CONFIG_LPUART_32B_REG
 #else
 #define CONFIG_CONS_INDEX		1
diff --git a/include/configs/ls1021atwr.h b/include/configs/ls1021atwr.h
index bbef2a7..317ba62 100644
--- a/include/configs/ls1021atwr.h
+++ b/include/configs/ls1021atwr.h
@@ -266,7 +266,6 @@
  * Serial Port
  */
 #ifdef CONFIG_LPUART
-#define CONFIG_FSL_LPUART
 #define CONFIG_LPUART_32B_REG
 #else
 #define CONFIG_CONS_INDEX		1
diff --git a/include/configs/pcm052.h b/include/configs/pcm052.h
index b851bba..891bdb0 100644
--- a/include/configs/pcm052.h
+++ b/include/configs/pcm052.h
@@ -27,7 +27,6 @@
 
 #define CONFIG_BOARD_EARLY_INIT_F
 
-#define CONFIG_FSL_LPUART
 #define LPUART_BASE			UART1_BASE
 
 /* Allow to overwrite serial and ethaddr */
diff --git a/include/configs/vf610twr.h b/include/configs/vf610twr.h
index 34df6f0..dcfafaf 100644
--- a/include/configs/vf610twr.h
+++ b/include/configs/vf610twr.h
@@ -34,7 +34,6 @@
 
 #define CONFIG_BOARD_EARLY_INIT_F
 
-#define CONFIG_FSL_LPUART
 #define LPUART_BASE			UART1_BASE
 
 /* Allow to overwrite serial and ethaddr */
-- 
cgit v0.10.2


From 47f1bfca404ab6d61c7d384183d82a5f9c14d09b Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:01 -0800
Subject: serial: lpuart: Fix several cosmetic issues

Clean up the driver codes a little bit, by:
- Use tab instead of space in the macro defines
- Use single line comment whenever possible
- Fix insertion of blank lines

Signed-off-by: Bin Meng 
Reviewed-by: Simon Glass 
Acked-by: Simon Glass 

diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 63fc388..ae47183 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -12,15 +12,15 @@
 #include 
 #include 
 
-#define US1_TDRE        (1 << 7)
-#define US1_RDRF        (1 << 5)
-#define US1_OR          (1 << 3)
-#define UC2_TE          (1 << 3)
-#define UC2_RE          (1 << 2)
-#define CFIFO_TXFLUSH   (1 << 7)
-#define CFIFO_RXFLUSH   (1 << 6)
-#define SFIFO_RXOF      (1 << 2)
-#define SFIFO_RXUF      (1 << 0)
+#define US1_TDRE	(1 << 7)
+#define US1_RDRF	(1 << 5)
+#define US1_OR		(1 << 3)
+#define UC2_TE		(1 << 3)
+#define UC2_RE		(1 << 2)
+#define CFIFO_TXFLUSH	(1 << 7)
+#define CFIFO_RXFLUSH	(1 << 6)
+#define SFIFO_RXOF	(1 << 2)
+#define SFIFO_RXUF	(1 << 0)
 
 #define STAT_LBKDIF	(1 << 31)
 #define STAT_RXEDGIF	(1 << 30)
@@ -34,7 +34,7 @@
 #define STAT_MA1F	(1 << 15)
 #define STAT_MA2F	(1 << 14)
 #define STAT_FLAGS	(STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
-			STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
+			 STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
 
 #define CTRL_TE		(1 << 19)
 #define CTRL_RE		(1 << 18)
@@ -59,8 +59,8 @@ static void lpuart_serial_setbrg(void)
 		gd->baudrate = CONFIG_BAUDRATE;
 
 	sbr = (u16)(clk / (16 * gd->baudrate));
-	/* place adjustment later - n/32 BRFA */
 
+	/* place adjustment later - n/32 BRFA */
 	__raw_writeb(sbr >> 8, &base->ubdh);
 	__raw_writeb(sbr & 0xff, &base->ubdl);
 }
@@ -86,9 +86,7 @@ static void lpuart_serial_putc(const char c)
 	__raw_writeb(c, &base->ud);
 }
 
-/*
- * Test whether a character is in the RX buffer
- */
+/* Test whether a character is in the RX buffer */
 static int lpuart_serial_tstc(void)
 {
 	if (__raw_readb(&base->urcfifo) == 0)
@@ -120,7 +118,6 @@ static int lpuart_serial_init(void)
 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
 
 	/* provide data bits, parity, stop bit, etc */
-
 	serial_setbrg();
 
 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
@@ -148,8 +145,8 @@ static void lpuart32_serial_setbrg(void)
 		gd->baudrate = CONFIG_BAUDRATE;
 
 	sbr = (clk / (16 * gd->baudrate));
-	/* place adjustment later - n/32 BRFA */
 
+	/* place adjustment later - n/32 BRFA */
 	out_be32(&base->baud, sbr);
 }
 
@@ -176,9 +173,7 @@ static void lpuart32_serial_putc(const char c)
 	out_be32(&base->data, c);
 }
 
-/*
- * Test whether a character is in the RX buffer
- */
+/* Test whether a character is in the RX buffer */
 static int lpuart32_serial_tstc(void)
 {
 	if ((in_be32(&base->water) >> 24) == 0)
@@ -204,8 +199,8 @@ static int lpuart32_serial_init(void)
 	out_be32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
 
 	out_be32(&base->match, 0);
-	/* provide data bits, parity, stop bit, etc */
 
+	/* provide data bits, parity, stop bit, etc */
 	serial_setbrg();
 
 	out_be32(&base->ctrl, CTRL_RE | CTRL_TE);
-- 
cgit v0.10.2


From ed3021af5c7ecd94298f9d70f1fdd8abdb4c545d Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:02 -0800
Subject: serial: lpuart: Call local version of setbrg and putc directly

There is no need to go through serial driver subsystem, instead
call the driver's setbrg and putc routines directly.

Signed-off-by: Bin Meng 
Reviewed-by: Simon Glass 
Acked-by: Simon Glass 

diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index ae47183..0c0ab87 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -78,7 +78,7 @@ static int lpuart_serial_getc(void)
 static void lpuart_serial_putc(const char c)
 {
 	if (c == '\n')
-		serial_putc('\r');
+		lpuart_serial_putc('\r');
 
 	while (!(__raw_readb(&base->us1) & US1_TDRE))
 		WATCHDOG_RESET();
@@ -118,7 +118,7 @@ static int lpuart_serial_init(void)
 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
 
 	/* provide data bits, parity, stop bit, etc */
-	serial_setbrg();
+	lpuart_serial_setbrg();
 
 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
 
@@ -165,7 +165,7 @@ static int lpuart32_serial_getc(void)
 static void lpuart32_serial_putc(const char c)
 {
 	if (c == '\n')
-		serial_putc('\r');
+		lpuart32_serial_putc('\r');
 
 	while (!(in_be32(&base->stat) & STAT_TDRE))
 		WATCHDOG_RESET();
@@ -201,7 +201,7 @@ static int lpuart32_serial_init(void)
 	out_be32(&base->match, 0);
 
 	/* provide data bits, parity, stop bit, etc */
-	serial_setbrg();
+	lpuart32_serial_setbrg();
 
 	out_be32(&base->ctrl, CTRL_RE | CTRL_TE);
 
-- 
cgit v0.10.2


From 6ca13b123989d6c462540dca70f8bdc379672c61 Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:03 -0800
Subject: serial: lpuart: Prepare the driver for DM conversion

Create internal routines which take lpuart's register base as
a parameter, in preparation for driver model conversion.

Signed-off-by: Bin Meng 
Reviewed-by: Simon Glass 
Acked-by: Simon Glass 

diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 0c0ab87..5cc1997 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -50,22 +50,19 @@ DECLARE_GLOBAL_DATA_PTR;
 struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE;
 
 #ifndef CONFIG_LPUART_32B_REG
-static void lpuart_serial_setbrg(void)
+static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate)
 {
 	u32 clk = mxc_get_clock(MXC_UART_CLK);
 	u16 sbr;
 
-	if (!gd->baudrate)
-		gd->baudrate = CONFIG_BAUDRATE;
-
-	sbr = (u16)(clk / (16 * gd->baudrate));
+	sbr = (u16)(clk / (16 * baudrate));
 
 	/* place adjustment later - n/32 BRFA */
 	__raw_writeb(sbr >> 8, &base->ubdh);
 	__raw_writeb(sbr & 0xff, &base->ubdl);
 }
 
-static int lpuart_serial_getc(void)
+static int _lpuart_serial_getc(struct lpuart_fsl *base)
 {
 	while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
 		WATCHDOG_RESET();
@@ -75,10 +72,10 @@ static int lpuart_serial_getc(void)
 	return __raw_readb(&base->ud);
 }
 
-static void lpuart_serial_putc(const char c)
+static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c)
 {
 	if (c == '\n')
-		lpuart_serial_putc('\r');
+		_lpuart_serial_putc(base, '\r');
 
 	while (!(__raw_readb(&base->us1) & US1_TDRE))
 		WATCHDOG_RESET();
@@ -87,7 +84,7 @@ static void lpuart_serial_putc(const char c)
 }
 
 /* Test whether a character is in the RX buffer */
-static int lpuart_serial_tstc(void)
+static int _lpuart_serial_tstc(struct lpuart_fsl *base)
 {
 	if (__raw_readb(&base->urcfifo) == 0)
 		return 0;
@@ -99,7 +96,7 @@ static int lpuart_serial_tstc(void)
  * Initialise the serial port with the given baudrate. The settings
  * are always 8 data bits, no parity, 1 stop bit, no start bits.
  */
-static int lpuart_serial_init(void)
+static int _lpuart_serial_init(struct lpuart_fsl *base)
 {
 	u8 ctrl;
 
@@ -118,13 +115,38 @@ static int lpuart_serial_init(void)
 	__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
 
 	/* provide data bits, parity, stop bit, etc */
-	lpuart_serial_setbrg();
+	_lpuart_serial_setbrg(base, gd->baudrate);
 
 	__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
 
 	return 0;
 }
 
+static void lpuart_serial_setbrg(void)
+{
+	_lpuart_serial_setbrg(base, gd->baudrate);
+}
+
+static int lpuart_serial_getc(void)
+{
+	return _lpuart_serial_getc(base);
+}
+
+static void lpuart_serial_putc(const char c)
+{
+	_lpuart_serial_putc(base, c);
+}
+
+static int lpuart_serial_tstc(void)
+{
+	return _lpuart_serial_tstc(base);
+}
+
+static int lpuart_serial_init(void)
+{
+	return _lpuart_serial_init(base);
+}
+
 static struct serial_device lpuart_serial_drv = {
 	.name = "lpuart_serial",
 	.start = lpuart_serial_init,
@@ -136,21 +158,18 @@ static struct serial_device lpuart_serial_drv = {
 	.tstc = lpuart_serial_tstc,
 };
 #else
-static void lpuart32_serial_setbrg(void)
+static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate)
 {
 	u32 clk = CONFIG_SYS_CLK_FREQ;
 	u32 sbr;
 
-	if (!gd->baudrate)
-		gd->baudrate = CONFIG_BAUDRATE;
-
-	sbr = (clk / (16 * gd->baudrate));
+	sbr = (clk / (16 * baudrate));
 
 	/* place adjustment later - n/32 BRFA */
 	out_be32(&base->baud, sbr);
 }
 
-static int lpuart32_serial_getc(void)
+static int _lpuart32_serial_getc(struct lpuart_fsl *base)
 {
 	u32 stat;
 
@@ -162,10 +181,10 @@ static int lpuart32_serial_getc(void)
 	return in_be32(&base->data) & 0x3ff;
 }
 
-static void lpuart32_serial_putc(const char c)
+static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c)
 {
 	if (c == '\n')
-		lpuart32_serial_putc('\r');
+		_lpuart32_serial_putc(base, '\r');
 
 	while (!(in_be32(&base->stat) & STAT_TDRE))
 		WATCHDOG_RESET();
@@ -174,7 +193,7 @@ static void lpuart32_serial_putc(const char c)
 }
 
 /* Test whether a character is in the RX buffer */
-static int lpuart32_serial_tstc(void)
+static int _lpuart32_serial_tstc(struct lpuart_fsl *base)
 {
 	if ((in_be32(&base->water) >> 24) == 0)
 		return 0;
@@ -186,7 +205,7 @@ static int lpuart32_serial_tstc(void)
  * Initialise the serial port with the given baudrate. The settings
  * are always 8 data bits, no parity, 1 stop bit, no start bits.
  */
-static int lpuart32_serial_init(void)
+static int _lpuart32_serial_init(struct lpuart_fsl *base)
 {
 	u8 ctrl;
 
@@ -201,13 +220,38 @@ static int lpuart32_serial_init(void)
 	out_be32(&base->match, 0);
 
 	/* provide data bits, parity, stop bit, etc */
-	lpuart32_serial_setbrg();
+	_lpuart32_serial_setbrg(base, gd->baudrate);
 
 	out_be32(&base->ctrl, CTRL_RE | CTRL_TE);
 
 	return 0;
 }
 
+static void lpuart32_serial_setbrg(void)
+{
+	_lpuart32_serial_setbrg(base, gd->baudrate);
+}
+
+static int lpuart32_serial_getc(void)
+{
+	return _lpuart32_serial_getc(base);
+}
+
+static void lpuart32_serial_putc(const char c)
+{
+	_lpuart32_serial_putc(base, c);
+}
+
+static int lpuart32_serial_tstc(void)
+{
+	return _lpuart32_serial_tstc(base);
+}
+
+static int lpuart32_serial_init(void)
+{
+	return _lpuart32_serial_init(base);
+}
+
 static struct serial_device lpuart32_serial_drv = {
 	.name = "lpuart32_serial",
 	.start = lpuart32_serial_init,
-- 
cgit v0.10.2


From fdbae099bf1337aac485ac2f96858bf3985a71e8 Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:04 -0800
Subject: serial: lpuart: Add driver model serial support

This adds driver model support to lpuart serial driver.

Signed-off-by: Bin Meng 
Acked-by: Bhuvanchandra DV 
Tested-by: Bhuvanchandra DV 

diff --git a/doc/driver-model/serial-howto.txt b/doc/driver-model/serial-howto.txt
index 4706d56..c933b90 100644
--- a/doc/driver-model/serial-howto.txt
+++ b/doc/driver-model/serial-howto.txt
@@ -11,7 +11,6 @@ is time for maintainers to start converting over the remaining serial drivers:
    opencores_yanu.c
    serial_bfin.c
    serial_imx.c
-   serial_lpuart.c
    serial_max3100.c
    serial_pxa.c
    serial_s3c24x0.c
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 5cc1997..3f9c4d1 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -5,6 +5,7 @@
  */
 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -49,6 +50,10 @@ DECLARE_GLOBAL_DATA_PTR;
 
 struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE;
 
+struct lpuart_serial_platdata {
+	struct lpuart_fsl *reg;
+};
+
 #ifndef CONFIG_LPUART_32B_REG
 static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate)
 {
@@ -122,6 +127,7 @@ static int _lpuart_serial_init(struct lpuart_fsl *base)
 	return 0;
 }
 
+#ifndef CONFIG_DM_SERIAL
 static void lpuart_serial_setbrg(void)
 {
 	_lpuart_serial_setbrg(base, gd->baudrate);
@@ -157,6 +163,54 @@ static struct serial_device lpuart_serial_drv = {
 	.getc = lpuart_serial_getc,
 	.tstc = lpuart_serial_tstc,
 };
+#else /* CONFIG_DM_SERIAL */
+static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart_serial_setbrg(reg, baudrate);
+
+	return 0;
+}
+
+static int lpuart_serial_getc(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart_serial_getc(reg);
+}
+
+static int lpuart_serial_putc(struct udevice *dev, const char c)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart_serial_putc(reg, c);
+
+	return 0;
+}
+
+static int lpuart_serial_pending(struct udevice *dev, bool input)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	if (input)
+		return _lpuart_serial_tstc(reg);
+	else
+		return __raw_readb(®->us1) & US1_TDRE ? 0 : 1;
+}
+
+static int lpuart_serial_probe(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart_serial_init(reg);
+}
+#endif /* CONFIG_DM_SERIAL */
 #else
 static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate)
 {
@@ -227,6 +281,7 @@ static int _lpuart32_serial_init(struct lpuart_fsl *base)
 	return 0;
 }
 
+#ifndef CONFIG_DM_SERIAL
 static void lpuart32_serial_setbrg(void)
 {
 	_lpuart32_serial_setbrg(base, gd->baudrate);
@@ -262,8 +317,57 @@ static struct serial_device lpuart32_serial_drv = {
 	.getc = lpuart32_serial_getc,
 	.tstc = lpuart32_serial_tstc,
 };
+#else /* CONFIG_DM_SERIAL */
+static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart32_serial_setbrg(reg, baudrate);
+
+	return 0;
+}
+
+static int lpuart32_serial_getc(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart32_serial_getc(reg);
+}
+
+static int lpuart32_serial_putc(struct udevice *dev, const char c)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	_lpuart32_serial_putc(reg, c);
+
+	return 0;
+}
+
+static int lpuart32_serial_pending(struct udevice *dev, bool input)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	if (input)
+		return _lpuart32_serial_tstc(reg);
+	else
+		return in_be32(®->stat) & STAT_TDRE ? 0 : 1;
+}
+
+static int lpuart32_serial_probe(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	struct lpuart_fsl *reg = plat->reg;
+
+	return _lpuart32_serial_init(reg);
+}
+#endif /* CONFIG_DM_SERIAL */
 #endif
 
+#ifndef CONFIG_DM_SERIAL
 void lpuart_serial_initialize(void)
 {
 #ifdef CONFIG_LPUART_32B_REG
@@ -281,3 +385,66 @@ __weak struct serial_device *default_serial_console(void)
 	return &lpuart_serial_drv;
 #endif
 }
+#else /* CONFIG_DM_SERIAL */
+static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
+{
+	struct lpuart_serial_platdata *plat = dev->platdata;
+	fdt_addr_t addr;
+
+	addr = dev_get_addr(dev);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	plat->reg = (struct lpuart_fsl *)addr;
+
+	return 0;
+}
+
+#ifndef CONFIG_LPUART_32B_REG
+static const struct dm_serial_ops lpuart_serial_ops = {
+	.putc = lpuart_serial_putc,
+	.pending = lpuart_serial_pending,
+	.getc = lpuart_serial_getc,
+	.setbrg = lpuart_serial_setbrg,
+};
+
+static const struct udevice_id lpuart_serial_ids[] = {
+	{ .compatible = "fsl,vf610-lpuart" },
+	{ }
+};
+
+U_BOOT_DRIVER(serial_lpuart) = {
+	.name	= "serial_lpuart",
+	.id	= UCLASS_SERIAL,
+	.of_match = lpuart_serial_ids,
+	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
+	.probe = lpuart_serial_probe,
+	.ops	= &lpuart_serial_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+#else /* CONFIG_LPUART_32B_REG */
+static const struct dm_serial_ops lpuart32_serial_ops = {
+	.putc = lpuart32_serial_putc,
+	.pending = lpuart32_serial_pending,
+	.getc = lpuart32_serial_getc,
+	.setbrg = lpuart32_serial_setbrg,
+};
+
+static const struct udevice_id lpuart32_serial_ids[] = {
+	{ .compatible = "fsl,ls1021a-lpuart" },
+	{ }
+};
+
+U_BOOT_DRIVER(serial_lpuart32) = {
+	.name	= "serial_lpuart32",
+	.id	= UCLASS_SERIAL,
+	.of_match = lpuart32_serial_ids,
+	.ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
+	.probe = lpuart32_serial_probe,
+	.ops	= &lpuart32_serial_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+#endif /* CONFIG_LPUART_32B_REG */
+#endif /* CONFIG_DM_SERIAL */
-- 
cgit v0.10.2


From 8b67761437099c81c18f825f195c70a25fed0097 Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:05 -0800
Subject: arm: ls1021atwr: Split off board device tree

Move /chosen node out of the board device tree.

Signed-off-by: Bin Meng 
Acked-by: Simon Glass 

diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index e4f8aae..5a6f75a 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -93,7 +93,7 @@ dtb-$(CONFIG_TARGET_BEAGLE_X15) += am57xx-beagle-x15.dtb
 dtb-$(CONFIG_TARGET_STV0991) += stv0991.dtb
 
 dtb-$(CONFIG_LS102XA) += ls1021a-qds.dtb \
-	ls1021a-twr.dtb
+	ls1021a-twr-duart.dtb
 dtb-$(CONFIG_FSL_LSCH3) += fsl-ls2080a-qds.dtb \
 	fsl-ls2080a-rdb.dtb
 dtb-$(CONFIG_FSL_LSCH2) += fsl-ls1043a-qds.dtb \
diff --git a/arch/arm/dts/ls1021a-twr-duart.dts b/arch/arm/dts/ls1021a-twr-duart.dts
new file mode 100644
index 0000000..aaf7296
--- /dev/null
+++ b/arch/arm/dts/ls1021a-twr-duart.dts
@@ -0,0 +1,16 @@
+/*
+ * Freescale ls1021a TWR board device tree source
+ *
+ * Copyright 2013-2015 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+#include "ls1021a-twr.dtsi"
+
+/ {
+	chosen {
+		stdout-path = &uart0;
+	};
+};
diff --git a/arch/arm/dts/ls1021a-twr.dts b/arch/arm/dts/ls1021a-twr.dts
deleted file mode 100644
index aead13f..0000000
--- a/arch/arm/dts/ls1021a-twr.dts
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Freescale ls1021a TWR board device tree source
- *
- * Copyright 2013-2015 Freescale Semiconductor, Inc.
- *
- * SPDX-License-Identifier:	GPL-2.0+
- */
-
-/dts-v1/;
-#include "ls1021a.dtsi"
-
-/ {
-	model = "LS1021A TWR Board";
-
-	aliases {
-		enet2_rgmii_phy = &rgmii_phy1;
-		enet0_sgmii_phy = &sgmii_phy2;
-		enet1_sgmii_phy = &sgmii_phy0;
-		spi0 = &qspi;
-		spi1 = &dspi1;
-	};
-
-	chosen {
-		stdout-path = &uart0;
-	};
-};
-
-&qspi {
-	bus-num = <0>;
-	status = "okay";
-
-	qflash0: n25q128a13@0 {
-		#address-cells = <1>;
-		#size-cells = <1>;
-		compatible = "spi-flash";
-		spi-max-frequency = <20000000>;
-		reg = <0>;
-	};
-};
-
-&dspi1 {
-	bus-num = <0>;
-	status = "okay";
-
-	dspiflash: at26df081a@0 {
-		#address-cells = <1>;
-		#size-cells = <1>;
-		compatible = "spi-flash";
-		spi-max-frequency = <16000000>;
-		spi-cpol;
-		spi-cpha;
-		reg = <0>;
-	};
-};
-
-&i2c0 {
-	status = "okay";
-};
-
-&i2c1 {
-	status = "okay";
-};
-
-&ifc {
-	#address-cells = <2>;
-	#size-cells = <1>;
-	/* NOR Flash on board */
-	ranges = <0x0 0x0 0x60000000 0x08000000>;
-	status = "okay";
-
-	nor@0,0 {
-		#address-cells = <1>;
-		#size-cells = <1>;
-		compatible = "cfi-flash";
-		reg = <0x0 0x0 0x8000000>;
-		bank-width = <2>;
-		device-width = <1>;
-	};
-};
-
-&lpuart0 {
-	status = "okay";
-};
-
-&mdio0 {
-	sgmii_phy0: ethernet-phy@0 {
-		reg = <0x0>;
-	};
-	rgmii_phy1: ethernet-phy@1 {
-		reg = <0x1>;
-	};
-	sgmii_phy2: ethernet-phy@2 {
-		reg = <0x2>;
-	};
-	tbi1: tbi-phy@1f {
-		reg = <0x1f>;
-		device_type = "tbi-phy";
-	};
-};
-
-&uart0 {
-	status = "okay";
-};
-
-&uart1 {
-	status = "okay";
-};
diff --git a/arch/arm/dts/ls1021a-twr.dtsi b/arch/arm/dts/ls1021a-twr.dtsi
new file mode 100644
index 0000000..d1be9ae
--- /dev/null
+++ b/arch/arm/dts/ls1021a-twr.dtsi
@@ -0,0 +1,106 @@
+/*
+ * Freescale ls1021a TWR board common device tree source
+ *
+ * Copyright 2013-2015 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include "ls1021a.dtsi"
+
+/ {
+	model = "LS1021A TWR Board";
+
+	aliases {
+		enet2_rgmii_phy = &rgmii_phy1;
+		enet0_sgmii_phy = &sgmii_phy2;
+		enet1_sgmii_phy = &sgmii_phy0;
+		spi0 = &qspi;
+		spi1 = &dspi1;
+	};
+
+	chosen {
+		stdout-path = &uart0;
+	};
+};
+
+&qspi {
+	bus-num = <0>;
+	status = "okay";
+
+	qflash0: n25q128a13@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spi-flash";
+		spi-max-frequency = <20000000>;
+		reg = <0>;
+	};
+};
+
+&dspi1 {
+	bus-num = <0>;
+	status = "okay";
+
+	dspiflash: at26df081a@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spi-flash";
+		spi-max-frequency = <16000000>;
+		spi-cpol;
+		spi-cpha;
+		reg = <0>;
+	};
+};
+
+&i2c0 {
+	status = "okay";
+};
+
+&i2c1 {
+	status = "okay";
+};
+
+&ifc {
+	#address-cells = <2>;
+	#size-cells = <1>;
+	/* NOR Flash on board */
+	ranges = <0x0 0x0 0x60000000 0x08000000>;
+	status = "okay";
+
+	nor@0,0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "cfi-flash";
+		reg = <0x0 0x0 0x8000000>;
+		bank-width = <2>;
+		device-width = <1>;
+	};
+};
+
+&lpuart0 {
+	status = "okay";
+};
+
+&mdio0 {
+	sgmii_phy0: ethernet-phy@0 {
+		reg = <0x0>;
+	};
+	rgmii_phy1: ethernet-phy@1 {
+		reg = <0x1>;
+	};
+	sgmii_phy2: ethernet-phy@2 {
+		reg = <0x2>;
+	};
+	tbi1: tbi-phy@1f {
+		reg = <0x1f>;
+		device_type = "tbi-phy";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
+
+&uart1 {
+	status = "okay";
+};
diff --git a/configs/ls1021atwr_nor_defconfig b/configs/ls1021atwr_nor_defconfig
index 2b79443..b7b3a8d 100644
--- a/configs/ls1021atwr_nor_defconfig
+++ b/configs/ls1021atwr_nor_defconfig
@@ -1,7 +1,7 @@
 CONFIG_ARM=y
 CONFIG_TARGET_LS1021ATWR=y
 CONFIG_DM_SERIAL=y
-CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr"
+CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart"
 # CONFIG_CMD_SETEXPR is not set
 CONFIG_OF_CONTROL=y
 CONFIG_DM=y
diff --git a/configs/ls1021atwr_qspi_defconfig b/configs/ls1021atwr_qspi_defconfig
index 0c71df6..d7c7e4c 100644
--- a/configs/ls1021atwr_qspi_defconfig
+++ b/configs/ls1021atwr_qspi_defconfig
@@ -1,7 +1,7 @@
 CONFIG_ARM=y
 CONFIG_TARGET_LS1021ATWR=y
 CONFIG_DM_SPI=y
-CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr"
+CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart"
 CONFIG_SYS_EXTRA_OPTIONS="QSPI_BOOT"
 # CONFIG_CMD_IMLS is not set
 # CONFIG_CMD_SETEXPR is not set
diff --git a/configs/ls1021atwr_sdcard_qspi_defconfig b/configs/ls1021atwr_sdcard_qspi_defconfig
index 2b4ebd9..453a3bb 100644
--- a/configs/ls1021atwr_sdcard_qspi_defconfig
+++ b/configs/ls1021atwr_sdcard_qspi_defconfig
@@ -1,7 +1,7 @@
 CONFIG_ARM=y
 CONFIG_TARGET_LS1021ATWR=y
 CONFIG_DM_SPI=y
-CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr"
+CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart"
 CONFIG_SPL=y
 CONFIG_SYS_EXTRA_OPTIONS="RAMBOOT_PBL,SPL_FSL_PBL,SD_BOOT,SD_BOOT_QSPI"
 CONFIG_OF_CONTROL=y
-- 
cgit v0.10.2


From 665ac00c9842bf45aacec2b9d8bd017ad201d7f9 Mon Sep 17 00:00:00 2001
From: Bin Meng 
Date: Wed, 13 Jan 2016 19:39:06 -0800
Subject: arm: ls1021atwr: Enable driver model lpuart serial driver

Convert ls1021atwr_nor_lpuart to driver model support. As a start,
enable lpuart serial port driver.

Signed-off-by: Bin Meng 
Reviewed-by: Simon Glass 
Reviewed-by: Alison Wang 
Tested-by: Alison Wang 
Acked-by: Simon Glass 

diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 5a6f75a..7706b41 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -93,7 +93,7 @@ dtb-$(CONFIG_TARGET_BEAGLE_X15) += am57xx-beagle-x15.dtb
 dtb-$(CONFIG_TARGET_STV0991) += stv0991.dtb
 
 dtb-$(CONFIG_LS102XA) += ls1021a-qds.dtb \
-	ls1021a-twr-duart.dtb
+	ls1021a-twr-duart.dtb ls1021a-twr-lpuart.dtb
 dtb-$(CONFIG_FSL_LSCH3) += fsl-ls2080a-qds.dtb \
 	fsl-ls2080a-rdb.dtb
 dtb-$(CONFIG_FSL_LSCH2) += fsl-ls1043a-qds.dtb \
diff --git a/arch/arm/dts/ls1021a-twr-lpuart.dts b/arch/arm/dts/ls1021a-twr-lpuart.dts
new file mode 100644
index 0000000..2941ec0
--- /dev/null
+++ b/arch/arm/dts/ls1021a-twr-lpuart.dts
@@ -0,0 +1,16 @@
+/*
+ * Freescale ls1021a TWR board device tree source
+ *
+ * Copyright 2013-2015 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/dts-v1/;
+#include "ls1021a-twr.dtsi"
+
+/ {
+	chosen {
+		stdout-path = &lpuart0;
+	};
+};
diff --git a/configs/ls1021atwr_nor_lpuart_defconfig b/configs/ls1021atwr_nor_lpuart_defconfig
index 58cd61f..599342f 100644
--- a/configs/ls1021atwr_nor_lpuart_defconfig
+++ b/configs/ls1021atwr_nor_lpuart_defconfig
@@ -1,7 +1,11 @@
 CONFIG_ARM=y
 CONFIG_TARGET_LS1021ATWR=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-lpuart"
 CONFIG_SYS_EXTRA_OPTIONS="LPUART"
 # CONFIG_CMD_SETEXPR is not set
+CONFIG_OF_CONTROL=y
+CONFIG_DM=y
 CONFIG_NETDEVICES=y
 CONFIG_E1000=y
 CONFIG_FSL_LPUART=y
-- 
cgit v0.10.2


From 1acafc73bfc7535c185f321012ac70821471b816 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:15 -0700
Subject: dm: video: Add a video uclass

U-Boot has separate code for LCDs and 'video' devices. Both now use a
very similar API thanks to earlier work by Nikita Kiryanov. With the driver-
model conversion we should unify these into a single uclass.

Unfortunately there are different features supported by each. This
implementation provides for a common set of features which should serve
most purposes. The intent is to support:

- bitmap devices with 8, 16 and 32 bits per pixel
- text console wih white on black or vice versa
- rotated text console
- bitmap display (BMP format)

More can be added as additional boards are ported over to use driver model
for video.

The name 'video' is chosen for the uclass since it is more generic than LCD.
Another option would be 'display' but that would introduce a third concept
to U-Boot which seems like the wrong approach.

The existing LCD and video init functions are not needed now, so this uclass
makes no attempt to implement them.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index caf1efc..946d958 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -4,6 +4,46 @@
 
 menu "Graphics support"
 
+config DM_VIDEO
+	bool "Enable driver model support for LCD/video"
+	depends on DM
+	help
+	  This enables driver model for LCD and video devices. These support
+	  a bitmap display of various sizes and depths which can be drawn on
+	  to display a command-line console or splash screen. Enabling this
+	  option compiles in the video uclass and routes all LCD/video access
+	  through this.
+
+config VIDEO_BPP8
+	bool "Support 8-bit-per-pixel displays"
+	depends on DM_VIDEO
+	default y if DM_VIDEO
+	help
+	  Support drawing text and bitmaps onto a 8-bit-per-pixel display.
+	  Enabling this will include code to support this display. Without
+	  this option, such displays will not be supported and console output
+	  will be empty.
+
+config VIDEO_BPP16
+	bool "Support 16-bit-per-pixel displays"
+	depends on DM_VIDEO
+	default y if DM_VIDEO
+	help
+	  Support drawing text and bitmaps onto a 16-bit-per-pixel display.
+	  Enabling this will include code to support this display. Without
+	  this option, such displays will not be supported and console output
+	  will be empty.
+
+config VIDEO_BPP32
+	bool "Support 32-bit-per-pixel displays"
+	depends on DM_VIDEO
+	default y if DM_VIDEO
+	help
+	  Support drawing text and bitmaps onto a 32-bit-per-pixel display.
+	  Enabling this will include code to support this display. Without
+	  this option, such displays will not be supported and console output
+	  will be empty.
+
 config VIDEO_VESA
 	bool "Enable VESA video driver support"
 	default n
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e85fd8a..7192013 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -7,6 +7,7 @@
 
 ifdef CONFIG_DM
 obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
+obj-$(CONFIG_DM_VIDEO) += video-uclass.o
 endif
 
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
new file mode 100644
index 0000000..1615889
--- /dev/null
+++ b/drivers/video/video-uclass.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#ifdef CONFIG_SANDBOX
+#include 
+#endif
+
+/*
+ * Theory of operation:
+ *
+ * Before relocation each device is bound. The driver for each device must
+ * set the @align and @size values in struct video_uc_platdata. This
+ * information represents the requires size and alignment of the frame buffer
+ * for the device. The values can be an over-estimate but cannot be too
+ * small. The actual values will be suppled (in the same manner) by the bind()
+ * method after relocation.
+ *
+ * This information is then picked up by video_reserve() which works out how
+ * much memory is needed for all devices. This is allocated between
+ * gd->video_bottom and gd->video_top.
+ *
+ * After relocation the same process occurs. The driver supplies the same
+ * @size and @align information and this time video_post_bind() checks that
+ * the drivers does not overflow the allocated memory.
+ *
+ * The frame buffer address is actually set (to plat->base) in
+ * video_post_probe(). This function also clears the frame buffer and
+ * allocates a suitable text console device. This can then be used to write
+ * text to the video device.
+ */
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong alloc_fb(struct udevice *dev, ulong *addrp)
+{
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+	ulong base, align, size;
+
+	align = plat->align ? plat->align : 1 << 20;
+	base = *addrp - plat->size;
+	base &= ~(align - 1);
+	plat->base = base;
+	size = *addrp - base;
+	*addrp = base;
+
+	return size;
+}
+
+int video_reserve(ulong *addrp)
+{
+	struct udevice *dev;
+	ulong size;
+
+	gd->video_top = *addrp;
+	for (uclass_find_first_device(UCLASS_VIDEO, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		size = alloc_fb(dev, addrp);
+		debug("%s: Reserving %lx bytes at %lx for video device '%s'\n",
+		      __func__, size, *addrp, dev->name);
+	}
+	gd->video_bottom = *addrp;
+	debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
+	      gd->video_top);
+
+	return 0;
+}
+
+static int video_clear(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+
+	if (priv->bpix == VIDEO_BPP32) {
+		u32 *ppix = priv->fb;
+		u32 *end = priv->fb + priv->fb_size;
+
+		while (ppix < end)
+			*ppix++ = priv->colour_bg;
+	} else {
+		memset(priv->fb, priv->colour_bg, priv->fb_size);
+	}
+
+	return 0;
+}
+
+/* Flush video activity to the caches */
+void video_sync(struct udevice *vid)
+{
+	/*
+	 * flush_dcache_range() is declared in common.h but it seems that some
+	 * architectures do not actually implement it. Is there a way to find
+	 * out whether it exists? For now, ARM is safe.
+	 */
+#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
+	struct video_priv *priv = dev_get_uclass_priv(vid);
+
+	if (priv->flush_dcache) {
+		flush_dcache_range((ulong)priv->fb,
+				   (ulong)priv->fb + priv->fb_size);
+	}
+#elif defined(CONFIG_VIDEO_SANDBOX_SDL)
+	struct video_priv *priv = dev_get_uclass_priv(vid);
+	static ulong last_sync;
+
+	if (get_timer(last_sync) > 10) {
+		sandbox_sdl_sync(priv->fb);
+		last_sync = get_timer(0);
+	}
+#endif
+}
+
+void video_sync_all(void)
+{
+	struct udevice *dev;
+
+	for (uclass_find_first_device(UCLASS_VIDEO, &dev);
+	     dev;
+	     uclass_find_next_device(&dev)) {
+		if (device_active(dev))
+			video_sync(dev);
+	}
+}
+
+int video_get_xsize(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+
+	return priv->xsize;
+}
+
+int video_get_ysize(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+
+	return priv->ysize;
+}
+
+/* Set up the colour map */
+static int video_pre_probe(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+
+	priv->cmap = calloc(256, sizeof(ushort));
+	if (!priv->cmap)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int video_pre_remove(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+
+	free(priv->cmap);
+
+	return 0;
+}
+
+/* Set up the display ready for use */
+static int video_post_probe(struct udevice *dev)
+{
+	struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+	char name[30], drv[15], *str;
+	struct udevice *cons;
+	int ret;
+
+	/* Set up the line and display size */
+	priv->fb = map_sysmem(plat->base, plat->size);
+	priv->line_length = priv->xsize * VNBYTES(priv->bpix);
+	priv->fb_size = priv->line_length * priv->ysize;
+
+	/* Set up colours - we could in future support other colours */
+#ifdef CONFIG_SYS_WHITE_ON_BLACK
+	priv->colour_fg = 0xffffff;
+#else
+	priv->colour_bg = 0xffffff;
+#endif
+	video_clear(dev);
+
+	return 0;
+};
+
+/* Post-relocation, allocate memory for the frame buffer */
+static int video_post_bind(struct udevice *dev)
+{
+	ulong addr = gd->video_top;
+	ulong size;
+
+	/* Before relocation there is nothing to do here */
+	if ((!gd->flags & GD_FLG_RELOC))
+		return 0;
+	size = alloc_fb(dev, &addr);
+	if (addr < gd->video_bottom) {
+		/* Device tree node may need the 'u-boot,dm-pre-reloc' tag */
+		printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n",
+		       dev->name);
+		return -ENOSPC;
+	}
+	debug("%s: Claiming %lx bytes at %lx for video device '%s'\n",
+	      __func__, size, addr, dev->name);
+	gd->video_bottom = addr;
+
+	return 0;
+}
+
+UCLASS_DRIVER(video) = {
+	.id		= UCLASS_VIDEO,
+	.name		= "video",
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+	.post_bind	= video_post_bind,
+	.pre_probe	= video_pre_probe,
+	.post_probe	= video_post_probe,
+	.pre_remove	= video_pre_remove,
+	.per_device_auto_alloc_size	= sizeof(struct video_priv),
+	.per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata),
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 27fa0b6..3934375 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -66,6 +66,7 @@ enum uclass_id {
 	UCLASS_USB,		/* USB bus */
 	UCLASS_USB_DEV_GENERIC,	/* USB generic device */
 	UCLASS_USB_HUB,		/* USB hub */
+	UCLASS_VIDEO,		/* Video or LCD device */
 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */
 
 	UCLASS_COUNT,
diff --git a/include/video.h b/include/video.h
index 65e4ec1..b20f06f 100644
--- a/include/video.h
+++ b/include/video.h
@@ -1,14 +1,167 @@
 /*
-** MPC823 Video Controller
-** =======================
-** (C) 2000 by Paolo Scaffardi (arsenio@tin.it)
-** AIRVENT SAM s.p.a - RIMINI(ITALY)
-**
-*/
+ * Video uclass and legacy implementation
+ *
+ * Copyright (c) 2015 Google, Inc
+ *
+ * MPC823 Video Controller
+ * =======================
+ * (C) 2000 by Paolo Scaffardi (arsenio@tin.it)
+ * AIRVENT SAM s.p.a - RIMINI(ITALY)
+ *
+ */
 
 #ifndef _VIDEO_H_
 #define _VIDEO_H_
 
+#ifdef CONFIG_DM_VIDEO
+
+#include 
+
+struct video_uc_platdata {
+	uint align;
+	uint size;
+	ulong base;
+};
+
+/*
+ * Bits per pixel selector. Each value n is such that the bits-per-pixel is
+ * 2 ^ n
+ */
+enum video_log2_bpp {
+	VIDEO_BPP1	= 0,
+	VIDEO_BPP2,
+	VIDEO_BPP4,
+	VIDEO_BPP8,
+	VIDEO_BPP16,
+	VIDEO_BPP32,
+};
+
+/*
+ * Convert enum video_log2_bpp to bytes and bits. Note we omit the outer
+ * brackets to allow multiplication by fractional pixels.
+ */
+#define VNBYTES(bpix)	(1 << (bpix)) / 8
+
+#define VNBITS(bpix)	(1 << (bpix))
+
+/**
+ * struct video_priv - Device information used by the video uclass
+ *
+ * @xsize:	Number of pixel columns (e.g. 1366)
+ * @ysize:	Number of pixels rows (e.g.. 768)
+ * @tor:	Display rotation (0=none, 1=90 degrees clockwise, etc.)
+ * @bpix:	Encoded bits per pixel
+ * @fb:		Frame buffer
+ * @fb_size:	Frame buffer size
+ * @line_length:	Length of each frame buffer line, in bytes
+ * @colour_fg:	Foreground colour (pixel value)
+ * @colour_bg:	Background colour (pixel value)
+ * @flush_dcache:	true to enable flushing of the data cache after
+ *		the LCD is updated
+ * @cmap:	Colour map for 8-bit-per-pixel displays
+ */
+struct video_priv {
+	/* Things set up by the driver: */
+	ushort xsize;
+	ushort ysize;
+	ushort rot;
+	enum video_log2_bpp bpix;
+
+	/*
+	 * Things that are private to the uclass: don't use these in the
+	 * driver
+	 */
+	void *fb;
+	int fb_size;
+	int line_length;
+	int colour_fg;
+	int colour_bg;
+	bool flush_dcache;
+	ushort *cmap;
+};
+
+/* Placeholder - there are no video operations at present */
+struct video_ops {
+};
+
+#define video_get_ops(dev)        ((struct video_ops *)(dev)->driver->ops)
+
+/**
+ * video_reserve() - Reserve frame-buffer memory for video devices
+ *
+ * Note: This function is for internal use.
+ *
+ * This uses the uclass platdata's @size and @align members to figure out
+ * a size and position for each frame buffer as part of the pre-relocation
+ * process of determining the post-relocation memory layout.
+ *
+ * gd->video_top is set to the initial value of *@addrp and gd->video_bottom
+ * is set to the final value.
+ *
+ * @addrp:	On entry, the top of available memory. On exit, the new top,
+ *		after allocating the required memory.
+ * @return 0
+ */
+int video_reserve(ulong *addrp);
+
+/**
+ * video_sync() - Sync a device's frame buffer with its hardware
+ *
+ * Some frame buffers are cached or have a secondary frame buffer. This
+ * function syncs these up so that the current contents of the U-Boot frame
+ * buffer are displayed to the user.
+ *
+ * @dev:	Device to sync
+ */
+void video_sync(struct udevice *vid);
+
+/**
+ * video_sync_all() - Sync all devices' frame buffers with there hardware
+ *
+ * This calls video_sync() on all active video devices.
+ */
+void video_sync_all(void);
+
+/**
+ * video_bmp_display() - Display a BMP file
+ *
+ * @dev:	Device to display the bitmap on
+ * @bmp_image:	Address of bitmap image to display
+ * @x:		X position in pixels from the left
+ * @y:		Y position in pixels from the top
+ * @align:	true to adjust the coordinates to centre the image. If false
+ *		the coordinates are used as is. If true:
+ *
+ *		- if a coordinate is 0x7fff then the image will be centred in
+ *		  that direction
+ *		- if a coordinate is -ve then it will be offset to the
+ *		  left/top of the centre by that many pixels
+ *		- if a coordinate is positive it will be used unchnaged.
+ * @return 0 if OK, -ve on error
+ */
+int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
+		      bool align);
+
+/**
+ * video_get_xsize() - Get the width of the display in pixels
+ *
+ * @dev:	Device to check
+ * @return device frame buffer width in pixels
+ */
+int video_get_xsize(struct udevice *dev);
+
+/**
+ * video_get_ysize() - Get the height of the display in pixels
+ *
+ * @dev:	Device to check
+ * @return device frame buffer height in pixels
+ */
+int video_get_ysize(struct udevice *dev);
+
+#endif /* CONFIG_DM_VIDEO */
+
+#ifndef CONFIG_DM_VIDEO
+
 /* Video functions */
 
 struct stdio_dev;
@@ -73,4 +226,7 @@ int kwh043st20_f01_spi_startup(unsigned int bus, unsigned int cs,
 int lg4573_spi_startup(unsigned int bus, unsigned int cs,
 	unsigned int max_hz, unsigned int spi_mode);
 #endif
+
+#endif /* CONFIG_DM_VIDEO */
+
 #endif
-- 
cgit v0.10.2


From 6cbf5de7082fdee0bdbc3dd5fe7ac4c84cbf9f7d Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:16 -0700
Subject: dm: lcd: Avoid using the lcd.h header file with driver model

The LCD functions and definitions are not used with the driver model video
uclass. When all boards are converted over we can remove the file. For now,
use #ifdef to omit the contents.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/common/Makefile b/common/Makefile
index 2a1d9f8..2492275 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -205,7 +205,9 @@ obj-$(CONFIG_I2C_EDID) += edid.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-y += splash.o
 obj-$(CONFIG_SPLASH_SOURCE) += splash_source.o
+ifndef CONFIG_DM_VIDEO
 obj-$(CONFIG_LCD) += lcd.o lcd_console.o
+endif
 obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o
 obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o
 obj-$(CONFIG_LYNXKDI) += lynxkdi.o
diff --git a/include/lcd.h b/include/lcd.h
index 59202b7..d7651a8 100644
--- a/include/lcd.h
+++ b/include/lcd.h
@@ -18,6 +18,12 @@
 #include 
 #endif
 
+int bmp_display(ulong addr, int x, int y);
+struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp,
+			     void **alloc_addr);
+
+#ifndef CONFIG_DM_VIDEO
+
 extern char lcd_is_enabled;
 extern int lcd_line_length;
 extern struct vidinfo panel_info;
@@ -26,10 +32,6 @@ void lcd_ctrl_init(void *lcdbase);
 void lcd_enable(void);
 void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue);
 
-struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp,
-			     void **alloc_addr);
-int bmp_display(ulong addr, int x, int y);
-
 /**
  * Set whether we need to flush the dcache when changing the LCD image. This
  * defaults to off.
@@ -209,4 +211,6 @@ void lcd_sync(void);
 #define PAGE_SIZE	4096
 #endif
 
+#endif /* !CONFIG_DM_VIDEO */
+
 #endif	/* _LCD_H_ */
-- 
cgit v0.10.2


From 83510766c90a52e58477267704fc5ca8f75c3dab Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:17 -0700
Subject: dm: video: Add a uclass for the text console

The existing LCD/video interface suffers from conflating the bitmap display
with text output on that display. As a result the implementation is more
complex than it needs to me.

We can support multiple text console drivers. Create a separate uclass to
support this, with its own API.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 7192013..e82f1ae 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -7,7 +7,7 @@
 
 ifdef CONFIG_DM
 obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
-obj-$(CONFIG_DM_VIDEO) += video-uclass.o
+obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o
 endif
 
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
new file mode 100644
index 0000000..ea10189
--- /dev/null
+++ b/drivers/video/vidconsole-uclass.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2001-2015
+ * DENX Software Engineering -- wd@denx.de
+ * Compulab Ltd - http://compulab.co.il/
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 		/* Get font data, width and height */
+
+/* By default we scroll by a single line */
+#ifndef CONFIG_CONSOLE_SCROLL_LINES
+#define CONFIG_CONSOLE_SCROLL_LINES 1
+#endif
+
+int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
+{
+	struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+	if (!ops->putc_xy)
+		return -ENOSYS;
+	return ops->putc_xy(dev, x, y, ch);
+}
+
+int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
+			 uint count)
+{
+	struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+	if (!ops->move_rows)
+		return -ENOSYS;
+	return ops->move_rows(dev, rowdst, rowsrc, count);
+}
+
+int vidconsole_set_row(struct udevice *dev, uint row, int clr)
+{
+	struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+	if (!ops->set_row)
+		return -ENOSYS;
+	return ops->set_row(dev, row, clr);
+}
+
+/* Move backwards one space */
+static void vidconsole_back(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+
+	if (--priv->curr_col < 0) {
+		priv->curr_col = priv->cols - 1;
+		if (--priv->curr_row < 0)
+			priv->curr_row = 0;
+	}
+
+	vidconsole_putc_xy(dev, priv->curr_col * VIDEO_FONT_WIDTH,
+			   priv->curr_row * VIDEO_FONT_HEIGHT, ' ');
+}
+
+/* Move to a newline, scrolling the display if necessary */
+static void vidconsole_newline(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct udevice *vid_dev = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
+	const int rows = CONFIG_CONSOLE_SCROLL_LINES;
+	int i;
+
+	priv->curr_col = 0;
+
+	/* Check if we need to scroll the terminal */
+	if (++priv->curr_row >= priv->rows) {
+		vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
+		for (i = 0; i < rows; i++)
+			vidconsole_set_row(dev, priv->rows - i - 1,
+					   vid_priv->colour_bg);
+		priv->curr_row -= rows;
+	}
+	video_sync(dev->parent);
+}
+
+int vidconsole_put_char(struct udevice *dev, char ch)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	int ret;
+
+	switch (ch) {
+	case '\r':
+		priv->curr_col = 0;
+		break;
+	case '\n':
+		vidconsole_newline(dev);
+		break;
+	case '\t':	/* Tab (8 chars alignment) */
+		priv->curr_col +=  8;
+		priv->curr_col &= ~7;
+
+		if (priv->curr_col >= priv->cols)
+			vidconsole_newline(dev);
+		break;
+	case '\b':
+		vidconsole_back(dev);
+		break;
+	default:
+		/*
+		 * Failure of this function normally indicates an unsupported
+		 * colour depth. Check this and return an error to help with
+		 * diagnosis.
+		 */
+		ret = vidconsole_putc_xy(dev,
+					 priv->curr_col * VIDEO_FONT_WIDTH,
+					 priv->curr_row * VIDEO_FONT_HEIGHT,
+					 ch);
+		if (ret)
+			return ret;
+		if (++priv->curr_col >= priv->cols)
+			vidconsole_newline(dev);
+		break;
+	}
+
+	return 0;
+}
+
+static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
+{
+	struct udevice *dev = sdev->priv;
+
+	vidconsole_put_char(dev, ch);
+}
+
+static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
+{
+	struct udevice *dev = sdev->priv;
+
+	while (*s)
+		vidconsole_put_char(dev, *s++);
+}
+
+/* Set up the number of rows and colours (rotated drivers override this) */
+static int vidconsole_pre_probe(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct udevice *vid = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+
+	priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT;
+	priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH;
+
+	return 0;
+}
+
+/* Register the device with stdio */
+static int vidconsole_post_probe(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct stdio_dev *sdev = &priv->sdev;
+	int ret;
+
+	strlcpy(sdev->name, dev->name, sizeof(sdev->name));
+	sdev->flags = DEV_FLAGS_OUTPUT;
+	sdev->putc = vidconsole_putc;
+	sdev->puts = vidconsole_puts;
+	sdev->priv = dev;
+	ret = stdio_register(sdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+UCLASS_DRIVER(vidconsole) = {
+	.id		= UCLASS_VIDEO_CONSOLE,
+	.name		= "vidconsole0",
+	.pre_probe	= vidconsole_pre_probe,
+	.post_probe	= vidconsole_post_probe,
+	.per_device_auto_alloc_size	= sizeof(struct vidconsole_priv),
+};
+
+void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+
+	priv->curr_col = min_t(short, col, priv->cols - 1);
+	priv->curr_row = min_t(short, row, priv->rows - 1);
+}
+
+static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
+			      char *const argv[])
+{
+	unsigned int col, row;
+	struct udevice *dev;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
+	if (!dev)
+		return CMD_RET_FAILURE;
+	col = simple_strtoul(argv[1], NULL, 10);
+	row = simple_strtoul(argv[2], NULL, 10);
+	vidconsole_position_cursor(dev, col, row);
+
+	return 0;
+}
+
+static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
+			 char *const argv[])
+{
+	struct udevice *dev;
+	const char *s;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+
+	uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
+	if (!dev)
+		return CMD_RET_FAILURE;
+	for (s = argv[1]; *s; s++)
+		vidconsole_put_char(dev, *s);
+
+	return 0;
+}
+
+U_BOOT_CMD(
+	setcurs, 3,	1,	do_video_setcursor,
+	"set cursor position within screen",
+	"      in character"
+);
+
+U_BOOT_CMD(
+	lcdputs, 2,	1,	do_video_puts,
+	"print string on video framebuffer",
+	"    "
+);
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
index 1615889..63d0d9d 100644
--- a/drivers/video/video-uclass.c
+++ b/drivers/video/video-uclass.c
@@ -189,6 +189,27 @@ static int video_post_probe(struct udevice *dev)
 #endif
 	video_clear(dev);
 
+	/*
+	 * Create a text console devices. For now we always do this, although
+	 * it might be useful to support only bitmap drawing on the device
+	 * for boards that don't need to display text.
+	 */
+	snprintf(name, sizeof(name), "%s.vidconsole", dev->name);
+	str = strdup(name);
+	if (!str)
+		return -ENOMEM;
+	snprintf(drv, sizeof(drv), "vidconsole%d", priv->rot);
+	ret = device_bind_driver(dev, drv, str, &cons);
+	if (ret) {
+		debug("%s: Cannot bind console driver\n", __func__);
+		return ret;
+	}
+	ret = device_probe(cons);
+	if (ret) {
+		debug("%s: Cannot probe console driver\n", __func__);
+		return ret;
+	}
+
 	return 0;
 };
 
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 3934375..a0a3a79 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -68,6 +68,7 @@ enum uclass_id {
 	UCLASS_USB_HUB,		/* USB hub */
 	UCLASS_VIDEO,		/* Video or LCD device */
 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */
+	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */
 
 	UCLASS_COUNT,
 	UCLASS_INVALID = -1,
diff --git a/include/video_console.h b/include/video_console.h
new file mode 100644
index 0000000..c0fc792
--- /dev/null
+++ b/include/video_console.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __video_console_h
+#define __video_console_h
+
+/**
+ * struct vidconsole_priv - uclass-private data about a console device
+ *
+ * @sdev:	stdio device, acting as an output sink
+ * @curr_col:	Current text column (0=left)
+ * @curr_row:	Current row (0=top)
+ * @rows:	Number of text rows
+ * @cols:	Number of text columns
+ */
+struct vidconsole_priv {
+	struct stdio_dev sdev;
+	int curr_col;
+	int curr_row;
+	int rows;
+	int cols;
+};
+
+/**
+ * struct vidconsole_ops - Video console operations
+ *
+ * These operations work on either an absolute console position (measured
+ * in pixels) or a text row number (measured in rows, where each row consists
+ * of an entire line of text - typically 16 pixels).
+ */
+struct vidconsole_ops {
+	/**
+	 * putc_xy() - write a single character to a position
+	 *
+	 * @dev:	Device to write to
+	 * @x:		Pixel X position (0=left-most pixel)
+	 * @y:		Pixel Y position (0=top-most pixel)
+	 * @ch:		Character to write
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*putc_xy)(struct udevice *dev, uint x, uint y, char ch);
+
+	/**
+	 * move_rows() - Move text rows from one place to another
+	 *
+	 * @dev:	Device to adjust
+	 * @rowdst:	Destination text row (0=top)
+	 * @rowsrc:	Source start text row
+	 * @count:	Number of text rows to move
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*move_rows)(struct udevice *dev, uint rowdst, uint rowsrc,
+			  uint count);
+
+	/**
+	 * set_row() - Set the colour of a text row
+	 *
+	 * Every pixel contained within the text row is adjusted
+	 *
+	 * @dev:	Device to adjust
+	 * @row:	Text row to adjust (0=top)
+	 * @clr:	Raw colour (pixel value) to write to each pixel
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_row)(struct udevice *dev, uint row, int clr);
+};
+
+/* Get a pointer to the driver operations for a video console device */
+#define vidconsole_get_ops(dev)  ((struct vidconsole_ops *)(dev)->driver->ops)
+
+/**
+ * vidconsole_putc_xy() - write a single character to a position
+ *
+ * @dev:	Device to write to
+ * @x:		Pixel X position (0=left-most pixel)
+ * @y:		Pixel Y position (0=top-most pixel)
+ * @ch:		Character to write
+ * @return 0 if OK, -ve on error
+ */
+int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch);
+
+/**
+ * vidconsole_move_rows() - Move text rows from one place to another
+ *
+ * @dev:	Device to adjust
+ * @rowdst:	Destination text row (0=top)
+ * @rowsrc:	Source start text row
+ * @count:	Number of text rows to move
+ * @return 0 if OK, -ve on error
+ */
+int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
+			 uint count);
+
+/**
+ * vidconsole_set_row() - Set the colour of a text row
+ *
+ * Every pixel contained within the text row is adjusted
+ *
+ * @dev:	Device to adjust
+ * @row:	Text row to adjust (0=top)
+ * @clr:	Raw colour (pixel value) to write to each pixel
+ * @return 0 if OK, -ve on error
+ */
+int vidconsole_set_row(struct udevice *dev, uint row, int clr);
+
+/**
+ * vidconsole_put_char() - Output a character to the current console position
+ *
+ * Outputs a character to the console and advances the cursor. This function
+ * handles wrapping to new lines and scrolling the console. Special
+ * characters are handled also: \n, \r, \b and \t.
+ *
+ * The device always starts with the cursor at position 0,0 (top left). It
+ * can be adjusted manually using vidconsole_position_cursor().
+ *
+ * @dev:	Device to adjust
+ * @ch:		Character to write
+ * @return 0 if OK, -ve on error
+ */
+int vidconsole_put_char(struct udevice *dev, char ch);
+
+/**
+ * vidconsole_position_cursor() - Move the text cursor
+ *
+ * @dev:	Device to adjust
+ * @col:	New cursor text column
+ * @row:	New cursor text row
+ * @return 0 if OK, -ve on error
+ */
+void vidconsole_position_cursor(struct udevice *dev, unsigned col,
+				unsigned row);
+
+#endif
-- 
cgit v0.10.2


From 72cded9ec0dd8e36e17372687b0284044ac079a6 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:18 -0700
Subject: dm: video: Add a 'normal' text console driver

Most of the time we don't need to rotate the display so a simple font
blitting feature is enough for our purposes. Add a simple driver which
supports this function. It provides text output on the console using
the standard 8x16-pixel font.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e82f1ae..b4eba8e 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -7,7 +7,7 @@
 
 ifdef CONFIG_DM
 obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
-obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o
+obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o
 endif
 
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c
new file mode 100644
index 0000000..d1031c8
--- /dev/null
+++ b/drivers/video/console_normal.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2001-2015
+ * DENX Software Engineering -- wd@denx.de
+ * Compulab Ltd - http://compulab.co.il/
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 		/* Get font data, width and height */
+
+static int console_normal_set_row(struct udevice *dev, uint row, int clr)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *line;
+	int pixels = VIDEO_FONT_HEIGHT * vid_priv->line_length;
+	int i;
+
+	line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+	switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+	case VIDEO_BPP8: {
+		uint8_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+	case VIDEO_BPP16: {
+		uint16_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+	case VIDEO_BPP32: {
+		uint32_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+	default:
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static int console_normal_move_rows(struct udevice *dev, uint rowdst,
+				     uint rowsrc, uint count)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *dst;
+	void *src;
+
+	dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+	src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+	memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
+
+	return 0;
+}
+
+static int console_normal_putc_xy(struct udevice *dev, uint x, uint y, char ch)
+{
+	struct udevice *vid = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+	int i, row;
+	void *line = vid_priv->fb + y * vid_priv->line_length +
+		x * VNBYTES(vid_priv->bpix);
+
+	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+		uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row];
+
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst++ = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst++ = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst++ = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line += vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+struct vidconsole_ops console_normal_ops = {
+	.putc_xy	= console_normal_putc_xy,
+	.move_rows	= console_normal_move_rows,
+	.set_row	= console_normal_set_row,
+};
+
+U_BOOT_DRIVER(vidconsole_normal) = {
+	.name	= "vidconsole0",
+	.id	= UCLASS_VIDEO_CONSOLE,
+	.ops	= &console_normal_ops,
+};
-- 
cgit v0.10.2


From b5146b2811b69775c88a51bc1275377369d6d3b3 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:19 -0700
Subject: dm: video: Add a driver for a rotated text console

Sometimes the console must be rotated. Add a driver which supports rotating
the text clockwise to 90, 180 and 270 degrees. This can support devices
where the display is rotated for mechanical reasons.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 946d958..499d00d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -44,6 +44,19 @@ config VIDEO_BPP32
 	  this option, such displays will not be supported and console output
 	  will be empty.
 
+config VIDEO_ROTATION
+	bool "Support rotated displays"
+	depends on DM_VIDEO
+	help
+	  Sometimes, for example if the display is mounted in portrait
+	  mode or even if it's mounted landscape but rotated by 180degree,
+	  we need to rotate our content of the display relative to the
+	  framebuffer, so that user can read the messages which are
+	  printed out. Enable this option to include a text driver which can
+	  support this. The rotation is set by the 'rot' parameter in
+	  struct video_priv: 0=unrotated, 1=90 degrees clockwise, 2=180
+	  degrees, 3=270 degrees.
+
 config VIDEO_VESA
 	bool "Enable VESA video driver support"
 	default n
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index b4eba8e..8f26d1d 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -8,6 +8,7 @@
 ifdef CONFIG_DM
 obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
 obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o
+obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o
 endif
 
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
diff --git a/drivers/video/console_rotate.c b/drivers/video/console_rotate.c
new file mode 100644
index 0000000..ebb31d8
--- /dev/null
+++ b/drivers/video/console_rotate.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2015
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 		/* Get font data, width and height */
+
+static int console_set_row_1(struct udevice *dev, uint row, int clr)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	int pbytes = VNBYTES(vid_priv->bpix);
+	void *line;
+	int i, j;
+
+	line = vid_priv->fb + vid_priv->line_length -
+		(row + 1) * VIDEO_FONT_HEIGHT * pbytes;
+	for (j = 0; j < vid_priv->ysize; j++) {
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line += vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc,
+			       uint count)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *dst;
+	void *src;
+	int pbytes = VNBYTES(vid_priv->bpix);
+	int j;
+
+	dst = vid_priv->fb + vid_priv->line_length -
+		(rowdst + count) * VIDEO_FONT_HEIGHT * pbytes;
+	src = vid_priv->fb + vid_priv->line_length -
+		(rowsrc + count) * VIDEO_FONT_HEIGHT * pbytes;
+
+	for (j = 0; j < vid_priv->ysize; j++) {
+		memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
+		src += vid_priv->line_length;
+		dst += vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+static int console_putc_xy_1(struct udevice *dev, uint x, uint y, char ch)
+{
+	struct udevice *vid = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+	int pbytes = VNBYTES(vid_priv->bpix);
+	int i, col;
+	int mask = 0x80;
+	void *line = vid_priv->fb + (x + 1) * vid_priv->line_length -
+		(y + 1) * pbytes;
+	uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT;
+
+	for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line += vid_priv->line_length;
+		mask >>= 1;
+	}
+
+	return 0;
+}
+
+
+static int console_set_row_2(struct udevice *dev, uint row, int clr)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *line;
+	int pixels = VIDEO_FONT_HEIGHT * vid_priv->xsize;
+	int i;
+
+	line = vid_priv->fb + vid_priv->ysize * vid_priv->line_length -
+		(row + 1) * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+	switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+	case VIDEO_BPP8: {
+		uint8_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+	case VIDEO_BPP16: {
+		uint16_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+	case VIDEO_BPP32: {
+		uint32_t *dst = line;
+
+		for (i = 0; i < pixels; i++)
+			*dst++ = clr;
+		break;
+	}
+#endif
+	default:
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+static int console_move_rows_2(struct udevice *dev, uint rowdst, uint rowsrc,
+			       uint count)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *dst;
+	void *src;
+	void *end;
+
+	end = vid_priv->fb + vid_priv->ysize * vid_priv->line_length;
+	dst = end - (rowdst + count) * VIDEO_FONT_HEIGHT *
+		vid_priv->line_length;
+	src = end - (rowsrc + count) * VIDEO_FONT_HEIGHT *
+		vid_priv->line_length;
+	memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
+
+	return 0;
+}
+
+static int console_putc_xy_2(struct udevice *dev, uint x, uint y, char ch)
+{
+	struct udevice *vid = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+	int i, row;
+	void *line;
+
+	line = vid_priv->fb + (vid_priv->ysize - y - 1) *
+		vid_priv->line_length +
+		(vid_priv->xsize - x - VIDEO_FONT_WIDTH - 1) *
+		VNBYTES(vid_priv->bpix);
+
+	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+		uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row];
+
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst-- = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst-- = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+				*dst-- = (bits & 0x80) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+				bits <<= 1;
+			}
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line -= vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+static int console_set_row_3(struct udevice *dev, uint row, int clr)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	int pbytes = VNBYTES(vid_priv->bpix);
+	void *line;
+	int i, j;
+
+	line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * pbytes;
+	for (j = 0; j < vid_priv->ysize; j++) {
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+				*dst++ = clr;
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line += vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+static int console_move_rows_3(struct udevice *dev, uint rowdst, uint rowsrc,
+			       uint count)
+{
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+	void *dst;
+	void *src;
+	int pbytes = VNBYTES(vid_priv->bpix);
+	int j;
+
+	dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * pbytes;
+	src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * pbytes;
+
+	for (j = 0; j < vid_priv->ysize; j++) {
+		memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
+		src += vid_priv->line_length;
+		dst += vid_priv->line_length;
+	}
+
+	return 0;
+}
+
+static int console_putc_xy_3(struct udevice *dev, uint x, uint y, char ch)
+{
+	struct udevice *vid = dev->parent;
+	struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+	int pbytes = VNBYTES(vid_priv->bpix);
+	int i, col;
+	int mask = 0x80;
+	void *line = vid_priv->fb + (vid_priv->ysize - x - 1) *
+		vid_priv->line_length + y * pbytes;
+	uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT;
+
+	for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
+		switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+		case VIDEO_BPP8: {
+			uint8_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+		case VIDEO_BPP16: {
+			uint16_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+		case VIDEO_BPP32: {
+			uint32_t *dst = line;
+
+			for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+				*dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+					: vid_priv->colour_bg;
+			}
+			break;
+		}
+#endif
+		default:
+			return -ENOSYS;
+		}
+		line -= vid_priv->line_length;
+		mask >>= 1;
+	}
+
+	return 0;
+}
+
+
+static int console_probe_1_3(struct udevice *dev)
+{
+	struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+	struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+
+	priv->cols = vid_priv->ysize / VIDEO_FONT_WIDTH;
+	priv->rows = vid_priv->xsize / VIDEO_FONT_HEIGHT;
+
+	return 0;
+}
+
+struct vidconsole_ops console_ops_1 = {
+	.putc_xy	= console_putc_xy_1,
+	.move_rows	= console_move_rows_1,
+	.set_row	= console_set_row_1,
+};
+
+struct vidconsole_ops console_ops_2 = {
+	.putc_xy	= console_putc_xy_2,
+	.move_rows	= console_move_rows_2,
+	.set_row	= console_set_row_2,
+};
+
+struct vidconsole_ops console_ops_3 = {
+	.putc_xy	= console_putc_xy_3,
+	.move_rows	= console_move_rows_3,
+	.set_row	= console_set_row_3,
+};
+
+U_BOOT_DRIVER(vidconsole_1) = {
+	.name	= "vidconsole1",
+	.id	= UCLASS_VIDEO_CONSOLE,
+	.ops	= &console_ops_1,
+	.probe	= console_probe_1_3,
+};
+
+U_BOOT_DRIVER(vidconsole_2) = {
+	.name	= "vidconsole2",
+	.id	= UCLASS_VIDEO_CONSOLE,
+	.ops	= &console_ops_2,
+};
+
+U_BOOT_DRIVER(vidconsole_3) = {
+	.name	= "vidconsole3",
+	.id	= UCLASS_VIDEO_CONSOLE,
+	.ops	= &console_ops_3,
+	.probe	= console_probe_1_3,
+};
-- 
cgit v0.10.2


From 8703ef3fdbfda239bcc67818e9b0f6ddaa7f6ad4 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:20 -0700
Subject: common: Move LCD and video memory reservation together

These two functions are conceptually the same. Move them together in the
pre-relocation init.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/common/board_f.c b/common/board_f.c
index 8094ac4..068db0d 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -451,22 +451,10 @@ static int reserve_lcd(void)
 }
 #endif /* CONFIG_LCD */
 
-static int reserve_trace(void)
-{
-#ifdef CONFIG_TRACE
-	gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE;
-	gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE);
-	debug("Reserving %dk for trace data at: %08lx\n",
-	      CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr);
-#endif
-
-	return 0;
-}
-
 #if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
 		!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
 		!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
-static int reserve_video(void)
+static int reserve_legacy_video(void)
 {
 	/* reserve memory for video display (always full pages) */
 	gd->relocaddr = video_setmem(gd->relocaddr);
@@ -476,6 +464,18 @@ static int reserve_video(void)
 }
 #endif
 
+static int reserve_trace(void)
+{
+#ifdef CONFIG_TRACE
+	gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE;
+	gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE);
+	debug("Reserving %dk for trace data at: %08lx\n",
+	      CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr);
+#endif
+
+	return 0;
+}
+
 static int reserve_uboot(void)
 {
 	/*
@@ -960,13 +960,13 @@ static init_fnc_t init_sequence_f[] = {
 #ifdef CONFIG_LCD
 	reserve_lcd,
 #endif
-	reserve_trace,
 	/* TODO: Why the dependency on CONFIG_8xx? */
 #if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
 		!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
 		!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
 	reserve_video,
 #endif
+	reserve_trace,
 #if !defined(CONFIG_BLACKFIN)
 	reserve_uboot,
 #endif
-- 
cgit v0.10.2


From 5a54194515caa4f4183dc9086938456612749573 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:21 -0700
Subject: dm: common: Add memory reservation for the video uclass

Before relocation we need to reserve memory for the video driver frame
buffers so that they can use this memory when they start up (after
relocation). Add a call to the uclass to permit this.

The current top and bottom of the region is stored in global_data so that
it can be checked post-relocation to ensure enough memory is available. No
video device should be probed before relocation.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/common/board_f.c b/common/board_f.c
index 068db0d..c470b59 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -46,6 +46,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -437,21 +438,38 @@ static int reserve_mmu(void)
 }
 #endif
 
-#ifdef CONFIG_LCD
+#ifdef CONFIG_DM_VIDEO
+static int reserve_video(void)
+{
+	ulong addr;
+	int ret;
+
+	addr = gd->relocaddr;
+	ret = video_reserve(&addr);
+	if (ret)
+		return ret;
+	gd->relocaddr = addr;
+
+	return 0;
+}
+#else
+
+# ifdef CONFIG_LCD
 static int reserve_lcd(void)
 {
-#ifdef CONFIG_FB_ADDR
+#  ifdef CONFIG_FB_ADDR
 	gd->fb_base = CONFIG_FB_ADDR;
-#else
+#  else
 	/* reserve memory for LCD display (always full pages) */
 	gd->relocaddr = lcd_setmem(gd->relocaddr);
 	gd->fb_base = gd->relocaddr;
-#endif /* CONFIG_FB_ADDR */
+#  endif /* CONFIG_FB_ADDR */
+
 	return 0;
 }
-#endif /* CONFIG_LCD */
+# endif /* CONFIG_LCD */
 
-#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
+# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
 		!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
 		!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
 static int reserve_legacy_video(void)
@@ -462,7 +480,8 @@ static int reserve_legacy_video(void)
 
 	return 0;
 }
-#endif
+# endif
+#endif /* !CONFIG_DM_VIDEO */
 
 static int reserve_trace(void)
 {
@@ -957,15 +976,19 @@ static init_fnc_t init_sequence_f[] = {
 		defined(CONFIG_ARM)
 	reserve_mmu,
 #endif
-#ifdef CONFIG_LCD
+#ifdef CONFIG_DM_VIDEO
+	reserve_video,
+#else
+# ifdef CONFIG_LCD
 	reserve_lcd,
-#endif
+# endif
 	/* TODO: Why the dependency on CONFIG_8xx? */
-#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
+# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
 		!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
 		!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
-	reserve_video,
-#endif
+	reserve_legacy_video,
+# endif
+#endif /* CONFIG_DM_VIDEO */
 	reserve_trace,
 #if !defined(CONFIG_BLACKFIN)
 	reserve_uboot,
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index 5d8b043..a587d3c 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -122,6 +122,10 @@ typedef struct global_data {
 	struct membuff console_out;	/* console output */
 	struct membuff console_in;	/* console input */
 #endif
+#ifdef CONFIG_DM_VIDEO
+	ulong video_top;		/* Top of video frame buffer area */
+	ulong video_bottom;		/* Bottom of video frame buffer area */
+#endif
 } gd_t;
 #endif
 
-- 
cgit v0.10.2


From b01c7923e3923680d9389bcd7cf5ad2e5f083e30 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:22 -0700
Subject: dm: video: Implement the bmp command for driver model

This command can use the bitmap display code in the uclass. This is similar
to the code in lcd.c and cfb_console.c. These other copies will go away when
all boards are converted to use driver model for video.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/common/cmd_bmp.c b/common/cmd_bmp.c
index f04b7d4..fd5b7db 100644
--- a/common/cmd_bmp.c
+++ b/common/cmd_bmp.c
@@ -10,7 +10,9 @@
  */
 
 #include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -225,6 +227,9 @@ static int bmp_info(ulong addr)
  */
 int bmp_display(ulong addr, int x, int y)
 {
+#ifdef CONFIG_DM_VIDEO
+	struct udevice *dev;
+#endif
 	int ret;
 	struct bmp_image *bmp = map_sysmem(addr, 0);
 	void *bmp_alloc_addr = NULL;
@@ -240,7 +245,22 @@ int bmp_display(ulong addr, int x, int y)
 	}
 	addr = map_to_sysmem(bmp);
 
-#if defined(CONFIG_LCD)
+#ifdef CONFIG_DM_VIDEO
+	ret = uclass_first_device(UCLASS_VIDEO, &dev);
+	if (!ret) {
+		if (!dev)
+			ret = -ENODEV;
+		if (!ret) {
+			bool align = false;
+
+# ifdef CONFIG_SPLASH_SCREEN_ALIGN
+			align = true;
+# endif /* CONFIG_SPLASH_SCREEN_ALIGN */
+			ret = video_bmp_display(dev, addr, x, y, align);
+		}
+	}
+	return ret ? CMD_RET_FAILURE : 0;
+#elif defined(CONFIG_LCD)
 	ret = lcd_display_bitmap(addr, x, y);
 #elif defined(CONFIG_VIDEO)
 	ret = video_display_bitmap(addr, x, y);
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 8f26d1d..ee04629 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -8,6 +8,7 @@
 ifdef CONFIG_DM
 obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
 obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o
+obj-$(CONFIG_DM_VIDEO) += video_bmp.o
 obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o
 endif
 
diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c
new file mode 100644
index 0000000..c9075d6
--- /dev/null
+++ b/drivers/video/video_bmp.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#ifdef CONFIG_VIDEO_BMP_RLE8
+#define BMP_RLE8_ESCAPE		0
+#define BMP_RLE8_EOL		0
+#define BMP_RLE8_EOBMP		1
+#define BMP_RLE8_DELTA		2
+
+static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
+				  int cnt)
+{
+	while (cnt > 0) {
+		*(*fbp)++ = cmap[*bmap++];
+		cnt--;
+	}
+}
+
+static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt)
+{
+	ushort *fb = *fbp;
+
+	while (cnt > 0) {
+		*fb++ = col;
+		cnt--;
+	}
+	*fbp = fb;
+}
+
+static void video_display_rle8_bitmap(struct udevice *dev,
+				      struct bmp_image *bmp, ushort *cmap,
+				      uchar *fb, int x_off, int y_off)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+	uchar *bmap;
+	ulong width, height;
+	ulong cnt, runlen;
+	int x, y;
+	int decode = 1;
+
+	debug("%s\n", __func__);
+	width = get_unaligned_le32(&bmp->header.width);
+	height = get_unaligned_le32(&bmp->header.height);
+	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
+
+	x = 0;
+	y = height - 1;
+
+	while (decode) {
+		if (bmap[0] == BMP_RLE8_ESCAPE) {
+			switch (bmap[1]) {
+			case BMP_RLE8_EOL:
+				/* end of line */
+				bmap += 2;
+				x = 0;
+				y--;
+				/* 16bpix, 2-byte per pixel, width should *2 */
+				fb -= (width * 2 + priv->line_length);
+				break;
+			case BMP_RLE8_EOBMP:
+				/* end of bitmap */
+				decode = 0;
+				break;
+			case BMP_RLE8_DELTA:
+				/* delta run */
+				x += bmap[2];
+				y -= bmap[3];
+				/* 16bpix, 2-byte per pixel, x should *2 */
+				fb = (uchar *)(priv->fb + (y + y_off - 1)
+					* priv->line_length + (x + x_off) * 2);
+				bmap += 4;
+				break;
+			default:
+				/* unencoded run */
+				runlen = bmap[1];
+				bmap += 2;
+				if (y < height) {
+					if (x < width) {
+						if (x + runlen > width)
+							cnt = width - x;
+						else
+							cnt = runlen;
+						draw_unencoded_bitmap(
+							(ushort **)&fb,
+							bmap, cmap, cnt);
+					}
+					x += runlen;
+				}
+				bmap += runlen;
+				if (runlen & 1)
+					bmap++;
+			}
+		} else {
+			/* encoded run */
+			if (y < height) {
+				runlen = bmap[0];
+				if (x < width) {
+					/* aggregate the same code */
+					while (bmap[0] == 0xff &&
+					       bmap[2] != BMP_RLE8_ESCAPE &&
+					       bmap[1] == bmap[3]) {
+						runlen += bmap[2];
+						bmap += 2;
+					}
+					if (x + runlen > width)
+						cnt = width - x;
+					else
+						cnt = runlen;
+					draw_encoded_bitmap((ushort **)&fb,
+						cmap[bmap[1]], cnt);
+				}
+				x += runlen;
+			}
+			bmap += 2;
+		}
+	}
+}
+#endif
+
+__weak void fb_put_byte(uchar **fb, uchar **from)
+{
+	*(*fb)++ = *(*from)++;
+}
+
+#if defined(CONFIG_BMP_16BPP)
+__weak void fb_put_word(uchar **fb, uchar **from)
+{
+	*(*fb)++ = *(*from)++;
+	*(*fb)++ = *(*from)++;
+}
+#endif /* CONFIG_BMP_16BPP */
+
+#define BMP_ALIGN_CENTER	0x7fff
+
+/**
+ * video_splash_align_axis() - Align a single coordinate
+ *
+ *- if a coordinate is 0x7fff then the image will be centred in
+ *  that direction
+ *- if a coordinate is -ve then it will be offset to the
+ *  left/top of the centre by that many pixels
+ *- if a coordinate is positive it will be used unchnaged.
+ *
+ * @axis:	Input and output coordinate
+ * @panel_size:	Size of panel in pixels for that axis
+ * @picture_size:	Size of bitmap in pixels for that axis
+ */
+static void video_splash_align_axis(int *axis, unsigned long panel_size,
+				    unsigned long picture_size)
+{
+	unsigned long panel_picture_delta = panel_size - picture_size;
+	unsigned long axis_alignment;
+
+	if (*axis == BMP_ALIGN_CENTER)
+		axis_alignment = panel_picture_delta / 2;
+	else if (*axis < 0)
+		axis_alignment = panel_picture_delta + *axis + 1;
+	else
+		return;
+
+	*axis = max(0, (int)axis_alignment);
+}
+
+static void video_set_cmap(struct udevice *dev,
+			   struct bmp_color_table_entry *cte, unsigned colours)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+	int i;
+	ushort *cmap = priv->cmap;
+
+	debug("%s: colours=%d\n", __func__, colours);
+	for (i = 0; i < colours; ++i) {
+		*cmap = ((cte->red   << 8) & 0xf800) |
+			((cte->green << 3) & 0x07e0) |
+			((cte->blue  >> 3) & 0x001f);
+		cmap++;
+		cte++;
+	}
+}
+
+int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
+		      bool align)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+	ushort *cmap_base = NULL;
+	ushort i, j;
+	uchar *fb;
+	struct bmp_image *bmp = map_sysmem(bmp_image, 0);
+	uchar *bmap;
+	ushort padded_width;
+	unsigned long width, height, byte_width;
+	unsigned long pwidth = priv->xsize;
+	unsigned colours, bpix, bmp_bpix;
+	struct bmp_color_table_entry *palette;
+	int hdr_size;
+
+	if (!bmp || !(bmp->header.signature[0] == 'B' &&
+	    bmp->header.signature[1] == 'M')) {
+		printf("Error: no valid bmp image at %lx\n", bmp_image);
+
+		return -EINVAL;
+	}
+
+	width = get_unaligned_le32(&bmp->header.width);
+	height = get_unaligned_le32(&bmp->header.height);
+	bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
+	hdr_size = get_unaligned_le16(&bmp->header.size);
+	debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
+	palette = (void *)bmp + 14 + hdr_size;
+
+	colours = 1 << bmp_bpix;
+
+	bpix = VNBITS(priv->bpix);
+
+	if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
+		printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
+		       bpix, bmp_bpix);
+
+		return -EINVAL;
+	}
+
+	/*
+	 * We support displaying 8bpp BMPs on 16bpp LCDs
+	 * and displaying 24bpp BMPs on 32bpp LCDs
+	 * */
+	if (bpix != bmp_bpix &&
+	    !(bmp_bpix == 8 && bpix == 16) &&
+	    !(bmp_bpix == 24 && bpix == 32)) {
+		printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
+		       bpix, get_unaligned_le16(&bmp->header.bit_count));
+		return -EPERM;
+	}
+
+	debug("Display-bmp: %d x %d  with %d colours, display %d\n",
+	      (int)width, (int)height, (int)colours, 1 << bpix);
+
+	if (bmp_bpix == 8)
+		video_set_cmap(dev, palette, colours);
+
+	padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
+
+	if (align) {
+		video_splash_align_axis(&x, priv->xsize, width);
+		video_splash_align_axis(&y, priv->ysize, height);
+	}
+
+	if ((x + width) > pwidth)
+		width = pwidth - x;
+	if ((y + height) > priv->ysize)
+		height = priv->ysize - y;
+
+	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
+	fb = (uchar *)(priv->fb +
+		(y + height - 1) * priv->line_length + x * bpix / 8);
+
+	switch (bmp_bpix) {
+	case 1:
+	case 8: {
+		cmap_base = priv->cmap;
+#ifdef CONFIG_VIDEO_BMP_RLE8
+		u32 compression = get_unaligned_le32(&bmp->header.compression);
+		debug("compressed %d %d\n", compression, BMP_BI_RLE8);
+		if (compression == BMP_BI_RLE8) {
+			if (bpix != 16) {
+				/* TODO implement render code for bpix != 16 */
+				printf("Error: only support 16 bpix");
+				return -EPROTONOSUPPORT;
+			}
+			video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x,
+						  y);
+			break;
+		}
+#endif
+
+		if (bpix != 16)
+			byte_width = width;
+		else
+			byte_width = width * 2;
+
+		for (i = 0; i < height; ++i) {
+			WATCHDOG_RESET();
+			for (j = 0; j < width; j++) {
+				if (bpix != 16) {
+					fb_put_byte(&fb, &bmap);
+				} else {
+					*(uint16_t *)fb = cmap_base[*bmap];
+					bmap++;
+					fb += sizeof(uint16_t) / sizeof(*fb);
+				}
+			}
+			bmap += (padded_width - width);
+			fb -= byte_width + priv->line_length;
+		}
+		break;
+	}
+#if defined(CONFIG_BMP_16BPP)
+	case 16:
+		for (i = 0; i < height; ++i) {
+			WATCHDOG_RESET();
+			for (j = 0; j < width; j++)
+				fb_put_word(&fb, &bmap);
+
+			bmap += (padded_width - width) * 2;
+			fb -= width * 2 + lcd_line_length;
+		}
+		break;
+#endif /* CONFIG_BMP_16BPP */
+#if defined(CONFIG_BMP_24BMP)
+	case 24:
+		for (i = 0; i < height; ++i) {
+			for (j = 0; j < width; j++) {
+				*(fb++) = *(bmap++);
+				*(fb++) = *(bmap++);
+				*(fb++) = *(bmap++);
+				*(fb++) = 0;
+			}
+			fb -= lcd_line_length + width * (bpix / 8);
+		}
+		break;
+#endif /* CONFIG_BMP_24BMP */
+#if defined(CONFIG_BMP_32BPP)
+	case 32:
+		for (i = 0; i < height; ++i) {
+			for (j = 0; j < width; j++) {
+				*(fb++) = *(bmap++);
+				*(fb++) = *(bmap++);
+				*(fb++) = *(bmap++);
+				*(fb++) = *(bmap++);
+			}
+			fb -= lcd_line_length + width * (bpix / 8);
+		}
+		break;
+#endif /* CONFIG_BMP_32BPP */
+	default:
+		break;
+	};
+
+	video_sync(dev);
+
+	return 0;
+}
+
-- 
cgit v0.10.2


From e3b81c1c0dbd677338163517ed22a1b6f5cb4e07 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:23 -0700
Subject: dm: stdio: video: Plumb the video uclass into stdio

Register video drivers with stdio so that they can be used for text output.
This needs to be done explicitly for now. At some point we should be able to
convert stdio itself to driver model and avoid this step.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/common/stdio.c b/common/stdio.c
index 8311ac7..7252bab 100644
--- a/common/stdio.c
+++ b/common/stdio.c
@@ -281,12 +281,23 @@ int stdio_add_devices(void)
 	i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
 #endif
 #endif
-#ifdef CONFIG_LCD
+#ifdef CONFIG_DM_VIDEO
+	struct udevice *vdev;
+
+	for (ret = uclass_first_device(UCLASS_VIDEO, &vdev);
+	     vdev;
+	     ret = uclass_next_device(&vdev))
+		;
+	if (ret)
+		printf("%s: Video device failed (ret=%d)\n", __func__, ret);
+#else
+# if defined(CONFIG_LCD)
 	drv_lcd_init ();
-#endif
-#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
+# endif
+# if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)
 	drv_video_init ();
-#endif
+# endif
+#endif /* CONFIG_DM_VIDEO */
 #if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD)
 	drv_keyboard_init ();
 #endif
-- 
cgit v0.10.2


From 1e69ad014c68bf375ba2113486c5c0095d3292d0 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:24 -0700
Subject: sandbox: Move CONFIG_VIDEO_SANDBOX_SDL to Kconfig

Move this option to Kconfig. This is quite simple as only sandbox uses the
driver.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index caa7336..65184c8 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -1,6 +1,7 @@
 CONFIG_SYS_MALLOC_F_LEN=0x2000
 CONFIG_PCI=y
 CONFIG_DEFAULT_DEVICE_TREE="sandbox"
+CONFIG_DM_PCI_COMPAT=y
 CONFIG_FIT=y
 CONFIG_FIT_VERBOSE=y
 CONFIG_FIT_SIGNATURE=y
@@ -51,7 +52,6 @@ CONFIG_SPI_FLASH_SST=y
 CONFIG_SPI_FLASH_WINBOND=y
 CONFIG_DM_ETH=y
 CONFIG_DM_PCI=y
-CONFIG_DM_PCI_COMPAT=y
 CONFIG_PCI_SANDBOX=y
 CONFIG_PINCTRL=y
 CONFIG_PINCONF=y
@@ -76,6 +76,7 @@ CONFIG_USB_EMUL=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_KEYBOARD=y
 CONFIG_SYS_USB_EVENT_POLL=y
+CONFIG_VIDEO_SANDBOX_SDL=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
 CONFIG_LZ4=y
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 499d00d..ae122da 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -300,6 +300,15 @@ config DISPLAY_PORT
 	   to drive LCD panels. This framework provides support for enabling
 	   these displays where supported by the video hardware.
 
+config VIDEO_SANDBOX_SDL
+	bool "Enable sandbox video console using SDL"
+	depends on SANDBOX
+	help
+	  When using sandbox you can enable an emulated LCD display which
+	  appears as an SDL (Simple DirectMedia Layer) window. This is a
+	  console device and can display stdout output. Within U-Boot is is
+	  a normal bitmap display and can display images as well as text.
+
 config VIDEO_TEGRA124
 	bool "Enable video support on Tegra124"
 	help
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index d3112e1..e1be603 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -159,7 +159,6 @@
 /* LCD and keyboard require SDL support */
 #ifdef CONFIG_SANDBOX_SDL
 #define CONFIG_LCD
-#define CONFIG_VIDEO_SANDBOX_SDL
 #define CONFIG_CMD_BMP
 #define CONFIG_BOARD_EARLY_INIT_F
 #define CONFIG_CONSOLE_MUX
-- 
cgit v0.10.2


From 3ade5bc4dc24edf5e1f13f3c43a9e8b7f8c2d853 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:25 -0700
Subject: dm: video: sandbox: Convert sandbox to use driver model for video

Now that driver model support is available, convert sandbox over to use it.
We can remove a few of the special hooks that sandbox currently has.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index d2addb4..e3f02bf 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -117,6 +117,7 @@
 	};
 
 	lcd {
+		u-boot,dm-pre-reloc;
 		compatible = "sandbox,lcd-sdl";
 		xres = <1366>;
 		yres = <768>;
diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c
index 592f772..b41e9de 100644
--- a/board/sandbox/sandbox.c
+++ b/board/sandbox/sandbox.c
@@ -47,23 +47,6 @@ int dram_init(void)
 	return 0;
 }
 
-#ifdef CONFIG_BOARD_EARLY_INIT_F
-int board_early_init_f(void)
-{
-#ifdef CONFIG_VIDEO_SANDBOX_SDL
-	int ret;
-
-	ret = sandbox_lcd_sdl_early_init();
-	if (ret) {
-		puts("Could not init sandbox LCD emulation\n");
-		return ret;
-	}
-#endif
-
-	return 0;
-}
-#endif
-
 #ifdef CONFIG_BOARD_LATE_INIT
 int board_late_init(void)
 {
diff --git a/common/lcd.c b/common/lcd.c
index d29308a..2f3594a 100644
--- a/common/lcd.c
+++ b/common/lcd.c
@@ -31,10 +31,6 @@
 #endif
 #endif
 
-#ifdef CONFIG_SANDBOX
-#include 
-#endif
-
 #ifndef CONFIG_LCD_ALIGNMENT
 #define CONFIG_LCD_ALIGNMENT PAGE_SIZE
 #endif
@@ -72,13 +68,6 @@ void lcd_sync(void)
 	if (lcd_flush_dcache)
 		flush_dcache_range((u32)lcd_base,
 			(u32)(lcd_base + lcd_get_size(&line_length)));
-#elif defined(CONFIG_SANDBOX) && defined(CONFIG_VIDEO_SANDBOX_SDL)
-	static ulong last_sync;
-
-	if (get_timer(last_sync) > 10) {
-		sandbox_sdl_sync(lcd_base);
-		last_sync = get_timer(0);
-	}
 #endif
 }
 
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 65184c8..898c5f4 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -76,6 +76,7 @@ CONFIG_USB_EMUL=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_KEYBOARD=y
 CONFIG_SYS_USB_EVENT_POLL=y
+CONFIG_DM_VIDEO=y
 CONFIG_VIDEO_SANDBOX_SDL=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c
index cd2f91e..45dff98 100644
--- a/drivers/serial/sandbox.c
+++ b/drivers/serial/sandbox.c
@@ -16,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -114,9 +115,7 @@ static int sandbox_serial_pending(struct udevice *dev, bool input)
 		return 0;
 
 	os_usleep(100);
-#ifdef CONFIG_LCD
-	lcd_sync();
-#endif
+	video_sync_all();
 	if (next_index == serial_buf_read)
 		return 1;	/* buffer full */
 
diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c
index ba4578e..450628e 100644
--- a/drivers/video/sandbox_sdl.c
+++ b/drivers/video/sandbox_sdl.c
@@ -5,75 +5,76 @@
  */
 
 #include 
+#include 
 #include 
-#include 
-#include 
+#include 
 #include 
 #include 
+#include 
 
 DECLARE_GLOBAL_DATA_PTR;
 
 enum {
-	/* Maximum LCD size we support */
+	/* Default LCD size we support */
 	LCD_MAX_WIDTH		= 1366,
 	LCD_MAX_HEIGHT		= 768,
-	LCD_MAX_LOG2_BPP	= 4,		/* 2^4 = 16 bpp */
 };
 
-vidinfo_t panel_info;
 
-void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
-{
-}
+/* This platform data is needed in tests, so declare it here */
+struct sandbox_sdl_plat {
+	int xres;
+	int yres;
+	int bpix;
+	int rot;
+};
 
-void lcd_ctrl_init(void *lcdbase)
+static int sandbox_sdl_probe(struct udevice *dev)
 {
-	/*
-	 * Allocate memory to keep BMP color conversion map. This is required
-	 * for 8 bit BMPs only (hence 256 colors). If malloc fails - keep
-	 * going, it is not even clear if displyaing the bitmap will be
-	 * required on the way up.
-	 */
-	panel_info.cmap = malloc(256 * NBITS(panel_info.vl_bpix) / 8);
-}
+	struct sandbox_sdl_plat *plat = dev_get_platdata(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	int ret;
 
-void lcd_enable(void)
-{
-	if (sandbox_sdl_init_display(panel_info.vl_col, panel_info.vl_row,
-				     panel_info.vl_bpix))
+	ret = sandbox_sdl_init_display(plat->xres, plat->yres, plat->bpix);
+	if (ret) {
 		puts("LCD init failed\n");
+		return ret;
+	}
+	uc_priv->xsize = plat->xres;
+	uc_priv->ysize = plat->yres;
+	uc_priv->bpix = plat->bpix;
+	uc_priv->rot = plat->rot;
+
+	return 0;
 }
 
-int sandbox_lcd_sdl_early_init(void)
+static int sandbox_sdl_bind(struct udevice *dev)
 {
+	struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+	struct sandbox_sdl_plat *plat = dev_get_platdata(dev);
 	const void *blob = gd->fdt_blob;
-	int xres = LCD_MAX_WIDTH, yres = LCD_MAX_HEIGHT;
-	int node;
+	int node = dev->of_offset;
 	int ret = 0;
 
-	/*
-	 * The code in common/lcd.c does not cope with not being able to
-	 * set up a frame buffer. It will just happily keep writing to
-	 * invalid memory. So here we make sure that at least some buffer
-	 * is available even if it actually won't be displayed.
-	 */
-	node = fdtdec_next_compatible(blob, 0, COMPAT_SANDBOX_LCD_SDL);
-	if (node >= 0) {
-		xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH);
-		yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT);
-		if (xres < 0 || xres > LCD_MAX_WIDTH) {
-			xres = LCD_MAX_WIDTH;
-			ret = -EINVAL;
-		}
-		if (yres < 0 || yres > LCD_MAX_HEIGHT) {
-			yres = LCD_MAX_HEIGHT;
-			ret = -EINVAL;
-		}
-	}
-
-	panel_info.vl_col = xres;
-	panel_info.vl_row = yres;
-	panel_info.vl_bpix = LCD_COLOR16;
+	plat->xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH);
+	plat->yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT);
+	plat->bpix = VIDEO_BPP16;
+	uc_plat->size = plat->xres * plat->yres * (1 << plat->bpix) / 8;
+	debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
 
 	return ret;
 }
+
+static const struct udevice_id sandbox_sdl_ids[] = {
+	{ .compatible = "sandbox,lcd-sdl" },
+	{ }
+};
+
+U_BOOT_DRIVER(sdl_sandbox) = {
+	.name	= "sdl_sandbox",
+	.id	= UCLASS_VIDEO,
+	.of_match = sandbox_sdl_ids,
+	.bind	= sandbox_sdl_bind,
+	.probe	= sandbox_sdl_probe,
+	.platdata_auto_alloc_size	= sizeof(struct sandbox_sdl_plat),
+};
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index e1be603..23ae44c 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -158,9 +158,7 @@
 
 /* LCD and keyboard require SDL support */
 #ifdef CONFIG_SANDBOX_SDL
-#define CONFIG_LCD
 #define CONFIG_CMD_BMP
-#define CONFIG_BOARD_EARLY_INIT_F
 #define CONFIG_CONSOLE_MUX
 #define CONFIG_SYS_CONSOLE_IS_IN_ENV
 #define LCD_BPP			LCD_COLOR16
@@ -169,12 +167,12 @@
 #define CONFIG_KEYBOARD
 
 #define SANDBOX_SERIAL_SETTINGS		"stdin=serial,cros-ec-keyb,usbkbd\0" \
-					"stdout=serial,lcd\0" \
-					"stderr=serial,lcd\0"
+					"stdout=serial,lcd.vidconsole\0" \
+					"stderr=serial,lcd.vidconsole\0"
 #else
 #define SANDBOX_SERIAL_SETTINGS		"stdin=serial\0" \
-					"stdout=serial,lcd\0" \
-					"stderr=serial,lcd\0"
+					"stdout=serial,lcd.vidconsole\0" \
+					"stderr=serial,lcd.vidconsole\0"
 #endif
 
 #define SANDBOX_ETH_SETTINGS		"ethaddr=00:00:11:22:33:44\0" \
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 27b350e..25e98c9 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -151,7 +151,6 @@ enum fdt_compat_id {
 	COMPAT_GENERIC_SPI_FLASH,	/* Generic SPI Flash chip */
 	COMPAT_MAXIM_98095_CODEC,	/* MAX98095 Codec */
 	COMPAT_SAMSUNG_EXYNOS5_I2C,	/* Exynos5 High Speed I2C Controller */
-	COMPAT_SANDBOX_LCD_SDL,		/* Sandbox LCD emulation with SDL */
 	COMPAT_SAMSUNG_EXYNOS_SYSMMU,	/* Exynos sysmmu */
 	COMPAT_INTEL_MICROCODE,		/* Intel microcode update */
 	COMPAT_MEMORY_SPD,		/* Memory SPD information */
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index b50d105..d56e1b1 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -56,7 +56,6 @@ static const char * const compat_names[COMPAT_COUNT] = {
 	COMPAT(GENERIC_SPI_FLASH, "spi-flash"),
 	COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"),
 	COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"),
-	COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"),
 	COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"),
 	COMPAT(INTEL_MICROCODE, "intel,microcode"),
 	COMPAT(MEMORY_SPD, "memory-spd"),
-- 
cgit v0.10.2


From 3c97c4fb52af9d4da4546cbb8a52bdb6f0d4b57f Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:26 -0700
Subject: dm: video: test: Add tests for the video uclass

Add tests that check that the video console is working correcty. Also check
that text output produces the expected result. Test coverage includes
character output, wrapping and scrolling.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 2e0d320..9b8d658 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -195,6 +195,13 @@
 		vss-microvolts = <0>;
 	};
 
+	lcd {
+		u-boot,dm-pre-reloc;
+		compatible = "sandbox,lcd-sdl";
+		xres = <1366>;
+		yres = <768>;
+	};
+
 	leds {
 		compatible = "gpio-leds";
 
diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c
index 450628e..21448a1 100644
--- a/drivers/video/sandbox_sdl.c
+++ b/drivers/video/sandbox_sdl.c
@@ -20,15 +20,6 @@ enum {
 	LCD_MAX_HEIGHT		= 768,
 };
 
-
-/* This platform data is needed in tests, so declare it here */
-struct sandbox_sdl_plat {
-	int xres;
-	int yres;
-	int bpix;
-	int rot;
-};
-
 static int sandbox_sdl_probe(struct udevice *dev)
 {
 	struct sandbox_sdl_plat *plat = dev_get_platdata(dev);
diff --git a/include/dm/test.h b/include/dm/test.h
index a4bc5c8..ca924d9 100644
--- a/include/dm/test.h
+++ b/include/dm/test.h
@@ -155,6 +155,14 @@ enum {
 /* Declare a new driver model test */
 #define DM_TEST(_name, _flags)	UNIT_TEST(_name, _flags, dm_test)
 
+/* This platform data is needed in tests, so declare it here */
+struct sandbox_sdl_plat {
+	int xres;
+	int yres;
+	int bpix;
+	int rot;
+};
+
 /* Declare ping methods for the drivers */
 int test_ping(struct udevice *dev, int pingval, int *pingret);
 int testfdt_ping(struct udevice *dev, int pingval, int *pingret);
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 3ff1b75..d4f3f22 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -34,5 +34,6 @@ obj-$(CONFIG_DM_USB) += usb.o
 obj-$(CONFIG_DM_PMIC) += pmic.o
 obj-$(CONFIG_DM_REGULATOR) += regulator.o
 obj-$(CONFIG_TIMER) += timer.o
+obj-$(CONFIG_DM_VIDEO) += video.o
 obj-$(CONFIG_ADC) += adc.o
 endif
diff --git a/test/dm/video.c b/test/dm/video.c
new file mode 100644
index 0000000..b197b01
--- /dev/null
+++ b/test/dm/video.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014 Google, Inc
+ * Written by Simon Glass 
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * These tests use the standard sandbox frame buffer, the resolution of which
+ * is defined in the device tree. This only supports 16bpp so the tests only
+ * test that code path. It would be possible to adjust this fairly easily,
+ * by adjusting the bpix value in struct sandbox_sdl_plat. However the code
+ * in sandbox_sdl_sync() would also need to change to handle the different
+ * surface depth.
+ */
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Basic test of the video uclass */
+static int dm_test_video_base(struct unit_test_state *uts)
+{
+	struct video_priv *priv;
+	struct udevice *dev;
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_asserteq(1366, video_get_xsize(dev));
+	ut_asserteq(768, video_get_ysize(dev));
+	priv = dev_get_uclass_priv(dev);
+	ut_asserteq(priv->fb_size, 1366 * 768 * 2);
+
+	return 0;
+}
+DM_TEST(dm_test_video_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/**
+ * compress_frame_buffer() - Compress the frame buffer and return its size
+ *
+ * We want to write tests which perform operations on the video console and
+ * check that the frame buffer ends up with the correct contents. But it is
+ * painful to store 'known good' images for comparison with the frame
+ * buffer. As an alternative, we can compress the frame buffer and check the
+ * size of the compressed data. This provides a pretty good level of
+ * certainty and the resulting tests need only check a single value.
+ *
+ * @dev:	Video device
+ * @return compressed size of the frame buffer, or -ve on error
+ */
+static int compress_frame_buffer(struct udevice *dev)
+{
+	struct video_priv *priv = dev_get_uclass_priv(dev);
+	uint destlen;
+	void *dest;
+	int ret;
+
+	destlen = priv->fb_size;
+	dest = malloc(priv->fb_size);
+	if (!dest)
+		return -ENOMEM;
+	ret = BZ2_bzBuffToBuffCompress(dest, &destlen,
+				       priv->fb, priv->fb_size,
+				       3, 0, 0);
+	free(dest);
+	if (ret)
+		return ret;
+
+	return destlen;
+}
+
+/*
+ * Call this function at any point to halt and show the current display. Be
+ * sure to run the test with the -l flag.
+ */
+static void __maybe_unused see_output(void)
+{
+	video_sync_all();
+	while (1);
+}
+
+/* Test text output works on the video console */
+static int dm_test_video_text(struct unit_test_state *uts)
+{
+	struct udevice *dev, *con;
+	int i;
+
+#define WHITE		0xffff
+#define SCROLL_LINES	100
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_asserteq(46, compress_frame_buffer(dev));
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+	vidconsole_putc_xy(con, 0, 0, 'a');
+	ut_asserteq(79, compress_frame_buffer(dev));
+
+	vidconsole_putc_xy(con, 0, 0, ' ');
+	ut_asserteq(46, compress_frame_buffer(dev));
+
+	for (i = 0; i < 20; i++)
+		vidconsole_putc_xy(con, i * 8, 0, ' ' + i);
+	ut_asserteq(273, compress_frame_buffer(dev));
+
+	vidconsole_set_row(con, 0, WHITE);
+	ut_asserteq(46, compress_frame_buffer(dev));
+
+	for (i = 0; i < 20; i++)
+		vidconsole_putc_xy(con, i * 8, 0, ' ' + i);
+	ut_asserteq(273, compress_frame_buffer(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_video_text, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test handling of special characters in the console */
+static int dm_test_video_chars(struct unit_test_state *uts)
+{
+	struct udevice *dev, *con;
+	const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very modest  \bman\n\t\tand Has much to\b\bto be modest about.";
+	const char *s;
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+	for (s = test_string; *s; s++)
+		vidconsole_put_char(con, *s);
+	ut_asserteq(466, compress_frame_buffer(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_video_chars, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/**
+ * check_vidconsole_output() - Run a text console test
+ *
+ * @uts:	Test state
+ * @rot:	Console rotation (0, 90, 180, 270)
+ * @wrap_size:	Expected size of compressed frame buffer for the wrap test
+ * @scroll_size: Same for the scroll test
+ * @return 0 on success
+ */
+static int check_vidconsole_output(struct unit_test_state *uts, int rot,
+				   int wrap_size, int scroll_size)
+{
+	struct udevice *dev, *con;
+	struct sandbox_sdl_plat *plat;
+	int i;
+
+	ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
+	ut_assert(!device_active(dev));
+	plat = dev_get_platdata(dev);
+	plat->rot = rot;
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+	ut_asserteq(46, compress_frame_buffer(dev));
+
+	/* Check display wrap */
+	for (i = 0; i < 120; i++)
+		vidconsole_put_char(con, 'A' + i % 50);
+	ut_asserteq(wrap_size, compress_frame_buffer(dev));
+
+	/* Check display scrolling */
+	for (i = 0; i < SCROLL_LINES; i++) {
+		vidconsole_put_char(con, 'A' + i % 50);
+		vidconsole_put_char(con, '\n');
+	}
+	ut_asserteq(scroll_size, compress_frame_buffer(dev));
+
+	/* If we scroll enough, the screen becomes blank again */
+	for (i = 0; i < SCROLL_LINES; i++)
+		vidconsole_put_char(con, '\n');
+	ut_asserteq(46, compress_frame_buffer(dev));
+
+	return 0;
+}
+
+/* Test text output through the console uclass */
+static int dm_test_video_context(struct unit_test_state *uts)
+{
+	return check_vidconsole_output(uts, 0, 788, 453);
+}
+DM_TEST(dm_test_video_context, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-- 
cgit v0.10.2


From 85e08db85bb9d446f3b48e42cec73f9f24073d52 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:27 -0700
Subject: dm: video: test: Add tests for rotated consoles

Test that text is displayed correctly when the console is rotated.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 898c5f4..09ced01 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -77,6 +77,7 @@ CONFIG_USB_STORAGE=y
 CONFIG_USB_KEYBOARD=y
 CONFIG_SYS_USB_EVENT_POLL=y
 CONFIG_DM_VIDEO=y
+CONFIG_VIDEO_ROTATION=y
 CONFIG_VIDEO_SANDBOX_SDL=y
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
diff --git a/test/dm/video.c b/test/dm/video.c
index b197b01..65db216 100644
--- a/test/dm/video.c
+++ b/test/dm/video.c
@@ -188,3 +188,30 @@ static int dm_test_video_context(struct unit_test_state *uts)
 	return check_vidconsole_output(uts, 0, 788, 453);
 }
 DM_TEST(dm_test_video_context, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation1(struct unit_test_state *uts)
+{
+	ut_assertok(check_vidconsole_output(uts, 1, 1112, 680));
+
+	return 0;
+}
+DM_TEST(dm_test_video_rotation1, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation2(struct unit_test_state *uts)
+{
+	ut_assertok(check_vidconsole_output(uts, 2, 785, 446));
+
+	return 0;
+}
+DM_TEST(dm_test_video_rotation2, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation3(struct unit_test_state *uts)
+{
+	ut_assertok(check_vidconsole_output(uts, 3, 1134, 681));
+
+	return 0;
+}
+DM_TEST(dm_test_video_rotation3, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-- 
cgit v0.10.2


From 747440d0fa95f2205a8fcef49b6c7845700b6246 Mon Sep 17 00:00:00 2001
From: Simon Glass 
Date: Mon, 18 Jan 2016 19:52:28 -0700
Subject: dm: video: test: Test that bitmap display works correctly

Add a test for the 'bmp' command. Test both the uncompressed and compressed
versions of the file, since they use different code paths.

Signed-off-by: Simon Glass 
Acked-by: Anatolij Gustschin 

diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 23ae44c..6498981 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -163,6 +163,8 @@
 #define CONFIG_SYS_CONSOLE_IS_IN_ENV
 #define LCD_BPP			LCD_COLOR16
 #define CONFIG_LCD_BMP_RLE8
+#define CONFIG_VIDEO_BMP_RLE8
+#define CONFIG_SPLASH_SCREEN_ALIGN
 
 #define CONFIG_KEYBOARD
 
diff --git a/test/dm/video.c b/test/dm/video.c
index 65db216..9f5e7fc 100644
--- a/test/dm/video.c
+++ b/test/dm/video.c
@@ -215,3 +215,57 @@ static int dm_test_video_rotation3(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_video_rotation3, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Read a file into memory and return a pointer to it */
+static int read_file(struct unit_test_state *uts, const char *fname,
+		     ulong *addrp)
+{
+	int buf_size = 100000;
+	ulong addr = 0;
+	int size, fd;
+	char *buf;
+
+	buf = map_sysmem(addr, 0);
+	ut_assert(buf != NULL);
+	fd = os_open(fname, OS_O_RDONLY);
+	ut_assert(fd >= 0);
+	size = os_read(fd, buf, buf_size);
+	ut_assert(size >= 0);
+	ut_assert(size < buf_size);
+	os_close(fd);
+	*addrp = addr;
+
+	return 0;
+}
+
+/* Test drawing a bitmap file */
+static int dm_test_video_bmp(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	ulong addr;
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+	ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+	ut_asserteq(1368, compress_frame_buffer(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_video_bmp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test drawing a compressed bitmap file */
+static int dm_test_video_bmp_comp(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	ulong addr;
+
+	ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+	ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr));
+
+	ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+	ut_asserteq(1368, compress_frame_buffer(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_video_bmp_comp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/tools/logos/denx-comp.bmp b/tools/logos/denx-comp.bmp
new file mode 100644
index 0000000..89d0f47
Binary files /dev/null and b/tools/logos/denx-comp.bmp differ
-- 
cgit v0.10.2