diff options
186 files changed, 6366 insertions, 4289 deletions
@@ -489,8 +489,6 @@ endif # that (or fail if absent). Otherwise, search for a linker script in a # standard location. -LDSCRIPT_MAKEFILE_DIR = $(dir $(LDSCRIPT)) - ifndef LDSCRIPT #LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug ifdef CONFIG_SYS_LDSCRIPT @@ -515,8 +513,6 @@ ifndef LDSCRIPT endif ifeq ($(wildcard $(LDSCRIPT)),) LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot.lds - # We don't expect a Makefile here - LDSCRIPT_MAKEFILE_DIR = endif endif @@ -595,6 +591,7 @@ libs-y += fs/ libs-y += net/ libs-y += disk/ libs-y += drivers/ +libs-$(CONFIG_DM) += drivers/core/ libs-y += drivers/dma/ libs-y += drivers/gpio/ libs-y += drivers/i2c/ @@ -629,6 +626,8 @@ libs-y += lib/libfdt/ libs-$(CONFIG_API) += api/ libs-$(CONFIG_HAS_POST) += post/ libs-y += test/ +libs-y += test/dm/ +libs-$(CONFIG_DM_DEMO) += drivers/demo/ ifneq (,$(filter $(SOC), mx25 mx27 mx5 mx6 mx31 mx35 mxs vf610)) libs-y += arch/$(ARCH)/imx-common/ @@ -637,7 +636,7 @@ endif libs-$(CONFIG_ARM) += arch/arm/cpu/ libs-$(CONFIG_PPC) += arch/powerpc/cpu/ -libs-y += board/$(BOARDDIR)/ +libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/) libs-y := $(sort $(libs-y)) @@ -654,7 +653,7 @@ u-boot-main := $(libs-y) # Add GCC lib ifdef USE_PRIVATE_LIBGCC ifeq ("$(USE_PRIVATE_LIBGCC)", "yes") -PLATFORM_LIBGCC = $(OBJTREE)/arch/$(ARCH)/lib/lib.a +PLATFORM_LIBGCC = arch/$(ARCH)/lib/lib.a else PLATFORM_LIBGCC = -L $(USE_PRIVATE_LIBGCC) -lgcc endif @@ -712,6 +711,7 @@ ALL-$(CONFIG_SPL) += spl/u-boot-spl.bin ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.img ALL-$(CONFIG_TPL) += tpl/u-boot-tpl.bin ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb u-boot-dtb.bin +ALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtb ifneq ($(CONFIG_SPL_TARGET),) ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%) endif @@ -1067,6 +1067,13 @@ u-boot.lds: $(LDSCRIPT) prepare FORCE PHONY += nand_spl nand_spl: prepare $(Q)$(MAKE) $(build)=nand_spl/board/$(BOARDDIR) all + @echo >&2 + @echo >&2 "==================== WARNING =====================" + @echo >&2 "nand_spl will not be included in v2014.07 release." + @echo >&2 "Please switch over to SPL." + @echo >&2 "Otherwise, this board will be removed." + @echo >&2 "==================================================" + @echo >&2 nand_spl/u-boot-spl-16k.bin: nand_spl @: @@ -1145,9 +1152,12 @@ checkarmreloc: u-boot env: scripts_basic $(Q)$(MAKE) $(build)=tools/$@ -tools-all: HOST_TOOLS_ALL=y +tools-all: export HOST_TOOLS_ALL=y tools-all: env tools ; +cross_tools: export CROSS_BUILD_TOOLS=y +cross_tools: tools ; + .PHONY : CHANGELOG CHANGELOG: git log --no-merges U-Boot-1_1_5.. | \ @@ -3470,6 +3470,9 @@ typically in board_init_f() and board_init_r(). Configuration Settings: ----------------------- +- CONFIG_SYS_SUPPORT_64BIT_DATA: Defined automatically if compiled as 64-bit. + Optionally it can be defined to support 64-bit memory commands. + - CONFIG_SYS_LONGHELP: Defined when you want long help messages included; undefine this when you're short of memory. diff --git a/arch/arc/config.mk b/arch/arc/config.mk index 76f4f7c..d9f5d9d 100644 --- a/arch/arc/config.mk +++ b/arch/arc/config.mk @@ -9,14 +9,18 @@ CONFIG_SYS_LITTLE_ENDIAN = 1 endif ifdef CONFIG_SYS_LITTLE_ENDIAN -CROSS_COMPILE ?= arc-buildroot-linux-uclibc- +ARC_CROSS_COMPILE := arc-buildroot-linux-uclibc- endif ifdef CONFIG_SYS_BIG_ENDIAN -CROSS_COMPILE ?= arceb-buildroot-linux-uclibc- +ARC_CROSS_COMPILE := arceb-buildroot-linux-uclibc- PLATFORM_LDFLAGS += -EB endif +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := $(ARC_CROSS_COMPILE) +endif + PLATFORM_CPPFLAGS += -ffixed-r25 -D__ARC__ -DCONFIG_ARC -gdwarf-2 LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 17b7408..792cb43 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= arm-linux- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := arm-linux- +endif ifndef CONFIG_STANDALONE_LOAD_ADDR ifneq ($(CONFIG_OMAP_COMMON),) @@ -67,13 +69,8 @@ ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS))) # times. Also, the prefix needs to be different based on whether # CONFIG_SPL_BUILD is defined or not. 'filter-out' the existing entry # before adding the correct one. -ifdef CONFIG_SPL_BUILD -PLATFORM_LIBS := $(SPLTREE)/arch/arm/lib/eabi_compat.o \ - $(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS)) -else -PLATFORM_LIBS := $(OBJTREE)/arch/arm/lib/eabi_compat.o \ - $(filter-out %/arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS)) -endif +PLATFORM_LIBS := arch/arm/lib/eabi_compat.o \ + $(filter-out arch/arm/lib/eabi_compat.o, $(PLATFORM_LIBS)) endif # needed for relocation diff --git a/arch/arm/cpu/armv7/kona-common/hwinit-common.c b/arch/arm/cpu/armv7/kona-common/hwinit-common.c index f8b1e06..2b3a840 100644 --- a/arch/arm/cpu/armv7/kona-common/hwinit-common.c +++ b/arch/arm/cpu/armv7/kona-common/hwinit-common.c @@ -5,7 +5,7 @@ */ #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #ifndef CONFIG_SYS_DCACHE_OFF void enable_caches(void) diff --git a/arch/arm/cpu/armv7/omap-common/hwinit-common.c b/arch/arm/cpu/armv7/omap-common/hwinit-common.c index ade744e..8ebc0ce 100644 --- a/arch/arm/cpu/armv7/omap-common/hwinit-common.c +++ b/arch/arm/cpu/armv7/omap-common/hwinit-common.c @@ -14,7 +14,7 @@ #include <common.h> #include <spl.h> #include <asm/arch/sys_proto.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/emif.h> #include <asm/omap_common.h> #include <linux/compiler.h> diff --git a/arch/arm/cpu/armv7/omap4/hwinit.c b/arch/arm/cpu/armv7/omap4/hwinit.c index b0598a0..db16548 100644 --- a/arch/arm/cpu/armv7/omap4/hwinit.c +++ b/arch/arm/cpu/armv7/omap4/hwinit.c @@ -15,7 +15,7 @@ #include <asm/armv7.h> #include <asm/arch/cpu.h> #include <asm/arch/sys_proto.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/emif.h> #include <asm/arch/gpio.h> #include <asm/omap_common.h> diff --git a/arch/arm/cpu/armv7/omap5/hwinit.c b/arch/arm/cpu/armv7/omap5/hwinit.c index 737d23c..93feb16 100644 --- a/arch/arm/cpu/armv7/omap5/hwinit.c +++ b/arch/arm/cpu/armv7/omap5/hwinit.c @@ -17,7 +17,7 @@ #include <asm/arch/cpu.h> #include <asm/arch/sys_proto.h> #include <asm/arch/clock.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/utils.h> #include <asm/arch/gpio.h> #include <asm/emif.h> diff --git a/arch/arm/cpu/at91-common/phy.c b/arch/arm/cpu/at91-common/phy.c index 3b6c60c..2cba716 100644 --- a/arch/arm/cpu/at91-common/phy.c +++ b/arch/arm/cpu/at91-common/phy.c @@ -14,7 +14,7 @@ #include <common.h> #include <asm/io.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/at91_pmc.h> #include <asm/arch/at91_rstc.h> #include <watchdog.h> diff --git a/arch/arm/include/asm/arch-davinci/hardware.h b/arch/arm/include/asm/arch-davinci/hardware.h index 27b1844..98fe56e 100644 --- a/arch/arm/include/asm/arch-davinci/hardware.h +++ b/arch/arm/include/asm/arch-davinci/hardware.h @@ -15,7 +15,7 @@ #define __ASM_ARCH_HARDWARE_H #include <config.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define REG(addr) (*(volatile unsigned int *)(addr)) #define REG_P(addr) ((volatile unsigned int *)(addr)) diff --git a/arch/arm/include/asm/arch-mb86r0x/hardware.h b/arch/arm/include/asm/arch-mb86r0x/hardware.h index c0e3f20..42a52bc 100644 --- a/arch/arm/include/asm/arch-mb86r0x/hardware.h +++ b/arch/arm/include/asm/arch-mb86r0x/hardware.h @@ -9,7 +9,7 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/mb86r0x.h> #endif diff --git a/arch/arm/include/asm/arch-tnetv107x/hardware.h b/arch/arm/include/asm/arch-tnetv107x/hardware.h index aed6c46..2a7ca4e 100644 --- a/arch/arm/include/asm/arch-tnetv107x/hardware.h +++ b/arch/arm/include/asm/arch-tnetv107x/hardware.h @@ -9,7 +9,7 @@ #ifndef __ASSEMBLY__ -#include <asm/sizes.h> +#include <linux/sizes.h> #define ASYNC_EMIF_NUM_CS 4 #define ASYNC_EMIF_MODE_NOR 0 diff --git a/arch/arm/include/asm/arch-zynq/sys_proto.h b/arch/arm/include/asm/arch-zynq/sys_proto.h index 0a2ba05..a68e1b3 100644 --- a/arch/arm/include/asm/arch-zynq/sys_proto.h +++ b/arch/arm/include/asm/arch-zynq/sys_proto.h @@ -19,5 +19,6 @@ extern void zynq_ddrc_init(void); /* Driver extern functions */ extern int zynq_sdhci_init(u32 regbase); +extern int zynq_sdhci_of_init(const void *blob); #endif /* _SYS_PROTO_H_ */ diff --git a/arch/arm/include/asm/sizes.h b/arch/arm/include/asm/sizes.h deleted file mode 100644 index 28cf5ea..0000000 --- a/arch/arm/include/asm/sizes.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-2.0+ - */ -/* Size defintions - * Copyright (C) ARM Limited 1998. All rights reserved. - */ - -#ifndef __sizes_h -#define __sizes_h 1 - -/* handy sizes */ -#define SZ_1K 0x00000400 -#define SZ_4K 0x00001000 -#define SZ_8K 0x00002000 -#define SZ_16K 0x00004000 -#define SZ_32K 0x00008000 -#define SZ_64K 0x00010000 -#define SZ_128K 0x00020000 -#define SZ_256K 0x00040000 -#define SZ_512K 0x00080000 - -#define SZ_1M 0x00100000 -#define SZ_2M 0x00200000 -#define SZ_4M 0x00400000 -#define SZ_8M 0x00800000 -#define SZ_16M 0x01000000 -#define SZ_31M 0x01F00000 -#define SZ_32M 0x02000000 -#define SZ_64M 0x04000000 -#define SZ_128M 0x08000000 -#define SZ_256M 0x10000000 -#define SZ_512M 0x20000000 - -#define SZ_1G 0x40000000 -#define SZ_2G 0x80000000 - -#endif - -/* END */ diff --git a/arch/avr32/config.mk b/arch/avr32/config.mk index b9b9631..28a371c 100644 --- a/arch/avr32/config.mk +++ b/arch/avr32/config.mk @@ -5,7 +5,10 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= avr32-linux- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := avr32-linux- +endif + PLATFORM_CPPFLAGS += -DCONFIG_AVR32 CONFIG_STANDALONE_LOAD_ADDR ?= 0x00000000 diff --git a/arch/blackfin/config.mk b/arch/blackfin/config.mk index c752025..adc9712 100644 --- a/arch/blackfin/config.mk +++ b/arch/blackfin/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= bfin-uclinux- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := bfin-uclinux- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x1000 -m elf32bfin diff --git a/arch/m68k/config.mk b/arch/m68k/config.mk index 9c3e24f..33b3d51 100644 --- a/arch/m68k/config.mk +++ b/arch/m68k/config.mk @@ -5,11 +5,14 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= m68k-elf- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := m68k-elf- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x20000 PLATFORM_CPPFLAGS += -DCONFIG_M68K -D__M68K__ PLATFORM_LDFLAGS += -n -PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections +PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections +PLATFORM_RELFLAGS += -ffixed-d7 -msep-data LDFLAGS_FINAL += --gc-sections diff --git a/arch/m68k/cpu/mcf5227x/config.mk b/arch/m68k/cpu/mcf5227x/config.mk index 2681171..b5c26e4 100644 --- a/arch/m68k/cpu/mcf5227x/config.mk +++ b/arch/m68k/cpu/mcf5227x/config.mk @@ -7,5 +7,4 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data PLATFORM_CPPFLAGS += -mcpu=52277 -fPIC diff --git a/arch/m68k/cpu/mcf523x/config.mk b/arch/m68k/cpu/mcf523x/config.mk index 620769f..c9435ab 100644 --- a/arch/m68k/cpu/mcf523x/config.mk +++ b/arch/m68k/cpu/mcf523x/config.mk @@ -7,5 +7,4 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data PLATFORM_CPPFLAGS += -mcpu=5235 -fPIC diff --git a/arch/m68k/cpu/mcf52x2/config.mk b/arch/m68k/cpu/mcf52x2/config.mk index d0be46f..25f98de 100644 --- a/arch/m68k/cpu/mcf52x2/config.mk +++ b/arch/m68k/cpu/mcf52x2/config.mk @@ -7,8 +7,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data - cfg=$(shell grep configs $(OBJTREE)/include/config.h | sed 's/.*<\(configs.*\)>/\1/') is5208:=$(shell grep CONFIG_M5208 $(TOPDIR)/include/$(cfg)) is5249:=$(shell grep CONFIG_M5249 $(TOPDIR)/include/$(cfg)) diff --git a/arch/m68k/cpu/mcf532x/config.mk b/arch/m68k/cpu/mcf532x/config.mk index be12203..0604ab0 100644 --- a/arch/m68k/cpu/mcf532x/config.mk +++ b/arch/m68k/cpu/mcf532x/config.mk @@ -7,8 +7,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data - cfg=$(shell grep configs $(OBJTREE)/include/config.h | sed 's/.*<\(configs.*\)>/\1/') is5301x:=$(shell grep CONFIG_MCF5301x $(TOPDIR)/include/$(cfg)) is532x:=$(shell grep CONFIG_MCF532x $(TOPDIR)/include/$(cfg)) diff --git a/arch/m68k/cpu/mcf5445x/config.mk b/arch/m68k/cpu/mcf5445x/config.mk index d546b22..6da08d5 100644 --- a/arch/m68k/cpu/mcf5445x/config.mk +++ b/arch/m68k/cpu/mcf5445x/config.mk @@ -9,8 +9,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data - cfg=$(shell grep configs $(OBJTREE)/include/config.h | sed 's/.*<\(configs.*\)>/\1/') is5441x:=$(shell grep CONFIG_MCF5441x $(TOPDIR)/include/$(cfg)) diff --git a/arch/m68k/cpu/mcf547x_8x/config.mk b/arch/m68k/cpu/mcf547x_8x/config.mk index 345f584..825f6cc 100644 --- a/arch/m68k/cpu/mcf547x_8x/config.mk +++ b/arch/m68k/cpu/mcf547x_8x/config.mk @@ -7,7 +7,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -PLATFORM_RELFLAGS += -ffixed-d7 -msep-data PLATFORM_CPPFLAGS += -mcpu=5485 -fPIC ifneq (,$(findstring -linux-,$(shell $(CC) --version))) diff --git a/arch/microblaze/config.mk b/arch/microblaze/config.mk index fc545a9..cdb321a 100644 --- a/arch/microblaze/config.mk +++ b/arch/microblaze/config.mk @@ -8,7 +8,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= mb- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := mb- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x80F00000 diff --git a/arch/mips/config.mk b/arch/mips/config.mk index 2abdebb..1899f51 100644 --- a/arch/mips/config.mk +++ b/arch/mips/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= mips_4KC- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := mips_4KC- +endif # Handle special prefix in ELDK 4.0 toolchain ifneq (,$(findstring 4KCle,$(CROSS_COMPILE))) diff --git a/arch/nds32/config.mk b/arch/nds32/config.mk index 550f8a4..0cbc4ad 100644 --- a/arch/nds32/config.mk +++ b/arch/nds32/config.mk @@ -8,7 +8,9 @@ # # SPDX-License-Identifier: GPL-2.0+ -CROSS_COMPILE ?= nds32le-linux- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := nds32le-linux- +endif CONFIG_STANDALONE_LOAD_ADDR = 0x300000 -T $(srctree)/$(src)/nds32.lds diff --git a/arch/nios2/config.mk b/arch/nios2/config.mk index 7d546ef..65a5a40 100644 --- a/arch/nios2/config.mk +++ b/arch/nios2/config.mk @@ -6,7 +6,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= nios2-elf- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := nios2-elf- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x02000000 diff --git a/arch/openrisc/config.mk b/arch/openrisc/config.mk index 13015eb..981edff 100644 --- a/arch/openrisc/config.mk +++ b/arch/openrisc/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= or32-elf- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := or32-elf- +endif # r10 used for global object pointer, already set in OR32 GCC but just to be # clear diff --git a/arch/powerpc/config.mk b/arch/powerpc/config.mk index f75c3bf..e398f97 100644 --- a/arch/powerpc/config.mk +++ b/arch/powerpc/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= ppc_8xx- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := ppc_8xx- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x40000 LDFLAGS_FINAL += --gc-sections diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 23b057e..668aa71 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -11,3 +11,5 @@ __HAVE_ARCH_GENERIC_BOARD := y cmd_u-boot__ = $(CC) -o $@ -T u-boot.lds \ -Wl,--start-group $(u-boot-main) -Wl,--end-group \ $(PLATFORM_LIBS) -Wl,-Map -Wl,u-boot.map + +CONFIG_ARCH_DEVICE_TREE := sandbox diff --git a/arch/sandbox/dts/Makefile b/arch/sandbox/dts/Makefile new file mode 100644 index 0000000..a4c980b --- /dev/null +++ b/arch/sandbox/dts/Makefile @@ -0,0 +1,11 @@ +dtb-$(CONFIG_SANDBOX) += sandbox.dtb + +targets += $(dtb-y) + +DTC_FLAGS += -R 4 -p 0x1000 + +PHONY += dtbs +dtbs: $(addprefix $(obj)/, $(dtb-y)) + @: + +clean-files := *.dtb diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts new file mode 100644 index 0000000..96a4438 --- /dev/null +++ b/arch/sandbox/dts/sandbox.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + triangle { + compatible = "demo-shape"; + colour = "cyan"; + sides = <3>; + character = <83>; + }; + square { + compatible = "demo-shape"; + colour = "blue"; + sides = <4>; + }; + hexagon { + compatible = "demo-simple"; + colour = "white"; + sides = <6>; + }; +}; diff --git a/arch/sandbox/include/asm/gpio.h b/arch/sandbox/include/asm/gpio.h index afb9c78..95b59da 100644 --- a/arch/sandbox/include/asm/gpio.h +++ b/arch/sandbox/include/asm/gpio.h @@ -29,7 +29,7 @@ * @param gp GPIO number * @return -1 on error, 0 if GPIO is low, >0 if high */ -int sandbox_gpio_get_value(unsigned gp); +int sandbox_gpio_get_value(struct device *dev, unsigned int offset); /** * Set the simulated value of a GPIO (used only in sandbox test code) @@ -38,7 +38,7 @@ int sandbox_gpio_get_value(unsigned gp); * @param value value to set (0 for low, non-zero for high) * @return -1 on error, 0 if ok */ -int sandbox_gpio_set_value(unsigned gp, int value); +int sandbox_gpio_set_value(struct device *dev, unsigned int offset, int value); /** * Return the simulated direction of a GPIO (used only in sandbox test code) @@ -46,7 +46,7 @@ int sandbox_gpio_set_value(unsigned gp, int value); * @param gp GPIO number * @return -1 on error, 0 if GPIO is input, >0 if output */ -int sandbox_gpio_get_direction(unsigned gp); +int sandbox_gpio_get_direction(struct device *dev, unsigned int offset); /** * Set the simulated direction of a GPIO (used only in sandbox test code) @@ -55,11 +55,7 @@ int sandbox_gpio_get_direction(unsigned gp); * @param output 0 to set as input, 1 to set as output * @return -1 on error, 0 if ok */ -int sandbox_gpio_set_direction(unsigned gp, int output); - -/* Display information about each GPIO */ -void gpio_info(void); - -#define gpio_status() gpio_info() +int sandbox_gpio_set_direction(struct device *dev, unsigned int offset, + int output); #endif diff --git a/arch/sh/config.mk b/arch/sh/config.mk index 758c070..0578fa3 100644 --- a/arch/sh/config.mk +++ b/arch/sh/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= sh4-linux- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := sh4-linux- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x8C000000 ifeq ($(CPU),sh2) @@ -15,3 +17,4 @@ endif PLATFORM_CPPFLAGS += -DCONFIG_SH -D__SH__ PLATFORM_LDFLAGS += -e $(CONFIG_SYS_TEXT_BASE) --defsym reloc_dst=$(CONFIG_SYS_TEXT_BASE) LDFLAGS_FINAL = --gc-sections +PLATFORM_RELFLAGS += -ffixed-r13 diff --git a/arch/sh/cpu/sh2/config.mk b/arch/sh/cpu/sh2/config.mk index 69273b4..4904d76 100644 --- a/arch/sh/cpu/sh2/config.mk +++ b/arch/sh/cpu/sh2/config.mk @@ -8,11 +8,10 @@ ENDIANNESS += -EB ifdef CONFIG_SH2A -PLATFORM_CPPFLAGS += -m2a -m2a-nofpu -mb -ffreestanding +PLATFORM_CPPFLAGS += -m2a -m2a-nofpu -mb else # SH2 PLATFORM_CPPFLAGS += -m3e -mb endif PLATFORM_CPPFLAGS += -DCONFIG_SH2 $(call cc-option,-mno-fdpic) -PLATFORM_RELFLAGS += -ffixed-r13 PLATFORM_LDFLAGS += $(ENDIANNESS) diff --git a/arch/sh/cpu/sh3/config.mk b/arch/sh/cpu/sh3/config.mk index abd4b8d..24b5c47 100644 --- a/arch/sh/cpu/sh3/config.mk +++ b/arch/sh/cpu/sh3/config.mk @@ -12,4 +12,3 @@ # # PLATFORM_CPPFLAGS += -DCONFIG_SH3 -m3 -PLATFORM_RELFLAGS += -ffixed-r13 diff --git a/arch/sh/cpu/sh4/config.mk b/arch/sh/cpu/sh4/config.mk index 753580b..5773d4f 100644 --- a/arch/sh/cpu/sh4/config.mk +++ b/arch/sh/cpu/sh4/config.mk @@ -9,4 +9,3 @@ # # PLATFORM_CPPFLAGS += -DCONFIG_SH4 -m4-nofpu -PLATFORM_RELFLAGS += -ffixed-r13 diff --git a/arch/sparc/config.mk b/arch/sparc/config.mk index 9bb3724..e2327ec 100644 --- a/arch/sparc/config.mk +++ b/arch/sparc/config.mk @@ -5,7 +5,9 @@ # SPDX-License-Identifier: GPL-2.0+ # -CROSS_COMPILE ?= sparc-elf- +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := sparc-elf- +endif CONFIG_STANDALONE_LOAD_ADDR ?= 0x00000000 -L $(gcclibdir) \ -T $(srctree)/$(src)/sparc.lds diff --git a/arch/x86/config.mk b/arch/x86/config.mk index 4a4ad80..58dff14 100644 --- a/arch/x86/config.mk +++ b/arch/x86/config.mk @@ -8,13 +8,10 @@ CONFIG_STANDALONE_LOAD_ADDR ?= 0x40000 PLATFORM_CPPFLAGS += -fno-strict-aliasing -PLATFORM_CPPFLAGS += -Wstrict-prototypes PLATFORM_CPPFLAGS += -mregparm=3 PLATFORM_CPPFLAGS += -fomit-frame-pointer -PF_CPPFLAGS_X86 := $(call cc-option, -ffreestanding) \ - $(call cc-option, -fno-toplevel-reorder, \ +PF_CPPFLAGS_X86 := $(call cc-option, -fno-toplevel-reorder, \ $(call cc-option, -fno-unit-at-a-time)) \ - $(call cc-option, -fno-stack-protector) \ $(call cc-option, -mpreferred-stack-boundary=2) PLATFORM_CPPFLAGS += $(PF_CPPFLAGS_X86) PLATFORM_CPPFLAGS += -fno-dwarf2-cfi-asm diff --git a/board/BuS/vl_ma2sc/vl_ma2sc.c b/board/BuS/vl_ma2sc/vl_ma2sc.c index 63f7ad9..da39c86 100644 --- a/board/BuS/vl_ma2sc/vl_ma2sc.c +++ b/board/BuS/vl_ma2sc/vl_ma2sc.c @@ -8,7 +8,7 @@ #include <config.h> #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/io.h> #include <asm/gpio.h> #include <asm/arch/hardware.h> diff --git a/board/atmel/at91sam9263ek/at91sam9263ek.c b/board/atmel/at91sam9263ek/at91sam9263ek.c index d42a173..7784cd7 100644 --- a/board/atmel/at91sam9263ek/at91sam9263ek.c +++ b/board/atmel/at91sam9263ek/at91sam9263ek.c @@ -7,7 +7,7 @@ */ #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/at91sam9263.h> #include <asm/arch/at91sam9_smc.h> #include <asm/arch/at91_common.h> diff --git a/board/compulab/cm_t335/spl.c b/board/compulab/cm_t335/spl.c index b3b150a..d574364 100644 --- a/board/compulab/cm_t335/spl.c +++ b/board/compulab/cm_t335/spl.c @@ -18,7 +18,7 @@ #include <asm/arch/clocks_am33xx.h> #include <asm/arch/sys_proto.h> #include <asm/arch/hardware_am33xx.h> -#include <asm/sizes.h> +#include <linux/sizes.h> const struct ctrl_ioregs ioregs = { .cm0ioctl = MT41J128MJT125_IOCTRL_VALUE, diff --git a/board/cray/L1/.gitignore b/board/cray/L1/.gitignore new file mode 100644 index 0000000..cd76d66 --- /dev/null +++ b/board/cray/L1/.gitignore @@ -0,0 +1,2 @@ +bootscript.c +bootscript.image diff --git a/board/freescale/m54418twr/config.mk b/board/freescale/m54418twr/config.mk index b306d03..07f52e0 100644 --- a/board/freescale/m54418twr/config.mk +++ b/board/freescale/m54418twr/config.mk @@ -4,6 +4,4 @@ # SPDX-License-Identifier: GPL-2.0+ # -sinclude $(OBJTREE)/board/$(BOARDDIR)/config.tmp - PLATFORM_CPPFLAGS += -DTEXT_BASE=$(CONFIG_SYS_TEXT_BASE) diff --git a/board/freescale/mx6slevk/mx6slevk.c b/board/freescale/mx6slevk/mx6slevk.c index c496254..aadad32 100644 --- a/board/freescale/mx6slevk/mx6slevk.c +++ b/board/freescale/mx6slevk/mx6slevk.c @@ -14,7 +14,7 @@ #include <asm/gpio.h> #include <asm/imx-common/iomux-v3.h> #include <asm/io.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <common.h> #include <fsl_esdhc.h> #include <mmc.h> diff --git a/board/gaisler/gr_cpci_ax2000/config.mk b/board/gaisler/gr_cpci_ax2000/config.mk index 309c879..731a539 100644 --- a/board/gaisler/gr_cpci_ax2000/config.mk +++ b/board/gaisler/gr_cpci_ax2000/config.mk @@ -17,5 +17,3 @@ CONFIG_SYS_TEXT_BASE = 0x00000000 # U-BOOT IN SDRAM #CONFIG_SYS_TEXT_BASE = 0x60000000 - -PLATFORM_CPPFLAGS += -I$(TOPDIR)/board diff --git a/board/gaisler/gr_ep2s60/config.mk b/board/gaisler/gr_ep2s60/config.mk index d57efae..6e01f07 100644 --- a/board/gaisler/gr_ep2s60/config.mk +++ b/board/gaisler/gr_ep2s60/config.mk @@ -15,5 +15,3 @@ CONFIG_SYS_TEXT_BASE = 0x00000000 # U-BOOT IN SDRAM #CONFIG_SYS_TEXT_BASE = 0x40000000 - -PLATFORM_CPPFLAGS += -I$(TOPDIR)/board diff --git a/board/gaisler/gr_xc3s_1500/config.mk b/board/gaisler/gr_xc3s_1500/config.mk index e87320b..e4a66cb 100644 --- a/board/gaisler/gr_xc3s_1500/config.mk +++ b/board/gaisler/gr_xc3s_1500/config.mk @@ -14,5 +14,3 @@ CONFIG_SYS_TEXT_BASE = 0x00000000 # U-BOOT IN RAM #CONFIG_SYS_TEXT_BASE = 0x40000000 - -PLATFORM_CPPFLAGS += -I$(TOPDIR)/board diff --git a/board/gaisler/grsim/config.mk b/board/gaisler/grsim/config.mk index df26f82..d1f61da 100644 --- a/board/gaisler/grsim/config.mk +++ b/board/gaisler/grsim/config.mk @@ -14,5 +14,3 @@ CONFIG_SYS_TEXT_BASE = 0x00000000 # U-BOOT IN RAM #CONFIG_SYS_TEXT_BASE = 0x40000000 - -PLATFORM_CPPFLAGS += -I$(TOPDIR)/board diff --git a/board/gaisler/grsim_leon2/config.mk b/board/gaisler/grsim_leon2/config.mk index 99f9a68..f98b23b 100644 --- a/board/gaisler/grsim_leon2/config.mk +++ b/board/gaisler/grsim_leon2/config.mk @@ -14,5 +14,3 @@ CONFIG_SYS_TEXT_BASE = 0x00000000 # RUN U-BOOT FROM RAM #CONFIG_SYS_TEXT_BASE = 0x40000000 - -PLATFORM_CPPFLAGS += -I$(TOPDIR)/board diff --git a/board/highbank/highbank.c b/board/highbank/highbank.c index 4b272c7..371b0a2 100644 --- a/board/highbank/highbank.c +++ b/board/highbank/highbank.c @@ -9,7 +9,7 @@ #include <netdev.h> #include <scsi.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/io.h> #define HB_AHCI_BASE 0xffe08000 diff --git a/board/matrix_vision/mvblm7/.gitignore b/board/matrix_vision/mvblm7/.gitignore new file mode 100644 index 0000000..469f1bc --- /dev/null +++ b/board/matrix_vision/mvblm7/.gitignore @@ -0,0 +1 @@ +bootscript.img diff --git a/board/matrix_vision/mvsmr/.gitignore b/board/matrix_vision/mvsmr/.gitignore new file mode 100644 index 0000000..469f1bc --- /dev/null +++ b/board/matrix_vision/mvsmr/.gitignore @@ -0,0 +1 @@ +bootscript.img diff --git a/board/micronas/vct/config.mk b/board/micronas/vct/config.mk index 0f004e0..354d918 100644 --- a/board/micronas/vct/config.mk +++ b/board/micronas/vct/config.mk @@ -8,8 +8,6 @@ # vct_xxx boards with MIPS 4Kc CPU core # -sinclude $(TOPDIR)/board/$(BOARDDIR)/config.tmp - ifndef CONFIG_SYS_TEXT_BASE CONFIG_SYS_TEXT_BASE = 0x87000000 endif diff --git a/board/ronetix/pm9261/pm9261.c b/board/ronetix/pm9261/pm9261.c index a634383..ec3ac89 100644 --- a/board/ronetix/pm9261/pm9261.c +++ b/board/ronetix/pm9261/pm9261.c @@ -9,7 +9,7 @@ */ #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/io.h> #include <asm/gpio.h> #include <asm/arch/at91sam9_smc.h> diff --git a/board/ronetix/pm9263/pm9263.c b/board/ronetix/pm9263/pm9263.c index 3cedeef..3aaffa8 100644 --- a/board/ronetix/pm9263/pm9263.c +++ b/board/ronetix/pm9263/pm9263.c @@ -9,7 +9,7 @@ */ #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/io.h> #include <asm/gpio.h> #include <asm/arch/at91sam9_smc.h> diff --git a/board/ronetix/pm9g45/pm9g45.c b/board/ronetix/pm9g45/pm9g45.c index c9f2747..15aa4ac 100644 --- a/board/ronetix/pm9g45/pm9g45.c +++ b/board/ronetix/pm9g45/pm9g45.c @@ -12,7 +12,7 @@ */ #include <common.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/io.h> #include <asm/gpio.h> #include <asm/arch/at91sam9_smc.h> diff --git a/board/samsung/common/misc.c b/board/samsung/common/misc.c index eb15739..3ff4289 100644 --- a/board/samsung/common/misc.c +++ b/board/samsung/common/misc.c @@ -11,7 +11,7 @@ #include <samsung/misc.h> #include <errno.h> #include <version.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> #include <asm/gpio.h> diff --git a/board/sandbox/sandbox/sandbox.c b/board/sandbox/sandbox/sandbox.c index 95efaff..2f90df5 100644 --- a/board/sandbox/sandbox/sandbox.c +++ b/board/sandbox/sandbox/sandbox.c @@ -4,7 +4,7 @@ */ #include <common.h> - +#include <dm.h> #include <os.h> /* @@ -14,6 +14,11 @@ */ gd_t *gd; +/* Add a simple GPIO device */ +U_BOOT_DEVICE(gpio_sandbox) = { + .name = "gpio_sandbox", +}; + void flush_cache(unsigned long start, unsigned long size) { } diff --git a/board/synopsys/arcangel4/Makefile b/board/synopsys/arcangel4/Makefile deleted file mode 100644 index 575e58f..0000000 --- a/board/synopsys/arcangel4/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. -# -# SPDX-License-Identifier: GPL-2.0+ -# - -# This board is mostly used for debugging U-Boot in simulation (ISS). -# The only peripheral which is used on this board is a serial port which -# requires no initialization except those in "include/configs/arcangel4.h". -# And now there's no specific initializations for this board. -# So this Makefile is only required for satisfaction of U-Boot build system. diff --git a/board/wandboard/wandboard.c b/board/wandboard/wandboard.c index 72e9bb2..0508457 100644 --- a/board/wandboard/wandboard.c +++ b/board/wandboard/wandboard.c @@ -17,7 +17,7 @@ #include <asm/imx-common/iomux-v3.h> #include <asm/imx-common/boot_mode.h> #include <asm/io.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <common.h> #include <fsl_esdhc.h> #include <ipu_pixfmt.h> diff --git a/board/xilinx/zynq/board.c b/board/xilinx/zynq/board.c index 82f2345..485a5e4 100644 --- a/board/xilinx/zynq/board.c +++ b/board/xilinx/zynq/board.c @@ -5,6 +5,7 @@ */ #include <common.h> +#include <fdtdec.h> #include <netdev.h> #include <zynqpl.h> #include <asm/arch/hardware.h> @@ -134,8 +135,27 @@ int board_mmc_init(bd_t *bd) int dram_init(void) { +#ifdef CONFIG_OF_CONTROL + int node; + fdt_addr_t addr; + fdt_size_t size; + const void *blob = gd->fdt_blob; + + node = fdt_node_offset_by_prop_value(blob, -1, "device_type", + "memory", 7); + if (node == -FDT_ERR_NOTFOUND) { + debug("ZYNQ DRAM: Can't get memory node\n"); + return -1; + } + addr = fdtdec_get_addr_size(blob, node, "reg", &size); + if (addr == FDT_ADDR_T_NONE || size == 0) { + debug("ZYNQ DRAM: Can't get base address or size\n"); + return -1; + } + gd->ram_size = size; +#else gd->ram_size = CONFIG_SYS_SDRAM_SIZE; - +#endif zynq_ddrc_init(); return 0; @@ -44,9 +44,9 @@ ########################################################################################################### Active aarch64 armv8 - armltd vexpress64 vexpress_aemv8a vexpress_aemv8a:ARM64 David Feng <fenghua@phytium.com.cn> -Active arc arc700 - synopsys - arcangel4 - Alexey Brodkin <abrodkin@synopsys.com> +Active arc arc700 - synopsys <none> arcangel4 - Alexey Brodkin <abrodkin@synopsys.com> Active arc arc700 - synopsys - axs101 - Alexey Brodkin <abrodkin@synopsys.com> -Active arc arc700 - synopsys arcangel4 arcangel4-be - Alexey Brodkin <abrodkin@synopsys.com> +Active arc arc700 - synopsys <none> arcangel4-be - Alexey Brodkin <abrodkin@synopsys.com> Active arm arm1136 - armltd integrator integratorcp_cm1136 integratorcp:CM1136 Linus Walleij <linus.walleij@linaro.org> Active arm arm1136 mx31 - - imx31_phycore - - Active arm arm1136 mx31 davedenx - qong - Wolfgang Denk <wd@denx.de> diff --git a/common/Makefile b/common/Makefile index ca9af13..04e9cdd 100644 --- a/common/Makefile +++ b/common/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_CMD_CONSOLE) += cmd_console.o obj-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o obj-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o obj-$(CONFIG_CMD_DATE) += cmd_date.o +obj-$(CONFIG_CMD_DEMO) += cmd_demo.o obj-$(CONFIG_CMD_SOUND) += cmd_sound.o ifdef CONFIG_4xx obj-$(CONFIG_CMD_SETGETDCR) += cmd_dcr.o diff --git a/common/board_r.c b/common/board_r.c index 899f377..8629a65 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -18,6 +18,7 @@ #ifdef CONFIG_HAS_DATAFLASH #include <dataflash.h> #endif +#include <dm.h> #include <environment.h> #include <fdtdec.h> #if defined(CONFIG_CMD_IDE) @@ -51,7 +52,9 @@ #ifdef CONFIG_X86 #include <asm/init_helpers.h> #endif +#include <dm/root.h> #include <linux/compiler.h> +#include <linux/err.h> DECLARE_GLOBAL_DATA_PTR; @@ -263,6 +266,33 @@ static int initr_malloc(void) return 0; } +#ifdef CONFIG_DM +static int initr_dm(void) +{ + int ret; + + ret = dm_init(); + if (ret) { + debug("dm_init() failed: %d\n", ret); + return ret; + } + ret = dm_scan_platdata(); + if (ret) { + debug("dm_scan_platdata() failed: %d\n", ret); + return ret; + } +#ifdef CONFIG_OF_CONTROL + ret = dm_scan_fdt(gd->fdt_blob); + if (ret) { + debug("dm_scan_fdt() failed: %d\n", ret); + return ret; + } +#endif + + return 0; +} +#endif + __weak int power_init_board(void) { return 0; @@ -761,6 +791,9 @@ init_fnc_t init_sequence_r[] = { initr_barrier, initr_malloc, bootstage_relocate, +#ifdef CONFIG_DM + initr_dm, +#endif #ifdef CONFIG_ARCH_EARLY_INIT_R arch_early_init_r, #endif diff --git a/common/cmd_demo.c b/common/cmd_demo.c new file mode 100644 index 0000000..a3bba7f --- /dev/null +++ b/common/cmd_demo.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm-demo.h> +#include <asm/io.h> + +struct device *demo_dev; + +static int do_demo_hello(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int ch = 0; + + if (argc) + ch = *argv[0]; + + return demo_hello(demo_dev, ch); +} + +static int do_demo_status(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int status; + int ret; + + ret = demo_status(demo_dev, &status); + if (ret) + return ret; + + printf("Status: %d\n", status); + + return 0; +} + +int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct device *dev; + int i, ret; + + puts("Demo uclass entries:\n"); + + for (i = 0, ret = uclass_first_device(UCLASS_DEMO, &dev); + dev; + ret = uclass_next_device(&dev)) { + printf("entry %d - instance %08x, ops %08x, platdata %08x\n", + i++, map_to_sysmem(dev), + map_to_sysmem(dev->driver->ops), + map_to_sysmem(dev_get_platdata(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static cmd_tbl_t demo_commands[] = { + U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""), + U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""), + U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""), +}; + +static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *demo_cmd; + int devnum = 0; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + demo_cmd = find_cmd_tbl(argv[1], demo_commands, + ARRAY_SIZE(demo_commands)); + argc -= 2; + argv += 2; + if (!demo_cmd || argc > demo_cmd->maxargs) + return CMD_RET_USAGE; + + if (argc) { + devnum = simple_strtoul(argv[0], NULL, 10); + ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); + if (ret) + return cmd_process_error(cmdtp, ret); + argc--; + argv++; + } + + ret = demo_cmd->cmd(demo_cmd, flag, argc, argv); + + return cmd_process_error(demo_cmd, ret); +} + +U_BOOT_CMD( + demo, 4, 1, do_demo, + "Driver model (dm) demo operations", + "list List available demo devices\n" + "demo hello <num> [<char>] Say hello\n" + "demo status <num> Get demo device status" +); diff --git a/common/cmd_gpio.c b/common/cmd_gpio.c index 47eee89..778aa5f 100644 --- a/common/cmd_gpio.c +++ b/common/cmd_gpio.c @@ -8,7 +8,7 @@ #include <common.h> #include <command.h> - +#include <dm.h> #include <asm/gpio.h> #ifndef name_to_gpio @@ -22,25 +22,115 @@ enum gpio_cmd { GPIO_TOGGLE, }; +#if defined(CONFIG_DM_GPIO) && !defined(gpio_status) +static const char * const gpio_function[] = { + "input", + "output", + "unknown", +}; + +static void show_gpio(struct device *dev, const char *bank_name, int offset) +{ + struct dm_gpio_ops *ops = gpio_get_ops(dev); + char buf[80]; + int ret; + + *buf = '\0'; + if (ops->get_state) { + ret = ops->get_state(dev, offset, buf, sizeof(buf)); + if (ret) { + puts("<unknown>"); + return; + } + } else { + int func = GPIOF_UNKNOWN; + int ret; + + if (ops->get_function) { + ret = ops->get_function(dev, offset); + if (ret >= 0 && ret < ARRAY_SIZE(gpio_function)) + func = ret; + } + sprintf(buf, "%s%u: %8s %d", bank_name, offset, + gpio_function[func], ops->get_value(dev, offset)); + } + + puts(buf); + puts("\n"); +} + +static int do_gpio_status(const char *gpio_name) +{ + struct device *dev; + int newline = 0; + int ret; + + if (gpio_name && !*gpio_name) + gpio_name = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + const char *bank_name; + int num_bits; + + bank_name = gpio_get_bank_info(dev, &num_bits); + + if (!gpio_name || !bank_name || + !strncmp(gpio_name, bank_name, strlen(bank_name))) { + const char *p = NULL; + int offset; + + if (bank_name) { + if (newline) + putc('\n'); + printf("Bank %s:\n", bank_name); + } + newline = 1; + if (gpio_name && bank_name) { + p = gpio_name + strlen(bank_name); + offset = simple_strtoul(p, NULL, 10); + show_gpio(dev, bank_name, offset); + } else { + for (offset = 0; offset < num_bits; offset++) + show_gpio(dev, bank_name, offset); + } + } + } + + return ret; +} +#endif + static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - int gpio; + unsigned int gpio; enum gpio_cmd sub_cmd; ulong value; - const char *str_cmd, *str_gpio; + const char *str_cmd, *str_gpio = NULL; +#ifdef CONFIG_DM_GPIO + int ret; +#endif + if (argc < 2) + show_usage: + return CMD_RET_USAGE; + str_cmd = argv[1]; + if (argc > 2) + str_gpio = argv[2]; + if (!strcmp(str_cmd, "status")) { + /* Support deprecated gpio_status() */ #ifdef gpio_status - if (argc == 2 && !strcmp(argv[1], "status")) { gpio_status(); return 0; - } +#elif defined(CONFIG_DM_GPIO) + return cmd_process_error(cmdtp, do_gpio_status(str_gpio)); +#else + goto show_usage; #endif + } - if (argc != 3) - show_usage: - return CMD_RET_USAGE; - str_cmd = argv[1]; - str_gpio = argv[2]; + if (!str_gpio) + goto show_usage; /* parse the behavior */ switch (*str_cmd) { @@ -51,11 +141,23 @@ static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) default: goto show_usage; } +#if defined(CONFIG_DM_GPIO) + /* + * TODO(sjg@chromium.org): For now we must fit into the existing GPIO + * framework, so we look up the name here and convert it to a GPIO number. + * Once all GPIO drivers are converted to driver model, we can change the + * code here to use the GPIO uclass interface instead of the numbered + * GPIO compatibility layer. + */ + ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio); + if (ret) + return cmd_process_error(cmdtp, ret); +#else /* turn the gpio name into a gpio number */ gpio = name_to_gpio(str_gpio); if (gpio < 0) goto show_usage; - +#endif /* grab the pin before we tweak it */ if (gpio_request(gpio, "cmd_gpio")) { printf("gpio: requesting pin %u failed\n", gpio); @@ -84,6 +186,7 @@ static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } U_BOOT_CMD(gpio, 3, 0, do_gpio, - "input/set/clear/toggle gpio pins", + "query and control gpio pins", "<input|set|clear|toggle> <pin>\n" - " - input/set/clear/toggle the specified pin"); + " - input/set/clear/toggle the specified pin\n" + "gpio status [<bank> | <pin>]"); diff --git a/common/cmd_mem.c b/common/cmd_mem.c index 6d75d02..5b03c2d 100644 --- a/common/cmd_mem.c +++ b/common/cmd_mem.c @@ -41,7 +41,7 @@ static ulong base_address = 0; /* Memory Display * * Syntax: - * md{.b, .w, .l} {addr} {len} + * md{.b, .w, .l, .q} {addr} {len} */ #define DISP_LINE_LEN 16 static int do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -155,7 +155,12 @@ static int do_mem_nm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - ulong addr, writeval, count; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 writeval; +#else + ulong writeval; +#endif + ulong addr, count; int size; void *buf; ulong bytes; @@ -175,7 +180,11 @@ static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /* Get the value to write. */ +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + writeval = simple_strtoull(argv[2], NULL, 16); +#else writeval = simple_strtoul(argv[2], NULL, 16); +#endif /* Count ? */ if (argc == 4) { @@ -189,6 +198,10 @@ static int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) while (count-- > 0) { if (size == 4) *((u32 *)buf) = (u32)writeval; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)buf) = (u64)writeval; +#endif else if (size == 2) *((u16 *)buf) = (u16)writeval; else @@ -262,6 +275,11 @@ static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) int rcode = 0; const char *type; const void *buf1, *buf2, *base; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 word1, word2; +#else + ulong word1, word2; +#endif if (argc != 4) return CMD_RET_USAGE; @@ -270,7 +288,9 @@ static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) */ if ((size = cmd_get_data_size(argv[0], 4)) < 0) return 1; - type = size == 4 ? "word" : size == 2 ? "halfword" : "byte"; + type = size == 8 ? "double word" : + size == 4 ? "word" : + size == 2 ? "halfword" : "byte"; addr1 = simple_strtoul(argv[1], NULL, 16); addr1 += base_address; @@ -298,10 +318,14 @@ static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) base = buf1 = map_sysmem(addr1, bytes); buf2 = map_sysmem(addr2, bytes); for (ngood = 0; ngood < count; ++ngood) { - ulong word1, word2; if (size == 4) { word1 = *(u32 *)buf1; word2 = *(u32 *)buf2; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + } else if (size == 8) { + word1 = *(u64 *)buf1; + word2 = *(u64 *)buf2; +#endif } else if (size == 2) { word1 = *(u16 *)buf1; word2 = *(u16 *)buf2; @@ -311,10 +335,15 @@ static int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } if (word1 != word2) { ulong offset = buf1 - base; - +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + printf("%s at 0x%p (%#0*llx) != %s at 0x%p (%#0*llx)\n", + type, (void *)(addr1 + offset), size, word1, + type, (void *)(addr2 + offset), size, word2); +#else printf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n", type, (ulong)(addr1 + offset), size, word1, type, (ulong)(addr2 + offset), size, word2); +#endif rcode = 1; break; } @@ -434,6 +463,10 @@ static int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) while (count-- > 0) { if (size == 4) *((u32 *)buf) = *((u32 *)src); +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)buf) = *((u64 *)src); +#endif else if (size == 2) *((u16 *)buf) = *((u16 *)src); else @@ -467,6 +500,9 @@ static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, { ulong addr, length, i, bytes; int size; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + volatile u64 *llp; +#endif volatile u32 *longp; volatile u16 *shortp; volatile u8 *cp; @@ -497,6 +533,13 @@ static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, * If we have only one object, just run infinite loops. */ if (length == 1) { +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + llp = (u64 *)buf; + for (;;) + i = *llp; + } +#endif if (size == 4) { longp = (u32 *)buf; for (;;) @@ -512,6 +555,16 @@ static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, i = *cp; } +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + for (;;) { + llp = (u64 *)buf; + i = length; + while (i-- > 0) + *llp++; + } + } +#endif if (size == 4) { for (;;) { longp = (u32 *)buf; @@ -542,8 +595,14 @@ static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, #ifdef CONFIG_LOOPW int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - ulong addr, length, i, data, bytes; + ulong addr, length, i, bytes; int size; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + volatile u64 *llp; + u64 data; +#else + ulong data; +#endif volatile u32 *longp; volatile u16 *shortp; volatile u8 *cp; @@ -568,7 +627,11 @@ int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) length = simple_strtoul(argv[2], NULL, 16); /* data to write */ +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + data = simple_strtoull(argv[3], NULL, 16); +#else data = simple_strtoul(argv[3], NULL, 16); +#endif bytes = size * length; buf = map_sysmem(addr, bytes); @@ -577,11 +640,18 @@ int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) * If we have only one object, just run infinite loops. */ if (length == 1) { +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + llp = (u64 *)buf; + for (;;) + *llp = data; + } +#endif if (size == 4) { longp = (u32 *)buf; for (;;) *longp = data; - } + } if (size == 2) { shortp = (u16 *)buf; for (;;) @@ -592,6 +662,16 @@ int do_mem_loopw (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) *cp = data; } +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + if (size == 8) { + for (;;) { + llp = (u64 *)buf; + i = length; + while (i-- > 0) + *llp++ = data; + } + } +#endif if (size == 4) { for (;;) { longp = (u32 *)buf; @@ -998,13 +1078,18 @@ static int do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, /* Modify memory. * * Syntax: - * mm{.b, .w, .l} {addr} - * nm{.b, .w, .l} {addr} + * mm{.b, .w, .l, .q} {addr} + * nm{.b, .w, .l, .q} {addr} */ static int mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) { - ulong addr, i; + ulong addr; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + u64 i; +#else + ulong i; +#endif int nbytes, size; void *ptr = NULL; @@ -1055,6 +1140,10 @@ mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) printf("%08lx:", addr); if (size == 4) printf(" %08x", *((u32 *)ptr)); +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + printf(" %016llx", *((u64 *)ptr)); +#endif else if (size == 2) printf(" %04x", *((u16 *)ptr)); else @@ -1079,7 +1168,11 @@ mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) #endif else { char *endp; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + i = simple_strtoull(console_buffer, &endp, 16); +#else i = simple_strtoul(console_buffer, &endp, 16); +#endif nbytes = endp - console_buffer; if (nbytes) { #ifdef CONFIG_BOOT_RETRY_TIME @@ -1089,6 +1182,10 @@ mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) #endif if (size == 4) *((u32 *)ptr) = i; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (size == 8) + *((u64 *)ptr) = i; +#endif else if (size == 2) *((u16 *)ptr) = i; else @@ -1136,39 +1233,63 @@ static int do_mem_crc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD( md, 3, 1, do_mem_md, "memory display", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address [# of objects]" +#else "[.b, .w, .l] address [# of objects]" +#endif ); U_BOOT_CMD( mm, 2, 1, do_mem_mm, "memory modify (auto-incrementing address)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address" +#else "[.b, .w, .l] address" +#endif ); U_BOOT_CMD( nm, 2, 1, do_mem_nm, "memory modify (constant address)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address" +#else "[.b, .w, .l] address" +#endif ); U_BOOT_CMD( mw, 4, 1, do_mem_mw, "memory write (fill)", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address value [count]" +#else "[.b, .w, .l] address value [count]" +#endif ); U_BOOT_CMD( cp, 4, 1, do_mem_cp, "memory copy", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] source target count" +#else "[.b, .w, .l] source target count" +#endif ); U_BOOT_CMD( cmp, 4, 1, do_mem_cmp, "memory compare", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] addr1 addr2 count" +#else "[.b, .w, .l] addr1 addr2 count" +#endif ); #ifdef CONFIG_CMD_CRC32 @@ -1220,14 +1341,22 @@ U_BOOT_CMD( U_BOOT_CMD( loop, 3, 1, do_mem_loop, "infinite loop on address range", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address number_of_objects" +#else "[.b, .w, .l] address number_of_objects" +#endif ); #ifdef CONFIG_LOOPW U_BOOT_CMD( loopw, 4, 1, do_mem_loopw, "infinite write loop on address range", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address number_of_objects data_to_write" +#else "[.b, .w, .l] address number_of_objects data_to_write" +#endif ); #endif /* CONFIG_LOOPW */ @@ -1243,13 +1372,21 @@ U_BOOT_CMD( U_BOOT_CMD( mdc, 4, 1, do_mem_mdc, "memory display cyclic", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address count delay(ms)" +#else "[.b, .w, .l] address count delay(ms)" +#endif ); U_BOOT_CMD( mwc, 4, 1, do_mem_mwc, "memory write cyclic", +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + "[.b, .w, .l, .q] address value delay(ms)" +#else "[.b, .w, .l] address value delay(ms)" +#endif ); #endif /* CONFIG_MX_CYCLIC */ diff --git a/common/command.c b/common/command.c index 597ab4c..746b7e3 100644 --- a/common/command.c +++ b/common/command.c @@ -421,6 +421,10 @@ int cmd_get_data_size(char* arg, int default_size) return 2; case 'l': return 4; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + case 'q': + return 8; +#endif case 's': return -2; default: @@ -538,3 +542,13 @@ enum command_ret_t cmd_process(int flag, int argc, char * const argv[], rc = cmd_usage(cmdtp); return rc; } + +int cmd_process_error(cmd_tbl_t *cmdtp, int err) +{ + if (err) { + printf("Command '%s' failed: Error %d\n", cmdtp->name, err); + return 1; + } + + return 0; +} @@ -28,11 +28,13 @@ sinclude $(TOPDIR)/$(CPUDIR)/config.mk # include CPU specific rules ifdef SOC sinclude $(TOPDIR)/$(CPUDIR)/$(SOC)/config.mk # include SoC specific rules endif +ifneq ($(BOARD),) ifdef VENDOR BOARDDIR = $(VENDOR)/$(BOARD) else BOARDDIR = $(BOARD) endif +endif ifdef BOARD sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules endif @@ -46,7 +48,5 @@ OBJCOPYFLAGS += --gap-fill=0xff CPPFLAGS = $(RELFLAGS) CPPFLAGS += -pipe $(PLATFORM_CPPFLAGS) -BCURDIR = $(subst $(SRCTREE)/,,$(CURDIR:$(obj)%=%)) - LDFLAGS += $(PLATFORM_LDFLAGS) LDFLAGS_FINAL += -Bstatic diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt new file mode 100644 index 0000000..e0b395a --- /dev/null +++ b/doc/driver-model/README.txt @@ -0,0 +1,368 @@ +Driver Model +============ + +This README contains high-level information about driver model, a unified +way of declaring and accessing drivers in U-Boot. The original work was done +by: + + Marek Vasut <marex@denx.de> + Pavel Herrmann <morpheus.ibis@gmail.com> + Viktor Křivák <viktor.krivak@gmail.com> + Tomas Hlavacek <tmshlvck@gmail.com> + +This has been both simplified and extended into the current implementation +by: + + Simon Glass <sjg@chromium.org> + + +Terminology +----------- + +Uclass - a group of devices which operate in the same way. A uclass provides + a way of accessing invidual devices within the group, but always + using the same interface. For example a GPIO uclass provides + operations for get/set value. An I2C uclass may have 10 I2C ports, + 4 with one driver, and 6 with another. + +Driver - some code which talks to a peripheral and presents a higher-level + interface to it. + +Device - an instance of a driver, tied to a particular port or peripheral. + + +How to try it +------------- + +Build U-Boot sandbox and run it: + + make sandbox_config + make + ./u-boot + + (type 'reset' to exit U-Boot) + + +There is a uclass called 'demo'. This uclass handles +saying hello, and reporting its status. There are two drivers in this +uclass: + + - simple: Just prints a message for hello, doesn't implement status + - shape: Prints shapes and reports number of characters printed as status + +The demo class is pretty simple, but not trivial. The intention is that it +can be used for testing, so it will implement all driver model features and +provide good code coverage of them. It does have multiple drivers, it +handles parameter data and platdata (data which tells the driver how +to operate on a particular platform) and it uses private driver data. + +To try it, see the example session below: + +=>demo hello 1 +Hello '@' from 07981110: red 4 +=>demo status 2 +Status: 0 +=>demo hello 2 +g +r@ +e@@ +e@@@ +n@@@@ +g@@@@@ +=>demo status 2 +Status: 21 +=>demo hello 4 ^ + y^^^ + e^^^^^ +l^^^^^^^ +l^^^^^^^ + o^^^^^ + w^^^ +=>demo status 4 +Status: 36 +=> + + +Running the tests +----------------- + +The intent with driver model is that the core portion has 100% test coverage +in sandbox, and every uclass has its own test. As a move towards this, tests +are provided in test/dm. To run them, try: + + ./test/dm/test-dm.sh + +You should see something like this: + + <...U-Boot banner...> + Running 12 driver model tests + Test: dm_test_autobind + Test: dm_test_autoprobe + Test: dm_test_children + Test: dm_test_fdt + Test: dm_test_gpio + sandbox_gpio: sb_gpio_get_value: error: offset 4 not reserved + Test: dm_test_leak + Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c + Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c + Test: dm_test_lifecycle + Test: dm_test_operations + Test: dm_test_ordering + Test: dm_test_platdata + Test: dm_test_remove + Test: dm_test_uclass + Failures: 0 + +(You can add '#define DEBUG' as suggested to check for memory leaks) + + +What is going on? +----------------- + +Let's start at the top. The demo command is in common/cmd_demo.c. It does +the usual command procesing and then: + + struct device *demo_dev; + + ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); + +UCLASS_DEMO means the class of devices which implement 'demo'. Other +classes might be MMC, or GPIO, hashing or serial. The idea is that the +devices in the class all share a particular way of working. The class +presents a unified view of all these devices to U-Boot. + +This function looks up a device for the demo uclass. Given a device +number we can find the device because all devices have registered with +the UCLASS_DEMO uclass. + +The device is automatically activated ready for use by uclass_get_device(). + +Now that we have the device we can do things like: + + return demo_hello(demo_dev, ch); + +This function is in the demo uclass. It takes care of calling the 'hello' +method of the relevant driver. Bearing in mind that there are two drivers, +this particular device may use one or other of them. + +The code for demo_hello() is in drivers/demo/demo-uclass.c: + +int demo_hello(struct device *dev, int ch) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->hello) + return -ENOSYS; + + return ops->hello(dev, ch); +} + +As you can see it just calls the relevant driver method. One of these is +in drivers/demo/demo-simple.c: + +static int simple_hello(struct device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev_get_platdata(dev); + + printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), + pdata->colour, pdata->sides); + + return 0; +} + + +So that is a trip from top (command execution) to bottom (driver action) +but it leaves a lot of topics to address. + + +Declaring Drivers +----------------- + +A driver declaration looks something like this (see +drivers/demo/demo-shape.c): + +static const struct demo_ops shape_ops = { + .hello = shape_hello, + .status = shape_status, +}; + +U_BOOT_DRIVER(demo_shape_drv) = { + .name = "demo_shape_drv", + .id = UCLASS_DEMO, + .ops = &shape_ops, + .priv_data_size = sizeof(struct shape_data), +}; + + +This driver has two methods (hello and status) and requires a bit of +private data (accessible through dev_get_priv(dev) once the driver has +been probed). It is a member of UCLASS_DEMO so will register itself +there. + +In U_BOOT_DRIVER it is also possible to specify special methods for bind +and unbind, and these are called at appropriate times. For many drivers +it is hoped that only 'probe' and 'remove' will be needed. + +The U_BOOT_DRIVER macro creates a data structure accessible from C, +so driver model can find the drivers that are available. + +The methods a device can provide are documented in the device.h header. +Briefly, they are: + + bind - make the driver model aware of a device (bind it to its driver) + unbind - make the driver model forget the device + ofdata_to_platdata - convert device tree data to platdata - see later + probe - make a device ready for use + remove - remove a device so it cannot be used until probed again + +The sequence to get a device to work is bind, ofdata_to_platdata (if using +device tree) and probe. + + +Platform Data +------------- + +Where does the platform data come from? See demo-pdata.c which +sets up a table of driver names and their associated platform data. +The data can be interpreted by the drivers however they like - it is +basically a communication scheme between the board-specific code and +the generic drivers, which are intended to work on any board. + +Drivers can acceess their data via dev->info->platdata. Here is +the declaration for the platform data, which would normally appear +in the board file. + + static const struct dm_demo_cdata red_square = { + .colour = "red", + .sides = 4. + }; + static const struct driver_info info[] = { + { + .name = "demo_shape_drv", + .platdata = &red_square, + }, + }; + + demo1 = driver_bind(root, &info[0]); + + +Device Tree +----------- + +While platdata is useful, a more flexible way of providing device data is +by using device tree. With device tree we replace the above code with the +following device tree fragment: + + red-square { + compatible = "demo-shape"; + colour = "red"; + sides = <4>; + }; + + +The easiest way to make this work it to add a few members to the driver: + + .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), + .ofdata_to_platdata = testfdt_ofdata_to_platdata, + .probe = testfdt_drv_probe, + +The 'auto_alloc' feature allowed space for the platdata to be allocated +and zeroed before the driver's ofdata_to_platdata method is called. This +method reads the information out of the device tree and puts it in +dev->platdata. Then the probe method is called to set up the device. + +Note that both methods are optional. If you provide an ofdata_to_platdata +method then it wlil be called first (after bind). If you provide a probe +method it will be called next. + +If you don't want to have the platdata automatically allocated then you +can leave out platdata_auto_alloc_size. In this case you can use malloc +in your ofdata_to_platdata (or probe) method to allocate the required memory, +and you should free it in the remove method. + + +Declaring Uclasses +------------------ + +The demo uclass is declared like this: + +U_BOOT_CLASS(demo) = { + .id = UCLASS_DEMO, +}; + +It is also possible to specify special methods for probe, etc. The uclass +numbering comes from include/dm/uclass.h. To add a new uclass, add to the +end of the enum there, then declare your uclass as above. + + +Data Structures +--------------- + +Driver model uses a doubly-linked list as the basic data structure. Some +nodes have several lists running through them. Creating a more efficient +data structure might be worthwhile in some rare cases, once we understand +what the bottlenecks are. + + +Changes since v1 +---------------- + +For the record, this implementation uses a very similar approach to the +original patches, but makes at least the following changes: + +- Tried to agressively remove boilerplate, so that for most drivers there +is little or no 'driver model' code to write. +- Moved some data from code into data structure - e.g. store a pointer to +the driver operations structure in the driver, rather than passing it +to the driver bind function. +- Rename some structures to make them more similar to Linux (struct device +instead of struct instance, struct platdata, etc.) +- Change the name 'core' to 'uclass', meaning U-Boot class. It seems that +this concept relates to a class of drivers (or a subsystem). We shouldn't +use 'class' since it is a C++ reserved word, so U-Boot class (uclass) seems +better than 'core'. +- Remove 'struct driver_instance' and just use a single 'struct device'. +This removes a level of indirection that doesn't seem necessary. +- Built in device tree support, to avoid the need for platdata +- Removed the concept of driver relocation, and just make it possible for +the new driver (created after relocation) to access the old driver data. +I feel that relocation is a very special case and will only apply to a few +drivers, many of which can/will just re-init anyway. So the overhead of +dealing with this might not be worth it. +- Implemented a GPIO system, trying to keep it simple + + +Things to punt for later +------------------------ + +- SPL support - this will have to be present before many drivers can be +converted, but it seems like we can add it once we are happy with the +core implementation. +- Pre-relocation support - similar story + +That is not to say that no thinking has gone into these - in fact there +is quite a lot there. However, getting these right is non-trivial and +there is a high cost associated with going down the wrong path. + +For SPL, it may be possible to fit in a simplified driver model with only +bind and probe methods, to reduce size. + +For pre-relocation we can simply call the driver model init function. Then +post relocation we throw that away and re-init driver model again. For drivers +which require some sort of continuity between pre- and post-relocation +devices, we can provide access to the pre-relocation device pointers. + +Uclasses are statically numbered at compile time. It would be possible to +change this to dynamic numbering, but then we would require some sort of +lookup service, perhaps searching by name. This is slightly less efficient +so has been left out for now. One small advantage of dynamic numbering might +be fewer merge conflicts in uclass-id.h. + + +Simon Glass +sjg@chromium.org +April 2013 +Updated 7-May-13 +Updated 14-Jun-13 +Updated 18-Oct-13 +Updated 5-Nov-13 diff --git a/doc/driver-model/UDM-block.txt b/doc/driver-model/UDM-block.txt deleted file mode 100644 index 0437d9b..0000000 --- a/doc/driver-model/UDM-block.txt +++ /dev/null @@ -1,278 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Block device subsystem analysis -=============================== - -Pavel Herrmann <morpheus.ibis@gmail.com> -2012-03-08 - -I) Overview ------------ - - U-Boot currently implements several distinct APIs for block devices - some - drivers use the SATA API, some drivers use the IDE API, sym53c8xx and - AHCI use the SCSI API, mg_disk has a separate API, and systemace also has a - separate API. There are also MMC and USB APIs used outside of drivers/block, - those will be detailed in their specific documents. - - Block devices are described by block_dev_desc structure, that holds, among - other things, the read/write/erase callbacks. Block device structures are - stored in any way depending on the API, but can be accessed by - - block_dev_desc_t * $api_get_dev(int dev) - - function, as seen in disk/part.c. - - 1) SATA interface - ----------------- - - The SATA interface drivers implement the following functions: - - int init_sata(int dev) - int scan_sata(int dev) - ulong sata_read(int dev, ulong blknr, ulong blkcnt, void *buffer) - ulong sata_write(int dev, ulong blknr, ulong blkcnt, const void *buffer) - - Block devices are kept in sata_dev_desc[], which is prefilled with values - common to all SATA devices in cmd_sata.c, and then modified in init_sata - function in the drivers. Callbacks of the block device use SATA API - directly. The sata_get_dev function is defined in cmd_sata.c. - - 2) SCSI interface - ----------------- - - The SCSI interface drivers implement the following functions: - - void scsi_print_error(ccb *pccb) - int scsi_exec(ccb *pccb) - void scsi_bus_reset(void) - void scsi_low_level_init(int busdevfunc) - - The SCSI API works through the scsi_exec function, the actual operation - requested is found in the ccb structure. - - Block devices are kept in scsi_dev_desc[], which lives only in cmd_scsi.c. - Callbacks of the block device use functions from cmd_scsi.c, which in turn - call scsi_exec of the controller. The scsi_get_dev function is also defined - in cmd_scsi.c. - - 3) mg_disk interface - -------------------- - - The mg_disk interface drivers implement the following functions: - - struct mg_drv_data* mg_get_drv_data (void) - uint mg_disk_init (void) - uint mg_disk_read (u32 addr, u8 *buff, u32 len) - uint mg_disk_write(u32 addr, u8 *buff, u32 len) - uint mg_disk_write_sects(void *buff, u32 sect_num, u32 sect_cnt) - uint mg_disk_read_sects(void *buff, u32 sect_num, u32 sect_cnt) - - The mg_get_drv_data function is to be overridden per-board, but there are no - board in-tree that do this. - - Only one driver for this API exists, and it only supports one block device. - Callbacks for this device are implemented in mg_disk.c and call the mg_disk - API. The mg_disk_get_dev function is defined in mg_disk.c and ignores the - device number, always returning the same device. - - 4) systemace interface - ---------------------- - - The systemace interface does not define any driver API, and has no command - itself. The single defined function is systemace_get_devs() from - systemace.c, which returns a single static structure for the only supported - block device. Callbacks for this device are also implemented in systemace.c. - - 5) IDE interface - ---------------- - - The IDE interface drivers implement the following functions, but only if - CONFIG_IDE_AHB is set: - - uchar ide_read_register(int dev, unsigned int port); - void ide_write_register(int dev, unsigned int port, unsigned char val); - void ide_read_data(int dev, ulong *sect_buf, int words); - void ide_write_data(int dev, const ulong *sect_buf, int words); - - The first two functions are called from ide_inb()/ide_outb(), and will - default to direct memory access if CONFIG_IDE_AHB is not set, or - ide_inb()/ide_outb() functions will get overridden by the board altogether. - - The second two functions are called from input_data()/output_data() - functions, and also default to direct memory access, but cannot be - overridden by the board. - - One function shared by IDE drivers (but not defined in ide.h) is - int ide_preinit(void) - This function gets called from ide_init in cmd_ide.c if CONFIG_IDE_PREINIT - is defined, and will do the driver-specific initialization of the device. - - Block devices are kept in ide_dev_desc[], which is filled in cmd_ide.c. - Callbacks of the block device are defined in cmd_ide.c, and use the - ide_inb()/ide_outb()/input_data()/output_data() functions mentioned above. - The ide_get_dev function is defined in cmd_ide.c. - -II) Approach ------------- - - A new block controller core and an associated API will be created to mimic the - current SATA API, its drivers will have the following ops: - - struct block_ctrl_ops { - int scan(instance *i); - int reset(instance *i, int port); - lbaint_t read(instance *i, int port, lbaint_t start, lbatin_t length, - void *buffer); - lbaint_t write(instance *i, int port, lbaint_t start, lbatin_t length, - void*buffer); - } - - The current sata_init() function will be changed into the driver probe() - function. The read() and write() functions should never be called directly, - instead they should be called by block device driver for disks. - - Other block APIs would either be transformed into this API, or be kept as - legacy for old drivers, or be dropped altogether. - - Legacy driver APIs will each have its own driver core that will contain the - shared logic, which is currently located mostly in cmd_* files. Callbacks for - block device drivers will then probably be implemented as a part of the core - logic, and will use the driver ops (which will copy current state of - respective APIs) to do the work. - - All drivers will be cleaned up, most ifdefs should be converted into - platform_data, to enable support for multiple devices with different settings. - - A new block device core will also be created, and will keep track of all - block devices on all interfaces. - - Current block_dev_desc structure will be changed to fit the driver model, all - identification and configuration will be placed in private data, and - a single accessor and modifier will be defined, to accommodate the need for - different sets of options for different interfaces, while keeping the - structure small. The new block device drivers will have the following ops - structure (lbaint_t is either 32bit or 64bit unsigned, depending on - CONFIG_LBA48): - - struct blockdev_ops { - lbaint_t (*block_read)(struct instance *i, lbaint_t start, lbaint_t blkcnt, - void *buffer); - lbaint_t (*block_write)(struct instance *i, lbaint_t start, lbaint_t blkcnt, - void *buffer); - lbaint_t (*block_erase)(struct instance *i, lbaint_t start, lbaint_t blkcnt - ); - int (*get_option)(struct instance *i, enum blockdev_option_code op, - struct option *res); - int (*set_option)(struct instance *i, enum blockdev_option_code op, - struct option *val); - } - - struct option { - uint32_t flags - union data { - uint64_t data_u; - char* data_s; - void* data_p; - } - } - - enum blockdev_option_code { - BLKD_OPT_IFTYPE=0, - BLKD_OPT_TYPE, - BLKD_OPT_BLOCKSIZE, - BLKD_OPT_BLOCKCOUNT, - BLKD_OPT_REMOVABLE, - BLKD_OPT_LBA48, - BLKD_OPT_VENDOR, - BLKD_OPT_PRODICT, - BLKD_OPT_REVISION, - BLKD_OPT_SCSILUN, - BLKD_OPT_SCSITARGET, - BLKD_OPT_OFFSET - } - - Flags in option above will contain the type of returned data (which should be - checked against what is expected, even though the option requested should - specify it), and a flag to indicate whether the returned pointer needs to be - free()'d. - - The block device core will contain the logic now located in disk/part.c and - related files, and will be used to forward requests to block devices. The API - for the block device core will copy the ops of a block device (with a string - identifier instead of instance pointer). This means that partitions will also - be handled by the block device core, and exported as block devices, making - them transparent to the rest of the code. - - Sadly, this will change how file systems can access the devices, and thus will - affect a lot of places. However, these changes should be localized and easy to - implement. - - AHCI driver will be rewritten to fit the new unified block controller API, - making SCSI API easy to merge with sym53c8xx, or remove it once the device - driver has died. - - Optionally, IDE core may be changed into one driver with unified block - controller API, as most of it is already in one place and device drivers are - just sets of hooks. Additionally, mg_disk driver is unused and may be removed - in near future. - - -III) Analysis of in-tree drivers --------------------------------- - - ahci.c - ------ - SCSI API, will be rewritten for a different API. - - ata_piix.c - ---------- - SATA API, easy to port. - - fsl_sata.c - ---------- - SATA API, few CONFIG macros, easy to port. - - ftide020.c - ---------- - IDE API, defines CONFIG_IDE_AHB and ide_preinit hook functions. - - mg_disk.c - --------- - Single driver with mg_disk API, not much to change, easy to port. - - mvsata_ide.c - ------------ - IDE API, only defines ide_preinit hook function. - - mxc_ata.c - --------- - IDE API, only defines ide_preinit hook function. - - pata_bfin.c - ----------- - SATA API, easy to port. - - sata_dwc.c - ---------- - SATA API, easy to port. - - sata_sil3114.c - -------------- - SATA API, easy to port. - - sata_sil.c - ---------- - SATA API, easy to port. - - sil680.c - -------- - IDE API, only defines ide_preinit hook function. - - sym53c8xx.c - ----------- - SCSI API, may be merged with code from cmd_scsi. - - systemace.c - ----------- - Single driver with systemace API, not much to change, easy to port. diff --git a/doc/driver-model/UDM-cores.txt b/doc/driver-model/UDM-cores.txt deleted file mode 100644 index 6032333..0000000 --- a/doc/driver-model/UDM-cores.txt +++ /dev/null @@ -1,126 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Driver cores API document -========================= - -Pavel Herrmann <morpheus.ibis@gmail.com> - -1) Overview ------------ - Driver cores will be used as a wrapper for devices of the same type, and as - an abstraction for device driver APIs. For each driver API (which roughly - correspond to device types), there will be one driver core. Each driver core - will implement three APIs - a driver API (which will be the same as API of - drivers the core wraps around), a core API (which will be implemented by all - cores) and a command API (core-specific API which will be exposed to - commands). - - A) Command API - The command API will provide access to shared functionality for a specific - device, which is currently located mostly in commands. Commands will be - rewritten to be more lightweight by using this API. As this API will be - different for each core, it is out of scope of this document. - - B) Driver API - The driver API will act as a wrapper around actual device drivers, - providing a single entrypoint for device access. All functions in this API - have an instance* argument (probably called "this" or "i"), which will be - examined by the core, and a correct function for the specified driver will - get called. - - If the core gets called with a group instance pointer (as discussed in - design), it will automatically select the instance that is associated - with this core, and use it as target of the call. if the group contains - multiple instances of a single type, the caller must explicitly use an - accessor to select the correct instance. - - This accessor will look like: - struct instance *get_instance_from_group(struct instance *group, int i) - - When called with a non-group instance, it will simply return the instance. - - C) Core API - The core API will be implemented by all cores, and will provide - functionality for getting driver instances from non-driver code. This API - will consist of following functions: - - int get_count(struct instance *core); - struct instance* get_instance(struct instance *core, int index); - int init(struct instance *core); - int bind(struct instance *core, struct instance *dev, void *ops, - void *hint); - int unbind(struct instance *core, instance *dev); - int replace(struct instance *core, struct_instance *new_dev, - struct instance *old_dev); - int destroy(struct instance *core); - int reloc(struct instance *new_core, struct instance *old_core); - - The 'hint' parameter of bind() serves for additional data a driver can - pass to the core, to help it create the correct internal state for this - instance. the replace() function will get called during instance - relocation, and will replace the old instance with the new one, keeping - the internal state untouched. - - -2) Lifetime of a driver core ----------------------------- - Driver cores will be initialized at runtime, to limit memory footprint in - early-init stage, when we have to fit into ~1KB of memory. All active cores - will be stored in a tree structure (referenced as "Core tree") in global data, - which provides good tradeoff between size and access time. - Every core will have a number constant associated with it, which will be used - to find the instance in Core tree, and to refer to the core in all calls - working with the Core tree. - The Core Tree should be implemented using B-tree (or a similar structure) - to guarantee acceptable time overhead in all cases. - - Code for working with the core (i2c in this example) follows: - - core_init(CORE_I2C); - This will check whether we already have a i2c core, and if not it creates - a new instance and adds it into the Core tree. This will not be exported, - all code should depend on get_core_instance to init the core when - necessary. - - get_core_instance(CORE_I2C); - This is an accessor into the Core tree, which will return the instance - of i2c core, creating it if necessary - - core_bind(CORE_I2C, instance, driver_ops); - This will get called in bind() function of a driver, and will add the - instance into cores internal list of devices. If the core is not found, it - will get created. - - driver_activate(instance *inst); - This call will recursively activate all devices necessary for using the - specified device. the code could be simplified as: - { - if (is_activated(inst)) - return; - driver_activate(inst->bus); - get_driver(inst)->probe(inst); - } - - The case with multiple parents will need to be handled here as well. - get_driver is an accessor to available drivers, which will get struct - driver based on a name in the instance. - - i2c_write(instance *inst, ...); - An actual call to some method of the driver. This code will look like: - { - driver_activate(inst); - struct instance *core = get_core_instance(CORE_I2C); - device_ops = get_ops(inst); - device_ops->write(...); - } - - get_ops will not be an exported function, it will be internal and specific - to the core, as it needs to know how are the ops stored, and what type - they are. - - Please note that above examples represent the algorithm, not the actual code, - as they are missing checks for validity of return values. - - core_init() function will get called the first time the core is requested, - either by core_link() or core_get_instance(). This way, the cores will get - created only when they are necessary, which will reduce our memory footprint. diff --git a/doc/driver-model/UDM-design.txt b/doc/driver-model/UDM-design.txt deleted file mode 100644 index 9f03bba..0000000 --- a/doc/driver-model/UDM-design.txt +++ /dev/null @@ -1,315 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Design document -=============== -Marek Vasut <marek.vasut@gmail.com> -Pavel Herrmann <morpheus.ibis@gmail.com> -2012-05-17 - -I) The modular concept ----------------------- - -The driver core design is done with modularity in mind. The long-term plan is to -extend this modularity to allow loading not only drivers, but various other -objects into U-Boot at runtime -- like commands, support for other boards etc. - -II) Driver core initialization stages -------------------------------------- - -The drivers have to be initialized in two stages, since the U-Boot bootloader -runs in two stages itself. The first stage is the one which is executed before -the bootloader itself is relocated. The second stage then happens after -relocation. - - 1) First stage - -------------- - - The first stage runs after the bootloader did very basic hardware init. This - means the stack pointer was configured, caches disabled and that's about it. - The problem with this part is the memory management isn't running at all. To - make things even worse, at this point, the RAM is still likely uninitialized - and therefore unavailable. - - 2) Second stage - --------------- - - At this stage, the bootloader has initialized RAM and is running from it's - final location. Dynamic memory allocations are working at this point. Most of - the driver initialization is executed here. - -III) The drivers ----------------- - - 1) The structure of a driver - ---------------------------- - - The driver will contain a structure located in a separate section, which - will allow linker to create a list of compiled-in drivers at compile time. - Let's call this list "driver_list". - - struct driver __attribute__((section(driver_list))) { - /* The name of the driver */ - char name[STATIC_CONFIG_DRIVER_NAME_LENGTH]; - - /* - * This function should connect this driver with cores it depends on and - * with other drivers, likely bus drivers - */ - int (*bind)(struct instance *i); - - /* This function actually initializes the hardware. */ - int (*probe)(struct instance *i); - - /* - * The function of the driver called when U-Boot finished relocation. - * This is particularly important to eg. move pointers to DMA buffers - * and such from the location before relocation to their final location. - */ - int (*reloc)(struct instance *i); - - /* - * This is called when the driver is shuting down, to deinitialize the - * hardware. - */ - int (*remove)(struct instance *i); - - /* This is called to remove the driver from the driver tree */ - int (*unbind)(struct instance *i); - - /* This is a list of cores this driver depends on */ - struct driver *cores[]; - }; - - The cores[] array in here is very important. It allows u-boot to figure out, - in compile-time, which possible cores can be activated at runtime. Therefore - if there are cores that won't be ever activated, GCC LTO might remove them - from the final binary. Actually, this information might be used to drive build - of the cores. - - FIXME: Should *cores[] be really struct driver, pointing to drivers that - represent the cores? Shouldn't it be core instance pointer? - - 2) Instantiation of a driver - ---------------------------- - - The driver is instantiated by calling: - - driver_bind(struct instance *bus, const struct driver_info *di) - - The "struct instance *bus" is a pointer to a bus with which this driver should - be registered with. The "root" bus pointer is supplied to the board init - functions. - - FIXME: We need some functions that will return list of busses of certain type - registered with the system so the user can find proper instance even if - he has no bus pointer (this will come handy if the user isn't - registering the driver from board init function, but somewhere else). - - The "const struct driver_info *di" pointer points to a structure defining the - driver to be registered. The structure is defined as follows: - - struct driver_info { - char name[STATIC_CONFIG_DRIVER_NAME_LENGTH]; - void *platform_data; - } - - The instantiation of a driver by calling driver_bind() creates an instance - of the driver by allocating "struct driver_instance". Note that only struct - instance is passed to the driver. The wrapping struct driver_instance is there - for purposes of the driver core: - - struct driver_instance { - uint32_t flags; - struct instance i; - }; - - struct instance { - /* Pointer to a driver information passed by driver_register() */ - const struct driver_info *info; - /* Pointer to a bus this driver is bound with */ - struct instance *bus; - /* Pointer to this driver's own private data */ - void *private_data; - /* Pointer to the first block of successor nodes (optional) */ - struct successor_block *succ; - } - - The instantiation of a driver does not mean the hardware is initialized. The - driver_bind() call only creates the instance of the driver, fills in the "bus" - pointer and calls the drivers' .bind() function. The .bind() function of the - driver should hook the driver with the remaining cores and/or drivers it - depends on. - - It's important to note here, that in case the driver instance has multiple - parents, such parent can be connected with this instance by calling: - - driver_link(struct instance *parent, struct instance *dev); - - This will connect the other parent driver with the newly instantiated driver. - Note that this must be called after driver_bind() and before driver_acticate() - (driver_activate() will be explained below). To allow struct instance to have - multiple parent pointer, the struct instance *bus will utilize it's last bit - to indicate if this is a pointer to struct instance or to an array if - instances, struct successor block. The approach is similar as the approach to - *succ in struct instance, described in the following paragraph. - - The last pointer of the struct instance, the pointer to successor nodes, is - used only in case of a bus driver. Otherwise the pointer contains NULL value. - The last bit of this field indicates if this is a bus having a single child - node (so the last bit is 0) or if this bus has multiple child nodes (the last - bit is 1). In the former case, the driver core should clear the last bit and - this pointer points directly to the child node. In the later case of a bus - driver, the pointer points to an instance of structure: - - struct successor_block { - /* Array of pointers to instances of devices attached to this bus */ - struct instance *dev[BLOCKING_FACTOR]; - /* Pointer to next block of successors */ - struct successor_block *next; - } - - Some of the *dev[] array members might be NULL in case there are no more - devices attached. The *next is NULL in case the list of attached devices - doesn't continue anymore. The BLOCKING_FACTOR is used to allocate multiple - slots for successor devices at once to avoid fragmentation of memory. - - 3) The bind() function of a driver - ---------------------------------- - - The bind function of a driver connects the driver with various cores the - driver provides functions for. The driver model related part will look like - the following example for a bus driver: - - int driver_bind(struct instance *in) - { - ... - core_bind(&core_i2c_static_instance, in, i2c_bus_funcs); - ... - } - - FIXME: What if we need to run-time determine, depending on some hardware - register, what kind of i2c_bus_funcs to pass? - - This makes the i2c core aware of a new bus. The i2c_bus_funcs is a constant - structure of functions any i2c bus driver must provide to work. This will - allow the i2c command operate with the bus. The core_i2c_static_instance is - the pointer to the instance of a core this driver provides function to. - - FIXME: Maybe replace "core-i2c" with CORE_I2C global pointer to an instance of - the core? - - 4) The instantiation of a core driver - ------------------------------------- - - The core driver is special in the way that it's single-instance driver. It is - always present in the system, though it might not be activated. The fact that - it's single instance allows it to be instantiated at compile time. - - Therefore, all possible structures of this driver can be in read-only memory, - especially struct driver and struct driver_instance. But the successor list, - which needs special treatment. - - To solve the problem with a successor list and the core driver flags, a new - entry in struct gd (global data) will be introduced. This entry will point to - runtime allocated array of struct driver_instance. It will be possible to - allocate the exact amount of struct driver_instance necessary, as the number - of cores that might be activated will be known at compile time. The cores will - then behave like any usual driver. - - Pointers to the struct instance of cores can be computed at compile time, - therefore allowing the resulting u-boot binary to save some overhead. - - 5) The probe() function of a driver - ----------------------------------- - - The probe function of a driver allocates necessary resources and does required - initialization of the hardware itself. This is usually called only when the - driver is needed, as a part of the defered probe mechanism. - - The driver core should implement a function called - - int driver_activate(struct instance *in); - - which should call the .probe() function of the driver and then configure the - state of the driver instance to "ACTIVATED". This state of a driver instance - should be stored in a wrap-around structure for the structure instance, the - struct driver_instance. - - 6) The command side interface to a driver - ----------------------------------------- - - The U-Boot command shall communicate only with the specific driver core. The - driver core in turn exports necessary API towards the command. - - 7) Demonstration imaginary board - -------------------------------- - - Consider the following computer: - - * - | - +-- System power management logic - | - +-- CPU clock controlling logc - | - +-- NAND controller - | | - | +-- NAND flash chip - | - +-- 128MB of DDR DRAM - | - +-- I2C bus #0 - | | - | +-- RTC - | | - | +-- EEPROM #0 - | | - | +-- EEPROM #1 - | - +-- USB host-only IP core - | | - | +-- USB storage device - | - +-- USB OTG-capable IP core - | | - | +-- connection to the host PC - | - +-- GPIO - | | - | +-- User LED #0 - | | - | +-- User LED #1 - | - +-- UART0 - | - +-- UART1 - | - +-- Ethernet controller #0 - | - +-- Ethernet controller #1 - | - +-- Audio codec - | - +-- PCI bridge - | | - | +-- Ethernet controller #2 - | | - | +-- SPI host card - | | | - | | +-- Audio amplifier (must be operational before codec) - | | - | +-- GPIO host card - | | - | +-- User LED #2 - | - +-- LCD controller - | - +-- PWM controller (must be enabled after LCD controller) - | - +-- SPI host controller - | | - | +-- SD/MMC connected via SPI - | | - | +-- SPI flash - | - +-- CPLD/FPGA with stored configuration of the board diff --git a/doc/driver-model/UDM-fpga.txt b/doc/driver-model/UDM-fpga.txt deleted file mode 100644 index 4f9df94..0000000 --- a/doc/driver-model/UDM-fpga.txt +++ /dev/null @@ -1,115 +0,0 @@ -The U-Boot Driver Model Project -=============================== -I/O system analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-21 - -I) Overview ------------ - -The current FPGA implementation is handled by command "fpga". This command in -turn calls the following functions: - -fpga_info() -fpga_load() -fpga_dump() - -These functions are implemented by what appears to be FPGA multiplexer, located -in drivers/fpga/fpga.c . This code determines which device to operate with -depending on the device ID. - -The fpga_info() function is multiplexer of the functions providing information -about the particular FPGA device. These functions are implemented in the drivers -for the particular FPGA device: - -xilinx_info() -altera_info() -lattice_info() - -Similar approach is used for fpga_load(), which multiplexes "xilinx_load()", -"altera_load()" and "lattice_load()" and is used to load firmware into the FPGA -device. - -The fpga_dump() function, which prints the contents of the FPGA device, is no -different either, by multiplexing "xilinx_dump()", "altera_dump()" and -"lattice_dump()" functions. - -Finally, each new FPGA device is registered by calling "fpga_add()" function. -This function takes two arguments, the second one being particularly important, -because it's basically what will become platform_data. Currently, it's data that -are passed to the driver from the board/platform code. - -II) Approach ------------- - -The path to conversion of the FPGA subsystem will be very straightforward, since -the FPGA subsystem is already quite dynamic. Multiple things will need to be -modified though. - -First is the registration of the new FPGA device towards the FPGA core. This -will be achieved by calling: - - fpga_device_register(struct instance *i, const struct fpga_ops *ops); - -The particularly interesting part is the struct fpga_ops, which contains -operations supported by the FPGA device. These are basically the already used -calls in the current implementation: - -struct fpga_ops { - int info(struct instance *i); - int load(struct instance *i, const char *buf, size_t size); - int dump(struct instance *i, const char *buf, size_t size); -} - -The other piece that'll have to be modified is how the devices are tracked. -It'll be necessary to introduce a linked list of devices within the FPGA core -instead of tracking them by ID number. - -Next, the "Xilinx_desc", "Lattice_desc" and "Altera_desc" structures will have -to be moved to driver's private_data. Finally, structures passed from the board -and/or platform files, like "Xilinx_Virtex2_Slave_SelectMap_fns" would be passed -via platform_data to the driver. - -III) Analysis of in-tree drivers --------------------------------- - - 1) Altera driver - ---------------- - The driver is realized using the following files: - - drivers/fpga/altera.c - drivers/fpga/ACEX1K.c - drivers/fpga/cyclon2.c - drivers/fpga/stratixII.c - - All of the sub-drivers implement basically the same info-load-dump interface - and there's no expected problem during the conversion. The driver itself will - be realised by altera.c and all the sub-drivers will be linked in. The - distinction will be done by passing different platform data. - - 2) Lattice driver - ----------------- - The driver is realized using the following files: - - drivers/fpga/lattice.c - drivers/fpga/ivm_core.c - - This driver also implements the standard interface, but to realise the - operations with the FPGA device, uses functions from "ivm_core.c" file. This - file implements the main communications logic and has to be linked in together - with "lattice.c". No problem converting is expected here. - - 3) Xilinx driver - ---------------- - The driver is realized using the following files: - - drivers/fpga/xilinx.c - drivers/fpga/spartan2.c - drivers/fpga/spartan3.c - drivers/fpga/virtex2.c - - This set of sub-drivers is special by defining a big set of macros in - "include/spartan3.h" and similar files. These macros would need to be either - rewritten or replaced. Otherwise, there are no problems expected during the - conversion process. diff --git a/doc/driver-model/UDM-gpio.txt b/doc/driver-model/UDM-gpio.txt deleted file mode 100644 index 585d458..0000000 --- a/doc/driver-model/UDM-gpio.txt +++ /dev/null @@ -1,106 +0,0 @@ -The U-Boot Driver Model Project -=============================== -GPIO analysis -============= -Viktor Krivak <viktor.krivak@gmail.com> -2012-02-24 - -I) Overview ------------ - - At this moment U-Boot provides standard API that consists of 7 functions. - - int gpio_request(unsigned gpio, const char *label) - int gpio_free(unsigned gpio) - int gpio_direction_input(unsigned gpio) - int gpio_direction_output(unsigned gpio, int value) - int gpio_get_value(unsigned gpio) - void gpio_set_value(unsigned gpio, int value) - - Methods "gpio_request()" and "gpio_free()" are used for claiming and releasing - GPIOs. First one should check if the desired pin exists and if the pin wasn't - requested already elsewhere. The method also has a label argument that can be - used for debug purposes. The label argument should be copied into the internal - memory, but only if the DEBUG macro is set. The "gpio_free()" is the exact - opposite. It releases the particular pin. Other methods are used for setting - input or output direction and obtaining or setting values of the pins. - -II) Approach ------------- - - 1) Request and free GPIO - ------------------------ - - The "gpio_request()" implementation is basically the same for all boards. - The function checks if the particular GPIO is correct and checks if the - GPIO pin is still free. If the conditions are met, the method marks the - GPIO claimed in it's internal structure. If macro DEBUG is defined, the - function also copies the label argument to the structure. If the pin is - already locked, the function returns -1 and if DEBUG is defined, certain - debug output is generated, including the contents of the label argument. - The "gpio_free()" function releases the lock and eventually deallocates - data used by the copied label argument. - - 2) Internal data - ---------------- - - Internal data are driver specific. They have to contain some mechanism to - realise the locking though. This can be done for example using a bit field. - - 3) Operations provided by the driver - ------------------------------------ - - The driver operations basically meet API that is already defined and used. - Except for "gpio_request()" and "gpio_free()", all methods can be converted in - a simple manner. The driver provides the following structure: - - struct gpio_driver_ops { - int (*gpio_request)(struct instance *i, unsigned gpio, - const char *label); - int (*gpio_free)(struct instance *i, unsigned gpio); - int (*gpio_direction_input)(struct instance *i, unsigned gpio); - int (*gpio_direction_output)(struct instance *i, unsigned gpio, - int value); - int (*gpio_get_value)(struct instance *i, unsigned gpio); - void (*gpio_set_value)(struct instance *i, unsigned gpio, int value); - } - -III) Analysis of in-tree drivers --------------------------------- - - altera_pio.c - ------------ - Meets standard API. Implements gpio_request() properly. Simple conversion - possible. - - at91_gpio.c - ----------- - Don't meet standard API. Need some other methods to implement. - - da8xx_gpio.c - ------------ - Meets standard API. Implements gpio_request() properly. Simple conversion - possible. - - kw_gpio.c - --------- - Doesn't meet standard API. Needs some other methods to implement and move some - methods to another file. - - mpc83xx_gpio.c - -------------- - Meets standard API. Doesn't implement gpio_request() properly (only checks - if the pin is valid). Simple conversion possible. - - mvgpio.c - -------- - Meets standard API. Doesn't implement gpio_request() properly (only checks - if the pin is valid). Simple conversion possible. - - mvgpio.h - -------- - Wrong placement. Will be moved to another location. - - mvmfp.c - ------- - Wrong placement. Will be moved to another location. diff --git a/doc/driver-model/UDM-hwmon.txt b/doc/driver-model/UDM-hwmon.txt deleted file mode 100644 index 03a96a0..0000000 --- a/doc/driver-model/UDM-hwmon.txt +++ /dev/null @@ -1,118 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Hwmon device subsystem analysis -=============================== - -Tomas Hlavacek <tmshlvck@gmail.com> -2012-03-02 - -I) Overview ------------ - -U-Boot currently implements one API for HW monitoring devices. The -interface is defined in include/dtt.h and comprises of functions: - - void dtt_init(void); - int dtt_init_one(int); - int dtt_read(int sensor, int reg); - int dtt_write(int sensor, int reg, int val); - int dtt_get_temp(int sensor); - -The functions are implemented by a proper device driver in drivers/hwmon -directory and the driver to be compiled in is selected in a Makefile. -Drivers are mutually exclusive. - -Drivers depends on I2O code and naturally on board specific data. There are -few ad-hoc constants put in dtt.h file and driver headers and code. This -has to be consolidated into board specific data or driver headers if those -constants makes sense globally. - - -II) Approach ------------- - - 1) New API - ---------- - In the UDM each hwmon driver would register itself by a function - - int hwmon_device_register(struct instance *i, - struct hwmon_device_ops *o); - - The structure being defined as follows: - - struct hwmon_device_ops { - int (*read)(struct instance *i, int sensor, int reg); - int (*write)(struct instance *i, int sensor, int reg, - int val); - int (*get_temp)(struct instance *i, int sensor); - }; - - - 2) Conversion thougths - ---------------------- - U-Boot hwmon drivers exports virtually the same functions (with exceptions) - and we are considering low number of drivers and code anyway. The interface - is already similar and unified by the interface defined in dtt.h. - Current initialization functions dtt_init() and dtt_init_one() will be - converted into probe() and hwmon_device_register(), so the funcionality will - be kept in more proper places. Besides implementing core registration and - initialization we need to do code cleanup, especially separate - driver-specific and HW specific constants. - - 3) Special consideration due to early initialization - ---------------------------------------------------- - The dtt_init() function call is used during early initialization in - board/gdsys/405ex/io64.c for starting up fans. The dtt code is perfectly - usable in the early stage because it uses only local variables and no heap - memory is required at this level. However the underlying code of I2C has to - keep the same properties with regard to possibility of running in early - initialization stage. - -III) Analysis of in-tree drivers --------------------------------- - - drivers/hwmon/lm81.c - -------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/ds1722.c - ---------------------- - The driver is not standard dtt, but interface is similar to dtt. - The interface has to be changed in order to comply to above mentioned - specification. - - - drivers/hwmon/ds1775.c - ---------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/lm73.c - -------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/lm63.c - -------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/adt7460.c - ----------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/lm75.c - -------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/ds1621.c - ---------------------- - The driver is standard dtt. Simple conversion is possible. - - - drivers/hwmon/adm1021.c - ----------------------- - The driver is standard dtt. Simple conversion is possible. diff --git a/doc/driver-model/UDM-keyboard.txt b/doc/driver-model/UDM-keyboard.txt deleted file mode 100644 index 5babfc5..0000000 --- a/doc/driver-model/UDM-keyboard.txt +++ /dev/null @@ -1,47 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Keyboard input analysis -======================= -Marek Vasut <marek.vasut@gmail.com> -2012-02-20 - -I) Overview ------------ - -The keyboard drivers are most often registered with STDIO subsystem. There are -components of the keyboard drivers though, which operate in severe ad-hoc -manner, often being related to interrupt-driven keypress reception. This -components will require the most sanitization of all parts of keyboard input -subsystem. - -Otherwise, the keyboard is no different from other standard input but with the -necessity to decode scancodes. These are decoded using tables provided by -keyboard drivers. These tables are often driver specific. - -II) Approach ------------- - -The most problematic part is the interrupt driven keypress reception. For this, -the buffers that are currently shared throughout the whole U-Boot would need to -be converted into driver's private data. - -III) Analysis of in-tree drivers --------------------------------- - - board/mpl/common/kbd.c - ---------------------- - This driver is a classic STDIO driver, no problem with conversion is expected. - Only necessary change will be to move this driver to a proper location. - - board/rbc823/kbd.c - ------------------ - This driver is a classic STDIO driver, no problem with conversion is expected. - Only necessary change will be to move this driver to a proper location. - - drivers/input/keyboard.c - ------------------------ - This driver is special in many ways. Firstly because this is a universal stub - driver for converting scancodes from i8042 and the likes. Secondly because the - buffer is filled by various other ad-hoc implementations of keyboard input by - using this buffer as an extern. This will need to be fixed by allowing drivers - to pass certain routines to this driver via platform data. diff --git a/doc/driver-model/UDM-mmc.txt b/doc/driver-model/UDM-mmc.txt deleted file mode 100644 index 97f83a7..0000000 --- a/doc/driver-model/UDM-mmc.txt +++ /dev/null @@ -1,319 +0,0 @@ -The U-Boot Driver Model Project -=============================== -MMC system analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-25 - -I) Overview ------------ - -The MMC subsystem is already quite dynamic in it's nature. It's only necessary -to flip the subsystem to properly defined API. - -The probing process of MMC drivers start by calling "mmc_initialize()", -implemented by MMC framework, from the architecture initialization file. The -"mmc_initialize()" function in turn calls "board_mmc_init()" function and if -this doesn't succeed, "cpu_mmc_init()" function is called. It is important to -note that both of the "*_mmc_init()" functions have weak aliases to functions -which automatically fail. - -Both of the "*_mmc_init()" functions though serve only one purpose. To call -driver specific probe function, which in turn actually registers the driver with -MMC subsystem. Each of the driver specific probe functions is currently done in -very ad-hoc manner. - -The registration with the MMC subsystem is done by calling "mmc_register()", -whose argument is a runtime configured structure of information about the MMC -driver. Currently, the information structure is intermixed with driver's internal -data. The description of the structure follows: - -struct mmc { - /* - * API: Allows this driver to be a member of the linked list of all MMC drivers - * registered with MMC subsystem - */ - struct list_head link; - - /* DRIVER: Name of the registered driver */ - char name[32]; - - /* DRIVER: Driver's private data */ - void *priv; - - /* DRIVER: Voltages the host bus can provide */ - uint voltages; - - /* API: Version of the card */ - uint version; - - /* API: Test if the driver was already initialized */ - uint has_init; - - /* DRIVER: Minimum frequency the host bus can provide */ - uint f_min; - - /* DRIVER: Maximum frequency the host bus can provide */ - uint f_max; - - /* API: Is the card SDHC */ - int high_capacity; - - /* API: Actual width of the bus used by the current card */ - uint bus_width; - - /* - * DRIVER: Clock frequency to be configured on the host bus, this is read-only - * for the driver. - */ - uint clock; - - /* API: Capabilities of the card */ - uint card_caps; - - /* DRIVER: MMC bus capabilities */ - uint host_caps; - - /* API: Configuration and ID data retrieved from the card */ - uint ocr; - uint scr[2]; - uint csd[4]; - uint cid[4]; - ushort rca; - - /* API: Partition configuration */ - char part_config; - - /* API: Number of partitions */ - char part_num; - - /* API: Transmission speed */ - uint tran_speed; - - /* API: Read block length */ - uint read_bl_len; - - /* API: Write block length */ - uint write_bl_len; - - /* API: Erase group size */ - uint erase_grp_size; - - /* API: Capacity of the card */ - u64 capacity; - - /* API: Descriptor of this block device */ - block_dev_desc_t block_dev; - - /* DRIVER: Function used to submit command to the card */ - int (*send_cmd)(struct mmc *mmc, - struct mmc_cmd *cmd, struct mmc_data *data); - - /* DRIVER: Function used to configure the host */ - void (*set_ios)(struct mmc *mmc); - - /* DRIVER: Function used to initialize the host */ - int (*init)(struct mmc *mmc); - - /* DRIVER: Function used to report the status of Card Detect pin */ - int (*getcd)(struct mmc *mmc); - - /* - * DRIVER: Maximum amount of blocks sent during multiblock xfer, - * set to 0 to autodetect. - */ - uint b_max; -}; - -The API above is the new API used by most of the drivers. There're still drivers -in the tree that use old, legacy API though. - -2) Approach ------------ - -To convert the MMC subsystem to a proper driver model, the "struct mmc" -structure will have to be properly split in the first place. The result will -consist of multiple parts, first will be the structure defining operations -provided by the MMC driver: - -struct mmc_driver_ops { - /* Function used to submit command to the card */ - int (*send_cmd)(struct mmc *mmc, - struct mmc_cmd *cmd, struct mmc_data *data); - /* DRIVER: Function used to configure the host */ - void (*set_ios)(struct mmc *mmc); - /* Function used to initialize the host */ - int (*init)(struct mmc *mmc); - /* Function used to report the status of Card Detect pin */ - int (*getcd)(struct mmc *mmc); -} - -The second part will define the parameters of the MMC driver: - -struct mmc_driver_params { - /* Voltages the host bus can provide */ - uint32_t voltages; - /* Minimum frequency the host bus can provide */ - uint32_t f_min; - /* Maximum frequency the host bus can provide */ - uint32_t f_max; - /* MMC bus capabilities */ - uint32_t host_caps; - /* - * Maximum amount of blocks sent during multiblock xfer, - * set to 0 to autodetect. - */ - uint32_t b_max; -} - -And finally, the internal per-card data of the MMC subsystem core: - -struct mmc_card_props { - /* Version of the card */ - uint32_t version; - /* Test if the driver was already initializes */ - bool has_init; - /* Is the card SDHC */ - bool high_capacity; - /* Actual width of the bus used by the current card */ - uint8_t bus_width; - /* Capabilities of the card */ - uint32_t card_caps; - /* Configuration and ID data retrieved from the card */ - uint32_t ocr; - uint32_t scr[2]; - uint32_t csd[4]; - uint32_t cid[4]; - uint16_t rca; - /* Partition configuration */ - uint8_t part_config; - /* Number of partitions */ - uint8_t part_num; - /* Transmission speed */ - uint32_t tran_speed; - /* Read block length */ - uint32_t read_bl_len; - /* Write block length */ - uint32_t write_bl_len; - /* Erase group size */ - uint32_t erase_grp_size; - /* Capacity of the card */ - uint64_t capacity; - /* Descriptor of this block device */ - block_dev_desc_t block_dev; -} - -The probe() function will then register the MMC driver by calling: - - mmc_device_register(struct instance *i, struct mmc_driver_ops *o, - struct mmc_driver_params *p); - -The struct mmc_driver_params will have to be dynamic in some cases, but the -driver shouldn't modify it's contents elsewhere than in probe() call. - -Next, since the MMC drivers will now be consistently registered into the driver -tree from board file, the functions "board_mmc_init()" and "cpu_mmc_init()" will -disappear altogether. - -As for the legacy drivers, these will either be converted or removed altogether. - -III) Analysis of in-tree drivers --------------------------------- - - arm_pl180_mmci.c - ---------------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - atmel_mci.c - ----------- - This driver uses the legacy API and should be removed unless converted. It is - probably possbible to replace this driver with gen_atmel_mci.c . No conversion - will be done on this driver. - - bfin_sdh.c - ---------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - davinci_mmc.c - ------------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - fsl_esdhc.c - ----------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple, unless some problem appears due to the FDT - component of the driver. - - ftsdc010_esdhc.c - ---------------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - gen_atmel_mci.c - --------------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - mmc_spi.c - --------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - mv_sdhci.c - ---------- - This is a component of the SDHCI support, allowing it to run on Marvell - Kirkwood chip. It is probable the SDHCI support will have to be modified to - allow calling functions from this file based on information passed via - platform_data. - - mxcmmc.c - -------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - mxsmmc.c - -------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - omap_hsmmc.c - ------------ - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - pxa_mmc.c - --------- - This driver uses the legacy API and is written in a severely ad-hoc manner. - This driver will be removed in favor of pxa_mmc_gen.c, which is proved to work - better and is already well tested. No conversion will be done on this driver - anymore. - - pxa_mmc_gen.c - ------------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - s5p_mmc.c - --------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - sdhci.c - ------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple, though it'd be necessary to modify this driver - to also support the Kirkwood series and probably also Tegra series of CPUs. - See the respective parts of this section for details. - - sh_mmcif.c - ---------- - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. - - tegra2_mmc.c - ------------ - Follows the new API and also has a good encapsulation of the whole driver. The - conversion here will be simple. diff --git a/doc/driver-model/UDM-net.txt b/doc/driver-model/UDM-net.txt deleted file mode 100644 index 097ed69..0000000 --- a/doc/driver-model/UDM-net.txt +++ /dev/null @@ -1,428 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Net system analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-03-03 - -I) Overview ------------ - -The networking subsystem already supports multiple devices. Therefore the -conversion shall not be very hard. - -The network subsystem is operated from net/eth.c, which tracks all registered -ethernet interfaces and calls their particular functions registered via -eth_register(). - -The eth_register() is called from the network driver initialization function, -which in turn is called most often either from "board_net_init()" or -"cpu_net_init()". This function has one important argument, which is the -"struct eth_device", defined at include/net.h: - -struct eth_device { - /* DRIVER: Name of the device */ - char name[NAMESIZE]; - /* DRIVER: MAC address */ - unsigned char enetaddr[6]; - /* DRIVER: Register base address */ - int iobase; - /* CORE: state of the device */ - int state; - - /* DRIVER: Device initialization function */ - int (*init) (struct eth_device*, bd_t*); - /* DRIVER: Function for sending packets */ - int (*send) (struct eth_device*, volatile void* packet, int length); - /* DRIVER: Function for receiving packets */ - int (*recv) (struct eth_device*); - /* DRIVER: Function to cease operation of the device */ - void (*halt) (struct eth_device*); - /* DRIVER: Function to send multicast packet (OPTIONAL) */ - int (*mcast) (struct eth_device*, u32 ip, u8 set); - /* DRIVER: Function to change ethernet MAC address */ - int (*write_hwaddr) (struct eth_device*); - /* CORE: Next device in the linked list of devices managed by net core */ - struct eth_device *next; - /* CORE: Device index */ - int index; - /* DRIVER: Driver's private data */ - void *priv; -}; - -This structure defines the particular driver, though also contains elements that -should not be exposed to the driver, like core state. - -Small, but important part of the networking subsystem is the PHY management -layer, whose drivers are contained in drivers/net/phy. These drivers register in -a very similar manner to network drivers, by calling "phy_register()" with the -argument of "struct phy_driver": - -struct phy_driver { - /* DRIVER: Name of the PHY driver */ - char *name; - /* DRIVER: UID of the PHY driver */ - unsigned int uid; - /* DRIVER: Mask for UID of the PHY driver */ - unsigned int mask; - /* DRIVER: MMDS of the PHY driver */ - unsigned int mmds; - /* DRIVER: Features the PHY driver supports */ - u32 features; - /* DRIVER: Initialize the PHY hardware */ - int (*probe)(struct phy_device *phydev); - /* DRIVER: Reconfigure the PHY hardware */ - int (*config)(struct phy_device *phydev); - /* DRIVER: Turn on the PHY hardware, allow it to send/receive */ - int (*startup)(struct phy_device *phydev); - /* DRIVER: Turn off the PHY hardware */ - int (*shutdown)(struct phy_device *phydev); - /* CORE: Allows this driver to be part of list of drivers */ - struct list_head list; -}; - -II) Approach ------------- - -To convert the elements of network subsystem to proper driver model method, the -"struct eth_device" will have to be split into multiple components. The first -will be a structure defining the driver operations: - -struct eth_driver_ops { - int (*init)(struct instance*, bd_t*); - int (*send)(struct instance*, void *packet, int length); - int (*recv)(struct instance*); - void (*halt)(struct instance*); - int (*mcast)(struct instance*, u32 ip, u8 set); - int (*write_hwaddr)(struct instance*); -}; - -Next, there'll be platform data which will be per-driver and will replace the -"priv" part of "struct eth_device". Last part will be the per-device core state. - -With regards to the PHY part of the API, the "struct phy_driver" is almost ready -to be used with the new driver model approach. The only change will be the -replacement of per-driver initialization functions and removal of -"phy_register()" function in favor or driver model approach. - -III) Analysis of in-tree drivers --------------------------------- - - drivers/net/4xx_enet.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/altera_tse.c - ------------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/armada100_fec.c - --------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/at91_emac.c - ----------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ax88180.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ax88796.c - --------------------- - - This file contains a components of the NE2000 driver, implementing only - different parts on the NE2000 clone AX88796. This being no standalone driver, - no conversion will be done here. - - drivers/net/bfin_mac.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/calxedaxgmac.c - -------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/cs8900.c - -------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/davinci_emac.c - -------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/dc2114x.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/designware.c - ------------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/dm9000x.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/dnet.c - ------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/e1000.c - ------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/e1000_spi.c - ----------------------- - - Driver for the SPI bus integrated on the Intel E1000. This is not part of the - network stack. - - drivers/net/eepro100.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/enc28j60.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ep93xx_eth.c - ------------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ethoc.c - ------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/fec_mxc.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/fsl_mcdmafec.c - -------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/fsl_mdio.c - ---------------------- - - This file contains driver for FSL MDIO interface, which is not part of the - networking stack. - - drivers/net/ftgmac100.c - ----------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ftmac100.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/greth.c - ------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/inca-ip_sw.c - ------------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ks8695eth.c - ----------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/lan91c96.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/macb.c - ------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/mcffec.c - -------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/mcfmii.c - -------------------- - - This file contains MII interface driver for MCF FEC. - - drivers/net/mpc512x_fec.c - ------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/mpc5xxx_fec.c - ------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/mvgbe.c - ------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/natsemi.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/ne2000_base.c - ------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. This driver contains the core - implementation of NE2000, which needs a few external functions, implemented by - AX88796, NE2000 etc. - - drivers/net/ne2000.c - -------------------- - - This file implements external functions necessary for native NE2000 compatible - networking card to work. - - drivers/net/netconsole.c - ------------------------ - - This is actually an STDIO driver. - - drivers/net/ns8382x.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/pcnet.c - ------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/plb2800_eth.c - ------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/rtl8139.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/rtl8169.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/sh_eth.c - -------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/smc91111.c - ---------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/smc911x.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/tsec.c - ------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/tsi108_eth.c - ------------------------ - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/uli526x.c - --------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/vsc7385.c - --------------------- - - This is a driver that only uploads firmware to a switch. This is not subject - of conversion. - - drivers/net/xilinx_axi_emac.c - ----------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. - - drivers/net/xilinx_emaclite.c - ----------------------------- - - This driver uses the standard new networking API, therefore there should be no - obstacles throughout the conversion process. diff --git a/doc/driver-model/UDM-pci.txt b/doc/driver-model/UDM-pci.txt deleted file mode 100644 index 1dce99d..0000000 --- a/doc/driver-model/UDM-pci.txt +++ /dev/null @@ -1,253 +0,0 @@ -The U-Boot Driver Model Project -=============================== -PCI subsystem analysis -====================== - -Pavel Herrmann <morpheus.ibis@gmail.com> -2012-03-17 - -I) Overview ------------ - - U-Boot already supports multiple PCI busses, stored in a linked-list of - pci_controller structures. This structure contains generic driver data, bus - interface operations and private data for the driver. - - Bus interface operations for PCI are (names are self-explanatory): - - read_byte() - read_word() - read_dword() - write_byte() - write_word() - write_dword() - - Each driver has to implement dword operations, and either implement word and - byte operations, or use shared $operation_config_$type_via_dword (eg. - read_config_byte_via_dword and similar) function. These functions are used - for config space I/O (read_config_dword and similar functions of the PCI - subsystem), which is used to configure the connected devices for standard MMIO - operations. All data transfers by respective device drivers are then done by - MMIO - - Each driver also defines a separate init function, which has unique symbol - name, and thus more drivers can be compiled in without colliding. This init - function is typically called from pci_init_board(), different for each - particular board. - - Some boards also define a function called fixup_irq, which gets called after - scanning the PCI bus for devices, and should dismiss any interrupts. - - Several drivers are also located in arch/ and should be moved to drivers/pci. - -II) Approach ------------- - - The pci_controller structure needs to be broken down to fit the new driver - model. Due to a large number of members, this will be done through three - distinct accessors, one for memory regions, one for config table and one for - everything else. That will make the pci_ops structure look like this: - - struct pci_ops { - int (*read_byte)(struct instance *bus, pci_dev_t *dev, int addr, - u8 *buf); - int (*read_word)(struct instance *bus, pci_dev_t *dev, int addr, - u16 *buf); - int (*read_dword)(struct instance *bus, pci_dev_t *dev, int addr, - u32 *buf); - int (*write_byte)(struct instance *bus, pci_dev_t *dev, int addr, - u8 val); - int (*write_byte)(struct instance *bus, pci_dev_t *dev, int addr, - u8 val); - int (*write_dword)(struct instance *bus, pci_dev_t *dev, int addr, - u32 val); - void (*fixup_irq)(struct instance *bus, pci_dev_t *dev); - struct pci_region* (*get_region)(struct instance *, uint num); - struct pci_config_table* (*get_cfg_table)(struct instance *bus); - uint (*get_option)(struct instance * bus, enum pci_option_code op); - } - - enum pci_option_code { - PCI_OPT_BUS_NUMBER=0, - PCI_OPT_REGION_COUNT, - PCI_OPT_INDIRECT_TYPE, - PCI_OPT_AUTO_MEM, - PCI_OPT_AUTO_IO, - PCI_OPT_AUTO_PREFETCH, - PCI_OPT_AUTO_FB, - PCI_OPT_CURRENT_BUS, - PCI_OPT_CFG_ADDR, - } - - The return value for get_option will be an unsigned integer value for any - option code. If the option currently is a pointer to pci_region, it will - return an index for get_region function. Special case has to be made for - PCI_OPT_CFG_ADDR, which should be interpreted as a pointer, but it is only - used for equality in find_hose_by_cfg_addr, and thus can be returned as an - uint. Other function using cfg_addr value are read/write functions for - specific drivers (especially ops for indirect bridges), and thus have access - to private_data of the driver instance. - - The config table accessor will return a pointer to a NULL-terminated array of - pci_config_table, which is supplied by the board in platform_data, or NULL if - the board didn't specify one. This table is used to override PnP - auto-initialization, or to specific initialization functions for non-PNP - devices. - - Transparent PCI-PCI bridges will get their own driver, and will forward all - operations to operations of their parent bus. This however makes it - impossible to use instances to identify devices, as not all devices will be - directly visible to the respective bus driver. - - Init functions of controller drivers will be moved to their respective - probe() functions, in accordance to the driver model. - - The PCI core will handle all mapping functions currently found in pci.c, as - well as proxy functions for read/write operations of the drivers. The PCI - core will also handle bus scanning and device configuration. - - The PnP helper functions currently in pci_auto.c will also be a part of PCI - core, but they will be exposed only to PCI controller drivers, not to other - device drivers. - - The PCI API for device drivers will remain largely unchanged, most drivers - will require no changes at all, and all modifications will be limited to - changing the pci_controlle into instance*. - -III) Analysis of in-tree drivers --------------------------------- - - A) drivers in drivers/pci/ - -------------------------- - - pci_indirect.c - -------------- - Shared driver for indirect PCI bridges, several CONFIG macros - will - require significant cleanup. - - pci_sh4.c - --------- - Shared init function for SH4 drivers, uses dword for read/write ops. - - pci_sh7751.c - ------------ - Standard driver, uses SH4 shared init. - - pci_sh7780.c - ------------ - Standard driver, uses SH4 shared init. - - tsi108_pci.c - ------------ - Standard driver, uses dword for read/write ops. - - fsl_pci_init.c - -------------- - Driver for PCI and PCI-e, uses indirect functions. - - pci_ftpci100.c - -------------- - Standard driver, uses indirect functions, has separate scan/setup - functions. - - B) driver in arch/ - ------------------ - - x86/lib/pci_type1.c - ------------------- - Standard driver, specifies all read/write functions separately. - - m68k/cpu/mcf5445x/pci.c - ----------------------- - Standard driver, specifies all read/write functions separately. - - m68k/cpu/mcf547x_8x/pci.c - ------------------------- - Standard driver, specifies all read/write functions separately. - - powerpc/cpu/mpc824x/pci.c - ------------------------- - Standard driver, uses indirect functions, does not setup HW. - - powerpc/cpu/mpc8260/pci.c - ------------------------- - Standard driver, uses indirect functions. - - powerpc/cpu/ppc4xx/4xx_pci.c - ---------------------------- - Standard driver, uses indirect functions. - - powerpc/cpu/ppc4xx/4xx_pcie.c - ----------------------------- - PCI-e driver, specifies all read/write functions separately. - - powerpc/cpu/mpc83xx/pci.c - ------------------------- - Standard driver, uses indirect functions. - - powerpc/cpu/mpc83xx/pcie.c - -------------------------- - PCI-e driver, specifies all read/write functions separately. - - powerpc/cpu/mpc5xxx/pci_mpc5200.c - --------------------------------- - Standard driver, uses dword for read/write ops. - - powerpc/cpu/mpc512x/pci.c - ------------------------- - Standard driver, uses indirect functions. - - powerpc/cpu/mpc85xx/pci.c - ------------------------- - Standard driver, uses indirect functions, has two busses. - - C) drivers in board/ - -------------------- - - eltec/elppc/pci.c - ----------------- - Standard driver, uses indirect functions. - - amirix/ap1000/pci.c - ------------------- - Standard driver, specifies all read/write functions separately. - - prodrive/p3mx/pci.c - ------------------- - Standard driver, uses dword for read/write ops, has two busses. - - esd/cpci750/pci.c - ----------------- - Standard driver, uses dword for read/write ops, has two busses. - - esd/common/pci.c - ---------------- - Standard driver, uses dword for read/write ops. - - dave/common/pci.c - ----------------- - Standard driver, uses dword for read/write ops. - - ppmc7xx/pci.c - ------------- - Standard driver, uses indirect functions. - - Marvell/db64360/pci.c - --------------------- - Standard driver, uses dword for read/write ops, has two busses. - - Marvell/db64460/pci.c - --------------------- - Standard driver, uses dword for read/write ops, has two busses. - - evb64260/pci.c - -------------- - Standard driver, uses dword for read/write ops, has two busses. - - armltd/integrator/pci.c - ----------------------- - Standard driver, specifies all read/write functions separately. - - All drivers will be moved to drivers/pci. Several drivers seem - similar/identical, especially those located under board, and may be merged - into one. diff --git a/doc/driver-model/UDM-pcmcia.txt b/doc/driver-model/UDM-pcmcia.txt deleted file mode 100644 index d55e89d..0000000 --- a/doc/driver-model/UDM-pcmcia.txt +++ /dev/null @@ -1,78 +0,0 @@ -The U-Boot Driver Model Project -=============================== -PCMCIA analysis -=============== -Viktor Krivak <viktor.krivak@gmail.com> -2012-03-17 - -I) Overview ------------ - - U-boot implements only 2 methods to interoperate with pcmcia. One to turn - device on and other to turn device off. Names of these methods are usually - pcmcia_on() and pcmcia_off() without any parameters. Some files in driver - directory implements only internal API. These methods aren't used outside - driver directory and they are not converted to new driver model. - -II) Approach ------------ - - 1) New API - ---------- - - Current API is preserved and all internal methods are hiden. - - struct ops { - void (*pcmcia_on)(struct instance *i); - void (*pcmcia_off)(struct instance *i); - } - - 2) Conversion - ------------- - - In header file pcmcia.h are some other variables which are used for - additional configuration. But all have to be moved to platform data or to - specific driver implementation. - - 3) Platform data - ---------------- - - Many boards have custom implementation of internal API. Pointers to these - methods are stored in platform_data. But the most implementations for Intel - 82365 and compatible PC Card controllers and Yenta-compatible - PCI-to-CardBus controllers implement whole API per board. In these cases - pcmcia_on() and pcmcia_off() behave only as wrappers and call specific - board methods. - -III) Analysis of in-tree drivers --------------------------------- - - i82365.c - -------- - Driver methods have different name i82365_init() and i82365_exit but - all functionality is the same. Board files board/atc/ti113x.c and - board/cpc45/pd67290.c use their own implementation of these method. - In this case all methods in driver behave only as wrappers. - - marubun_pcmcia.c - ---------------- - Meets standard API behaviour. Simple conversion. - - mpc8xx_pcmcia.c - --------------- - Meets standard API behaviour. Simple conversion. - - rpx_pcmcia.c - ------------ - Implements only internal API used in other drivers. Non of methods - implemented here are used outside driver model. - - ti_pci1410a.c - ------------- - Has different API but methods in this file are never called. Probably - dead code. - - tqm8xx_pcmcia.c - --------------- - Implements only internal API used in other drivers. Non of methods - implemented here are used outside driver model. diff --git a/doc/driver-model/UDM-power.txt b/doc/driver-model/UDM-power.txt deleted file mode 100644 index 666d553..0000000 --- a/doc/driver-model/UDM-power.txt +++ /dev/null @@ -1,88 +0,0 @@ -The U-Boot Driver Model Project -=============================== -POWER analysis -============== -Viktor Krivak <viktor.krivak@gmail.com> -2012-03-09 - -I) Overview ------------ - - 1) Actual state - --------------- - - At this moment power doesn't contain API. There are many methods for - initialization of some board specific functions but only few does what is - expected. Basically only one file contains something meaningful for this - driver. - - 2) Current implementation - ------------------------- - - In file twl6030.c are methods twl6030_stop_usb_charging() and - twl6030_start_usb_charging() for start and stop charging from USB. There are - also methods to get information about battery state and initialization of - battery charging. Only these methods are used in converted API. - - -II) Approach ------------- - - 1) New API - ---------- - - New API implements only functions specific for managing power. All board - specific init methods are moved to other files. Name of methods are - self-explanatory. - - struct ops { - void (*start_usb_charging)(struct instance *i); - void (*stop_usb_charging)(struct instance *i); - int (*get_battery_current)(struct instance *i); - int (*get_battery_voltage)(struct instance *i); - void (*init_battery_charging)(struct instance *i); - } - - 2) Conversions of other methods - ------------------------------- - - Methods that can't be converted to new API are moved to board file or to - special file for board hacks. - -III) Analysis of in-tree drivers --------------------------------- - - ftpmu010.c - ---------- - All methods of this file are moved to another location. - void ftpmu010_32768osc_enable(void): Move to boards hacks - void ftpmu010_mfpsr_select_dev(unsigned int dev): Move to board file - arch/nds32/lib/board.c - void ftpmu010_mfpsr_diselect_dev(unsigned int dev): Dead code - void ftpmu010_dlldis_disable(void): Dead code - void ftpmu010_sdram_clk_disable(unsigned int cr0): Move to board file - arch/nds32/lib/board.c - void ftpmu010_sdramhtc_set(unsigned int val): Move to board file - arch/nds32/lib/board.c - - twl4030.c - --------- - All methods of this file are moved to another location. - void twl4030_power_reset_init(void): Move to board hacks - void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val, u8 dev_grp, - u8 dev_grp_sel): Move to board hacks - void twl4030_power_init(void): Move to board hacks - void twl4030_power_mmc_init(void): Move to board hacks - - twl6030.c - --------- - Some methods are converted to new API and rest are moved to another location. - void twl6030_stop_usb_charging(void): Convert to new API - void twl6030_start_usb_charging(void): Convert to new API - int twl6030_get_battery_current(void): Convert to new API - int twl6030_get_battery_voltage(void): Convert to new API - void twl6030_init_battery_charging(void): Convert to new API - void twl6030_power_mmc_init(): Move to board file - drivers/mmc/omap_hsmmc.c - void twl6030_usb_device_settings(): Move to board file - drivers/usb/musb/omap3.c diff --git a/doc/driver-model/UDM-rtc.txt b/doc/driver-model/UDM-rtc.txt deleted file mode 100644 index 3640d24..0000000 --- a/doc/driver-model/UDM-rtc.txt +++ /dev/null @@ -1,253 +0,0 @@ -============================= -RTC device subsystem analysis -============================= - -Tomas Hlavacek <tmshlvck@gmail.com> -2012-03-10 - -I) Overview ------------ - -U-Boot currently implements one common API for RTC devices. The interface -is defined in include/rtc.h and comprises of functions and structures: - - struct rtc_time { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; - }; - - int rtc_get (struct rtc_time *); - int rtc_set (struct rtc_time *); - void rtc_reset (void); - -The functions are implemented by a proper device driver in drivers/rtc -directory and the driver to be compiled in is selected in a Makefile. -Drivers are mutually exclusive. - -Drivers depends on date code in drivers/rtc/date.c and naturally on board -specific data. - -II) Approach ------------- - - 1) New API - ---------- - In the UDM each rtc driver would register itself by a function - - int rtc_device_register(struct instance *i, - struct rtc_device_ops *o); - - The structure being defined as follows: - - struct rtc_device_ops { - int (*get_time)(struct instance *i, struct rtc_time *t); - int (*set_time)(struct instance *i, struct rtc_time *t); - int (*reset)(struct instance *i); - }; - - - 2) Conversion thougths - ---------------------- - U-Boot RTC drivers exports the same functions and therefore the conversion - of the drivers is straight-forward. There is no initialization needed. - - -III) Analysis of in-tree drivers --------------------------------- - - drivers/rtc/rv3029.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/s3c24x0_rtc.c - ------------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/pt7c4338.c - ---------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mvrtc.c - ------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ftrtc010.c - ---------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mpc5xxx.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds164x.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/rs5c372.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/m41t94.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mc13xxx-rtc.c - ------------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mcfrtc.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/davinci.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/rx8025.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/bfin_rtc.c - ---------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/m41t62.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1306.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mpc8xx.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds3231.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds12887.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1302.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1374.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds174x.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/m41t60.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/m48t35ax.c - ---------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/pl031.c - ------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/x1205.c - ------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/m41t11.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/pcf8563.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mk48t59.c - --------------------- - Macros needs cleanup. Besides that the driver is standard rtc. - Simple conversion is possible. - - - drivers/rtc/mxsrtc.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1307.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1556.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/rtc4543.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/ds1337.c - -------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/isl1208.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/max6900.c - --------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/mc146818.c - ---------------------- - The driver is standard rtc. Simple conversion is possible. - - - drivers/rtc/at91sam9_rtt.c - -------------------------- - The driver is standard rtc. Simple conversion is possible. diff --git a/doc/driver-model/UDM-serial.txt b/doc/driver-model/UDM-serial.txt deleted file mode 100644 index ed804a8..0000000 --- a/doc/driver-model/UDM-serial.txt +++ /dev/null @@ -1,155 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Serial I/O analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-20 - -I) Overview ------------ - -The serial port support currently requires the driver to export the following -functions: - - serial_putc() ...... Output a character - serial_puts() ...... Output string, often done using serial_putc() - serial_tstc() ...... Test if incoming character is in a buffer - serial_getc() ...... Retrieve incoming character - serial_setbrg() .... Configure port options - serial_init() ...... Initialize the hardware - -The simpliest implementation, supporting only one port, simply defines these six -functions and calls them. Such calls are scattered all around U-Boot, especiall -serial_putc(), serial_puts(), serial_tstc() and serial_getc(). The serial_init() -and serial_setbrg() are often called from platform-dependent places. - -It's important to consider current implementation of CONFIG_SERIAL_MULTI though. -This resides in common/serial.c and behaves as a multiplexer for serial ports. -This, by calling serial_assign(), allows user to switch I/O from one serial port -to another. Though the environmental variables "stdin", "stdout", "stderr" -remain set to "serial". - -These variables are managed by the IOMUX. This resides in common/iomux.c and -manages all console input/output from U-Boot. For serial port, only one IOMUX is -always registered, called "serial" and the switching of different serial ports -is done by code in common/serial.c. - -On a final note, it's important to mention function default_serial_console(), -which is platform specific and reports the default serial console for the -platform, unless proper environment variable overrides this. - -II) Approach ------------- - -Drivers not using CONFIG_SERIAL_MULTI already will have to be converted to -similar approach. The probe() function of a driver will call a function -registering the driver with a STDIO subsystem core, stdio_device_register(). - -The serial_init() function will now be replaced by probe() function of the -driver, the rest of the components of the driver will be converted to standard -STDIO driver calls. See [ UDM-stdio.txt ] for details. - -The serial_setbrg() function depends on global data pointer. This is wrong, -since there is likely to be user willing to configure different baudrate on two -different serial ports. The function will be replaced with STDIO's "conf()" -call, with STDIO_CONFIG_SERIAL_BAUDRATE argument. - -III) Analysis of in-tree drivers --------------------------------- - - altera_jtag_uart.c - ------------------ - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - altera_uart.c - ------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - arm_dcc.c - --------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible, unless used - with CONFIG_ARM_DCC_MULTI. Then it registers another separate IOMUX. - - atmel_usart.c - ------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - mcfuart.c - --------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - ns16550.c - --------- - This driver seems complicated and certain consideration will need to be made - during conversion. This driver is implemented in very universal manner, - therefore it'll be necessary to properly design it's platform_data. - - opencores_yanu.c - ---------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - sandbox.c - --------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial.c - -------- - This is a complementary part of NS16550 UART driver, see above. - - serial_imx.c - ------------ - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. This driver - might be removed in favor of serial_mxc.c . - - serial_ks8695.c - --------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_max3100.c - ---------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_mxc.c - ------------ - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_pl01x.c - -------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible, though this - driver in fact contains two drivers in total. - - serial_pxa.c - ------------ - This driver is a bit complicated, but due to clean support for - CONFIG_SERIAL_MULTI, there are no expected obstructions throughout the - conversion process. - - serial_s3c24x0.c - ---------------- - This driver, being quite ad-hoc might need some work to bring back to shape. - - serial_s5p.c - ------------ - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_sa1100.c - --------------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_sh.c - ----------- - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - serial_xuartlite.c - ------------------ - No support for CONFIG_SERIAL_MULTI. Simple conversion possible. - - usbtty.c - -------- - This driver seems very complicated and entangled with USB framework. The - conversion might be complicated here. - - arch/powerpc/cpu/mpc512x/serial.c - --------------------------------- - This driver supports CONFIG_SERIAL_MULTI. This driver will need to be moved to - proper place. diff --git a/doc/driver-model/UDM-spi.txt b/doc/driver-model/UDM-spi.txt deleted file mode 100644 index 9ba0f84..0000000 --- a/doc/driver-model/UDM-spi.txt +++ /dev/null @@ -1,200 +0,0 @@ -The U-Boot Driver Model Project -=============================== -SPI analysis -============ -Viktor Krivak <viktor.krivak@gmail.com> -2012-03-03 - -I) Overview ------------ - - 1) The SPI driver - ----------------- - - At this moment U-Boot provides standard API that consist of 7 functions: - - void spi_init(void); - struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode); - void spi_free_slave(struct spi_slave *slave); - int spi_claim_bus(struct spi_slave *slave); - void spi_release_bus(struct spi_slave *slave); - int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags); - int spi_cs_is_valid(unsigned int bus, unsigned int cs); - void spi_cs_activate(struct spi_slave *slave); - void spi_cs_deactivate(struct spi_slave *slave); - void spi_set_speed(struct spi_slave *slave, uint hz); - - Method spi_init() is usually empty. All necessary configuration are sets by - spi_setup_slave(). But this configuration is usually stored only in memory. - No real hardware sets are made. All hardware settings are provided by method - spi_claim_bus(). This method claims the bus and it can't be claimed again - until it's release. That's mean all calls of method spi_claim_bus() will - fail. But lots of cpu implementation don't meet this behaviour. - Method spi_release_bus() does exact opposite. It release bus directly by - some hardware sets. spi_free_slave() only free memory allocated by - spi_setup_slave(). Method spi_xfer() do actually read and write operation - throw specified bus and cs. Other methods are self explanatory. - - 2) Current limitations - ---------------------- - - Theoretically at this moment api allows use more then one bus per device at - the time. But in real this can be achieved only when all buses have their - own base addresses in memory. - - -II) Approach ------------- - - 1) Claiming bus - --------------- - - The current api cannot be used because struct spi_slave have to be in - private data. In that case user are prohibited to use different bus on one - device at same time. But when base memory address for bus are different. - It's possible make more instance of this driver. Otherwise it can't can be - done because of hardware limitation. - - 2) API change - ------------- - - Method spi_init() is moved to probe. Methods spi_setup_slave() and - spi_claim_bus() are joined to one method. This method checks if desired bus - exists and is available then configure necessary hardware and claims bus. - Method spi_release_bus() and spi_free_slave() are also joined to meet this - new approach. Other function remain same. Only struct spi_slave was change - to instance. - - struct ops { - int (*spi_request_bus)(struct instance *i, unsigned int bus, - unsigned int cs, unsigned int max_hz, - unsigned int mode); - void (*spi_release_bus)(struct instance *i); - int (*spi_xfer) (struct instance *i, unsigned int bitlen, - const void *dout, void *din, unsigned long flags); - int (*spi_cs_is_valid)(struct instance *i, unsigned int bus, - unsigned int cs); - void (*spi_cs_activate)(struct instance *i); - void (*spi_cs_deactivate)(struct instance *i); - void (*spi_set_speed)(struct instance *i, uint hz); - } - - 3) Legacy API - ------------- - - To easy conversion of the whole driver. Original and new api can exist next - to each other. New API is designed to be only a wrapper that extracts - necessary information from private_data and use old api. When driver can - use more than one bus at the time. New API require multiple instance. One - for each bus. In this case spi_slave have to be copied in each instance. - - 4) Conversion TIME-LINE - ----------------------- - - To prevent build corruption api conversion have to be processed in several - independent steps. In first step all old API methods are renamed. After that - new API and core function are implemented. Next step is conversion of all - board init methods to set platform data. After all these steps it is possible - to start conversion of all remaining calls. This procedure guarantees that - build procedure and binaries are never broken. - -III) Analysis of in-tree drivers --------------------------------- - - altera_spi.c - ------------ - All methods have designated structure. Simple conversion possible. - - andes_spi.c - ----------- - All methods have designated structure. Simple conversion possible. - - andes_spi.h - ----------- - Support file for andes_spi.c. No conversion is needed. - - armada100_spi.c - --------------- - All methods have designated structure. Simple conversion possible. - - atmel_dataflash_spi.c - --------------------- - Wrong placement. Will be moved to another location. - - atmel_spi.c - ----------- - Supports more than one bus. Need some minor change. - - atmel_spi.h - ----------- - Support file for andes_spi.c. No conversion is needed. - - bfin_spi.c - ---------- - Supports more than one bus. Need some minor change. - - cf_spi.c - -------- - Cooperate with some cpu specific methods from other files. Hard conversion. - - davinci_spi.c - ------------- - All methods have designated structure. Simple conversion possible. - - davinci_spi.h - ------------- - Support file for davinci_spi.h. No conversion is needed. - - fsl_espi.c - ---------- - All methods have designated structure. Simple conversion possible. - - kirkwood_spi.c - -------------- - All methods have designated structure. Simple conversion possible. - - mpc8xxx_spi.c - ------------- - All methods have designated structure. Simple conversion possible. - - mpc52xx_spi.c - ------------- - All methods have designated structure. Simple conversion possible. - - mxc_spi.c - --------- - All methods have designated structure. Simple conversion possible. - - mxs_spi.c - --------- - All methods have designated structure. Simple conversion possible. - - oc_tiny_spi.c - ------------- - Supports more than one bus. Need some minor change. - - omap3_spi.c - ----------- - Supports more than one bus. Need some minor change. - - omap3_spi.h - ----------- - Support file for omap3_spi.c. No conversion is needed. - - sh_spi.c - -------- - All methods have designated structure. Simple conversion possible. - - sh_spi.h - -------- - Support file for sh_spi.h. No conversion is needed. - - soft_spi.c - ---------- - Use many board specific method linked from other files. Need careful debugging. - - tegra2_spi.c - ------------ - Some hardware specific problem when releasing bus. diff --git a/doc/driver-model/UDM-stdio.txt b/doc/driver-model/UDM-stdio.txt deleted file mode 100644 index 156627b..0000000 --- a/doc/driver-model/UDM-stdio.txt +++ /dev/null @@ -1,191 +0,0 @@ -The U-Boot Driver Model Project -=============================== -I/O system analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-20 - -I) Overview ------------ - -The console input and output is currently done using the STDIO subsystem in -U-Boot. The design of this subsystem is already flexible enough to be easily -converted to new driver model approach. Minor changes will need to be done -though. - -Each device that wants to register with STDIO subsystem has to define struct -stdio_dev, defined in include/stdio_dev.h and containing the following fields: - -struct stdio_dev { - int flags; /* Device flags: input/output/system */ - int ext; /* Supported extensions */ - char name[16]; /* Device name */ - -/* GENERAL functions */ - - int (*start) (void); /* To start the device */ - int (*stop) (void); /* To stop the device */ - -/* OUTPUT functions */ - - void (*putc) (const char c); /* To put a char */ - void (*puts) (const char *s); /* To put a string (accelerator) */ - -/* INPUT functions */ - - int (*tstc) (void); /* To test if a char is ready... */ - int (*getc) (void); /* To get that char */ - -/* Other functions */ - - void *priv; /* Private extensions */ - struct list_head list; -}; - -Currently used flags are DEV_FLAGS_INPUT, DEV_FLAGS_OUTPUT and DEV_FLAGS_SYSTEM, -extensions being only one, the DEV_EXT_VIDEO. - -The private extensions are now used as a per-device carrier of private data and -finally list allows this structure to be a member of linked list of STDIO -devices. - -The STDIN, STDOUT and STDERR routing is handled by environment variables -"stdin", "stdout" and "stderr". By configuring the variable to the name of a -driver, functions of such driver are called to execute that particular -operation. - -II) Approach ------------- - - 1) Similarity of serial, video and keyboard drivers - --------------------------------------------------- - - All of these drivers can be unified under the STDIO subsystem if modified - slightly. The serial drivers basically define both input and output functions - and need function to configure baudrate. The keyboard drivers provide only - input. On the other hand, video drivers provide output, but need to be - configured in certain way. This configuration might be dynamic, therefore the - STDIO has to be modified to provide such flexibility. - - 2) Unification of serial, video and keyboard drivers - ---------------------------------------------------- - - Every STDIO device would register a structure containing operation it supports - with the STDIO core by calling: - - int stdio_device_register(struct instance *i, struct stdio_device_ops *o); - - The structure being defined as follows: - - struct stdio_device_ops { - void (*putc)(struct instance *i, const char c); - void (*puts)(struct instance *i, const char *s); /* OPTIONAL */ - - int (*tstc)(struct instance *i); - int (*getc)(struct instance *i); - - int (*init)(struct instance *i); - int (*exit)(struct instance *i); - int (*conf)(struct instance *i, enum stdio_config c, const void *data); - }; - - The "putc()" function will emit a character, the "puts()" function will emit a - string. If both of these are set to NULL, the device is considered STDIN only, - aka input only device. - - The "getc()" retrieves a character from a STDIN device, while "tstc()" tests - if there is a character in the buffer of STDIN device. In case these two are - set to NULL, this device is STDOUT / STDERR device. - - Setting all "putc()", "puts()", "getc()" and "tstc()" calls to NULL isn't an - error condition, though such device does nothing. By instroducing tests for - these functions being NULL, the "flags" and "ext" fields from original struct - stdio_dev can be eliminated. - - The "init()" and "exit()" calls are replacement for "start()" and "exit()" - calls in the old approach. The "priv" part of the old struct stdio_dev will be - replaced by common private data in the driver model and the struct list_head - list will be eliminated by introducing common STDIO core, that tracks all the - STDIO devices. - - Lastly, the "conf()" call will allow the user to configure various options of - the driver. The enum stdio_config contains all possible configuration options - available to the STDIO devices, const void *data being the argument to be - configured. Currently, the enum stdio_config will contain at least the - following options: - - enum stdio_config { - STDIO_CONFIG_SERIAL_BAUDRATE, - }; - - 3) Transformation of stdio routing - ---------------------------------- - - By allowing multiple instances of drivers, the environment variables "stdin", - "stdout" and "stderr" can no longer be set to the name of the driver. - Therefore the STDIO core, tracking all of the STDIO devices in the system will - need to have a small amount of internal data for each device: - - struct stdio_device_node { - struct instance *i; - struct stdio_device_ops *ops; - uint8_t id; - uint8_t flags; - struct list_head list; - } - - The "id" is the order of the instance of the same driver. The "flags" variable - allows multiple drivers to be used at the same time and even for different - purpose. The following flags will be defined: - - STDIO_FLG_STDIN ..... This device will be used as an input device. All input - from all devices with this flag set will be received - and passed to the upper layers. - STDIO_FLG_STDOUT .... This device will be used as an output device. All - output sent to stdout will be routed to all devices - with this flag set. - STDIO_FLG_STDERR .... This device will be used as an standard error output - device. All output sent to stderr will be routed to - all devices with this flag set. - - The "list" member of this structure allows to have a linked list of all - registered STDIO devices. - -III) Analysis of in-tree drivers --------------------------------- - -For in-depth analysis of serial port drivers, refer to [ UDM-serial.txt ]. -For in-depth analysis of keyboard drivers, refer to [ UDM-keyboard.txt ]. -For in-depth analysis of video drivers, refer to [ UDM-video.txt ]. - - arch/blackfin/cpu/jtag-console.c - -------------------------------- - This driver is a classic STDIO driver, no problem with conversion is expected. - - board/mpl/pati/pati.c - --------------------- - This driver registers with the STDIO framework, though it uses a lot of ad-hoc - stuff which will need to be sorted out. - - board/netphone/phone_console.c - ------------------------------ - This driver is a classic STDIO driver, no problem with conversion is expected. - - drivers/net/netconsole.c - ------------------------ - This driver is a classic STDIO driver, no problem with conversion is expected. - -IV) Other involved files (To be removed) ----------------------------------------- - -common/cmd_console.c -common/cmd_log.c -common/cmd_terminal.c -common/console.c -common/fdt_support.c -common/iomux.c -common/lcd.c -common/serial.c -common/stdio.c -common/usb_kbd.c -doc/README.iomux diff --git a/doc/driver-model/UDM-tpm.txt b/doc/driver-model/UDM-tpm.txt deleted file mode 100644 index 0beff4a..0000000 --- a/doc/driver-model/UDM-tpm.txt +++ /dev/null @@ -1,48 +0,0 @@ -The U-Boot Driver Model Project -=============================== -TPM system analysis -=================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-23 - -I) Overview ------------ - -There is currently only one TPM chip driver available and therefore the API -controlling it is very much based on this. The API is very simple: - - int tis_open(void); - int tis_close(void); - int tis_sendrecv(const u8 *sendbuf, size_t send_size, - u8 *recvbuf, size_t *recv_len); - -The command operating the TPM chip only provides operations to send and receive -bytes from the chip. - -II) Approach ------------- - -The API can't be generalised too much considering there's only one TPM chip -supported. But it's a good idea to split the tis_sendrecv() function in two -functions. Therefore the new API will use register the TPM chip by calling: - - tpm_device_register(struct instance *i, const struct tpm_ops *ops); - -And the struct tpm_ops will contain the following members: - - struct tpm_ops { - int (*tpm_open)(struct instance *i); - int (*tpm_close)(struct instance *i); - int (*tpm_send)(const uint8_t *buf, const size_t size); - int (*tpm_recv)(uint8_t *buf, size_t *size); - }; - -The behaviour of "tpm_open()" and "tpm_close()" will basically copy the -behaviour of "tis_open()" and "tis_close()". The "tpm_send()" will be based on -the "tis_senddata()" and "tis_recv()" will be based on "tis_readresponse()". - -III) Analysis of in-tree drivers --------------------------------- - -There is only one in-tree driver present, the "drivers/tpm/generic_lpc_tpm.c", -which will be simply converted as outlined in previous chapter. diff --git a/doc/driver-model/UDM-twserial.txt b/doc/driver-model/UDM-twserial.txt deleted file mode 100644 index 5f2c5a3..0000000 --- a/doc/driver-model/UDM-twserial.txt +++ /dev/null @@ -1,47 +0,0 @@ -================================== -TWserial device subsystem analysis -================================== - -Tomas Hlavacek<tmshlvck@gmail.com> -2012-03-21 - -I) Overview ------------ - -U-Boot currently implements one common API for TWSerial devices. The interface -is defined in include/tws.h and comprises of functions: - - int tws_read(uchar *buffer, int len); - int tws_write(uchar *buffer, int len); - -The functions are implemented by a proper device driver in drivers/twserial -directory and the driver to be compiled in is selected in a Makefile. There is -only one driver present now. - -The driver depends on ad-hoc code in board specific data, namely functions: - - void tws_ce(unsigned bit); - void tws_wr(unsigned bit); - void tws_clk(unsigned bit); - void tws_data(unsigned bit); - unsigned tws_data_read(void); - void tws_data_config_output(unsigned output); - -implemented in include/configs/inka4x0.h . - -II) Approach ------------- - - U-Boot TWserial drivers exports two simple functions and therefore the conversion - of the driver and creating a core for it is not needed. It should be consolidated - with include/configs/inka4x0.h and taken to the misc/ dir. - - -III) Analysis of in-tree drivers --------------------------------- - - drivers/twserial/soft_tws.c - --------------------------- - The driver is the only TWserial driver. The ad-hoc part in - include/configs/inka4x0.h and the core soft_tws driver should be consolidated - to one compact driver and moved to misc/ . diff --git a/doc/driver-model/UDM-usb.txt b/doc/driver-model/UDM-usb.txt deleted file mode 100644 index 5ce85b5..0000000 --- a/doc/driver-model/UDM-usb.txt +++ /dev/null @@ -1,94 +0,0 @@ -The U-Boot Driver Model Project -=============================== -USB analysis -============ -Marek Vasut <marek.vasut@gmail.com> -2012-02-16 - -I) Overview ------------ - - 1) The USB Host driver - ---------------------- - There are basically four or five USB host drivers. All such drivers currently - provide at least the following fuctions: - - usb_lowlevel_init() ... Do the initialization of the USB controller hardware - usb_lowlevel_stop() ... Do the shutdown of the USB controller hardware - - usb_event_poll() ...... Poll interrupt from USB device, often used by KBD - - submit_control_msg() .. Submit message via Control endpoint - submit_int_msg() ...... Submit message via Interrupt endpoint - submit_bulk_msg() ..... Submit message via Bulk endpoint - - - This allows for the host driver to be easily abstracted. - - 2) The USB hierarchy - -------------------- - - In the current implementation, the USB Host driver provides operations to - communicate via the USB bus. This is realised by providing access to a USB - root port to which an USB root hub is attached. The USB bus is scanned and for - each newly found device, a struct usb_device is allocated. See common/usb.c - and include/usb.h for details. - -II) Approach ------------- - - 1) The USB Host driver - ---------------------- - - Converting the host driver will follow the classic driver model consideration. - Though, the host driver will have to call a function that registers a root - port with the USB core in it's probe() function, let's call this function - - usb_register_root_port(&ops); - - This will allow the USB core to track all available root ports. The ops - parameter will contain structure describing operations supported by the root - port: - - struct usb_port_ops { - void (*usb_event_poll)(); - int (*submit_control_msg)(); - int (*submit_int_msg)(); - int (*submit_bulk_msg)(); - } - - 2) The USB hierarchy and hub drivers - ------------------------------------ - - Converting the USB heirarchy should be fairy simple, considering the already - dynamic nature of the implementation. The current usb_hub_device structure - will have to be converted to a struct instance. Every such instance will - contain components of struct usb_device and struct usb_hub_device in it's - private data, providing only accessors in order to properly encapsulate the - driver. - - By registering the root port, the USB framework will instantiate a USB hub - driver, which is always present, the root hub. The root hub and any subsequent - hub instance is represented by struct instance and it's private data contain - amongst others common bits from struct usb_device. - - Note the USB hub driver is partly defying the usual method of registering a - set of callbacks to a particular core driver. Instead, a static set of - functions is defined and the USB hub instance is passed to those. This creates - certain restrictions as of how the USB hub driver looks, but considering the - specification for USB hub is given and a different type of USB hub won't ever - exist, this approach is ok: - - - Report how many ports does this hub have: - uint get_nr_ports(struct instance *hub); - - Get pointer to device connected to a port: - struct instance *(*get_child)(struct instance *hub, int port); - - Instantiate and configure device on port: - struct instance *(*enum_dev_on_port)(struct instance *hub, int port); - - 3) USB device drivers - --------------------- - - The USB device driver, in turn, will have to register various ops structures - with certain cores. For example, USB disc driver will have to register it's - ops with core handling USB discs etc. diff --git a/doc/driver-model/UDM-video.txt b/doc/driver-model/UDM-video.txt deleted file mode 100644 index e67e9e4..0000000 --- a/doc/driver-model/UDM-video.txt +++ /dev/null @@ -1,74 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Video output analysis -===================== -Marek Vasut <marek.vasut@gmail.com> -2012-02-20 - -I) Overview ------------ - -The video drivers are most often registered with video subsystem. This subsystem -often expects to be allowed access to framebuffer of certain parameters. This -subsystem also provides calls for STDIO subsystem to allow it to output -characters on the screen. For this part, see [ UDM-stdio.txt ]. - -Therefore the API has two parts, the video driver part and the part where the -video driver core registers with STDIO API. - -The video driver part will follow the current cfb_console approach, though -allowing it to be more dynamic. - -II) Approach ------------- - -Registering the video driver into the video driver core is done by calling the -following function from the driver probe() function: - - video_device_register(struct instance *i, GraphicDevice *gd); - -Because the video driver core is in charge or rendering characters as well as -bitmaps on the screen, it will in turn call stdio_device_register(i, so), where -"i" is the same instance as the video driver's one. But "so" will be special -static struct stdio_device_ops handling the character output. - - -III) Analysis of in-tree drivers --------------------------------- - - arch/powerpc/cpu/mpc8xx/video.c - ------------------------------- - This driver copies the cfb_console [ see drivers/video/cfb_console.c ] - approach and acts only as a STDIO device. Therefore there are currently two - possible approaches, first being the conversion of this driver to usual STDIO - device and second, long-term one, being conversion of this driver to video - driver that provides console. - - arch/x86/lib/video.c - -------------------- - This driver registers two separate STDIO devices and should be therefore - converted as such. - - board/bf527-ezkit/video.c - ------------------------- - This driver seems bogus as it behaves as STDIO device, but provides no input - or output capabilities. It relies on DEV_EXT_VIDEO, which is no longer in use - or present otherwise than as a dead code/define. - - board/bf533-stamp/video.c - ------------------------- - This driver seems bogus as it behaves as STDIO device, but provides no input - or output capabilities. It relies on DEV_EXT_VIDEO, which is no longer in use - or present otherwise than as a dead code/define. - - board/bf548-ezkit/video.c - ------------------------- - This driver seems bogus as it behaves as STDIO device, but provides no input - or output capabilities. It relies on DEV_EXT_VIDEO, which is no longer in use - or present otherwise than as a dead code/define. - - board/cm-bf548/video.c - ---------------------- - This driver seems bogus as it behaves as STDIO device, but provides no input - or output capabilities. It relies on DEV_EXT_VIDEO, which is no longer in use - or present otherwise than as a dead code/define. diff --git a/doc/driver-model/UDM-watchdog.txt b/doc/driver-model/UDM-watchdog.txt deleted file mode 100644 index 3f13063..0000000 --- a/doc/driver-model/UDM-watchdog.txt +++ /dev/null @@ -1,329 +0,0 @@ -The U-Boot Driver Model Project -=============================== -Watchdog device subsystem analysis -================================== - -Tomas Hlavacek <tmshlvck@gmail.com> -2012-03-09 - -I) Overview ------------ - -U-Boot currently implements an API for HW watchdog devices as explicit drivers -in drivers/watchdog directory. There are also drivers for both hardware and -software watchdog on particular CPUs implemented in arch/*/cpu/*/cpu.c. There -are macros in include/watchdog.h that selects between SW and HW watchdog and -assembly SW implementation. - -The current common interface comprises of one set out of these two possible -variants: - - 1) - void watchdog_reset(void); - int watchdog_disable(void); - int watchdog_init(void); - - 2) - void hw_watchdog_reset(void); - void hw_watchdog_init(void); - -The watchdog implementations are also spread through board/*/*.c that in -some cases. The API and semantics is in most cases same as the above -mentioned common functions. - - -II) Approach ------------- - - 1) New API - ---------- - - In the UDM each watchdog driver would register itself by a function - - int watchdog_device_register(struct instance *i, - const struct watchdog_device_ops *o); - - The structure being defined as follows: - - struct watchdog_device_ops { - int (*disable)(struct instance *i); - void (*reset)(struct instance *i); - }; - - The watchdog_init() function will be dissolved into probe() function. - - 2) Conversion thougths - ---------------------- - - Conversion of watchdog implementations to a new API could be divided - to three subsections: a) HW implementations, which are mostly compliant - to the above mentioned API; b) SW implementations, which are compliant - to the above mentioned API and c) SW implementations that are not compliant - to the API and has to be rectified or partially rewritten. - -III) Analysis of in-tree drivers --------------------------------- - - drivers/watchdog/at91sam9_wdt.c - ------------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - drivers/watchdog/ftwdt010_wdt.c - ------------------------------- - The driver is ad-hoc HW watchdog. Conversion has to take into account - driver parts spread in include/faraday/*. Restructuring the driver and - code cleanup has to be considered. - - - arch/arm/cpu/arm1136/mx31/timer.c - --------------------------------- - The driver is semi-standard ad-hoc HW watchdog. Conversion has to take - into account driver parts spread in the timer.c file. - - - arch/arm/cpu/arm926ejs/davinci/timer.c - -------------------------------------- - The driver is ad-hoc semi-standard HW watchdog. Conversion has to take - into account driver parts spread in the timer.c file. - - - arch/arm/cpu/armv7/omap-common/hwinit-common.c - ---------------------------------------------- - The driver is non-standard ad-hoc HW watchdog. Conversion is possible - but functions has to be renamed and constants moved to another places. - - - arch/arm/cpu/armv7/omap3/board.c - -------------------------------- - The driver is non-standard ad-hoc HW watchdog. Conversion is possible - but functions has to be renamed and constants moved to another places. - - - arch/blackfin/cpu/watchdog.c - ---------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/m68k/cpu/mcf523x/cpu.c - --------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/m68k/cpu/mcf52x2/cpu.c - --------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/m68k/cpu/mcf532x/cpu.c - --------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/m68k/cpu/mcf547x_8x/cpu.c - ------------------------------ - The driver is standard HW watchdog (there is slight naming convention - violation that has to be rectified). Simple conversion is possible. - - - arch/powerpc/cpu/74xx_7xx/cpu.c - ------------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc512x/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc5xx/cpu.c - ----------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc5xxx/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc8260/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc83xx/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc85xx/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc86xx/cpu.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/mpc8xx/cpu.c - ----------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/powerpc/cpu/ppc4xx/cpu.c - ----------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/sh/cpu/sh2/watchdog.c - -------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/sh/cpu/sh3/watchdog.c - -------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - arch/sh/cpu/sh4/watchdog.c - -------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/amcc/luan/luan.c - ---------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/amcc/yosemite/yosemite.c - ------------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - board/apollon/apollon.c - ----------------------- - The driver is standard HW watchdog however the watchdog_init() - function is called in early initialization. Simple conversion is possible. - - - board/bmw/m48t59y.c - ------------------- - Special watchdog driver. Dead code. To be removed. - - - board/davedenx/qong/qong.c - -------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/dvlhost/watchdog.c - ------------------------ - The driver is standard HW watchdog. Simple conversion is possible. - - - board/eNET/eNET.c - ----------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/eltec/elppc/elppc.c - ------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/enbw/enbw_cmc/enbw_cmc.c - ------------------------------ - Only function proxy call. Code cleanup needed. - - - board/freescale/mx31pdk/mx31pdk.c - --------------------------------- - Only function proxy call. Code cleanup needed. - - - board/gth2/gth2.c - ----------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/lwmon5/lwmon5.c - --------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/manroland/mucmc52/mucmc52.c - --------------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/manroland/uc101/uc101.c - ----------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/mousse/m48t59y.c - ---------------------- - Special watchdog driver. Dead code. To be removed. - - - board/mvblue/mvblue.c - --------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/netphone/netphone.c - ------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/netta/netta.c - ------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/netta2/netta2.c - --------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/omicron/calimain/calimain.c - --------------------------------- - Only function proxy call. Code cleanup needed. - - - board/pcs440ep/pcs440ep.c - ------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/stx/stxxtc/stxxtc.c - ------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/ti/omap2420h4/omap2420h4.c - -------------------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/ttcontrol/vision2/vision2.c - --------------------------------- - The driver is standard HW watchdog but namespace is polluted by - non-standard macros. Simple conversion is possible, code cleanup - needed. - - - board/v38b/v38b.c - ----------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/ve8313/ve8313.c - --------------------- - The driver is standard HW watchdog. Simple conversion is possible. - - - board/w7o/watchdog.c - -------------------- - The driver is standard HW watchdog. Simple conversion is possible. diff --git a/drivers/core/Makefile b/drivers/core/Makefile new file mode 100644 index 0000000..90b2a7f --- /dev/null +++ b/drivers/core/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM) := device.o lists.o root.o uclass.o util.o diff --git a/drivers/core/device.c b/drivers/core/device.c new file mode 100644 index 0000000..55ba281 --- /dev/null +++ b/drivers/core/device.c @@ -0,0 +1,348 @@ +/* + * Device manager + * + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/platdata.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <linux/err.h> +#include <linux/list.h> + +/** + * device_chld_unbind() - Unbind all device's children from the device + * + * On error, the function continues to unbind all children, and reports the + * first error. + * + * @dev: The device that is to be stripped of its children + * @return 0 on success, -ve on error + */ +static int device_chld_unbind(struct device *dev) +{ + struct device *pos, *n; + int ret, saved_ret = 0; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_unbind(pos); + if (ret && !saved_ret) + saved_ret = ret; + } + + return saved_ret; +} + +/** + * device_chld_remove() - Stop all device's children + * @dev: The device whose children are to be removed + * @return 0 on success, -ve on error + */ +static int device_chld_remove(struct device *dev) +{ + struct device *pos, *n; + int ret; + + assert(dev); + + list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { + ret = device_remove(pos); + if (ret) + return ret; + } + + return 0; +} + +int device_bind(struct device *parent, struct driver *drv, const char *name, + void *platdata, int of_offset, struct device **devp) +{ + struct device *dev; + struct uclass *uc; + int ret = 0; + + *devp = NULL; + if (!name) + return -EINVAL; + + ret = uclass_get(drv->id, &uc); + if (ret) + return ret; + + dev = calloc(1, sizeof(struct device)); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->sibling_node); + INIT_LIST_HEAD(&dev->child_head); + INIT_LIST_HEAD(&dev->uclass_node); + dev->platdata = platdata; + dev->name = name; + dev->of_offset = of_offset; + dev->parent = parent; + dev->driver = drv; + dev->uclass = uc; + if (!dev->platdata && drv->platdata_auto_alloc_size) + dev->flags |= DM_FLAG_ALLOC_PDATA; + + /* put dev into parent's successor list */ + if (parent) + list_add_tail(&dev->sibling_node, &parent->child_head); + + ret = uclass_bind_device(dev); + if (ret) + goto fail_bind; + + /* if we fail to bind we remove device from successors and free it */ + if (drv->bind) { + ret = drv->bind(dev); + if (ret) { + if (uclass_unbind_device(dev)) { + dm_warn("Failed to unbind dev '%s' on error path\n", + dev->name); + } + goto fail_bind; + } + } + if (parent) + dm_dbg("Bound device %s to %s\n", dev->name, parent->name); + *devp = dev; + + return 0; + +fail_bind: + list_del(&dev->sibling_node); + free(dev); + return ret; +} + +int device_bind_by_name(struct device *parent, const struct driver_info *info, + struct device **devp) +{ + struct driver *drv; + + drv = lists_driver_lookup_name(info->name); + if (!drv) + return -ENOENT; + + return device_bind(parent, drv, info->name, (void *)info->platdata, + -1, devp); +} + +int device_unbind(struct device *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (dev->flags & DM_FLAG_ACTIVATED) + return -EINVAL; + + drv = dev->driver; + assert(drv); + + if (drv->unbind) { + ret = drv->unbind(dev); + if (ret) + return ret; + } + + ret = device_chld_unbind(dev); + if (ret) + return ret; + + ret = uclass_unbind_device(dev); + if (ret) + return ret; + + if (dev->parent) + list_del(&dev->sibling_node); + free(dev); + + return 0; +} + +/** + * device_free() - Free memory buffers allocated by a device + * @dev: Device that is to be started + */ +static void device_free(struct device *dev) +{ + int size; + + if (dev->driver->priv_auto_alloc_size) { + free(dev->priv); + dev->priv = NULL; + } + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + free(dev->platdata); + dev->platdata = NULL; + } + size = dev->uclass->uc_drv->per_device_auto_alloc_size; + if (size) { + free(dev->uclass_priv); + dev->uclass_priv = NULL; + } +} + +int device_probe(struct device *dev) +{ + struct driver *drv; + int size = 0; + int ret; + + if (!dev) + return -EINVAL; + + if (dev->flags & DM_FLAG_ACTIVATED) + return 0; + + drv = dev->driver; + assert(drv); + + /* Allocate private data and platdata if requested */ + if (drv->priv_auto_alloc_size) { + dev->priv = calloc(1, drv->priv_auto_alloc_size); + if (!dev->priv) { + ret = -ENOMEM; + goto fail; + } + } + /* Allocate private data if requested */ + if (dev->flags & DM_FLAG_ALLOC_PDATA) { + dev->platdata = calloc(1, drv->platdata_auto_alloc_size); + if (!dev->platdata) { + ret = -ENOMEM; + goto fail; + } + } + size = dev->uclass->uc_drv->per_device_auto_alloc_size; + if (size) { + dev->uclass_priv = calloc(1, size); + if (!dev->uclass_priv) { + ret = -ENOMEM; + goto fail; + } + } + + /* Ensure all parents are probed */ + if (dev->parent) { + ret = device_probe(dev->parent); + if (ret) + goto fail; + } + + if (drv->ofdata_to_platdata && dev->of_offset >= 0) { + ret = drv->ofdata_to_platdata(dev); + if (ret) + goto fail; + } + + if (drv->probe) { + ret = drv->probe(dev); + if (ret) + goto fail; + } + + dev->flags |= DM_FLAG_ACTIVATED; + + ret = uclass_post_probe_device(dev); + if (ret) { + dev->flags &= ~DM_FLAG_ACTIVATED; + goto fail_uclass; + } + + return 0; +fail_uclass: + if (device_remove(dev)) { + dm_warn("%s: Device '%s' failed to remove on error path\n", + __func__, dev->name); + } +fail: + device_free(dev); + + return ret; +} + +int device_remove(struct device *dev) +{ + struct driver *drv; + int ret; + + if (!dev) + return -EINVAL; + + if (!(dev->flags & DM_FLAG_ACTIVATED)) + return 0; + + drv = dev->driver; + assert(drv); + + ret = uclass_pre_remove_device(dev); + if (ret) + return ret; + + ret = device_chld_remove(dev); + if (ret) + goto err; + + if (drv->remove) { + ret = drv->remove(dev); + if (ret) + goto err_remove; + } + + device_free(dev); + + dev->flags &= ~DM_FLAG_ACTIVATED; + + return 0; + +err_remove: + /* We can't put the children back */ + dm_warn("%s: Device '%s' failed to remove, but children are gone\n", + __func__, dev->name); +err: + ret = uclass_post_probe_device(dev); + if (ret) { + dm_warn("%s: Device '%s' failed to post_probe on error path\n", + __func__, dev->name); + } + + return ret; +} + +void *dev_get_platdata(struct device *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->platdata; +} + +void *dev_get_priv(struct device *dev) +{ + if (!dev) { + dm_warn("%s: null device", __func__); + return NULL; + } + + return dev->priv; +} diff --git a/drivers/core/lists.c b/drivers/core/lists.c new file mode 100644 index 0000000..4f2c126 --- /dev/null +++ b/drivers/core/lists.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/platdata.h> +#include <dm/uclass.h> +#include <dm/util.h> +#include <linux/compiler.h> + +struct driver *lists_driver_lookup_name(const char *name) +{ + struct driver *drv = + ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + int len; + + if (!drv || !n_ents) + return NULL; + + len = strlen(name); + + for (entry = drv; entry != drv + n_ents; entry++) { + if (strncmp(name, entry->name, len)) + continue; + + /* Full match */ + if (len == strlen(entry->name)) + return entry; + } + + /* Not found */ + return NULL; +} + +struct uclass_driver *lists_uclass_lookup(enum uclass_id id) +{ + struct uclass_driver *uclass = + ll_entry_start(struct uclass_driver, uclass); + const int n_ents = ll_entry_count(struct uclass_driver, uclass); + struct uclass_driver *entry; + + if ((id == UCLASS_INVALID) || !uclass) + return NULL; + + for (entry = uclass; entry != uclass + n_ents; entry++) { + if (entry->id == id) + return entry; + } + + return NULL; +} + +int lists_bind_drivers(struct device *parent) +{ + struct driver_info *info = + ll_entry_start(struct driver_info, driver_info); + const int n_ents = ll_entry_count(struct driver_info, driver_info); + struct driver_info *entry; + struct device *dev; + int result = 0; + int ret; + + for (entry = info; entry != info + n_ents; entry++) { + ret = device_bind_by_name(parent, entry, &dev); + if (ret) { + dm_warn("No match for driver '%s'\n", entry->name); + if (!result || ret != -ENOENT) + result = ret; + } + } + + return result; +} + +#ifdef CONFIG_OF_CONTROL +/** + * driver_check_compatible() - Check if a driver is compatible with this node + * + * @param blob: Device tree pointer + * @param offset: Offset of node in device tree + * @param of_matchL List of compatible strings to match + * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node + * does not have a compatible string, other error <0 if there is a device + * tree error + */ +static int driver_check_compatible(const void *blob, int offset, + const struct device_id *of_match) +{ + int ret; + + if (!of_match) + return -ENOENT; + + while (of_match->compatible) { + ret = fdt_node_check_compatible(blob, offset, + of_match->compatible); + if (!ret) + return 0; + else if (ret == -FDT_ERR_NOTFOUND) + return -ENODEV; + else if (ret < 0) + return -EINVAL; + of_match++; + } + + return -ENOENT; +} + +int lists_bind_fdt(struct device *parent, const void *blob, int offset) +{ + struct driver *driver = ll_entry_start(struct driver, driver); + const int n_ents = ll_entry_count(struct driver, driver); + struct driver *entry; + struct device *dev; + const char *name; + int result = 0; + int ret; + + dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL)); + for (entry = driver; entry != driver + n_ents; entry++) { + ret = driver_check_compatible(blob, offset, entry->of_match); + if (ret == -ENOENT) { + continue; + } else if (ret == -ENODEV) { + break; + } else if (ret) { + dm_warn("Device tree error at offset %d\n", offset); + if (!result || ret != -ENOENT) + result = ret; + break; + } + + name = fdt_get_name(blob, offset, NULL); + dm_dbg(" - found match at '%s'\n", entry->name); + ret = device_bind(parent, entry, name, NULL, offset, &dev); + if (ret) { + dm_warn("No match for driver '%s'\n", entry->name); + if (!result || ret != -ENOENT) + result = ret; + } + } + + return result; +} +#endif diff --git a/drivers/core/root.c b/drivers/core/root.c new file mode 100644 index 0000000..407bc0d --- /dev/null +++ b/drivers/core/root.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/platdata.h> +#include <dm/uclass.h> +#include <dm/util.h> +#include <linux/list.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct driver_info root_info = { + .name = "root_driver", +}; + +struct device *dm_root(void) +{ + if (!gd->dm_root) { + dm_warn("Virtual root driver does not exist!\n"); + return NULL; + } + + return gd->dm_root; +} + +int dm_init(void) +{ + int ret; + + if (gd->dm_root) { + dm_warn("Virtual root driver already exists!\n"); + return -EINVAL; + } + INIT_LIST_HEAD(&gd->uclass_root); + + ret = device_bind_by_name(NULL, &root_info, &gd->dm_root); + if (ret) + return ret; + + return 0; +} + +int dm_scan_platdata(void) +{ + int ret; + + ret = lists_bind_drivers(gd->dm_root); + if (ret == -ENOENT) { + dm_warn("Some drivers were not found\n"); + ret = 0; + } + if (ret) + return ret; + + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int dm_scan_fdt(const void *blob) +{ + int offset = 0; + int ret = 0, err; + int depth = 0; + + do { + offset = fdt_next_node(blob, offset, &depth); + if (offset > 0 && depth == 1) { + err = lists_bind_fdt(gd->dm_root, blob, offset); + if (err && !ret) + ret = err; + } + } while (offset > 0); + + if (ret) + dm_warn("Some drivers failed to bind\n"); + + return ret; +} +#endif + +/* This is the root driver - all drivers are children of this */ +U_BOOT_DRIVER(root_driver) = { + .name = "root_driver", + .id = UCLASS_ROOT, +}; + +/* This is the root uclass */ +UCLASS_DRIVER(root) = { + .name = "root", + .id = UCLASS_ROOT, +}; diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c new file mode 100644 index 0000000..4df5a8b --- /dev/null +++ b/drivers/core/uclass.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct uclass *uclass_find(enum uclass_id key) +{ + struct uclass *uc; + + /* + * TODO(sjg@chromium.org): Optimise this, perhaps moving the found + * node to the start of the list, or creating a linear array mapping + * id to node. + */ + list_for_each_entry(uc, &gd->uclass_root, sibling_node) { + if (uc->uc_drv->id == key) + return uc; + } + + return NULL; +} + +/** + * uclass_add() - Create new uclass in list + * @id: Id number to create + * @ucp: Returns pointer to uclass, or NULL on error + * @return 0 on success, -ve on error + * + * The new uclass is added to the list. There must be only one uclass for + * each id. + */ +static int uclass_add(enum uclass_id id, struct uclass **ucp) +{ + struct uclass_driver *uc_drv; + struct uclass *uc; + int ret; + + *ucp = NULL; + uc_drv = lists_uclass_lookup(id); + if (!uc_drv) { + dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", + id); + return -ENOENT; + } + if (uc_drv->ops) { + dm_warn("No ops for uclass id %d\n", id); + return -EINVAL; + } + uc = calloc(1, sizeof(*uc)); + if (!uc) + return -ENOMEM; + if (uc_drv->priv_auto_alloc_size) { + uc->priv = calloc(1, uc_drv->priv_auto_alloc_size); + if (!uc->priv) { + ret = -ENOMEM; + goto fail_mem; + } + } + uc->uc_drv = uc_drv; + INIT_LIST_HEAD(&uc->sibling_node); + INIT_LIST_HEAD(&uc->dev_head); + list_add(&uc->sibling_node, &gd->uclass_root); + + if (uc_drv->init) { + ret = uc_drv->init(uc); + if (ret) + goto fail; + } + + *ucp = uc; + + return 0; +fail: + if (uc_drv->priv_auto_alloc_size) { + free(uc->priv); + uc->priv = NULL; + } + list_del(&uc->sibling_node); +fail_mem: + free(uc); + + return ret; +} + +int uclass_destroy(struct uclass *uc) +{ + struct uclass_driver *uc_drv; + struct device *dev, *tmp; + int ret; + + list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) { + ret = device_remove(dev); + if (ret) + return ret; + ret = device_unbind(dev); + if (ret) + return ret; + } + + uc_drv = uc->uc_drv; + if (uc_drv->destroy) + uc_drv->destroy(uc); + list_del(&uc->sibling_node); + if (uc_drv->priv_auto_alloc_size) + free(uc->priv); + free(uc); + + return 0; +} + +int uclass_get(enum uclass_id id, struct uclass **ucp) +{ + struct uclass *uc; + + *ucp = NULL; + uc = uclass_find(id); + if (!uc) + return uclass_add(id, ucp); + *ucp = uc; + + return 0; +} + +int uclass_find_device(enum uclass_id id, int index, struct device **devp) +{ + struct uclass *uc; + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (!index--) { + *devp = dev; + return 0; + } + } + + return -ENODEV; +} + +int uclass_get_device(enum uclass_id id, int index, struct device **devp) +{ + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_find_device(id, index, &dev); + if (ret) + return ret; + + ret = device_probe(dev); + if (ret) + return ret; + + *devp = dev; + + return 0; +} + +int uclass_first_device(enum uclass_id id, struct device **devp) +{ + struct uclass *uc; + struct device *dev; + int ret; + + *devp = NULL; + ret = uclass_get(id, &uc); + if (ret) + return ret; + if (list_empty(&uc->dev_head)) + return 0; + + dev = list_first_entry(&uc->dev_head, struct device, uclass_node); + ret = device_probe(dev); + if (ret) + return ret; + *devp = dev; + + return 0; +} + +int uclass_next_device(struct device **devp) +{ + struct device *dev = *devp; + int ret; + + *devp = NULL; + if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head)) + return 0; + + dev = list_entry(dev->uclass_node.next, struct device, uclass_node); + ret = device_probe(dev); + if (ret) + return ret; + *devp = dev; + + return 0; +} + +int uclass_bind_device(struct device *dev) +{ + struct uclass *uc; + int ret; + + uc = dev->uclass; + + list_add_tail(&dev->uclass_node, &uc->dev_head); + + if (uc->uc_drv->post_bind) { + ret = uc->uc_drv->post_bind(dev); + if (ret) { + list_del(&dev->uclass_node); + return ret; + } + } + + return 0; +} + +int uclass_unbind_device(struct device *dev) +{ + struct uclass *uc; + int ret; + + uc = dev->uclass; + if (uc->uc_drv->pre_unbind) { + ret = uc->uc_drv->pre_unbind(dev); + if (ret) + return ret; + } + + list_del(&dev->uclass_node); + return 0; +} + +int uclass_post_probe_device(struct device *dev) +{ + struct uclass_driver *uc_drv = dev->uclass->uc_drv; + + if (uc_drv->post_probe) + return uc_drv->post_probe(dev); + + return 0; +} + +int uclass_pre_remove_device(struct device *dev) +{ + struct uclass_driver *uc_drv; + struct uclass *uc; + int ret; + + uc = dev->uclass; + uc_drv = uc->uc_drv; + if (uc->uc_drv->pre_remove) { + ret = uc->uc_drv->pre_remove(dev); + if (ret) + return ret; + } + if (uc_drv->per_device_auto_alloc_size) { + free(dev->uclass_priv); + dev->uclass_priv = NULL; + } + + return 0; +} diff --git a/drivers/core/util.c b/drivers/core/util.c new file mode 100644 index 0000000..e01dd06 --- /dev/null +++ b/drivers/core/util.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <vsprintf.h> + +void dm_warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +void dm_dbg(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +int list_count_items(struct list_head *head) +{ + struct list_head *node; + int count = 0; + + list_for_each(node, head) + count++; + + return count; +} diff --git a/drivers/demo/Makefile b/drivers/demo/Makefile new file mode 100644 index 0000000..baaa2ba --- /dev/null +++ b/drivers/demo/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_DM_DEMO) += demo-uclass.o demo-pdata.o +obj-$(CONFIG_DM_DEMO_SIMPLE) += demo-simple.o +obj-$(CONFIG_DM_DEMO_SHAPE) += demo-shape.o diff --git a/drivers/demo/demo-pdata.c b/drivers/demo/demo-pdata.c new file mode 100644 index 0000000..e92841d --- /dev/null +++ b/drivers/demo/demo-pdata.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm-demo.h> + +static const struct dm_demo_pdata red_square = { + .colour = "red", + .sides = 4. +}; +static const struct dm_demo_pdata green_triangle = { + .colour = "green", + .sides = 3. +}; +static const struct dm_demo_pdata yellow_hexagon = { + .colour = "yellow", + .sides = 6. +}; + +U_BOOT_DEVICE(demo0) = { + .name = "demo_shape_drv", + .platdata = &red_square, +}; + +U_BOOT_DEVICE(demo1) = { + .name = "demo_simple_drv", + .platdata = &red_square, +}; + +U_BOOT_DEVICE(demo2) = { + .name = "demo_shape_drv", + .platdata = &green_triangle, +}; + +U_BOOT_DEVICE(demo3) = { + .name = "demo_simple_drv", + .platdata = &yellow_hexagon, +}; + +U_BOOT_DEVICE(demo4) = { + .name = "demo_shape_drv", + .platdata = &yellow_hexagon, +}; diff --git a/drivers/demo/demo-shape.c b/drivers/demo/demo-shape.c new file mode 100644 index 0000000..2f0eb96 --- /dev/null +++ b/drivers/demo/demo-shape.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <dm-demo.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Shape size */ +#define WIDTH 8 +#define HEIGHT 6 + +struct shape_data { + int num_chars; /* Number of non-space characters output so far */ +}; + +/* Crazy little function to draw shapes on the console */ +static int shape_hello(struct device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev_get_platdata(dev); + struct shape_data *data = dev_get_priv(dev); + static const struct shape { + int start; + int end; + int dstart; + int dend; + } shapes[3] = { + { 0, 1, 0, 1 }, + { 0, WIDTH, 0, 0 }, + { HEIGHT / 2 - 1, WIDTH - HEIGHT / 2 + 1, -1, 1}, + }; + struct shape shape; + unsigned int index; + int line, pos, inside; + const char *colour = pdata->colour; + int first = 0; + + if (!ch) + ch = pdata->default_char; + if (!ch) + ch = '@'; + + index = (pdata->sides / 2) - 1; + if (index >= ARRAY_SIZE(shapes)) + return -EIO; + shape = shapes[index]; + + for (line = 0; line < HEIGHT; line++) { + first = 1; + for (pos = 0; pos < WIDTH; pos++) { + inside = pos >= shape.start && pos < shape.end; + if (inside) { + putc(first ? *colour++ : ch); + data->num_chars++; + first = 0; + if (!*colour) + colour = pdata->colour; + } else { + putc(' '); + } + } + putc('\n'); + shape.start += shape.dstart; + shape.end += shape.dend; + if (shape.start < 0) { + shape.dstart = -shape.dstart; + shape.dend = -shape.dend; + shape.start += shape.dstart; + shape.end += shape.dend; + } + } + + return 0; +} + +static int shape_status(struct device *dev, int *status) +{ + struct shape_data *data = dev_get_priv(dev); + + *status = data->num_chars; + return 0; +} + +static const struct demo_ops shape_ops = { + .hello = shape_hello, + .status = shape_status, +}; + +static int shape_ofdata_to_platdata(struct device *dev) +{ + struct dm_demo_pdata *pdata = dev_get_platdata(dev); + int ret; + + /* Parse the data that is common with all demo devices */ + ret = demo_parse_dt(dev); + if (ret) + return ret; + + /* Parse the data that only we need */ + pdata->default_char = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "character", '@'); + + return 0; +} + +static const struct device_id demo_shape_id[] = { + { "demo-shape", 0 }, + { }, +}; + +U_BOOT_DRIVER(demo_shape_drv) = { + .name = "demo_shape_drv", + .of_match = demo_shape_id, + .id = UCLASS_DEMO, + .ofdata_to_platdata = shape_ofdata_to_platdata, + .ops = &shape_ops, + .priv_auto_alloc_size = sizeof(struct shape_data), + .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), +}; diff --git a/drivers/demo/demo-simple.c b/drivers/demo/demo-simple.c new file mode 100644 index 0000000..6ba8131 --- /dev/null +++ b/drivers/demo/demo-simple.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm-demo.h> +#include <asm/io.h> + +static int simple_hello(struct device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev_get_platdata(dev); + + printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), pdata->colour, + pdata->sides); + + return 0; +} + +static const struct demo_ops simple_ops = { + .hello = simple_hello, +}; + +static int demo_shape_ofdata_to_platdata(struct device *dev) +{ + /* Parse the data that is common with all demo devices */ + return demo_parse_dt(dev); +} + +static const struct device_id demo_shape_id[] = { + { "demo-simple", 0 }, + { }, +}; + +U_BOOT_DRIVER(demo_simple_drv) = { + .name = "demo_simple_drv", + .of_match = demo_shape_id, + .id = UCLASS_DEMO, + .ofdata_to_platdata = demo_shape_ofdata_to_platdata, + .ops = &simple_ops, + .platdata_auto_alloc_size = sizeof(struct dm_demo_pdata), +}; diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c new file mode 100644 index 0000000..48588be --- /dev/null +++ b/drivers/demo/demo-uclass.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <dm-demo.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/list.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(demo) = { + .id = UCLASS_DEMO, +}; + +int demo_hello(struct device *dev, int ch) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->hello) + return -ENOSYS; + + return ops->hello(dev, ch); +} + +int demo_status(struct device *dev, int *status) +{ + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->status) + return -ENOSYS; + + return ops->status(dev, status); +} + +int demo_parse_dt(struct device *dev) +{ + struct dm_demo_pdata *pdata = dev_get_platdata(dev); + int dn = dev->of_offset; + + pdata->sides = fdtdec_get_int(gd->fdt_blob, dn, "sides", 0); + pdata->colour = fdt_getprop(gd->fdt_blob, dn, "colour", NULL); + if (!pdata->sides || !pdata->colour) { + debug("%s: Invalid device tree data\n", __func__); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/fpga/zynqpl.c b/drivers/fpga/zynqpl.c index 15900c9..923a158 100644 --- a/drivers/fpga/zynqpl.c +++ b/drivers/fpga/zynqpl.c @@ -10,7 +10,7 @@ #include <common.h> #include <asm/io.h> #include <zynqpl.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/hardware.h> #include <asm/arch/sys_proto.h> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ed2c0c7..4e001e1 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -5,6 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_GPIO) += gpio-uclass.o + obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c index 8b76666..0b70071 100644 --- a/drivers/gpio/at91_gpio.c +++ b/drivers/gpio/at91_gpio.c @@ -11,7 +11,7 @@ #include <config.h> #include <common.h> #include <asm/io.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/hardware.h> #include <asm/arch/at91_pio.h> #include <asm/arch/gpio.h> diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c new file mode 100644 index 0000000..56bfd11 --- /dev/null +++ b/drivers/gpio/gpio-uclass.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/gpio.h> + +/** + * gpio_to_device() - Convert global GPIO number to device, number + * gpio: The numeric representation of the GPIO + * + * Convert the GPIO number to an entry in the list of GPIOs + * or GPIO blocks registered with the GPIO controller. Returns + * entry on success, NULL on error. + */ +static int gpio_to_device(unsigned int gpio, struct device **devp, + unsigned int *offset) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + uc_priv = dev->uclass_priv; + if (gpio >= uc_priv->gpio_base && + gpio < uc_priv->gpio_base + uc_priv->gpio_count) { + *devp = dev; + *offset = gpio - uc_priv->gpio_base; + return 0; + } + } + + /* No such GPIO */ + return ret ? ret : -EINVAL; +} + +int gpio_lookup_name(const char *name, struct device **devp, + unsigned int *offsetp, unsigned int *gpiop) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + int ret; + + if (devp) + *devp = NULL; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = uclass_next_device(&dev)) { + ulong offset; + int len; + + uc_priv = dev->uclass_priv; + len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0; + + if (!strncmp(name, uc_priv->bank_name, len)) { + if (strict_strtoul(name + len, 10, &offset)) + continue; + if (devp) + *devp = dev; + if (offsetp) + *offsetp = offset; + if (gpiop) + *gpiop = uc_priv->gpio_base + offset; + return 0; + } + } + + return ret ? ret : -EINVAL; +} + +/** + * gpio_request() - [COMPAT] Request GPIO + * gpio: GPIO number + * label: Name for the requested GPIO + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->request) + return 0; + + return gpio_get_ops(dev)->request(dev, offset, label); +} + +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + if (!gpio_get_ops(dev)->free) + return 0; + return gpio_get_ops(dev)->free(dev, offset); +} + +/** + * gpio_direction_input() - [COMPAT] Set GPIO direction to input + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_input(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_input(dev, offset); +} + +/** + * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->direction_output(dev, offset, value); +} + +/** + * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns the value of the GPIO pin, or negative value + * on error. + */ +int gpio_get_value(unsigned gpio) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->get_value(dev, offset); +} + +/** + * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin. + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int offset; + struct device *dev; + int ret; + + ret = gpio_to_device(gpio, &dev, &offset); + if (ret) + return ret; + + return gpio_get_ops(dev)->set_value(dev, offset, value); +} + +const char *gpio_get_bank_info(struct device *dev, int *bit_count) +{ + struct gpio_dev_priv *priv; + + /* Must be called on an active device */ + priv = dev->uclass_priv; + assert(priv); + + *bit_count = priv->gpio_count; + return priv->bank_name; +} + +/* We need to renumber the GPIOs when any driver is probed/removed */ +static int gpio_renumber(void) +{ + struct gpio_dev_priv *uc_priv; + struct device *dev; + struct uclass *uc; + unsigned base; + int ret; + + ret = uclass_get(UCLASS_GPIO, &uc); + if (ret) + return ret; + + /* Ensure that we have a base for each bank */ + base = 0; + uclass_foreach_dev(dev, uc) { + if (device_active(dev)) { + uc_priv = dev->uclass_priv; + uc_priv->gpio_base = base; + base += uc_priv->gpio_count; + } + } + + return 0; +} + +static int gpio_post_probe(struct device *dev) +{ + return gpio_renumber(); +} + +static int gpio_pre_remove(struct device *dev) +{ + return gpio_renumber(); +} + +UCLASS_DRIVER(gpio) = { + .id = UCLASS_GPIO, + .name = "gpio", + .post_probe = gpio_post_probe, + .pre_remove = gpio_pre_remove, + .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv), +}; diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 3c6cfec..22b6a5f 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -4,8 +4,13 @@ */ #include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> #include <asm/gpio.h> +DECLARE_GLOBAL_DATA_PTR; + /* Flags for each GPIO */ #define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */ #define GPIOF_HIGH (1 << 1) /* Currently set high */ @@ -16,34 +21,30 @@ struct gpio_state { u8 flags; /* flags (GPIOF_...) */ }; -/* - * State of GPIOs - * TODO: Put this into sandbox state - */ -static struct gpio_state state[CONFIG_SANDBOX_GPIO_COUNT]; - /* Access routines for GPIO state */ -static u8 *get_gpio_flags(unsigned gp) +static u8 *get_gpio_flags(struct device *dev, unsigned offset) { - /* assert()'s could be disabled, so make sure we handle that */ - assert(gp < ARRAY_SIZE(state)); - if (gp >= ARRAY_SIZE(state)) { + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + + if (offset >= uc_priv->gpio_count) { static u8 invalid_flags; - printf("sandbox_gpio: error: invalid gpio %u\n", gp); + printf("sandbox_gpio: error: invalid gpio %u\n", offset); return &invalid_flags; } - return &state[gp].flags; + return &state[offset].flags; } -static int get_gpio_flag(unsigned gp, int flag) +static int get_gpio_flag(struct device *dev, unsigned offset, int flag) { - return (*get_gpio_flags(gp) & flag) != 0; + return (*get_gpio_flags(dev, offset) & flag) != 0; } -static int set_gpio_flag(unsigned gp, int flag, int value) +static int set_gpio_flag(struct device *dev, unsigned offset, int flag, + int value) { - u8 *gpio = get_gpio_flags(gp); + u8 *gpio = get_gpio_flags(dev, offset); if (value) *gpio |= flag; @@ -53,11 +54,12 @@ static int set_gpio_flag(unsigned gp, int flag, int value) return 0; } -static int check_reserved(unsigned gpio, const char *func) +static int check_reserved(struct device *dev, unsigned offset, + const char *func) { - if (!get_gpio_flag(gpio, GPIOF_RESERVED)) { - printf("sandbox_gpio: %s: error: gpio %u not reserved\n", - func, gpio); + if (!get_gpio_flag(dev, offset, GPIOF_RESERVED)) { + printf("sandbox_gpio: %s: error: offset %u not reserved\n", + func, offset); return -1; } @@ -68,126 +70,185 @@ static int check_reserved(unsigned gpio, const char *func) * Back-channel sandbox-internal-only access to GPIO state */ -int sandbox_gpio_get_value(unsigned gp) +int sandbox_gpio_get_value(struct device *dev, unsigned offset) { - if (get_gpio_flag(gp, GPIOF_OUTPUT)) - debug("sandbox_gpio: get_value on output gpio %u\n", gp); - return get_gpio_flag(gp, GPIOF_HIGH); + if (get_gpio_flag(dev, offset, GPIOF_OUTPUT)) + debug("sandbox_gpio: get_value on output gpio %u\n", offset); + return get_gpio_flag(dev, offset, GPIOF_HIGH); } -int sandbox_gpio_set_value(unsigned gp, int value) +int sandbox_gpio_set_value(struct device *dev, unsigned offset, int value) { - return set_gpio_flag(gp, GPIOF_HIGH, value); + return set_gpio_flag(dev, offset, GPIOF_HIGH, value); } -int sandbox_gpio_get_direction(unsigned gp) +int sandbox_gpio_get_direction(struct device *dev, unsigned offset) { - return get_gpio_flag(gp, GPIOF_OUTPUT); + return get_gpio_flag(dev, offset, GPIOF_OUTPUT); } -int sandbox_gpio_set_direction(unsigned gp, int output) +int sandbox_gpio_set_direction(struct device *dev, unsigned offset, int output) { - return set_gpio_flag(gp, GPIOF_OUTPUT, output); + return set_gpio_flag(dev, offset, GPIOF_OUTPUT, output); } /* * These functions implement the public interface within U-Boot */ -/* set GPIO port 'gp' as an input */ -int gpio_direction_input(unsigned gp) +/* set GPIO port 'offset' as an input */ +static int sb_gpio_direction_input(struct device *dev, unsigned offset) { - debug("%s: gp:%u\n", __func__, gp); + debug("%s: offset:%u\n", __func__, offset); - if (check_reserved(gp, __func__)) + if (check_reserved(dev, offset, __func__)) return -1; - return sandbox_gpio_set_direction(gp, 0); + return sandbox_gpio_set_direction(dev, offset, 0); } -/* set GPIO port 'gp' as an output, with polarity 'value' */ -int gpio_direction_output(unsigned gp, int value) +/* set GPIO port 'offset' as an output, with polarity 'value' */ +static int sb_gpio_direction_output(struct device *dev, unsigned offset, + int value) { - debug("%s: gp:%u, value = %d\n", __func__, gp, value); + debug("%s: offset:%u, value = %d\n", __func__, offset, value); - if (check_reserved(gp, __func__)) + if (check_reserved(dev, offset, __func__)) return -1; - return sandbox_gpio_set_direction(gp, 1) | - sandbox_gpio_set_value(gp, value); + return sandbox_gpio_set_direction(dev, offset, 1) | + sandbox_gpio_set_value(dev, offset, value); } -/* read GPIO IN value of port 'gp' */ -int gpio_get_value(unsigned gp) +/* read GPIO IN value of port 'offset' */ +static int sb_gpio_get_value(struct device *dev, unsigned offset) { - debug("%s: gp:%u\n", __func__, gp); + debug("%s: offset:%u\n", __func__, offset); - if (check_reserved(gp, __func__)) + if (check_reserved(dev, offset, __func__)) return -1; - return sandbox_gpio_get_value(gp); + return sandbox_gpio_get_value(dev, offset); } -/* write GPIO OUT value to port 'gp' */ -int gpio_set_value(unsigned gp, int value) +/* write GPIO OUT value to port 'offset' */ +static int sb_gpio_set_value(struct device *dev, unsigned offset, int value) { - debug("%s: gp:%u, value = %d\n", __func__, gp, value); + debug("%s: offset:%u, value = %d\n", __func__, offset, value); - if (check_reserved(gp, __func__)) + if (check_reserved(dev, offset, __func__)) return -1; - if (!sandbox_gpio_get_direction(gp)) { - printf("sandbox_gpio: error: set_value on input gpio %u\n", gp); + if (!sandbox_gpio_get_direction(dev, offset)) { + printf("sandbox_gpio: error: set_value on input gpio %u\n", + offset); return -1; } - return sandbox_gpio_set_value(gp, value); + return sandbox_gpio_set_value(dev, offset, value); } -int gpio_request(unsigned gp, const char *label) +static int sb_gpio_request(struct device *dev, unsigned offset, + const char *label) { - debug("%s: gp:%u, label:%s\n", __func__, gp, label); + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + + debug("%s: offset:%u, label:%s\n", __func__, offset, label); - if (gp >= ARRAY_SIZE(state)) { - printf("sandbox_gpio: error: invalid gpio %u\n", gp); + if (offset >= uc_priv->gpio_count) { + printf("sandbox_gpio: error: invalid gpio %u\n", offset); return -1; } - if (get_gpio_flag(gp, GPIOF_RESERVED)) { - printf("sandbox_gpio: error: gpio %u already reserved\n", gp); + if (get_gpio_flag(dev, offset, GPIOF_RESERVED)) { + printf("sandbox_gpio: error: gpio %u already reserved\n", + offset); return -1; } - state[gp].label = label; - return set_gpio_flag(gp, GPIOF_RESERVED, 1); + state[offset].label = label; + return set_gpio_flag(dev, offset, GPIOF_RESERVED, 1); } -int gpio_free(unsigned gp) +static int sb_gpio_free(struct device *dev, unsigned offset) { - debug("%s: gp:%u\n", __func__, gp); + struct gpio_state *state = dev_get_priv(dev); + + debug("%s: offset:%u\n", __func__, offset); - if (check_reserved(gp, __func__)) + if (check_reserved(dev, offset, __func__)) return -1; - state[gp].label = NULL; - return set_gpio_flag(gp, GPIOF_RESERVED, 0); + state[offset].label = NULL; + return set_gpio_flag(dev, offset, GPIOF_RESERVED, 0); } -/* Display GPIO information */ -void gpio_info(void) +static int sb_gpio_get_state(struct device *dev, unsigned int offset, + char *buf, int bufsize) { - unsigned gpio; + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + struct gpio_state *state = dev_get_priv(dev); + const char *label; + + label = state[offset].label; + snprintf(buf, bufsize, "%s%d: %s: %d [%c]%s%s", + uc_priv->bank_name ? uc_priv->bank_name : "", offset, + sandbox_gpio_get_direction(dev, offset) ? "out" : " in", + sandbox_gpio_get_value(dev, offset), + get_gpio_flag(dev, offset, GPIOF_RESERVED) ? 'x' : ' ', + label ? " " : "", + label ? label : ""); - puts("Sandbox GPIOs\n"); + return 0; +} + +static const struct dm_gpio_ops gpio_sandbox_ops = { + .request = sb_gpio_request, + .free = sb_gpio_free, + .direction_input = sb_gpio_direction_input, + .direction_output = sb_gpio_direction_output, + .get_value = sb_gpio_get_value, + .set_value = sb_gpio_set_value, + .get_state = sb_gpio_get_state, +}; + +static int sandbox_gpio_ofdata_to_platdata(struct device *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; - for (gpio = 0; gpio < ARRAY_SIZE(state); ++gpio) { - const char *label = state[gpio].label; + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "num-gpios", 0); + uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "gpio-bank-name", NULL); - printf("%4d: %s: %d [%c] %s\n", - gpio, - sandbox_gpio_get_direction(gpio) ? "out" : " in", - sandbox_gpio_get_value(gpio), - get_gpio_flag(gpio, GPIOF_RESERVED) ? 'x' : ' ', - label ? label : ""); + return 0; +} + +static int gpio_sandbox_probe(struct device *dev) +{ + struct gpio_dev_priv *uc_priv = dev->uclass_priv; + + if (dev->of_offset == -1) { + /* Tell the uclass how many GPIOs we have */ + uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT; } + + dev->priv = calloc(sizeof(struct gpio_state), uc_priv->gpio_count); + + return 0; } + +static const struct device_id sandbox_gpio_ids[] = { + { .compatible = "sandbox,gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_sandbox) = { + .name = "gpio_sandbox", + .id = UCLASS_GPIO, + .of_match = sandbox_gpio_ids, + .ofdata_to_platdata = sandbox_gpio_ofdata_to_platdata, + .probe = gpio_sandbox_probe, + .ops = &gpio_sandbox_ops, +}; diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 72a272f..fdce2c2 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -7,6 +7,8 @@ */ #include <common.h> +#include <fdtdec.h> +#include <libfdt.h> #include <malloc.h> #include <sdhci.h> #include <asm/arch/sys_proto.h> @@ -32,3 +34,30 @@ int zynq_sdhci_init(u32 regbase) add_sdhci(host, 52000000, 52000000 >> 9); return 0; } + +#ifdef CONFIG_OF_CONTROL +int zynq_sdhci_of_init(const void *blob) +{ + int offset = 0; + u32 ret = 0; + u32 reg; + + debug("ZYNQ SDHCI: Initialization\n"); + + do { + offset = fdt_node_offset_by_compatible(blob, offset, + "arasan,sdhci-8.9a"); + if (offset != -1) { + reg = fdtdec_get_addr(blob, offset, "reg"); + if (reg != FDT_ADDR_T_NONE) { + ret |= zynq_sdhci_init(reg); + } else { + debug("ZYNQ SDHCI: Can't get base address\n"); + return -1; + } + } + } while (offset != -1); + + return ret; +} +#endif diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index 0a5209d..2a5cc44 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -14,8 +14,6 @@ #include <asm/io.h> #include <fdtdec.h> -DECLARE_GLOBAL_DATA_PTR; - #undef DEBUG #define ENET_ADDR_LENGTH 6 @@ -364,24 +362,27 @@ int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, } #ifdef CONFIG_OF_CONTROL -int xilinx_emaclite_init(bd_t *bis) +int xilinx_emaclite_of_init(const void *blob) { int offset = 0; u32 ret = 0; u32 reg; do { - offset = fdt_node_offset_by_compatible(gd->fdt_blob, offset, + offset = fdt_node_offset_by_compatible(blob, offset, "xlnx,xps-ethernetlite-1.00.a"); if (offset != -1) { - reg = fdtdec_get_addr(gd->fdt_blob, offset, "reg"); + reg = fdtdec_get_addr(blob, offset, "reg"); if (reg != FDT_ADDR_T_NONE) { - u32 rxpp = fdtdec_get_int(gd->fdt_blob, offset, + u32 rxpp = fdtdec_get_int(blob, offset, "xlnx,rx-ping-pong", 0); - u32 txpp = fdtdec_get_int(gd->fdt_blob, offset, + u32 txpp = fdtdec_get_int(blob, offset, "xlnx,tx-ping-pong", 0); - ret |= xilinx_emaclite_initialize(bis, reg, + ret |= xilinx_emaclite_initialize(NULL, reg, txpp, rxpp); + } else { + debug("EMACLITE: Can't get base address\n"); + return -1; } } } while (offset != -1); diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 6d4001b..101489c 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -12,6 +12,8 @@ #include <common.h> #include <net.h> #include <config.h> +#include <fdtdec.h> +#include <libfdt.h> #include <malloc.h> #include <asm/io.h> #include <phy.h> @@ -534,3 +536,43 @@ int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio) return 1; } + +#ifdef CONFIG_OF_CONTROL +int zynq_gem_of_init(const void *blob) +{ + int offset = 0; + u32 ret = 0; + u32 reg, phy_reg; + + debug("ZYNQ GEM: Initialization\n"); + + do { + offset = fdt_node_offset_by_compatible(blob, offset, + "xlnx,ps7-ethernet-1.00.a"); + if (offset != -1) { + reg = fdtdec_get_addr(blob, offset, "reg"); + if (reg != FDT_ADDR_T_NONE) { + offset = fdtdec_lookup_phandle(blob, offset, + "phy-handle"); + if (offset != -1) + phy_reg = fdtdec_get_addr(blob, offset, + "reg"); + else + phy_reg = 0; + + debug("ZYNQ GEM: addr %x, phyaddr %x\n", + reg, phy_reg); + + ret |= zynq_gem_initialize(NULL, reg, + phy_reg, 0); + + } else { + debug("ZYNQ GEM: Can't get base address\n"); + return -1; + } + } + } while (offset != -1); + + return ret; +} +#endif diff --git a/drivers/pci/pcie_imx.c b/drivers/pci/pcie_imx.c index 34377e9..1f600aa 100644 --- a/drivers/pci/pcie_imx.c +++ b/drivers/pci/pcie_imx.c @@ -17,7 +17,7 @@ #include <asm/arch/crm_regs.h> #include <asm/gpio.h> #include <asm/io.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <errno.h> #define PCI_ACCESS_READ 0 diff --git a/drivers/serial/serial_zynq.c b/drivers/serial/serial_zynq.c index 22c6bf0..53a8af0 100644 --- a/drivers/serial/serial_zynq.c +++ b/drivers/serial/serial_zynq.c @@ -6,6 +6,7 @@ */ #include <common.h> +#include <fdtdec.h> #include <watchdog.h> #include <asm/io.h> #include <linux/compiler.h> @@ -13,6 +14,8 @@ #include <asm/arch/clk.h> #include <asm/arch/hardware.h> +DECLARE_GLOBAL_DATA_PTR; + #define ZYNQ_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ #define ZYNQ_UART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ @@ -182,6 +185,30 @@ DECLARE_PSSERIAL_FUNCTIONS(1); struct serial_device uart_zynq_serial1_device = INIT_PSSERIAL_STRUCTURE(1, "ttyPS1"); +#ifdef CONFIG_OF_CONTROL +__weak struct serial_device *default_serial_console(void) +{ + const void *blob = gd->fdt_blob; + int node; + unsigned int base_addr; + + node = fdt_path_offset(blob, "serial0"); + if (node < 0) + return NULL; + + base_addr = fdtdec_get_addr(blob, node, "reg"); + if (base_addr == FDT_ADDR_T_NONE) + return NULL; + + if (base_addr == ZYNQ_SERIAL_BASEADDR0) + return &uart_zynq_serial0_device; + + if (base_addr == ZYNQ_SERIAL_BASEADDR1) + return &uart_zynq_serial1_device; + + return NULL; +} +#else __weak struct serial_device *default_serial_console(void) { #if defined(CONFIG_ZYNQ_SERIAL_UART0) @@ -194,6 +221,7 @@ __weak struct serial_device *default_serial_console(void) #endif return NULL; } +#endif void zynq_serial_initalize(void) { diff --git a/drivers/usb/gadget/f_thor.h b/drivers/usb/gadget/f_thor.h index 04ee9a2..833a9d2 100644 --- a/drivers/usb/gadget/f_thor.h +++ b/drivers/usb/gadget/f_thor.h @@ -11,7 +11,7 @@ #define _USB_THOR_H_ #include <linux/compiler.h> -#include <asm/sizes.h> +#include <linux/sizes.h> /* THOR Composite Gadget */ #define STRING_MANUFACTURER_IDX 0 diff --git a/fs/yaffs2/yaffs_list.h b/fs/yaffs2/yaffs_list.h deleted file mode 100644 index a7afaea..0000000 --- a/fs/yaffs2/yaffs_list.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * YAFFS: Yet another Flash File System . A NAND-flash specific file system. - * - * Copyright (C) 2002-2011 Aleph One Ltd. - * for Toby Churchill Ltd and Brightstar Engineering - * - * Created by Charles Manning <charles@aleph1.co.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1 as - * published by the Free Software Foundation. - * - * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. - */ - -/* - * This file is just holds extra declarations of macros that would normally - * be providesd in the Linux kernel. These macros have been written from - * scratch but are functionally equivalent to the Linux ones. - * - */ - -#ifndef __YAFFS_LIST_H__ -#define __YAFFS_LIST_H__ - - -/* - * This is a simple doubly linked list implementation that matches the - * way the Linux kernel doubly linked list implementation works. - */ - -struct list_head { - struct list_head *next; /* next in chain */ - struct list_head *prev; /* previous in chain */ -}; - - -/* Initialise a static list */ -#define LIST_HEAD(name) \ -struct list_head name = { &(name), &(name)} - - - -/* Initialise a list head to an empty list */ -#define INIT_LIST_HEAD(p) \ -do { \ - (p)->next = (p);\ - (p)->prev = (p); \ -} while (0) - - -/* Add an element to a list */ -static inline void list_add(struct list_head *new_entry, - struct list_head *list) -{ - struct list_head *list_next = list->next; - - list->next = new_entry; - new_entry->prev = list; - new_entry->next = list_next; - list_next->prev = new_entry; - -} - -static inline void list_add_tail(struct list_head *new_entry, - struct list_head *list) -{ - struct list_head *list_prev = list->prev; - - list->prev = new_entry; - new_entry->next = list; - new_entry->prev = list_prev; - list_prev->next = new_entry; - -} - - -/* Take an element out of its current list, with or without - * reinitialising the links.of the entry*/ -static inline void list_del(struct list_head *entry) -{ - struct list_head *list_next = entry->next; - struct list_head *list_prev = entry->prev; - - list_next->prev = list_prev; - list_prev->next = list_next; - -} - -static inline void list_del_init(struct list_head *entry) -{ - list_del(entry); - entry->next = entry->prev = entry; -} - - -/* Test if the list is empty */ -static inline int list_empty(struct list_head *entry) -{ - return (entry->next == entry); -} - - -/* list_entry takes a pointer to a list entry and offsets it to that - * we can find a pointer to the object it is embedded in. - */ - - -#define list_entry(entry, type, member) \ - ((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member))) - - -/* list_for_each and list_for_each_safe iterate over lists. - * list_for_each_safe uses temporary storage to make the list delete safe - */ - -#define list_for_each(itervar, list) \ - for (itervar = (list)->next; itervar != (list); itervar = itervar->next) - -#define list_for_each_safe(itervar, save_var, list) \ - for (itervar = (list)->next, save_var = (list)->next->next; \ - itervar != (list); \ - itervar = save_var, save_var = save_var->next) - - -#endif diff --git a/fs/yaffs2/ydirectenv.h b/fs/yaffs2/ydirectenv.h index c2ffbfd..c6614f1 100644 --- a/fs/yaffs2/ydirectenv.h +++ b/fs/yaffs2/ydirectenv.h @@ -77,7 +77,7 @@ void yaffs_qsort(void *aa, size_t n, size_t es, #define YAFFS_ROOT_MODE 0666 #define YAFFS_LOSTNFOUND_MODE 0666 -#include "yaffs_list.h" +#include "linux/list.h" #include "yaffsfs.h" diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 0de0bea..707400e 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -21,6 +21,8 @@ */ #ifndef __ASSEMBLY__ +#include <linux/list.h> + typedef struct global_data { bd_t *bd; unsigned long flags; @@ -61,6 +63,12 @@ typedef struct global_data { unsigned long start_addr_sp; /* start_addr_stackpointer */ unsigned long reloc_off; struct global_data *new_gd; /* relocated global data */ + +#ifdef CONFIG_DM + struct device *dm_root; /* Root instance for Driver Model */ + struct list_head uclass_root; /* Head of core tree */ +#endif + const void *fdt_blob; /* Our device tree, NULL if none */ void *new_fdt; /* Relocated FDT */ unsigned long fdt_size; /* Space reserved for relocated FDT */ diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index f541039..e325df4 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -78,4 +78,108 @@ int gpio_get_value(unsigned gpio); * @return 0 if ok, -1 on error */ int gpio_set_value(unsigned gpio, int value); + +/* State of a GPIO, as reported by get_state() */ +enum { + GPIOF_INPUT = 0, + GPIOF_OUTPUT, + GPIOF_UNKNOWN, +}; + +struct device; + +/** + * struct struct dm_gpio_ops - Driver model GPIO operations + * + * Refer to functions above for description. These function largely copy + * the old API. + * + * This is trying to be close to Linux GPIO API. Once the U-Boot uses the + * new DM GPIO API, this should be really easy to flip over to the Linux + * GPIO API-alike interface. + * + * Akso it would be useful to standardise additional functions like + * pullup, slew rate and drive strength. + * + * gpio_request)( and gpio_free() are optional - if NULL then they will + * not be called. + * + * Note that @offset is the offset from the base GPIO of the device. So + * offset 0 is the device's first GPIO and offset o-1 is the last GPIO, + * where o is the number of GPIO lines controlled by the device. A device + * is typically used to control a single bank of GPIOs. Within complex + * SoCs there may be many banks and therefore many devices all referring + * to the different IO addresses within the SoC. + * + * The uclass combines all GPIO devices togther to provide a consistent + * numbering from 0 to n-1, where n is the number of GPIOs in total across + * all devices. Be careful not to confuse offset with gpio in the parameters. + */ +struct dm_gpio_ops { + int (*request)(struct device *dev, unsigned offset, const char *label); + int (*free)(struct device *dev, unsigned offset); + int (*direction_input)(struct device *dev, unsigned offset); + int (*direction_output)(struct device *dev, unsigned offset, + int value); + int (*get_value)(struct device *dev, unsigned offset); + int (*set_value)(struct device *dev, unsigned offset, int value); + int (*get_function)(struct device *dev, unsigned offset); + int (*get_state)(struct device *dev, unsigned offset, char *state, + int maxlen); +}; + +/** + * struct gpio_dev_priv - information about a device used by the uclass + * + * The uclass combines all active GPIO devices into a unified numbering + * scheme. To do this it maintains some private information aobut each + * device. + * + * To implement driver model support in your GPIO driver, add a probe + * handler, and set @gpio_count and @bank_name correctly in that handler. + * This tells the uclass the name of the GPIO bank and the number of GPIOs + * it contains. + * + * @bank_name: Name of the GPIO device (e.g 'a' means GPIOs will be called + * 'A0', 'A1', etc. + * @gpio_count: Number of GPIOs in this device + * @gpio_base: Base GPIO number for this device. For the first active device + * this will be 0; the numbering for others will follow sequentially so that + * @gpio_base for device 1 will equal the number of GPIOs in device 0. + */ +struct gpio_dev_priv { + const char *bank_name; + unsigned gpio_count; + unsigned gpio_base; +}; + +/* Access the GPIO operations for a device */ +#define gpio_get_ops(dev) ((struct dm_gpio_ops *)(dev)->driver->ops) + +/** + * gpio_get_bank_info - Return information about a GPIO bank/device + * + * This looks up a device and returns both its GPIO base name and the number + * of GPIOs it controls. + * + * @dev: Device to look up + * @offset_count: Returns number of GPIOs within this bank + * @return bank name of this device + */ +const char *gpio_get_bank_info(struct device *dev, int *offset_count); + +/** + * gpio_lookup_name - Look up a GPIO name and return its details + * + * This is used to convert a named GPIO into a device, offset and GPIO + * number. + * + * @name: GPIO name to look up + * @devp: Returns pointer to device which contains this GPIO + * @offsetp: Returns the offset number within this device + * @gpiop: Returns the absolute GPIO number, numbered from 0 + */ +int gpio_lookup_name(const char *name, struct device **devp, + unsigned int *offsetp, unsigned int *gpiop); + #endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/include/command.h b/include/command.h index f782779..d3f700f 100644 --- a/include/command.h +++ b/include/command.h @@ -64,6 +64,15 @@ extern int var_complete(int argc, char * const argv[], char last_char, int maxv, extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp); #endif +/** + * cmd_process_error() - report and process a possible error + * + * @cmdtp: Command which caused the error + * @err: Error code (0 if none, -ve for error, like -EIO) + * @return 0 if there is not error, 1 (CMD_RET_FAILURE) if an error is found + */ +int cmd_process_error(cmd_tbl_t *cmdtp, int err); + /* * Monitor Command * diff --git a/include/common.h b/include/common.h index 96a45a6..15f5834 100644 --- a/include/common.h +++ b/include/common.h @@ -96,6 +96,10 @@ typedef volatile unsigned char vu_char; #include <flash.h> #include <image.h> +#ifdef __LP64__ +#define CONFIG_SYS_SUPPORT_64BIT_DATA +#endif + #ifdef DEBUG #define _DEBUG 1 #else diff --git a/include/configs/at91rm9200ek.h b/include/configs/at91rm9200ek.h index 5d96c31..a30c016 100644 --- a/include/configs/at91rm9200ek.h +++ b/include/configs/at91rm9200ek.h @@ -14,7 +14,7 @@ #ifndef __AT91RM9200EK_CONFIG_H__ #define __AT91RM9200EK_CONFIG_H__ -#include <asm/sizes.h> +#include <linux/sizes.h> /* * set some initial configurations depending on configure target diff --git a/include/configs/bcm28155_ap.h b/include/configs/bcm28155_ap.h index 8e1c81f..e93b855 100644 --- a/include/configs/bcm28155_ap.h +++ b/include/configs/bcm28155_ap.h @@ -7,7 +7,7 @@ #ifndef __BCM28155_AP_H #define __BCM28155_AP_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/sysmap.h> /* Architecture, CPU, chip, mach, etc */ diff --git a/include/configs/beaver.h b/include/configs/beaver.h index 801caca..df9a98b 100644 --- a/include/configs/beaver.h +++ b/include/configs/beaver.h @@ -17,7 +17,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra30-common.h" diff --git a/include/configs/cardhu.h b/include/configs/cardhu.h index e80d1a6..e15b527 100644 --- a/include/configs/cardhu.h +++ b/include/configs/cardhu.h @@ -17,7 +17,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra30-common.h" diff --git a/include/configs/cpuat91.h b/include/configs/cpuat91.h index 49cfabd..ce52101 100644 --- a/include/configs/cpuat91.h +++ b/include/configs/cpuat91.h @@ -10,7 +10,7 @@ #ifndef _CONFIG_CPUAT91_H #define _CONFIG_CPUAT91_H -#include <asm/sizes.h> +#include <linux/sizes.h> #ifdef CONFIG_RAMBOOT #define CONFIG_SKIP_LOWLEVEL_INIT diff --git a/include/configs/dalmore.h b/include/configs/dalmore.h index bdf012b..fd774a3 100644 --- a/include/configs/dalmore.h +++ b/include/configs/dalmore.h @@ -17,7 +17,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra114-common.h" diff --git a/include/configs/devkit3250.h b/include/configs/devkit3250.h index 3d39b10..bd96a7d 100644 --- a/include/configs/devkit3250.h +++ b/include/configs/devkit3250.h @@ -10,7 +10,7 @@ #define __CONFIG_DEVKIT3250_H__ /* SoC and board defines */ -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/cpu.h> /* diff --git a/include/configs/harmony.h b/include/configs/harmony.h index d733be9..c4ff4a2 100644 --- a/include/configs/harmony.h +++ b/include/configs/harmony.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra20-common.h" /* Enable fdt support for Harmony. Flash the image in u-boot-dtb.bin */ diff --git a/include/configs/hummingboard.h b/include/configs/hummingboard.h index 6bf3408..2895523 100644 --- a/include/configs/hummingboard.h +++ b/include/configs/hummingboard.h @@ -14,7 +14,7 @@ #include "mx6_common.h" #include <asm/arch/imx-regs.h> #include <asm/imx-common/gpio.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define CONFIG_MX6 #define CONFIG_DISPLAY_CPUINFO diff --git a/include/configs/mx6sabre_common.h b/include/configs/mx6sabre_common.h index 4efcebf..7a2c172 100644 --- a/include/configs/mx6sabre_common.h +++ b/include/configs/mx6sabre_common.h @@ -12,7 +12,7 @@ #define CONFIG_MX6 #include "mx6_common.h" -#include <asm/sizes.h> +#include <linux/sizes.h> #define CONFIG_DISPLAY_CPUINFO #define CONFIG_DISPLAY_BOARDINFO diff --git a/include/configs/mx6slevk.h b/include/configs/mx6slevk.h index e5bdcc2..1876dbf 100644 --- a/include/configs/mx6slevk.h +++ b/include/configs/mx6slevk.h @@ -10,7 +10,7 @@ #define __CONFIG_H #include <asm/arch/imx-regs.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include "mx6_common.h" #define CONFIG_MX6 diff --git a/include/configs/omap1510.h b/include/configs/omap1510.h index a578edd..41f7973 100644 --- a/include/configs/omap1510.h +++ b/include/configs/omap1510.h @@ -10,7 +10,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ -#include <asm/sizes.h> +#include <linux/sizes.h> /* There are 2 sets of general I/O --> diff --git a/include/configs/omap3_sdp3430.h b/include/configs/omap3_sdp3430.h index 6f1304d..a3e8a59 100644 --- a/include/configs/omap3_sdp3430.h +++ b/include/configs/omap3_sdp3430.h @@ -16,7 +16,7 @@ /* TODO: REMOVE THE FOLLOWING * Retained the following till size.h is removed in u-boot */ -#include <asm/sizes.h> +#include <linux/sizes.h> /* * High Level Configuration Options */ diff --git a/include/configs/paz00.h b/include/configs/paz00.h index 9e2686a..dd0abf8 100644 --- a/include/configs/paz00.h +++ b/include/configs/paz00.h @@ -17,7 +17,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra20-common.h" /* Enable fdt support for Paz00. Flash the image in u-boot-dtb.bin */ diff --git a/include/configs/rpi_b.h b/include/configs/rpi_b.h index 6306d61..ed8b4df 100644 --- a/include/configs/rpi_b.h +++ b/include/configs/rpi_b.h @@ -17,7 +17,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> /* Architecture, CPU, etc.*/ #define CONFIG_ARM1176 diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index e77d06b..6f424e1 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -20,6 +20,14 @@ #define CONFIG_BOOTSTAGE #define CONFIG_BOOTSTAGE_REPORT +#define CONFIG_DM +#define CONFIG_CMD_DEMO +#define CONFIG_CMD_DM +#define CONFIG_DM_DEMO +#define CONFIG_DM_DEMO_SIMPLE +#define CONFIG_DM_DEMO_SHAPE +#define CONFIG_DM_GPIO +#define CONFIG_DM_TEST /* Number of bits in a C 'long' on this architecture */ #define CONFIG_SANDBOX_BITS_PER_LONG 64 @@ -32,6 +40,7 @@ #define CONFIG_FIT_SIGNATURE #define CONFIG_RSA #define CONFIG_CMD_FDT +#define CONFIG_DEFAULT_DEVICE_TREE sandbox #define CONFIG_FS_FAT #define CONFIG_FS_EXT4 diff --git a/include/configs/sbc35_a9g20.h b/include/configs/sbc35_a9g20.h index 7e16c45..a1b5751 100644 --- a/include/configs/sbc35_a9g20.h +++ b/include/configs/sbc35_a9g20.h @@ -12,7 +12,7 @@ /* SoC type is defined in boards.cfg */ #include <asm/hardware.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #if defined(CONFIG_SYS_USE_NANDFLASH) #define CONFIG_ENV_IS_IN_NAND diff --git a/include/configs/seaboard.h b/include/configs/seaboard.h index 2a24ef3..fc4f976 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> /* LP0 suspend / resume */ #define CONFIG_TEGRA_LP0 diff --git a/include/configs/snapper9260.h b/include/configs/snapper9260.h index 94a65c4..1ebee71 100644 --- a/include/configs/snapper9260.h +++ b/include/configs/snapper9260.h @@ -13,7 +13,7 @@ /* SoC type is defined in boards.cfg */ #include <asm/hardware.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define CONFIG_SYS_TEXT_BASE 0x20000000 diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h index 522cd41..0b102aa 100644 --- a/include/configs/tegra-common.h +++ b/include/configs/tegra-common.h @@ -7,7 +7,7 @@ #ifndef _TEGRA_COMMON_H_ #define _TEGRA_COMMON_H_ -#include <asm/sizes.h> +#include <linux/sizes.h> #include <linux/stringify.h> /* diff --git a/include/configs/tnetv107x_evm.h b/include/configs/tnetv107x_evm.h index 48fcb24..162826f 100644 --- a/include/configs/tnetv107x_evm.h +++ b/include/configs/tnetv107x_evm.h @@ -11,7 +11,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include <asm/arch/hardware.h> #include <asm/arch/clock.h> diff --git a/include/configs/trats.h b/include/configs/trats.h index 718107a..7cea259 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -51,7 +51,7 @@ #define MACH_TYPE_TRATS 3928 #define CONFIG_MACH_TYPE MACH_TYPE_TRATS -#include <asm/sizes.h> +#include <linux/sizes.h> /* Size of malloc() pool */ #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 * SZ_1M)) diff --git a/include/configs/trats2.h b/include/configs/trats2.h index e30c428..6d389df 100644 --- a/include/configs/trats2.h +++ b/include/configs/trats2.h @@ -64,7 +64,7 @@ #define CONFIG_DISPLAY_CPUINFO -#include <asm/sizes.h> +#include <linux/sizes.h> /* Size of malloc() pool */ #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 * SZ_1M)) diff --git a/include/configs/trimslice.h b/include/configs/trimslice.h index 8e03f6f..f81cfa2 100644 --- a/include/configs/trimslice.h +++ b/include/configs/trimslice.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra20-common.h" /* Enable fdt support for TrimSlice. Flash the image in u-boot-dtb.bin */ diff --git a/include/configs/udoo.h b/include/configs/udoo.h index 4f51852..a0306de 100644 --- a/include/configs/udoo.h +++ b/include/configs/udoo.h @@ -12,7 +12,7 @@ #include "mx6_common.h" #include <asm/arch/imx-regs.h> #include <asm/imx-common/gpio.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define CONFIG_MX6 #define CONFIG_DISPLAY_CPUINFO diff --git a/include/configs/venice2.h b/include/configs/venice2.h index 91808e9..2d75f50 100644 --- a/include/configs/venice2.h +++ b/include/configs/venice2.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra124-common.h" diff --git a/include/configs/ventana.h b/include/configs/ventana.h index 5c02c96..edf3720 100644 --- a/include/configs/ventana.h +++ b/include/configs/ventana.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra20-common.h" /* Enable fdt support for Ventana. Flash the image in u-boot-dtb.bin */ diff --git a/include/configs/wandboard.h b/include/configs/wandboard.h index 3488472..6c74c72 100644 --- a/include/configs/wandboard.h +++ b/include/configs/wandboard.h @@ -12,7 +12,7 @@ #include "mx6_common.h" #include <asm/arch/imx-regs.h> #include <asm/imx-common/gpio.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #define CONFIG_MX6 #define CONFIG_DISPLAY_CPUINFO diff --git a/include/configs/whistler.h b/include/configs/whistler.h index d5c7e3b..9e09f03 100644 --- a/include/configs/whistler.h +++ b/include/configs/whistler.h @@ -8,7 +8,7 @@ #ifndef __CONFIG_H #define __CONFIG_H -#include <asm/sizes.h> +#include <linux/sizes.h> #include "tegra20-common.h" /* Enable fdt support for Whistler. Flash the image in u-boot-dtb.bin */ diff --git a/include/configs/zynq-common.h b/include/configs/zynq-common.h index 14f0b90..731e69b 100644 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -242,6 +242,7 @@ #ifdef CONFIG_SPL_BUILD #define CONFIG_SYS_DCACHE_OFF #undef CONFIG_FPGA +#undef CONFIG_OF_CONTROL #endif /* MMC support */ diff --git a/include/dm-demo.h b/include/dm-demo.h new file mode 100644 index 0000000..6e38d3c --- /dev/null +++ b/include/dm-demo.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DM_DEMO_H +#define __DM_DEMO_H + +#include <dm.h> + +/** + * struct dm_demo_pdata - configuration data for demo instance + * + * @colour: Color of the demo + * @sides: Numbers of sides + * @default_char: Default ASCII character to output (65 = 'A') + */ +struct dm_demo_pdata { + const char *colour; + int sides; + int default_char; +}; + +struct demo_ops { + int (*hello)(struct device *dev, int ch); + int (*status)(struct device *dev, int *status); +}; + +int demo_hello(struct device *dev, int ch); +int demo_status(struct device *dev, int *status); +int demo_list(void); + +int demo_parse_dt(struct device *dev); + +#endif diff --git a/include/dm.h b/include/dm.h new file mode 100644 index 0000000..8bbb21b --- /dev/null +++ b/include/dm.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_H_ +#define _DM_H + +#include <dm/device.h> +#include <dm/platdata.h> +#include <dm/uclass.h> + +#endif diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h new file mode 100644 index 0000000..c026e8e --- /dev/null +++ b/include/dm/device-internal.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_DEVICE_INTERNAL_H +#define _DM_DEVICE_INTERNAL_H + +struct device; + +/** + * device_bind() - Create a device and bind it to a driver + * + * Called to set up a new device attached to a driver. The device will either + * have platdata, or a device tree node which can be used to create the + * platdata. + * + * Once bound a device exists but is not yet active until device_probe() is + * called. + * + * @parent: Pointer to device's parent, under which this driver will exist + * @drv: Device's driver + * @name: Name of device (e.g. device tree node name) + * @platdata: Pointer to data for this device - the structure is device- + * specific but may include the device's I/O address, etc.. This is NULL for + * devices which use device tree. + * @of_offset: Offset of device tree node for this device. This is -1 for + * devices which don't use device tree. + * @devp: Returns a pointer to the bound device + * @return 0 if OK, -ve on error + */ +int device_bind(struct device *parent, struct driver *drv, + const char *name, void *platdata, int of_offset, + struct device **devp); + +/** + * device_bind_by_name: Create a device and bind it to a driver + * + * This is a helper function used to bind devices which do not use device + * tree. + * + * @parent: Pointer to device's parent + * @info: Name and platdata for this device + * @devp: Returns a pointer to the bound device + * @return 0 if OK, -ve on error + */ +int device_bind_by_name(struct device *parent, const struct driver_info *info, + struct device **devp); + +/** + * device_probe() - Probe a device, activating it + * + * Activate a device so that it is ready for use. All its parents are probed + * first. + * + * @dev: Pointer to device to probe + * @return 0 if OK, -ve on error + */ +int device_probe(struct device *dev); + +/** + * device_remove() - Remove a device, de-activating it + * + * De-activate a device so that it is no longer ready for use. All its + * children are deactivated first. + * + * @dev: Pointer to device to remove + * @return 0 if OK, -ve on error (an error here is normally a very bad thing) + */ +int device_remove(struct device *dev); + +/** + * device_unbind() - Unbind a device, destroying it + * + * Unbind a device and remove all memory used by it + * + * @dev: Pointer to device to unbind + * @return 0 if OK, -ve on error + */ +int device_unbind(struct device *dev); + +#endif diff --git a/include/dm/device.h b/include/dm/device.h new file mode 100644 index 0000000..4cd38ed --- /dev/null +++ b/include/dm/device.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_DEVICE_H +#define _DM_DEVICE_H + +#include <dm/uclass-id.h> +#include <linker_lists.h> +#include <linux/list.h> + +struct driver_info; + +/* Driver is active (probed). Cleared when it is removed */ +#define DM_FLAG_ACTIVATED (1 << 0) + +/* DM is responsible for allocating and freeing platdata */ +#define DM_FLAG_ALLOC_PDATA (2 << 0) + +/** + * struct device - An instance of a driver + * + * This holds information about a device, which is a driver bound to a + * particular port or peripheral (essentially a driver instance). + * + * A device will come into existence through a 'bind' call, either due to + * a U_BOOT_DEVICE() macro (in which case platdata is non-NULL) or a node + * in the device tree (in which case of_offset is >= 0). In the latter case + * we translate the device tree information into platdata in a function + * implemented by the driver ofdata_to_platdata method (called just before the + * probe method if the device has a device tree node. + * + * All three of platdata, priv and uclass_priv can be allocated by the + * driver, or you can use the auto_alloc_size members of struct driver and + * struct uclass_driver to have driver model do this automatically. + * + * @driver: The driver used by this device + * @name: Name of device, typically the FDT node name + * @platdata: Configuration data for this device + * @of_offset: Device tree node offset for this device (- for none) + * @parent: Parent of this device, or NULL for the top level device + * @priv: Private data for this device + * @uclass: Pointer to uclass for this device + * @uclass_priv: The uclass's private data for this device + * @uclass_node: Used by uclass to link its devices + * @child_head: List of children of this device + * @sibling_node: Next device in list of all devices + * @flags: Flags for this device DM_FLAG_... + */ +struct device { + struct driver *driver; + const char *name; + void *platdata; + int of_offset; + struct device *parent; + void *priv; + struct uclass *uclass; + void *uclass_priv; + struct list_head uclass_node; + struct list_head child_head; + struct list_head sibling_node; + uint32_t flags; +}; + +/* Returns the operations for a device */ +#define device_get_ops(dev) (dev->driver->ops) + +/* Returns non-zero if the device is active (probed and not removed) */ +#define device_active(dev) ((dev)->flags & DM_FLAG_ACTIVATED) + +/** + * struct device_id - Lists the compatible strings supported by a driver + * @compatible: Compatible string + * @data: Data for this compatible string + */ +struct device_id { + const char *compatible; + ulong data; +}; + +/** + * struct driver - A driver for a feature or peripheral + * + * This holds methods for setting up a new device, and also removing it. + * The device needs information to set itself up - this is provided either + * by platdata or a device tree node (which we find by looking up + * matching compatible strings with of_match). + * + * Drivers all belong to a uclass, representing a class of devices of the + * same type. Common elements of the drivers can be implemented in the uclass, + * or the uclass can provide a consistent interface to the drivers within + * it. + * + * @name: Device name + * @id: Identiies the uclass we belong to + * @of_match: List of compatible strings to match, and any identifying data + * for each. + * @bind: Called to bind a device to its driver + * @probe: Called to probe a device, i.e. activate it + * @remove: Called to remove a device, i.e. de-activate it + * @unbind: Called to unbind a device from its driver + * @ofdata_to_platdata: Called before probe to decode device tree data + * @priv_auto_alloc_size: If non-zero this is the size of the private data + * to be allocated in the device's ->priv pointer. If zero, then the driver + * is responsible for allocating any data required. + * @platdata_auto_alloc_size: If non-zero this is the size of the + * platform data to be allocated in the device's ->platdata pointer. + * This is typically only useful for device-tree-aware drivers (those with + * an of_match), since drivers which use platdata will have the data + * provided in the U_BOOT_DEVICE() instantiation. + * ops: Driver-specific operations. This is typically a list of function + * pointers defined by the driver, to implement driver functions required by + * the uclass. + */ +struct driver { + char *name; + enum uclass_id id; + const struct device_id *of_match; + int (*bind)(struct device *dev); + int (*probe)(struct device *dev); + int (*remove)(struct device *dev); + int (*unbind)(struct device *dev); + int (*ofdata_to_platdata)(struct device *dev); + int priv_auto_alloc_size; + int platdata_auto_alloc_size; + const void *ops; /* driver-specific operations */ +}; + +/* Declare a new U-Boot driver */ +#define U_BOOT_DRIVER(__name) \ + ll_entry_declare(struct driver, __name, driver) + +/** + * dev_get_platdata() - Get the platform data for a device + * + * This checks that dev is not NULL, but no other checks for now + * + * @dev Device to check + * @return platform data, or NULL if none + */ +void *dev_get_platdata(struct device *dev); + +/** + * dev_get_priv() - Get the private data for a device + * + * This checks that dev is not NULL, but no other checks for now + * + * @dev Device to check + * @return private data, or NULL if none + */ +void *dev_get_priv(struct device *dev); + +#endif diff --git a/include/dm/lists.h b/include/dm/lists.h new file mode 100644 index 0000000..0d09f9a --- /dev/null +++ b/include/dm/lists.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_LISTS_H_ +#define _DM_LISTS_H_ + +#include <dm/uclass-id.h> + +/** + * lists_driver_lookup_name() - Return u_boot_driver corresponding to name + * + * This function returns a pointer to a driver given its name. This is used + * for binding a driver given its name and platdata. + * + * @name: Name of driver to look up + * @return pointer to driver, or NULL if not found + */ +struct driver *lists_driver_lookup_name(const char *name); + +/** + * lists_uclass_lookup() - Return uclass_driver based on ID of the class + * id: ID of the class + * + * This function returns the pointer to uclass_driver, which is the class's + * base structure based on the ID of the class. Returns NULL on error. + */ +struct uclass_driver *lists_uclass_lookup(enum uclass_id id); + +int lists_bind_drivers(struct device *parent); + +int lists_bind_fdt(struct device *parent, const void *blob, int offset); + +#endif diff --git a/include/dm/platdata.h b/include/dm/platdata.h new file mode 100644 index 0000000..0ef3353 --- /dev/null +++ b/include/dm/platdata.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_PLATDATA_H +#define _DM_PLATDATA_H + +struct driver_info { + const char *name; + const void *platdata; +}; + +#define U_BOOT_DEVICE(__name) \ + ll_entry_declare(struct driver_info, __name, driver_info) + +#endif diff --git a/include/dm/root.h b/include/dm/root.h new file mode 100644 index 0000000..0ebccda --- /dev/null +++ b/include/dm/root.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_ROOT_H_ +#define _DM_ROOT_H_ + +struct device; + +/** + * dm_root() - Return pointer to the top of the driver tree + * + * This function returns pointer to the root node of the driver tree, + * + * @return pointer to root device, or NULL if not inited yet + */ +struct device *dm_root(void); + +/** + * dm_scan_platdata() - Scan all platform data and bind drivers + * + * This scans all available platdata and creates drivers for each + * + * @return 0 if OK, -ve on error + */ +int dm_scan_platdata(void); + +/** + * dm_scan_fdt() - Scan the device tree and bind drivers + * + * This scans the device tree and creates a driver for each node + * + * @blob: Pointer to device tree blob + * @return 0 if OK, -ve on error + */ +int dm_scan_fdt(const void *blob); + +/** + * dm_init() - Initialize Driver Model structures + * + * This function will initialize roots of driver tree and class tree. + * This needs to be called before anything uses the DM + * + * @return 0 if OK, -ve on error + */ +int dm_init(void); + +#endif diff --git a/include/dm/test.h b/include/dm/test.h new file mode 100644 index 0000000..eeaa2eb --- /dev/null +++ b/include/dm/test.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2013 Google, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DM_TEST_H +#define __DM_TEST_H + +#include <dm.h> + +/** + * struct dm_test_cdata - configuration data for test instance + * + * @ping_add: Amonut to add each time we get a ping + * @base: Base address of this device + */ +struct dm_test_pdata { + int ping_add; + uint32_t base; +}; + +/** + * struct test_ops - Operations supported by the test device + * + * @ping: Ping operation + * @dev: Device to operate on + * @pingval: Value to ping the device with + * @pingret: Returns resulting value from driver + * @return 0 if OK, -ve on error + */ +struct test_ops { + int (*ping)(struct device *dev, int pingval, int *pingret); +}; + +/* Operations that our test driver supports */ +enum { + DM_TEST_OP_BIND = 0, + DM_TEST_OP_UNBIND, + DM_TEST_OP_PROBE, + DM_TEST_OP_REMOVE, + + /* For uclass */ + DM_TEST_OP_POST_BIND, + DM_TEST_OP_PRE_UNBIND, + DM_TEST_OP_POST_PROBE, + DM_TEST_OP_PRE_REMOVE, + DM_TEST_OP_INIT, + DM_TEST_OP_DESTROY, + + DM_TEST_OP_COUNT, +}; + +/* Test driver types */ +enum { + DM_TEST_TYPE_FIRST = 0, + DM_TEST_TYPE_SECOND, +}; + +/* The number added to the ping total on each probe */ +#define DM_TEST_START_TOTAL 5 + +/** + * struct dm_test_priv - private data for the test devices + */ +struct dm_test_priv { + int ping_total; + int op_count[DM_TEST_OP_COUNT]; +}; + +/** + * struct dm_test_perdev_class_priv - private per-device data for test uclass + */ +struct dm_test_uclass_perdev_priv { + int base_add; +}; + +/** + * struct dm_test_uclass_priv - private data for test uclass + */ +struct dm_test_uclass_priv { + int total_add; +}; + +/* + * Operation counts for the test driver, used to check that each method is + * called correctly + */ +extern int dm_testdrv_op_count[DM_TEST_OP_COUNT]; + +extern struct dm_test_state global_test_state; + +/* + * struct dm_test_state - Entire state of dm test system + * + * This is often abreviated to dms. + * + * @root: Root device + * @testdev: Test device + * @fail_count: Number of tests that failed + * @force_fail_alloc: Force all memory allocs to fail + * @skip_post_probe: Skip uclass post-probe processing + */ +struct dm_test_state { + struct device *root; + struct device *testdev; + int fail_count; + int force_fail_alloc; + int skip_post_probe; +}; + +/* Test flags for each test */ +enum { + DM_TESTF_SCAN_PDATA = 1 << 0, /* test needs platform data */ + DM_TESTF_PROBE_TEST = 1 << 1, /* probe test uclass */ + DM_TESTF_SCAN_FDT = 1 << 2, /* scan device tree */ +}; + +/** + * struct dm_test - Information about a driver model test + * + * @name: Name of test + * @func: Function to call to perform test + * @flags: Flags indicated pre-conditions for test + */ +struct dm_test { + const char *name; + int (*func)(struct dm_test_state *dms); + int flags; +}; + +/* Declare a new driver model test */ +#define DM_TEST(_name, _flags) \ + ll_entry_declare(struct dm_test, _name, dm_test) = { \ + .name = #_name, \ + .flags = _flags, \ + .func = _name, \ + } + +/* Declare ping methods for the drivers */ +int test_ping(struct device *dev, int pingval, int *pingret); +int testfdt_ping(struct device *dev, int pingval, int *pingret); + +/** + * dm_check_operations() - Check that we can perform ping operations + * + * This checks that the ping operations work as expected for a device + * + * @dms: Overall test state + * @dev: Device to test + * @base: Base address, used to check ping return value + * @priv: Pointer to private test information + * @return 0 if OK, -ve on error + */ +int dm_check_operations(struct dm_test_state *dms, struct device *dev, + uint32_t base, struct dm_test_priv *priv); + +/** + * dm_test_main() - Run all the tests + * + * This runs all available driver model tests + * + * @return 0 if OK, -ve on error + */ +int dm_test_main(void); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h new file mode 100644 index 0000000..f0e691c --- /dev/null +++ b/include/dm/uclass-id.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_UCLASS_ID_H +#define _DM_UCLASS_ID_H + +/* TODO(sjg@chromium.org): this could be compile-time generated */ +enum uclass_id { + /* These are used internally by driver model */ + UCLASS_ROOT = 0, + UCLASS_DEMO, + UCLASS_TEST, + UCLASS_TEST_FDT, + + /* U-Boot uclasses start here */ + UCLASS_GPIO, + + UCLASS_COUNT, + UCLASS_INVALID = -1, +}; + +#endif diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h new file mode 100644 index 0000000..cc65d52 --- /dev/null +++ b/include/dm/uclass-internal.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_UCLASS_INTERNAL_H +#define _DM_UCLASS_INTERNAL_H + +/** + * uclass_find_device() - Return n-th child of uclass + * @id: Id number of the uclass + * @index: Position of the child in uclass's list + * #devp: Returns pointer to device, or NULL on error + * + * The device is not prepared for use - this is an internal function + * + * @return the uclass pointer of a child at the given index or + * return NULL on error. + */ +int uclass_find_device(enum uclass_id id, int index, struct device **devp); + +/** + * uclass_bind_device() - Associate device with a uclass + * + * Connect the device into uclass's list of devices. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_bind_device(struct device *dev); + +/** + * uclass_unbind_device() - Deassociate device with a uclass + * + * Disconnect the device from uclass's list of devices. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_unbind_device(struct device *dev); + +/** + * uclass_post_probe_device() - Deal with a device that has just been probed + * + * Perform any post-processing of a probed device that is needed by the + * uclass. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_post_probe_device(struct device *dev); + +/** + * uclass_pre_remove_device() - Handle a device which is about to be removed + * + * Perform any pre-processing of a device that is about to be removed. + * + * @dev: Pointer to the device + * #return 0 on success, -ve on error + */ +int uclass_pre_remove_device(struct device *dev); + +/** + * uclass_find() - Find uclass by its id + * + * @id: Id to serach for + * @return pointer to uclass, or NULL if not found + */ +struct uclass *uclass_find(enum uclass_id key); + +/** + * uclass_destroy() - Destroy a uclass + * + * Destroy a uclass and all its devices + * + * @uc: uclass to destroy + * @return 0 on success, -ve on error + */ +int uclass_destroy(struct uclass *uc); + +#endif diff --git a/include/dm/uclass.h b/include/dm/uclass.h new file mode 100644 index 0000000..cd23cfe --- /dev/null +++ b/include/dm/uclass.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _DM_UCLASS_H +#define _DM_UCLASS_H + +#include <dm/uclass-id.h> +#include <linux/list.h> + +/** + * struct uclass - a U-Boot drive class, collecting together similar drivers + * + * A uclass provides an interface to a particular function, which is + * implemented by one or more drivers. Every driver belongs to a uclass even + * if it is the only driver in that uclass. An example uclass is GPIO, which + * provides the ability to change read inputs, set and clear outputs, etc. + * There may be drivers for on-chip SoC GPIO banks, I2C GPIO expanders and + * PMIC IO lines, all made available in a unified way through the uclass. + * + * @priv: Private data for this uclass + * @uc_drv: The driver for the uclass itself, not to be confused with a + * 'struct driver' + * dev_head: List of devices in this uclass (devices are attached to their + * uclass when their bind method is called) + * @sibling_node: Next uclass in the linked list of uclasses + */ +struct uclass { + void *priv; + struct uclass_driver *uc_drv; + struct list_head dev_head; + struct list_head sibling_node; +}; + +struct device; + +/** + * struct uclass_driver - Driver for the uclass + * + * A uclass_driver provides a consistent interface to a set of related + * drivers. + * + * @name: Name of uclass driver + * @id: ID number of this uclass + * @post_bind: Called after a new device is bound to this uclass + * @pre_unbind: Called before a device is unbound from this uclass + * @post_probe: Called after a new device is probed + * @pre_remove: Called before a device is removed + * @init: Called to set up the uclass + * @destroy: Called to destroy the uclass + * @priv_auto_alloc_size: If non-zero this is the size of the private data + * to be allocated in the uclass's ->priv pointer. If zero, then the uclass + * driver is responsible for allocating any data required. + * @per_device_auto_alloc_size: Each device can hold private data owned + * by the uclass. If required this will be automatically allocated if this + * value is non-zero. + * @ops: Uclass operations, providing the consistent interface to devices + * within the uclass. + */ +struct uclass_driver { + const char *name; + enum uclass_id id; + int (*post_bind)(struct device *dev); + int (*pre_unbind)(struct device *dev); + int (*post_probe)(struct device *dev); + int (*pre_remove)(struct device *dev); + int (*init)(struct uclass *class); + int (*destroy)(struct uclass *class); + int priv_auto_alloc_size; + int per_device_auto_alloc_size; + const void *ops; +}; + +/* Declare a new uclass_driver */ +#define UCLASS_DRIVER(__name) \ + ll_entry_declare(struct uclass_driver, __name, uclass) + +/** + * uclass_get() - Get a uclass based on an ID, creating it if needed + * + * Every uclass is identified by an ID, a number from 0 to n-1 where n is + * the number of uclasses. This function allows looking up a uclass by its + * ID. + * + * @key: ID to look up + * @ucp: Returns pointer to uclass (there is only one per ID) + * @return 0 if OK, -ve on error + */ +int uclass_get(enum uclass_id key, struct uclass **ucp); + +/** + * uclass_get_device() - Get a uclass device based on an ID and index + * + * id: ID to look up + * @index: Device number within that uclass (0=first) + * @ucp: Returns pointer to uclass (there is only one per for each ID) + * @return 0 if OK, -ve on error + */ +int uclass_get_device(enum uclass_id id, int index, struct device **ucp); + +/** + * uclass_first_device() - Get the first device in a uclass + * + * @id: Uclass ID to look up + * @devp: Returns pointer to the first device in that uclass, or NULL if none + * @return 0 if OK (found or not found), -1 on error + */ +int uclass_first_device(enum uclass_id id, struct device **devp); + +/** + * uclass_next_device() - Get the next device in a uclass + * + * @devp: On entry, pointer to device to lookup. On exit, returns pointer + * to the next device in the same uclass, or NULL if none + * @return 0 if OK (found or not found), -1 on error + */ +int uclass_next_device(struct device **devp); + +/** + * uclass_foreach_dev() - Helper function to iteration through devices + * + * This creates a for() loop which works through the available devices in + * a uclass in order from start to end. + * + * @pos: struct device * to hold the current device. Set to NULL when there + * are no more devices. + * uc: uclass to scan + */ +#define uclass_foreach_dev(pos, uc) \ + for (pos = list_entry((&(uc)->dev_head)->next, typeof(*pos), \ + uclass_node); \ + prefetch(pos->uclass_node.next), \ + &pos->uclass_node != (&(uc)->dev_head); \ + pos = list_entry(pos->uclass_node.next, typeof(*pos), \ + uclass_node)) + +#endif diff --git a/include/dm/ut.h b/include/dm/ut.h new file mode 100644 index 0000000..fa9eac0 --- /dev/null +++ b/include/dm/ut.h @@ -0,0 +1,95 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DM_UT_H +#define __DM_UT_H + +struct dm_test_state; + +/** + * ut_fail() - Record failure of a unit test + * + * @dms: Test state + * @fname: Filename where the error occured + * @line: Line number where the error occured + * @func: Function name where the error occured + * @cond: The condition that failed + */ +void ut_fail(struct dm_test_state *dms, const char *fname, int line, + const char *func, const char *cond); + +/** + * ut_failf() - Record failure of a unit test + * + * @dms: Test state + * @fname: Filename where the error occured + * @line: Line number where the error occured + * @func: Function name where the error occured + * @cond: The condition that failed + * @fmt: printf() format string for the error, followed by args + */ +void ut_failf(struct dm_test_state *dms, const char *fname, int line, + const char *func, const char *cond, const char *fmt, ...) + __attribute__ ((format (__printf__, 6, 7))); + + +/* Assert that a condition is non-zero */ +#define ut_assert(cond) \ + if (!(cond)) { \ + ut_fail(dms, __FILE__, __LINE__, __func__, #cond); \ + return -1; \ + } + +/* Assert that a condition is non-zero, with printf() string */ +#define ut_assertf(cond, fmt, args...) \ + if (!(cond)) { \ + ut_failf(dms, __FILE__, __LINE__, __func__, #cond, \ + fmt, ##args); \ + return -1; \ + } + +/* Assert that two int expressions are equal */ +#define ut_asserteq(expr1, expr2) { \ + unsigned int val1 = (expr1), val2 = (expr2); \ + \ + if (val1 != val2) { \ + ut_failf(dms, __FILE__, __LINE__, __func__, \ + #expr1 " == " #expr2, \ + "Expected %d, got %d", val1, val2); \ + return -1; \ + } \ +} + +/* Assert that two string expressions are equal */ +#define ut_asserteq_str(expr1, expr2) { \ + const char *val1 = (expr1), *val2 = (expr2); \ + \ + if (strcmp(val1, val2)) { \ + ut_failf(dms, __FILE__, __LINE__, __func__, \ + #expr1 " = " #expr2, \ + "Expected \"%s\", got \"%s\"", val1, val2); \ + return -1; \ + } \ +} + +/* Assert that two pointers are equal */ +#define ut_asserteq_ptr(expr1, expr2) { \ + const void *val1 = (expr1), *val2 = (expr2); \ + \ + if (val1 != val2) { \ + ut_failf(dms, __FILE__, __LINE__, __func__, \ + #expr1 " = " #expr2, \ + "Expected %p, got %p", val1, val2); \ + return -1; \ + } \ +} + +/* Assert that an operation succeeds (returns 0) */ +#define ut_assertok(cond) ut_asserteq(0, cond) + +#endif diff --git a/include/dm/util.h b/include/dm/util.h new file mode 100644 index 0000000..8be64a9 --- /dev/null +++ b/include/dm/util.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DM_UTIL_H + +void dm_warn(const char *fmt, ...); + +#ifdef DEBUG +void dm_dbg(const char *fmt, ...); +#else +static inline void dm_dbg(const char *fmt, ...) +{ +} +#endif + +struct list_head; + +/** + * list_count_items() - Count number of items in a list + * + * @param head: Head of list + * @return number of items, or 0 if empty + */ +int list_count_items(struct list_head *head); + +#endif diff --git a/include/linux/sizes.h b/include/linux/sizes.h new file mode 100644 index 0000000..ce3e815 --- /dev/null +++ b/include/linux/sizes.h @@ -0,0 +1,47 @@ +/* + * include/linux/sizes.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LINUX_SIZES_H__ +#define __LINUX_SIZES_H__ + +#define SZ_1 0x00000001 +#define SZ_2 0x00000002 +#define SZ_4 0x00000004 +#define SZ_8 0x00000008 +#define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 + +#endif /* __LINUX_SIZES_H__ */ diff --git a/include/netdev.h b/include/netdev.h index 3705629..32b5073 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -86,10 +86,12 @@ int uli526x_initialize(bd_t *bis); int armada100_fec_register(unsigned long base_addr); int xilinx_axiemac_initialize(bd_t *bis, unsigned long base_addr, unsigned long dma_addr); +int xilinx_emaclite_of_init(const void *blob); int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, int txpp, int rxpp); int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, unsigned long ctrl_addr); +int zynq_gem_of_init(const void *blob); int zynq_gem_initialize(bd_t *bis, int base_addr, int phy_addr, u32 emio); /* * As long as the Xilinx xps_ll_temac ethernet driver has not its own interface diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h index ce3dd2c..70e48f8 100644 --- a/include/usb/s3c_udc.h +++ b/include/usb/s3c_udc.h @@ -10,7 +10,7 @@ #define __S3C_USB_GADGET #include <asm/errno.h> -#include <asm/sizes.h> +#include <linux/sizes.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/list.h> diff --git a/lib/display_options.c b/lib/display_options.c index 4a972b0..4c0c886 100644 --- a/lib/display_options.c +++ b/lib/display_options.c @@ -87,11 +87,19 @@ int print_buffer(ulong addr, const void *data, uint width, uint count, { /* linebuf as a union causes proper alignment */ union linebuf { +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + uint64_t uq[MAX_LINE_LENGTH_BYTES/sizeof(uint64_t) + 1]; +#endif uint32_t ui[MAX_LINE_LENGTH_BYTES/sizeof(uint32_t) + 1]; uint16_t us[MAX_LINE_LENGTH_BYTES/sizeof(uint16_t) + 1]; uint8_t uc[MAX_LINE_LENGTH_BYTES/sizeof(uint8_t) + 1]; } lb; int i; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + uint64_t x; +#else + uint32_t x; +#endif if (linelen*width > MAX_LINE_LENGTH_BYTES) linelen = MAX_LINE_LENGTH_BYTES / width; @@ -108,14 +116,21 @@ int print_buffer(ulong addr, const void *data, uint width, uint count, /* Copy from memory into linebuf and print hex values */ for (i = 0; i < thislinelen; i++) { - uint32_t x; if (width == 4) x = lb.ui[i] = *(volatile uint32_t *)data; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + else if (width == 8) + x = lb.uq[i] = *(volatile uint64_t *)data; +#endif else if (width == 2) x = lb.us[i] = *(volatile uint16_t *)data; else x = lb.uc[i] = *(volatile uint8_t *)data; +#ifdef CONFIG_SYS_SUPPORT_64BIT_DATA + printf(" %0*llx", width * 2, x); +#else printf(" %0*x", width * 2, x); +#endif data += width; } @@ -55,7 +55,9 @@ CONFIG_NAME="${7%_config}" arch="$2" cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'` spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'` -if [ "$6" = "-" ] ; then +if [ "$6" = "<none>" ] ; then + board= +elif [ "$6" = "-" ] ; then board=${BOARD_NAME} else board="$6" @@ -177,8 +179,8 @@ echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h [ "${soc}" ] && echo "#define CONFIG_SYS_SOC \"${soc}\"" >> config.h +[ "${board}" ] && echo "#define CONFIG_BOARDDIR board/$BOARDDIR" >> config.h cat << EOF >> config.h -#define CONFIG_BOARDDIR board/$BOARDDIR #include <config_cmd_defaults.h> #include <config_defaults.h> #include <configs/${CONFIG_NAME}.h> diff --git a/nand_spl/board/sheldon/simpc8313/Makefile b/nand_spl/board/sheldon/simpc8313/Makefile index 32afc27..8b29e97 100644 --- a/nand_spl/board/sheldon/simpc8313/Makefile +++ b/nand_spl/board/sheldon/simpc8313/Makefile @@ -7,6 +7,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +include $(srctree)/$(src)/config.mk + nandobj := $(OBJTREE)/nand_spl/ LDSCRIPT= $(TOPDIR)/nand_spl/board/$(BOARDDIR)/u-boot.lds diff --git a/board/sheldon/simpc8313/config.mk b/nand_spl/board/sheldon/simpc8313/config.mk index d1b4e2e..d1b4e2e 100644 --- a/board/sheldon/simpc8313/config.mk +++ b/nand_spl/board/sheldon/simpc8313/config.mk diff --git a/post/lib_powerpc/fpu/Makefile b/post/lib_powerpc/fpu/Makefile index 556a833..5c2e804 100644 --- a/post/lib_powerpc/fpu/Makefile +++ b/post/lib_powerpc/fpu/Makefile @@ -19,5 +19,5 @@ ccflags-y := -mhard-float -fkeep-inline-functions obj-y := $(objs-before-objcopy:.o=_.o) OBJCOPYFLAGS := -R .gnu.attributes -$(obj)/%_.o: $(obj)/%.o +$(obj)/%_.o: $(obj)/%.o FORCE $(call if_changed,objcopy) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index df0820c..3fed5e4 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -6,6 +6,7 @@ # Licensed under the terms of the GNU GPL License version 2 use strict; +use POSIX; my $P = $0; $P =~ s@.*/@@g; @@ -27,13 +28,20 @@ my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; +my $fix = 0; +my $fix_inplace = 0; my $root; my %debug; +my %camelcase = (); +my %use_type = (); +my @use = (); my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; my $max_line_length = 80; +my $ignore_perl_version = 0; +my $minimum_perl_version = 5.10.0; sub help { my ($exitcode) = @_; @@ -51,6 +59,7 @@ Options: --terse one line per report -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests + --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types --max-line-length=n set the maximum line length, if exceeded, warn --show-types show the message "types" in the output @@ -63,6 +72,16 @@ Options: is all off) --test-only=WORD report only warnings/errors containing WORD literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + "<inputfile>.EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style + --fix-inplace EXPERIMENTAL - may create horrible results + Is the same as --fix, but overwrites the input + file. It's your fault if there's no backup or git + --ignore-perl-version override checking of perl version. expect + runtime errors. -h, --help, --version display this help and exit When FILE is - read standard input. @@ -108,13 +127,16 @@ GetOptions( 'subjective!' => \$check, 'strict!' => \$check, 'ignore=s' => \@ignore, + 'types=s' => \@use, 'show-types!' => \$show_types, 'max-line-length=i' => \$max_line_length, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, - + 'fix!' => \$fix, + 'fix-inplace!' => \$fix_inplace, + 'ignore-perl-version!' => \$ignore_perl_version, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'h|help' => \$help, @@ -123,26 +145,54 @@ GetOptions( help(0) if ($help); +$fix = 1 if ($fix_inplace); + my $exit = 0; +if ($^V && $^V lt $minimum_perl_version) { + printf "$P: requires at least perl version %vd\n", $minimum_perl_version; + if (!$ignore_perl_version) { + exit(1); + } +} + if ($#ARGV < 0) { print "$P: no input files\n"; exit(1); } -@ignore = split(/,/, join(',',@ignore)); -foreach my $word (@ignore) { - $word =~ s/\s*\n?$//g; - $word =~ s/^\s*//g; - $word =~ s/\s+/ /g; - $word =~ tr/[a-z]/[A-Z]/; +sub hash_save_array_words { + my ($hashRef, $arrayRef) = @_; - next if ($word =~ m/^\s*#/); - next if ($word =~ m/^\s*$/); + my @array = split(/,/, join(',', @$arrayRef)); + foreach my $word (@array) { + $word =~ s/\s*\n?$//g; + $word =~ s/^\s*//g; + $word =~ s/\s+/ /g; + $word =~ tr/[a-z]/[A-Z]/; - $ignore_type{$word}++; + next if ($word =~ m/^\s*#/); + next if ($word =~ m/^\s*$/); + + $hashRef->{$word}++; + } } +sub hash_show_words { + my ($hashRef, $prefix) = @_; + + if ($quiet == 0 && keys %$hashRef) { + print "NOTE: $prefix message types:"; + foreach my $word (sort keys %$hashRef) { + print " $word"; + } + print "\n\n"; + } +} + +hash_save_array_words(\%ignore_type, \@ignore); +hash_save_array_words(\%use_type, \@use); + my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; @@ -198,6 +248,11 @@ our $Sparse = qr{ __ref| __rcu }x; +our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; +our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; +our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; +our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; +our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check @@ -219,7 +274,7 @@ our $Attribute = qr{ __deprecated| __read_mostly| __kprobes| - __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| + $InitAttribute| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| @@ -230,20 +285,26 @@ our $Inline = qr{inline|__always_inline|noinline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|(?i)(?:0x[0-9a-f]+|[0-9]+)[ul]*}; +our $Constant = qr{$Float|$Binary|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|>}; +our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; our $NonptrType; +our $NonptrTypeWithAttr; our $Type; our $Declare; @@ -269,12 +330,14 @@ our $typeTypedefs = qr{(?x: our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|)| - [a-z0-9]+_(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| WARN(?:_RATELIMIT|_ONCE|)| panic| debug| printf| - MODULE_[A-Z_]+ + puts| + MODULE_[A-Z_]+| + seq_vprintf|seq_printf|seq_puts )}; our $signature_tags = qr{(?xi: @@ -283,6 +346,7 @@ our $signature_tags = qr{(?xi: Tested-by:| Reviewed-by:| Reported-by:| + Suggested-by:| To:| Cc: )}; @@ -307,6 +371,12 @@ our @typeList = ( qr{${Ident}_handler}, qr{${Ident}_handler_fn}, ); +our @typeListWithAttr = ( + @typeList, + qr{struct\s+$InitAttribute\s+$Ident}, + qr{union\s+$InitAttribute\s+$Ident}, +); + our @modifierList = ( qr{fastcall}, ); @@ -320,6 +390,7 @@ our $allowed_asm_includes = qr{(?x: sub build_types { my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* @@ -330,6 +401,15 @@ sub build_types { ) (?:\s+$Modifier|\s+const)* }x; + $NonptrTypeWithAttr = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\([^\)]*\)| + (?:$typeTypedefs\b)| + (?:${allWithAttr}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; $Type = qr{ $NonptrType (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)? @@ -339,7 +419,6 @@ sub build_types { } build_types(); - our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg @@ -359,10 +438,95 @@ sub deparenthesize { return $string; } +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { + $camelcase{$1} = 1; + } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { + $camelcase{$1} = 1; + } + } +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + my $camelcase_cache = ""; + my @include_files = (); + + $camelcase_seeded = 1; + + if (-e ".git") { + my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; + } else { + my $last_mod_date = 0; + $files = `find $root/include -name "*.h"`; + @include_files = split('\n', $files); + foreach my $file (@include_files) { + my $date = POSIX::strftime("%Y%m%d%H%M", + localtime((stat $file)[9])); + $last_mod_date = $date if ($last_mod_date < $date); + } + $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; + } + + if ($camelcase_cache ne "" && -f $camelcase_cache) { + open(my $camelcase_file, '<', "$camelcase_cache") + or warn "$P: Can't read '$camelcase_cache' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } + + if (-e ".git") { + $files = `git ls-files "include/*.h"`; + @include_files = split('\n', $files); + } + + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + + if ($camelcase_cache ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_cache") + or warn "$P: Can't write '$camelcase_cache' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } +} + $chk_signoff = 0 if ($file); my @rawlines = (); my @lines = (); +my @fixed = (); my $vname; for my $filename (@ARGV) { my $FILE; @@ -390,6 +554,7 @@ for my $filename (@ARGV) { } @rawlines = (); @lines = (); + @fixed = (); } exit($exit); @@ -398,7 +563,7 @@ sub top_of_kernel_tree { my ($root) = @_; my @tree_check = ( - "COPYING", "CREDITS", "Kbuild", "Makefile", + "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", "README", "Documentation", "arch", "include", "drivers", "fs", "init", "ipc", "kernel", "lib", "scripts", ); @@ -430,7 +595,7 @@ sub parse_email { $comment = $2 if defined $2; $formatted_email =~ s/$address.*$//; $name = $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both @@ -445,9 +610,9 @@ sub parse_email { } } - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $address = trim($address); $address =~ s/^\<|\>$//g; if ($name =~ /[^\w \-]/i) { ##has "must quote" chars @@ -463,9 +628,9 @@ sub format_email { my $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $address = trim($address); if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?<!\\)"/\\"/g; ##escape quotes @@ -630,6 +795,13 @@ sub sanitise_line { return $res; } +sub get_quoted_string { + my ($line, $rawline) = @_; + + return "" if ($line !~ m/(\"[X]+\")/g); + return substr($rawline, $-[0], $+[0] - $-[0]); +} + sub ctx_statement_block { my ($linenr, $remain, $off) = @_; my $line = $linenr - 1; @@ -1252,7 +1424,9 @@ sub possible { my $prefix = ''; sub show_type { - return !defined $ignore_type{$_[0]}; + return defined $use_type{$_[0]} if (scalar keys %use_type > 0); + + return !defined $ignore_type{$_[0]}; } sub report { @@ -1280,19 +1454,25 @@ sub ERROR { if (report("ERROR", $_[0], $_[1])) { our $clean = 0; our $cnt_error++; + return 1; } + return 0; } sub WARN { if (report("WARNING", $_[0], $_[1])) { our $clean = 0; our $cnt_warn++; + return 1; } + return 0; } sub CHK { if ($check && report("CHECK", $_[0], $_[1])) { our $clean = 0; our $cnt_chk++; + return 1; } + return 0; } sub check_absolute_file { @@ -1323,6 +1503,53 @@ sub check_absolute_file { } } +sub trim { + my ($string) = @_; + + $string =~ s/^\s+|\s+$//g; + + return $string; +} + +sub ltrim { + my ($string) = @_; + + $string =~ s/^\s+//; + + return $string; +} + +sub rtrim { + my ($string) = @_; + + $string =~ s/\s+$//; + + return $string; +} + +sub string_find_replace { + my ($string, $find, $replace) = @_; + + $string =~ s/$find/$replace/g; + + return $string; +} + +sub tabify { + my ($leading) = @_; + + my $source_indent = 8; + my $max_spaces_before_tab = $source_indent - 1; + my $spaces_to_tab = " " x $source_indent; + + #convert leading spaces to tabs + 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; + #Remove spaces before a tab + 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; + + return "$leading"; +} + sub pos_last_openparen { my ($line) = @_; @@ -1400,7 +1627,7 @@ sub process { my %suppress_export; my $suppress_statement = 0; - my %camelcase = (); + my %signatures = (); # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. @@ -1408,12 +1635,16 @@ sub process { my @setup_docs = (); my $setup_docs = 0; + my $camelcase_file_seeded = 0; + sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; + push(@fixed, $rawline) if ($fix); + if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/kernel-parameters.txt$@) { @@ -1491,6 +1722,8 @@ sub process { $linenr = 0; foreach my $line (@lines) { $linenr++; + my $sline = $line; #copy of $line + $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; @@ -1546,11 +1779,11 @@ sub process { # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $p1_prefix = $1; @@ -1578,7 +1811,8 @@ sub process { # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + if ($realfile !~ m@scripts/@ && + $realfile !~ /\.(py|pl|awk|sh)$/) { ERROR("EXECUTE_PERMISSIONS", "do not set execute permissions for source files\n" . $permhere); } @@ -1604,16 +1838,29 @@ sub process { "Non-standard signature: $sign_off\n" . $herecurr); } if (defined $space_before && $space_before ne "") { - WARN("BAD_SIGN_OFF", - "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr); + if (WARN("BAD_SIGN_OFF", + "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] = + "$ucfirst_sign_off $email"; + } } if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { - WARN("BAD_SIGN_OFF", - "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr); + if (WARN("BAD_SIGN_OFF", + "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] = + "$ucfirst_sign_off $email"; + } + } if (!defined $space_after || $space_after ne " ") { - WARN("BAD_SIGN_OFF", - "Use a single space after $ucfirst_sign_off\n" . $herecurr); + if (WARN("BAD_SIGN_OFF", + "Use a single space after $ucfirst_sign_off\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] = + "$ucfirst_sign_off $email"; + } } my ($email_name, $email_address, $comment) = parse_email($email); @@ -1634,6 +1881,17 @@ sub process { "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); } } + +# Check for duplicate signatures + my $sig_nospace = $line; + $sig_nospace =~ s/\s//g; + $sig_nospace = lc($sig_nospace); + if (defined $signatures{$sig_nospace}) { + WARN("BAD_SIGN_OFF", + "Duplicate signature\n" . $herecurr); + } else { + $signatures{$sig_nospace} = 1; + } } # Check for wrappage within a valid hunk of the file @@ -1698,16 +1956,33 @@ sub process { #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("DOS_LINE_ENDINGS", - "DOS line endings\n" . $herevet); - + if (ERROR("DOS_LINE_ENDINGS", + "DOS line endings\n" . $herevet) && + $fix) { + $fixed[$linenr - 1] =~ s/[\s\015]+$//; + } } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("TRAILING_WHITESPACE", - "trailing whitespace\n" . $herevet); + if (ERROR("TRAILING_WHITESPACE", + "trailing whitespace\n" . $herevet) && + $fix) { + $fixed[$linenr - 1] =~ s/\s+$//; + } + $rpt_cleaners = 1; } +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_type = \&ERROR; + $msg_type = \&CHK if ($file); + &{$msg_type}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. @@ -1768,6 +2043,33 @@ sub process { "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); } +# check for DT compatible documentation + if (defined $root && $realfile =~ /\.dts/ && + $rawline =~ /^\+\s*compatible\s*=/) { + my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; + + foreach my $compat (@compats) { + my $compat2 = $compat; + my $dt_path = $root . "/Documentation/devicetree/bindings/"; + $compat2 =~ s/\,[a-z]*\-/\,<\.\*>\-/; + `grep -Erq "$compat|$compat2" $dt_path`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); + } + + my $vendor = $compat; + my $vendor_path = $dt_path . "vendor-prefixes.txt"; + next if (! -f $vendor_path); + $vendor =~ s/^([a-zA-Z0-9]+)\,.*/$1/; + `grep -Eq "$vendor" $vendor_path`; + if ( $? >> 8 ) { + WARN("UNDOCUMENTED_DT_STRING", + "DT compatible string vendor \"$vendor\" appears un-documented -- check $vendor_path\n" . $herecurr); + } + } + } + # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); @@ -1783,24 +2085,24 @@ sub process { } # Check for user-visible strings broken across lines, which breaks the ability -# to grep for the string. Limited to strings used as parameters (those -# following an open parenthesis), which almost completely eliminates false -# positives, as well as warning only once per parameter rather than once per -# line of the string. Make an exception when the previous string ends in a -# newline (multiple lines in one string constant) or \n\t (common in inline -# assembly to indent the instruction on the following line). +# to grep for the string. Make exceptions when the previous string ends in a +# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' +# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value if ($line =~ /^\+\s*"/ && $prevline =~ /"\s*$/ && - $prevline =~ /\(/ && - $prevrawline !~ /\\n(?:\\t)*"\s*$/) { + $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { WARN("SPLIT_STRING", "quoted string split across lines\n" . $hereprev); } # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { - WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", - "unnecessary whitespace before a quoted newline\n" . $herecurr); + if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", + "unnecessary whitespace before a quoted newline\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; + } + } # check for adding lines without a newline. @@ -1831,16 +2133,25 @@ sub process { if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - ERROR("CODE_INDENT", - "code indent should use tabs where possible\n" . $herevet); $rpt_cleaners = 1; + if (ERROR("CODE_INDENT", + "code indent should use tabs where possible\n" . $herevet) && + $fix) { + $fixed[$linenr - 1] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } } # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - WARN("SPACE_BEFORE_TAB", - "please, no space before tabs\n" . $herevet); + if (WARN("SPACE_BEFORE_TAB", + "please, no space before tabs\n" . $herevet) && + $fix) { + while ($fixed[$linenr - 1] =~ + s/(^\+.*) {8,8}+\t/$1\t\t/) {} + while ($fixed[$linenr - 1] =~ + s/(^\+.*) +\t/$1\t/) {} + } } # check for && or || at the start of a line @@ -1868,25 +2179,43 @@ sub process { if ($newindent ne $goodtabindent && $newindent ne $goodspaceindent) { - CHK("PARENTHESIS_ALIGNMENT", - "Alignment should match open parenthesis\n" . $hereprev); + + if (CHK("PARENTHESIS_ALIGNMENT", + "Alignment should match open parenthesis\n" . $hereprev) && + $fix && $line =~ /^\+/) { + $fixed[$linenr - 1] =~ + s/^\+[ \t]*/\+$goodtabindent/; + } } } } - if ($line =~ /^\+.*\*[ \t]*\)[ \t]+/) { - CHK("SPACING", - "No space is necessary after a cast\n" . $hereprev); + if ($line =~ /^\+.*\*[ \t]*\)[ \t]+(?!$Assignment|$Arithmetic)/) { + if (CHK("SPACING", + "No space is necessary after a cast\n" . $hereprev) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(\+.*\*[ \t]*\))[ \t]+/$1/; + } } if ($realfile =~ m@^(drivers/net/|net/)@ && - $rawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $prevrawline =~ /^\+[ \t]*$/) { + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/) { WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments start with * on subsequent lines\n" . $hereprev); + } + + if ($realfile =~ m@^(drivers/net/|net/)@ && $rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ @@ -1900,10 +2229,13 @@ sub process { # 1) within comments # 2) indented preprocessor commands # 3) hanging labels - if ($rawline =~ /^\+ / && $line !~ /\+ *(?:$;|#|$Ident:)/) { + if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; - WARN("LEADING_SPACE", - "please, no spaces at the start of a line\n" . $herevet); + if (WARN("LEADING_SPACE", + "please, no spaces at the start of a line\n" . $herevet) && + $fix) { + $fixed[$linenr - 1] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; + } } # check we are in a valid C source file if not then ignore this hunk @@ -1944,7 +2276,7 @@ sub process { $realline_next); #print "LINE<$line>\n"; if ($linenr >= $suppress_statement && - $realcnt && $line =~ /.\s*\S/) { + $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; @@ -2193,7 +2525,7 @@ sub process { $prev_values = substr($curr_values, -1); #ignore lines not being added - if ($line=~/^[^\+]/) {next;} + next if ($line =~ /^[^\+]/); # TEST: allow direct testing of the type matcher. if ($dbg_type) { @@ -2244,8 +2576,15 @@ sub process { # no C99 // comments if ($line =~ m{//}) { - ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr); + if (ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$linenr - 1]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$linenr - 1] =~ s@\/\/(.*)$@/\* $comment \*/@; + } + } } # Remove C99 comments. $line =~ s@//.*@@; @@ -2297,16 +2636,22 @@ sub process { } # check for global initialisers. - if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { - ERROR("GLOBAL_INITIALISERS", - "do not initialise globals to 0 or NULL\n" . - $herecurr); + if ($line =~ /^\+(\s*$Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/) { + if (ERROR("GLOBAL_INITIALISERS", + "do not initialise globals to 0 or NULL\n" . + $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/($Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/$1;/; + } } # check for static initialisers. - if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { - ERROR("INITIALISED_STATIC", - "do not initialise statics to 0 or NULL\n" . - $herecurr); + if ($line =~ /^\+.*\bstatic\s.*=\s*(0|NULL|false)\s*;/) { + if (ERROR("INITIALISED_STATIC", + "do not initialise statics to 0 or NULL\n" . + $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/(\bstatic\s.*?)\s*=\s*(0|NULL|false)\s*;/$1;/; + } } # check for static const char * arrays. @@ -2323,10 +2668,22 @@ sub process { $herecurr); } -# check for declarations of struct pci_device_id - if ($line =~ /\bstruct\s+pci_device_id\s+\w+\s*\[\s*\]\s*\=\s*\{/) { - WARN("DEFINE_PCI_DEVICE_TABLE", - "Use DEFINE_PCI_DEVICE_TABLE for struct pci_device_id\n" . $herecurr); +# check for function declarations without arguments like "int foo()" + if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { + if (ERROR("FUNCTION_WITHOUT_ARGS", + "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; + } + } + +# check for uses of DEFINE_PCI_DEVICE_TABLE + if ($line =~ /\bDEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=/) { + if (WARN("DEFINE_PCI_DEVICE_TABLE", + "Prefer struct pci_device_id over deprecated DEFINE_PCI_DEVICE_TABLE\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b(?:static\s+|)DEFINE_PCI_DEVICE_TABLE\s*\(\s*(\w+)\s*\)\s*=\s*/static const struct pci_device_id $1\[\] = /; + } } # check for new typedefs, only function parameters and sparse annotations @@ -2344,7 +2701,7 @@ sub process { # (char*[ const]) while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { #print "AA<$1>\n"; - my ($from, $to) = ($2, $2); + my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2354,15 +2711,22 @@ sub process { while ($to =~ s/\*\s+\*/\*\*/) { } - #print "from<$from> to<$to>\n"; +## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { - ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$linenr - 1] =~ + s@\Q$sub_from\E@$sub_to@; + } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; - my ($from, $to, $ident) = ($2, $2, $3); + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2374,10 +2738,18 @@ sub process { # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; - #print "from<$from> to<$to> ident<$ident>\n"; +## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { - ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$linenr - 1] =~ + s@\Q$sub_from\E@$sub_to@; + } } } @@ -2434,8 +2806,12 @@ sub process { } if ($line =~ /\bpr_warning\s*\(/) { - WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr); + if (WARN("PREFER_PR_LEVEL", + "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\bpr_warning\b/pr_warn/; + } } if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { @@ -2463,9 +2839,72 @@ sub process { } # missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - WARN("SPACING", - "missing space after $1 definition\n" . $herecurr); + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } + } + +# Function pointer declarations +# check spacing between type, funcptr, and args +# canonical declaration is "type (*funcptr)(args...)" +# +# the $Declare variable will capture all spaces after the type +# so check it for trailing missing spaces or multiple spaces + if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)$Ident(\s*)\)(\s*)\(/) { + my $declare = $1; + my $pre_pointer_space = $2; + my $post_pointer_space = $3; + my $funcname = $4; + my $post_funcname_space = $5; + my $pre_args_space = $6; + + if ($declare !~ /\s$/) { + WARN("SPACING", + "missing space after return type\n" . $herecurr); + } + +# unnecessary space "type (*funcptr)(args...)" + elsif ($declare =~ /\s{2,}$/) { + WARN("SPACING", + "Multiple spaces after return type\n" . $herecurr); + } + +# unnecessary space "type ( *funcptr)(args...)" + if (defined $pre_pointer_space && + $pre_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer open parenthesis\n" . $herecurr); + } + +# unnecessary space "type (* funcptr)(args...)" + if (defined $post_pointer_space && + $post_pointer_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr )(args...)" + if (defined $post_funcname_space && + $post_funcname_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space after function pointer name\n" . $herecurr); + } + +# unnecessary space "type (*funcptr) (args...)" + if (defined $pre_args_space && + $pre_args_space =~ /^\s/) { + WARN("SPACING", + "Unnecessary space before function pointer arguments\n" . $herecurr); + } + + if (show_type("SPACING") && $fix) { + $fixed[$linenr - 1] =~ + s/^(.\s*$Declare)\(\s*\*\s*($Ident)\s*\)\s*\(/rtrim($1) . " " . "\(\*$2\)\("/ex; + } } # check for spacing round square brackets; allowed: @@ -2477,8 +2916,12 @@ sub process { if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /[{,]\s+$/) { - ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr); + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(\+.*?)\s+\[/$1\[/; + } } } @@ -2495,7 +2938,6 @@ sub process { __attribute__|format|__extension__| asm|__asm__)$/x) { - # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. @@ -2509,32 +2951,53 @@ sub process { } elsif ($ctx =~ /$Type$/) { } else { - WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr); + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\b$name\s+\(/$name\(/; + } } } -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;/) { - CHK("SPACING", - "space prohibited before semicolon\n" . $herecurr); - } - # Check operator spacing. if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| - \?|: + \?:|\?|: }x; my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); my $off = 0; + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + my $blank = copy_spacing($opline); + my $last_after = -1; for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + $off += length($elements[$n]); # Pick up the preceding and succeeding characters. @@ -2591,8 +3054,11 @@ sub process { } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } # // is a comment @@ -2603,15 +3069,25 @@ sub process { # : when part of a bitfield } elsif ($op eq '->' || $opv eq ':B') { if ($ctx =~ /Wx.|.xW/) { - ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } # , must have a space on the right. } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + $last_after = $n; + } } # '*' as part of a type definition -- reported already. @@ -2625,34 +3101,56 @@ sub process { $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + if ($n != $last_after + 2) { + $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); + $line_fixed = 1; + } + } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } if ($ctx =~ /ExW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } - # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or @@ -2661,17 +3159,25 @@ sub process { $op eq '%') { if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . - $hereptr); + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } # All the others need spaces both sides. @@ -2684,21 +3190,49 @@ sub process { ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { - $ok = 1; - } - - # Ignore ?: - if (($opv eq ':O' && $ca =~ /\?$/) || - ($op eq '?' && $cc =~ /^:/)) { - $ok = 1; + $ok = 1; } + # messages are ERROR, but ?: are CHK if ($ok == 0) { - ERROR("SPACING", - "spaces required around that '$op' $at\n" . $hereptr); + my $msg_type = \&ERROR; + $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); + + if (&{$msg_type}("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + $line_fixed = 1; + } } } $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; + } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$linenr - 1]) { + $fixed[$linenr - 1] = $fixed_line; + } + + + } + +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;\s*$/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$linenr - 1] =~ + s/^(\+.*\S)\s+;/$1;/; } } @@ -2727,63 +3261,87 @@ sub process { #need space before brace following if, while, etc if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || $line =~ /do{/) { - ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr); + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/^(\+.*(?:do|\))){/$1 {/; + } } +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { - ERROR("SPACING", - "space required after that close brace '}'\n" . $herecurr); + if (ERROR("SPACING", + "space required after that close brace '}'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/}((?!(?:,|;|\)))\S)/} $1/; + } } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\[\s+/\[/; + } } if ($line =~ /\s\]/) { - ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\s+\]/\]/; + } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { - ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\(\s+/\(/; + } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { - ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\s+\)/\)/; + } } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr); + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.)\s+/$1/; + } } # Return is not a function. - if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { + if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; - my $value = $2; - - # Flatten any parentheses - $value =~ s/\(/ \(/g; - $value =~ s/\)/\) /g; - while ($value =~ s/\[[^\[\]]*\]/1/ || - $value !~ /(?:$Ident|-?$Constant)\s* - $Compare\s* - (?:$Ident|-?$Constant)/x && - $value =~ s/\([^\(\)]*\)/1/) { - } -#print "value<$value>\n"; - if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) { + if ($^V && $^V ge 5.10.0 && + $stat =~ /^.\s*return\s*$balanced_parens\s*;\s*$/) { ERROR("RETURN_PARENTHESES", "return is not a function, parentheses are not required\n" . $herecurr); @@ -2792,6 +3350,21 @@ sub process { "space required before the open parenthesis '('\n" . $herecurr); } } + +# if statements using unnecessary parentheses - ie: if ((foo == bar)) + if ($^V && $^V ge 5.10.0 && + $line =~ /\bif\s*((?:\(\s*){2,})/) { + my $openparens = $1; + my $count = $openparens =~ tr@\(@\(@; + my $msg = ""; + if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { + my $comp = $4; #Not $1 because of $LvalOrFunc + $msg = " - maybe == should be = ?" if ($comp eq "=="); + WARN("UNNECESSARY_PARENTHESES", + "Unnecessary parentheses$msg\n" . $herecurr); + } + } + # Return of what appears to be an errno should normally be -'ve if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { my $name = $1; @@ -2802,8 +3375,13 @@ sub process { } # Need a space before open parenthesis after if, while etc - if ($line=~/\b(if|while|for|switch)\(/) { - ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } } # Check for illegal assignment in if conditional -- and check for trailing @@ -2830,6 +3408,7 @@ sub process { } } if (!defined $suppress_whiletrailers{$linenr} && + defined($stat) && defined($cond) && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); @@ -2927,23 +3506,54 @@ sub process { } } -#CamelCase +#Specific variable tests while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; - if ($var !~ /$Constant/ && - $var =~ /[A-Z]\w*[a-z]|[a-z]\w*[A-Z]/ && - $var !~ /"^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && - !defined $camelcase{$var}) { - $camelcase{$var} = 1; - WARN("CAMELCASE", - "Avoid CamelCase: <$var>\n" . $herecurr); + +#gcc binary extension + if ($var =~ /^$Binary$/) { + if (WARN("GCC_BINARY_CONSTANT", + "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && + $fix) { + my $hexval = sprintf("0x%x", oct($var)); + $fixed[$linenr - 1] =~ + s/\b$var\b/$hexval/; + } + } + +#CamelCase + if ($var !~ /^$Constant$/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page<foo> variants + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/) { + while ($var =~ m{($Ident)}g) { + my $word = $1; + next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); + if ($check) { + seed_camelcase_includes(); + if (!$file && !$camelcase_file_seeded) { + seed_camelcase_file($realfile); + $camelcase_file_seeded = 1; + } + } + if (!defined $camelcase{$word}) { + $camelcase{$word} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$word>\n" . $herecurr); + } + } } } #no spaces allowed after \ in define - if ($line=~/\#\s*define.*\\\s$/) { - WARN("WHITESPACE_AFTER_LINE_CONTINUATION", - "Whitepspace after \\ makes next lines useless\n" . $herecurr); + if ($line =~ /\#\s*define.*\\\s+$/) { + if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", + "Whitespace after \\ makes next lines useless\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\s+$//; + } } #warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line) @@ -3014,15 +3624,17 @@ sub process { if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Ident|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz $dstat !~ /^'X'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = + $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... - $dstat !~ /^\({/) # ({... + $dstat !~ /^\({/ && # ({... + $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { $ctx =~ s/\n*$//; my $herectx = $here . "\n"; @@ -3222,11 +3834,11 @@ sub process { } # check for unnecessary blank lines around braces - if (($line =~ /^..*}\s*$/ && $prevline =~ /^.\s*$/)) { + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { CHK("BRACES", "Blank lines aren't necessary before a close brace '}'\n" . $hereprev); } - if (($line =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { CHK("BRACES", "Blank lines aren't necessary after an open brace '{'\n" . $hereprev); } @@ -3254,6 +3866,53 @@ sub process { } } +# check for bad placement of section $InitAttribute (e.g.: __initdata) + if ($line =~ /(\b$InitAttribute\b)/) { + my $attr = $1; + if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { + my $ptr = $1; + my $var = $2; + if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && + ERROR("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr)) || + ($ptr !~ /\b(union|struct)\s+$attr\b/ && + WARN("MISPLACED_INIT", + "$attr should be placed after $var\n" . $herecurr))) && + $fix) { + $fixed[$linenr - 1] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; + } + } + } + +# check for $InitAttributeData (ie: __initdata) with const + if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { + my $attr = $1; + $attr =~ /($InitAttributePrefix)(.*)/; + my $attr_prefix = $1; + my $attr_type = $2; + if (ERROR("INIT_ATTRIBUTE", + "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/$InitAttributeData/${attr_prefix}initconst/; + } + } + +# check for $InitAttributeConst (ie: __initconst) without const + if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { + my $attr = $1; + if (ERROR("INIT_ATTRIBUTE", + "Use of $attr requires a separate use of const\n" . $herecurr) && + $fix) { + my $lead = $fixed[$linenr - 1] =~ + /(^\+\s*(?:static\s+))/; + $lead = rtrim($1); + $lead = "$lead " if ($lead !~ /^\+$/); + $lead = "${lead}const "; + $fixed[$linenr - 1] =~ s/(^\+\s*(?:static\s+))/$lead/; + } + } + # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { # ignore udelay's < 10, however @@ -3271,6 +3930,18 @@ sub process { } } +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; @@ -3280,8 +3951,13 @@ sub process { # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr); + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + } # check for spinlock_t definitions without a comment. @@ -3296,8 +3972,8 @@ sub process { # check for memory barriers without a comment. if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { - CHK("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines @@ -3321,13 +3997,19 @@ sub process { } # Check for __inline__ and __inline, prefer inline - if ($line =~ /\b(__inline__|__inline)\b/) { - WARN("INLINE", - "plain inline is preferred over $1\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b(__inline__|__inline)\b/) { + if (WARN("INLINE", + "plain inline is preferred over $1\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b(__inline__|__inline)\b/inline/; + + } } # Check for __attribute__ packed, prefer __packed - if ($line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { WARN("PREFER_PACKED", "__packed is preferred over __attribute__((packed))\n" . $herecurr); } @@ -3338,21 +4020,31 @@ sub process { } # Check for __attribute__ aligned, prefer __aligned - if ($line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { WARN("PREFER_ALIGNED", "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); } # Check for __attribute__ format(printf, prefer __printf - if ($line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { - WARN("PREFER_PRINTF", - "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { + if (WARN("PREFER_PRINTF", + "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; + + } } # Check for __attribute__ format(scanf, prefer __scanf - if ($line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { - WARN("PREFER_SCANF", - "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr); + if ($realfile !~ m@\binclude/uapi/@ && + $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { + if (WARN("PREFER_SCANF", + "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; + } } # check for sizeof(&) @@ -3363,8 +4055,11 @@ sub process { # check for sizeof without parenthesis if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { - WARN("SIZEOF_PARENTHESIS", - "sizeof $1 should be sizeof($1)\n" . $herecurr); + if (WARN("SIZEOF_PARENTHESIS", + "sizeof $1 should be sizeof($1)\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; + } } # check for line continuations in quoted strings with odd counts of " @@ -3379,6 +4074,18 @@ sub process { "struct spinlock should be spinlock_t\n" . $herecurr); } +# check for seq_printf uses that could be seq_puts + if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { + my $fmt = get_quoted_string($line, $rawline); + if ($fmt ne "" && $fmt !~ /[^\\]\%/) { + if (WARN("PREFER_SEQ_PUTS", + "Prefer seq_puts to seq_printf\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\bseq_printf\b/seq_puts/; + } + } + } + # Check for misused memsets if ($^V && $^V ge 5.10.0 && defined $stat && @@ -3397,6 +4104,16 @@ sub process { } } +# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) + if ($^V && $^V ge 5.10.0 && + $line =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/s) { + if (WARN("PREFER_ETHER_ADDR_COPY", + "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; + } + } + # typecasts on min/max could be min_t/max_t if ($^V && $^V ge 5.10.0 && defined $stat && @@ -3437,6 +4154,33 @@ sub process { } } +# check for naked sscanf + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /\bsscanf\b/ && + ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && + $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && + $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + WARN("NAKED_SSCANF", + "unchecked sscanf return value\n" . "$here\n$stat_real\n"); + } + +# check for new externs in .h files. + if ($realfile =~ /\.h$/ && + $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { + if (CHK("AVOID_EXTERNS", + "extern prototypes should be avoided in .h files\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; + } + } + # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) @@ -3483,16 +4227,65 @@ sub process { "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + +# check for krealloc arg reuse + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + WARN("KREALLOC_ARG_REUSE", + "Reusing the krealloc arg is almost always a bug\n" . $herecurr); + } + # check for alloc argument mismatch if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { WARN("ALLOC_ARRAY_ARGS", "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); } +# check for GFP_NOWAIT use + if ($line =~ /\b__GFP_NOFAIL\b/) { + WARN("__GFP_NOFAIL", + "Use of __GFP_NOFAIL is deprecated, no new users should be added\n" . $herecurr); + } + # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { - WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr); + if (WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/(\s*;\s*){2,}$/;/g; + } + } + +# check for case / default statements not preceeded by break/fallthrough/switch + if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { + my $has_break = 0; + my $has_statement = 0; + my $count = 0; + my $prevline = $linenr; + while ($prevline > 1 && $count < 3 && !$has_break) { + $prevline--; + my $rline = $rawlines[$prevline - 1]; + my $fline = $lines[$prevline - 1]; + last if ($fline =~ /^\@\@/); + next if ($fline =~ /^\-/); + next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); + $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); + next if ($fline =~ /^.[\s$;]*$/); + $has_statement = 1; + $count++; + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + } + if (!$has_break && $has_statement) { + WARN("MISSING_BREAK", + "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr); + } } # check for switch/default statements without a break; @@ -3510,9 +4303,12 @@ sub process { } # check for gcc specific __FUNCTION__ - if ($line =~ /__FUNCTION__/) { - WARN("USE_FUNC", - "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); + if ($line =~ /\b__FUNCTION__\b/) { + if (WARN("USE_FUNC", + "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ s/\b__FUNCTION__\b/__func__/g; + } } # check for use of yield() @@ -3521,6 +4317,33 @@ sub process { "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); } +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", @@ -3597,6 +4420,12 @@ sub process { "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); } +# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. + if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { + ERROR("DEFINE_ARCH_HAS", + "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); + } + # check for %L{u,d,i} in strings my $string; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { @@ -3690,12 +4519,42 @@ sub process { } } - if ($quiet == 0 && keys %ignore_type) { - print "NOTE: Ignored message types:"; - foreach my $ignore (sort keys %ignore_type) { - print " $ignore"; - } - print "\n\n"; + hash_show_words(\%use_type, "Used"); + hash_show_words(\%ignore_type, "Ignored"); + + if ($clean == 0 && $fix && "@rawlines" ne "@fixed") { + my $newfile = $filename; + $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); + my $linecount = 0; + my $f; + + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line. "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... + +EOM + } } if ($clean == 1 && $quiet == 0) { @@ -3706,7 +4565,7 @@ sub process { $vname has style problems, please review. If any of these errors are false positives, please report -them to the maintainer, see boards.cfg. +them to the maintainer, see CHECKPATCH in MAINTAINERS. EOM } diff --git a/spl/Makefile b/spl/Makefile index 346d0aa..57bd43b 100644 --- a/spl/Makefile +++ b/spl/Makefile @@ -133,7 +133,7 @@ libs-y := $(patsubst %/, %/built-in.o, $(libs-y)) # Add GCC lib ifeq ("$(USE_PRIVATE_LIBGCC)", "yes") -PLATFORM_LIBGCC = $(SPLTREE)/arch/$(ARCH)/lib/lib.a +PLATFORM_LIBGCC = arch/$(ARCH)/lib/lib.a PLATFORM_LIBS := $(filter-out %/lib.a, $(filter-out -lgcc, $(PLATFORM_LIBS))) $(PLATFORM_LIBGCC) endif diff --git a/test/dm/.gitignore b/test/dm/.gitignore new file mode 100644 index 0000000..b741b8a --- /dev/null +++ b/test/dm/.gitignore @@ -0,0 +1 @@ +/test.dtb diff --git a/test/dm/Makefile b/test/dm/Makefile new file mode 100644 index 0000000..4e9afe6 --- /dev/null +++ b/test/dm/Makefile @@ -0,0 +1,18 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CMD_DM) += cmd_dm.o +obj-$(CONFIG_DM_TEST) += test-driver.o +obj-$(CONFIG_DM_TEST) += test-fdt.o +obj-$(CONFIG_DM_TEST) += test-main.o +obj-$(CONFIG_DM_TEST) += test-uclass.o +obj-$(CONFIG_DM_TEST) += ut.o + +# Tests for particular subsystems - when enabling driver model for a new +# subsystem you must add sandbox tests here. +obj-$(CONFIG_DM_TEST) += core.o +obj-$(CONFIG_DM_TEST) += ut.o +obj-$(CONFIG_DM_GPIO) += gpio.o diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c new file mode 100644 index 0000000..a03fe20 --- /dev/null +++ b/test/dm/cmd_dm.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Marek Vasut <marex@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> + +static int display_succ(struct device *in, char *buf) +{ + int len; + int ip = 0; + char local[16]; + struct device *pos, *n, *prev = NULL; + + printf("%s- %s @ %08x", buf, in->name, map_to_sysmem(in)); + if (in->flags & DM_FLAG_ACTIVATED) + puts(" - activated"); + puts("\n"); + + if (list_empty(&in->child_head)) + return 0; + + len = strlen(buf); + strncpy(local, buf, sizeof(local)); + snprintf(local + len, 2, "|"); + if (len && local[len - 1] == '`') + local[len - 1] = ' '; + + list_for_each_entry_safe(pos, n, &in->child_head, sibling_node) { + if (ip++) + display_succ(prev, local); + prev = pos; + } + + snprintf(local + len, 2, "`"); + display_succ(prev, local); + + return 0; +} + +static int dm_dump(struct device *dev) +{ + if (!dev) + return -EINVAL; + return display_succ(dev, ""); +} + +static int do_dm_dump_all(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct device *root; + + root = dm_root(); + printf("ROOT %08x\n", map_to_sysmem(root)); + return dm_dump(root); +} + +static int do_dm_dump_uclass(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + struct uclass *uc; + int ret; + int id; + + for (id = 0; id < UCLASS_COUNT; id++) { + struct device *dev; + + ret = uclass_get(id, &uc); + if (ret) + continue; + + printf("uclass %d: %s\n", id, uc->uc_drv->name); + for (ret = uclass_first_device(id, &dev); + dev; + ret = uclass_next_device(&dev)) { + printf(" %s @ %08x:\n", dev->name, + map_to_sysmem(dev)); + } + puts("\n"); + } + + return 0; +} + +static int do_dm_test(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + return dm_test_main(); +} + +static cmd_tbl_t test_commands[] = { + U_BOOT_CMD_MKENT(tree, 0, 1, do_dm_dump_all, "", ""), + U_BOOT_CMD_MKENT(uclass, 1, 1, do_dm_dump_uclass, "", ""), + U_BOOT_CMD_MKENT(test, 1, 1, do_dm_test, "", ""), +}; + +static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *test_cmd; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + test_cmd = find_cmd_tbl(argv[1], test_commands, + ARRAY_SIZE(test_commands)); + argc -= 2; + argv += 2; + if (!test_cmd || argc > test_cmd->maxargs) + return CMD_RET_USAGE; + + ret = test_cmd->cmd(test_cmd, flag, argc, argv); + + return cmd_process_error(test_cmd, ret); +} + +U_BOOT_CMD( + dm, 2, 1, do_dm, + "Driver model low level access", + "tree Dump driver model tree\n" + "dm uclass Dump list of instances for each uclass\n" + "dm test Run tests" +); diff --git a/test/dm/core.c b/test/dm/core.c new file mode 100644 index 0000000..14a57c3 --- /dev/null +++ b/test/dm/core.c @@ -0,0 +1,544 @@ +/* + * Tests for the core driver model code + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/ut.h> +#include <dm/util.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + TEST_INTVAL1 = 0, + TEST_INTVAL2 = 3, + TEST_INTVAL3 = 6, + TEST_INTVAL_MANUAL = 101112, +}; + +static const struct dm_test_pdata test_pdata[] = { + { .ping_add = TEST_INTVAL1, }, + { .ping_add = TEST_INTVAL2, }, + { .ping_add = TEST_INTVAL3, }, +}; + +static const struct dm_test_pdata test_pdata_manual = { + .ping_add = TEST_INTVAL_MANUAL, +}; + +U_BOOT_DEVICE(dm_test_info1) = { + .name = "test_drv", + .platdata = &test_pdata[0], +}; + +U_BOOT_DEVICE(dm_test_info2) = { + .name = "test_drv", + .platdata = &test_pdata[1], +}; + +U_BOOT_DEVICE(dm_test_info3) = { + .name = "test_drv", + .platdata = &test_pdata[2], +}; + +static struct driver_info driver_info_manual = { + .name = "test_manual_drv", + .platdata = &test_pdata_manual, +}; + +/* Test that binding with platdata occurs correctly */ +static int dm_test_autobind(struct dm_test_state *dms) +{ + struct device *dev; + + /* + * We should have a single class (UCLASS_ROOT) and a single root + * device with no children. + */ + ut_assert(dms->root); + ut_asserteq(1, list_count_items(&gd->uclass_root)); + ut_asserteq(0, list_count_items(&gd->dm_root->child_head)); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + + ut_assertok(dm_scan_platdata()); + + /* We should have our test class now at least, plus more children */ + ut_assert(1 < list_count_items(&gd->uclass_root)); + ut_assert(0 < list_count_items(&gd->dm_root->child_head)); + + /* Our 3 dm_test_infox children should be bound to the test uclass */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + + /* No devices should be probed */ + list_for_each_entry(dev, &gd->dm_root->child_head, sibling_node) + ut_assert(!(dev->flags & DM_FLAG_ACTIVATED)); + + /* Our test driver should have been bound 3 times */ + ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] == 3); + + return 0; +} +DM_TEST(dm_test_autobind, 0); + +/* Test that autoprobe finds all the expected devices */ +static int dm_test_autoprobe(struct dm_test_state *dms) +{ + int expected_base_add; + struct device *dev; + struct uclass *uc; + int i; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + ut_assert(uc); + + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + + /* The root device should not be activated until needed */ + ut_assert(!(dms->root->flags & DM_FLAG_ACTIVATED)); + + /* + * We should be able to find the three test devices, and they should + * all be activated as they are used (lazy activation, required by + * U-Boot) + */ + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED), + "Driver %d/%s already activated", i, dev->name); + + /* This should activate it */ + ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assert(dev->flags & DM_FLAG_ACTIVATED); + + /* Activating a device should activate the root device */ + if (!i) + ut_assert(dms->root->flags & DM_FLAG_ACTIVATED); + } + + /* Our 3 dm_test_infox children should be passed to post_probe */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + + /* Also we can check the per-device data */ + expected_base_add = 0; + for (i = 0; i < 3; i++) { + struct dm_test_uclass_perdev_priv *priv; + struct dm_test_pdata *pdata; + + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + + priv = dev->uclass_priv; + ut_assert(priv); + ut_asserteq(expected_base_add, priv->base_add); + + pdata = dev->platdata; + expected_base_add += pdata->ping_add; + } + + return 0; +} +DM_TEST(dm_test_autoprobe, DM_TESTF_SCAN_PDATA); + +/* Check that we see the correct platdata in each device */ +static int dm_test_platdata(struct dm_test_state *dms) +{ + const struct dm_test_pdata *pdata; + struct device *dev; + int i; + + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + pdata = dev->platdata; + ut_assert(pdata->ping_add == test_pdata[i].ping_add); + } + + return 0; +} +DM_TEST(dm_test_platdata, DM_TESTF_SCAN_PDATA); + +/* Test that we can bind, probe, remove, unbind a driver */ +static int dm_test_lifecycle(struct dm_test_state *dms) +{ + int op_count[DM_TEST_OP_COUNT]; + struct device *dev, *test_dev; + int pingret; + int ret; + + memcpy(op_count, dm_testdrv_op_count, sizeof(op_count)); + + ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, + &dev)); + ut_assert(dev); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] + == op_count[DM_TEST_OP_BIND] + 1); + ut_assert(!dev->priv); + + /* Probe the device - it should fail allocating private data */ + dms->force_fail_alloc = 1; + ret = device_probe(dev); + ut_assert(ret == -ENOMEM); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] + == op_count[DM_TEST_OP_PROBE] + 1); + ut_assert(!dev->priv); + + /* Try again without the alloc failure */ + dms->force_fail_alloc = 0; + ut_assertok(device_probe(dev)); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] + == op_count[DM_TEST_OP_PROBE] + 2); + ut_assert(dev->priv); + + /* This should be device 3 in the uclass */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev == test_dev); + + /* Try ping */ + ut_assertok(test_ping(dev, 100, &pingret)); + ut_assert(pingret == 102); + + /* Now remove device 3 */ + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); + ut_assertok(device_remove(dev)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); + + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + ut_assertok(device_unbind(dev)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + + return 0; +} +DM_TEST(dm_test_lifecycle, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST); + +/* Test that we can bind/unbind and the lists update correctly */ +static int dm_test_ordering(struct dm_test_state *dms) +{ + struct device *dev, *dev_penultimate, *dev_last, *test_dev; + int pingret; + + ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, + &dev)); + ut_assert(dev); + + /* Bind two new devices (numbers 4 and 5) */ + ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, + &dev_penultimate)); + ut_assert(dev_penultimate); + ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, + &dev_last)); + ut_assert(dev_last); + + /* Now remove device 3 */ + ut_assertok(device_remove(dev)); + ut_assertok(device_unbind(dev)); + + /* The device numbering should have shifted down one */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev_penultimate == test_dev); + ut_assertok(uclass_find_device(UCLASS_TEST, 4, &test_dev)); + ut_assert(dev_last == test_dev); + + /* Add back the original device 3, now in position 5 */ + ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, &dev)); + ut_assert(dev); + + /* Try ping */ + ut_assertok(test_ping(dev, 100, &pingret)); + ut_assert(pingret == 102); + + /* Remove 3 and 4 */ + ut_assertok(device_remove(dev_penultimate)); + ut_assertok(device_unbind(dev_penultimate)); + ut_assertok(device_remove(dev_last)); + ut_assertok(device_unbind(dev_last)); + + /* Our device should now be in position 3 */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev == test_dev); + + /* Now remove device 3 */ + ut_assertok(device_remove(dev)); + ut_assertok(device_unbind(dev)); + + return 0; +} +DM_TEST(dm_test_ordering, DM_TESTF_SCAN_PDATA); + +/* Check that we can perform operations on a device (do a ping) */ +int dm_check_operations(struct dm_test_state *dms, struct device *dev, + uint32_t base, struct dm_test_priv *priv) +{ + int expected; + int pingret; + + /* Getting the child device should allocate platdata / priv */ + ut_assertok(testfdt_ping(dev, 10, &pingret)); + ut_assert(dev->priv); + ut_assert(dev->platdata); + + expected = 10 + base; + ut_asserteq(expected, pingret); + + /* Do another ping */ + ut_assertok(testfdt_ping(dev, 20, &pingret)); + expected = 20 + base; + ut_asserteq(expected, pingret); + + /* Now check the ping_total */ + priv = dev->priv; + ut_asserteq(DM_TEST_START_TOTAL + 10 + 20 + base * 2, + priv->ping_total); + + return 0; +} + +/* Check that we can perform operations on devices */ +static int dm_test_operations(struct dm_test_state *dms) +{ + struct device *dev; + int i; + + /* + * Now check that the ping adds are what we expect. This is using the + * ping-add property in each node. + */ + for (i = 0; i < ARRAY_SIZE(test_pdata); i++) { + uint32_t base; + + ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + + /* + * Get the 'reg' property, which tells us what the ping add + * should be. We don't use the platdata because we want + * to test the code that sets that up (testfdt_drv_probe()). + */ + base = test_pdata[i].ping_add; + debug("dev=%d, base=%d\n", i, base); + + ut_assert(!dm_check_operations(dms, dev, base, dev->priv)); + } + + return 0; +} +DM_TEST(dm_test_operations, DM_TESTF_SCAN_PDATA); + +/* Remove all drivers and check that things work */ +static int dm_test_remove(struct dm_test_state *dms) +{ + struct device *dev; + int i; + + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assertf(dev->flags & DM_FLAG_ACTIVATED, + "Driver %d/%s not activated", i, dev->name); + ut_assertok(device_remove(dev)); + ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED), + "Driver %d/%s should have deactivated", i, + dev->name); + ut_assert(!dev->priv); + } + + return 0; +} +DM_TEST(dm_test_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST); + +/* Remove and recreate everything, check for memory leaks */ +static int dm_test_leak(struct dm_test_state *dms) +{ + int i; + + for (i = 0; i < 2; i++) { + struct mallinfo start, end; + struct device *dev; + int ret; + int id; + + start = mallinfo(); + if (!start.uordblks) + puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n"); + + ut_assertok(dm_scan_platdata()); + ut_assertok(dm_scan_fdt(gd->fdt_blob)); + + /* Scanning the uclass is enough to probe all the devices */ + for (id = UCLASS_ROOT; id < UCLASS_COUNT; id++) { + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + ut_assertok(ret); + } + + /* Don't delete the root class, since we started with that */ + for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) { + struct uclass *uc; + + uc = uclass_find(id); + if (!uc) + continue; + ut_assertok(uclass_destroy(uc)); + } + + end = mallinfo(); + ut_asserteq(start.uordblks, end.uordblks); + } + + return 0; +} +DM_TEST(dm_test_leak, 0); + +/* Test uclass init/destroy methods */ +static int dm_test_uclass(struct dm_test_state *dms) +{ + struct uclass *uc; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + ut_assert(uc->priv); + + ut_assertok(uclass_destroy(uc)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + + return 0; +} +DM_TEST(dm_test_uclass, 0); + +/** + * create_children() - Create children of a parent node + * + * @dms: Test system state + * @parent: Parent device + * @count: Number of children to create + * @key: Key value to put in first child. Subsequence children + * receive an incrementing value + * @child: If not NULL, then the child device pointers are written into + * this array. + * @return 0 if OK, -ve on error + */ +static int create_children(struct dm_test_state *dms, struct device *parent, + int count, int key, struct device *child[]) +{ + struct device *dev; + int i; + + for (i = 0; i < count; i++) { + struct dm_test_pdata *pdata; + + ut_assertok(device_bind_by_name(parent, &driver_info_manual, + &dev)); + pdata = calloc(1, sizeof(*pdata)); + pdata->ping_add = key + i; + dev->platdata = pdata; + if (child) + child[i] = dev; + } + + return 0; +} + +#define NODE_COUNT 10 + +static int dm_test_children(struct dm_test_state *dms) +{ + struct device *top[NODE_COUNT]; + struct device *child[NODE_COUNT]; + struct device *grandchild[NODE_COUNT]; + struct device *dev; + int total; + int ret; + int i; + + /* We don't care about the numbering for this test */ + dms->skip_post_probe = 1; + + ut_assert(NODE_COUNT > 5); + + /* First create 10 top-level children */ + ut_assertok(create_children(dms, dms->root, NODE_COUNT, 0, top)); + + /* Now a few have their own children */ + ut_assertok(create_children(dms, top[2], NODE_COUNT, 2, NULL)); + ut_assertok(create_children(dms, top[5], NODE_COUNT, 5, child)); + + /* And grandchildren */ + for (i = 0; i < NODE_COUNT; i++) + ut_assertok(create_children(dms, child[i], NODE_COUNT, 50 * i, + i == 2 ? grandchild : NULL)); + + /* Check total number of devices */ + total = NODE_COUNT * (3 + NODE_COUNT); + ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]); + + /* Try probing one of the grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, + NODE_COUNT * 3 + 2 * NODE_COUNT, &dev)); + ut_asserteq_ptr(grandchild[0], dev); + + /* + * This should have probed the child and top node also, for a total + * of 3 nodes. + */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Probe the other grandchildren */ + for (i = 1; i < NODE_COUNT; i++) + ut_assertok(device_probe(grandchild[i])); + + ut_asserteq(2 + NODE_COUNT, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Probe everything */ + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + ut_assertok(ret); + + ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Remove a top-level child and check that the children are removed */ + ut_assertok(device_remove(top[2])); + ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_REMOVE]); + dm_testdrv_op_count[DM_TEST_OP_REMOVE] = 0; + + /* Try one with grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); + ut_asserteq_ptr(dev, top[5]); + ut_assertok(device_remove(dev)); + ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), + dm_testdrv_op_count[DM_TEST_OP_REMOVE]); + + /* Try the same with unbind */ + ut_assertok(device_unbind(top[2])); + ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + dm_testdrv_op_count[DM_TEST_OP_UNBIND] = 0; + + /* Try one with grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); + ut_asserteq_ptr(dev, top[6]); + ut_assertok(device_unbind(top[5])); + ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), + dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + + return 0; +} +DM_TEST(dm_test_children, 0); diff --git a/test/dm/gpio.c b/test/dm/gpio.c new file mode 100644 index 0000000..bf632bc --- /dev/null +++ b/test/dm/gpio.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <dm.h> +#include <dm/ut.h> +#include <dm/test.h> +#include <dm/util.h> +#include <asm/gpio.h> + +/* Test that sandbox GPIOs work correctly */ +static int dm_test_gpio(struct dm_test_state *dms) +{ + unsigned int offset, gpio; + struct dm_gpio_ops *ops; + struct device *dev; + const char *name; + int offset_count; + char buf[80]; + + /* + * We expect to get 3 banks. One is anonymous (just numbered) and + * comes from platdata. The other two are named a (20 gpios) + * and b (10 gpios) and come from the device tree. See + * test/dm/test.dts. + */ + ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "extra-gpios"); + ut_asserteq(4, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("b", name); + ut_asserteq(10, offset_count); + + /* Get the operations for this device */ + ops = gpio_get_ops(dev); + ut_assert(ops->get_state); + + /* Cannot get a value until it is reserved */ + ut_asserteq(-1, ops->get_value(dev, offset)); + + /* + * Now some tests that use the 'sandbox' back door. All GPIOs + * should default to input, include b4 that we are using here. + */ + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [ ]", buf); + + /* Change it to an output */ + sandbox_gpio_set_direction(dev, offset, 1); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 0 [ ]", buf); + + sandbox_gpio_set_value(dev, offset, 1); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 1 [ ]", buf); + + ut_assertok(ops->request(dev, offset, "testing")); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 1 [x] testing", buf); + + /* Change the value a bit */ + ut_asserteq(1, ops->get_value(dev, offset)); + ut_assertok(ops->set_value(dev, offset, 0)); + ut_asserteq(0, ops->get_value(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: out: 0 [x] testing", buf); + ut_assertok(ops->set_value(dev, offset, 1)); + ut_asserteq(1, ops->get_value(dev, offset)); + + /* Make it an input */ + ut_assertok(ops->direction_input(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 1 [x] testing", buf); + sandbox_gpio_set_value(dev, offset, 0); + ut_asserteq(0, sandbox_gpio_get_value(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [x] testing", buf); + + ut_assertok(ops->free(dev, offset)); + ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: in: 0 [ ]", buf); + + /* Check the 'a' bank also */ + ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "base-gpios"); + ut_asserteq(15, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("a", name); + ut_asserteq(20, offset_count); + + /* And the anonymous bank */ + ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "gpio_sandbox"); + ut_asserteq(14, offset); + ut_asserteq(14, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_ptr(NULL, name); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count); + + return 0; +} +DM_TEST(dm_test_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test-dm.sh b/test/dm/test-dm.sh new file mode 100755 index 0000000..ef5aca5 --- /dev/null +++ b/test/dm/test-dm.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +NUM_CPUS=$(cat /proc/cpuinfo |grep -c processor) +dtc -I dts -O dtb test/dm/test.dts -o test/dm/test.dtb +make O=sandbox sandbox_config +make O=sandbox -s -j${NUM_CPUS} +./sandbox/u-boot -d test/dm/test.dtb -c "dm test" diff --git a/test/dm/test-driver.c b/test/dm/test-driver.c new file mode 100644 index 0000000..c4be8a1 --- /dev/null +++ b/test/dm/test-driver.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <dm/test.h> +#include <dm/ut.h> +#include <asm/io.h> + +int dm_testdrv_op_count[DM_TEST_OP_COUNT]; +static struct dm_test_state *dms = &global_test_state; + +static int testdrv_ping(struct device *dev, int pingval, int *pingret) +{ + const struct dm_test_pdata *pdata = dev_get_platdata(dev); + struct dm_test_priv *priv = dev_get_priv(dev); + + *pingret = pingval + pdata->ping_add; + priv->ping_total += *pingret; + + return 0; +} + +static const struct test_ops test_ops = { + .ping = testdrv_ping, +}; + +static int test_bind(struct device *dev) +{ + /* Private data should not be allocated */ + ut_assert(!dev_get_priv(dev)); + + dm_testdrv_op_count[DM_TEST_OP_BIND]++; + return 0; +} + +static int test_probe(struct device *dev) +{ + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Private data should be allocated */ + ut_assert(priv); + + dm_testdrv_op_count[DM_TEST_OP_PROBE]++; + priv->ping_total += DM_TEST_START_TOTAL; + return 0; +} + +static int test_remove(struct device *dev) +{ + /* Private data should still be allocated */ + ut_assert(dev_get_priv(dev)); + + dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; + return 0; +} + +static int test_unbind(struct device *dev) +{ + /* Private data should not be allocated */ + ut_assert(!dev->priv); + + dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; + return 0; +} + +U_BOOT_DRIVER(test_drv) = { + .name = "test_drv", + .id = UCLASS_TEST, + .ops = &test_ops, + .bind = test_bind, + .probe = test_probe, + .remove = test_remove, + .unbind = test_unbind, + .priv_auto_alloc_size = sizeof(struct dm_test_priv), +}; + +U_BOOT_DRIVER(test2_drv) = { + .name = "test2_drv", + .id = UCLASS_TEST, + .ops = &test_ops, + .bind = test_bind, + .probe = test_probe, + .remove = test_remove, + .unbind = test_unbind, + .priv_auto_alloc_size = sizeof(struct dm_test_priv), +}; + +static int test_manual_drv_ping(struct device *dev, int pingval, int *pingret) +{ + *pingret = pingval + 2; + + return 0; +} + +static const struct test_ops test_manual_ops = { + .ping = test_manual_drv_ping, +}; + +static int test_manual_bind(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_BIND]++; + + return 0; +} + +static int test_manual_probe(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_PROBE]++; + if (!dms->force_fail_alloc) + dev->priv = calloc(1, sizeof(struct dm_test_priv)); + if (!dev->priv) + return -ENOMEM; + + return 0; +} + +static int test_manual_remove(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; + return 0; +} + +static int test_manual_unbind(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; + return 0; +} + +U_BOOT_DRIVER(test_manual_drv) = { + .name = "test_manual_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, +}; diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c new file mode 100644 index 0000000..e1d982f --- /dev/null +++ b/test/dm/test-fdt.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/test.h> +#include <dm/root.h> +#include <dm/ut.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int testfdt_drv_ping(struct device *dev, int pingval, int *pingret) +{ + const struct dm_test_pdata *pdata = dev->platdata; + struct dm_test_priv *priv = dev_get_priv(dev); + + *pingret = pingval + pdata->ping_add; + priv->ping_total += *pingret; + + return 0; +} + +static const struct test_ops test_ops = { + .ping = testfdt_drv_ping, +}; + +static int testfdt_ofdata_to_platdata(struct device *dev) +{ + struct dm_test_pdata *pdata = dev_get_platdata(dev); + + pdata->ping_add = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "ping-add", -1); + pdata->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + + return 0; +} + +static int testfdt_drv_probe(struct device *dev) +{ + struct dm_test_priv *priv = dev_get_priv(dev); + + priv->ping_total += DM_TEST_START_TOTAL; + + return 0; +} + +static const struct device_id testfdt_ids[] = { + { + .compatible = "denx,u-boot-fdt-test", + .data = DM_TEST_TYPE_FIRST }, + { + .compatible = "google,another-fdt-test", + .data = DM_TEST_TYPE_SECOND }, + { } +}; + +U_BOOT_DRIVER(testfdt_drv) = { + .name = "testfdt_drv", + .of_match = testfdt_ids, + .id = UCLASS_TEST_FDT, + .ofdata_to_platdata = testfdt_ofdata_to_platdata, + .probe = testfdt_drv_probe, + .ops = &test_ops, + .priv_auto_alloc_size = sizeof(struct dm_test_priv), + .platdata_auto_alloc_size = sizeof(struct dm_test_pdata), +}; + +/* From here is the testfdt uclass code */ +int testfdt_ping(struct device *dev, int pingval, int *pingret) +{ + const struct test_ops *ops = device_get_ops(dev); + + if (!ops->ping) + return -ENOSYS; + + return ops->ping(dev, pingval, pingret); +} + +UCLASS_DRIVER(testfdt) = { + .name = "testfdt", + .id = UCLASS_TEST_FDT, +}; + +/* Test that FDT-based binding works correctly */ +static int dm_test_fdt(struct dm_test_state *dms) +{ + const int num_drivers = 3; + struct device *dev; + struct uclass *uc; + int ret; + int i; + + ret = dm_scan_fdt(gd->fdt_blob); + ut_assert(!ret); + + ret = uclass_get(UCLASS_TEST_FDT, &uc); + ut_assert(!ret); + + /* These are num_drivers compatible root-level device tree nodes */ + ut_asserteq(num_drivers, list_count_items(&uc->dev_head)); + + /* Each should have no platdata / priv */ + for (i = 0; i < num_drivers; i++) { + ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); + ut_assert(!ret); + ut_assert(!dev_get_priv(dev)); + ut_assert(!dev->platdata); + } + + /* + * Now check that the ping adds are what we expect. This is using the + * ping-add property in each node. + */ + for (i = 0; i < num_drivers; i++) { + uint32_t base; + + ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev); + ut_assert(!ret); + + /* + * Get the 'reg' property, which tells us what the ping add + * should be. We don't use the platdata because we want + * to test the code that sets that up (testfdt_drv_probe()). + */ + base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + debug("dev=%d, base=%d: %s\n", i, base, + fdt_get_name(gd->fdt_blob, dev->of_offset, NULL)); + + ut_assert(!dm_check_operations(dms, dev, base, + dev_get_priv(dev))); + } + + return 0; +} +DM_TEST(dm_test_fdt, 0); diff --git a/test/dm/test-main.c b/test/dm/test-main.c new file mode 100644 index 0000000..828ed46 --- /dev/null +++ b/test/dm/test-main.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <dm/test.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> +#include <dm/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dm_test_state global_test_state; + +/* Get ready for testing */ +static int dm_test_init(struct dm_test_state *dms) +{ + memset(dms, '\0', sizeof(*dms)); + gd->dm_root = NULL; + memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count)); + + ut_assertok(dm_init()); + dms->root = dm_root(); + + return 0; +} + +/* Ensure all the test devices are probed */ +static int do_autoprobe(struct dm_test_state *dms) +{ + struct device *dev; + int ret; + + /* Scanning the uclass is enough to probe all the devices */ + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + + return ret; +} + +static int dm_test_destroy(struct dm_test_state *dms) +{ + int id; + + for (id = 0; id < UCLASS_COUNT; id++) { + struct uclass *uc; + + /* + * If the uclass doesn't exist we don't want to create it. So + * check that here before we call uclass_find_device()/ + */ + uc = uclass_find(id); + if (!uc) + continue; + ut_assertok(uclass_destroy(uc)); + } + + return 0; +} + +int dm_test_main(void) +{ + struct dm_test *tests = ll_entry_start(struct dm_test, dm_test); + const int n_ents = ll_entry_count(struct dm_test, dm_test); + struct dm_test_state *dms = &global_test_state; + struct dm_test *test; + + /* + * If we have no device tree, or it only has a root node, then these + * tests clearly aren't going to work... + */ + if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) { + puts("Please run with test device tree:\n" + " dtc -I dts -O dtb test/dm/test.dts -o test/dm/test.dtb\n" + " ./u-boot -d test/dm/test.dtb\n"); + ut_assert(gd->fdt_blob); + } + + printf("Running %d driver model tests\n", n_ents); + + for (test = tests; test < tests + n_ents; test++) { + printf("Test: %s\n", test->name); + ut_assertok(dm_test_init(dms)); + + if (test->flags & DM_TESTF_SCAN_PDATA) + ut_assertok(dm_scan_platdata()); + if (test->flags & DM_TESTF_PROBE_TEST) + ut_assertok(do_autoprobe(dms)); + if (test->flags & DM_TESTF_SCAN_FDT) + ut_assertok(dm_scan_fdt(gd->fdt_blob)); + + if (test->func(dms)) + break; + + ut_assertok(dm_test_destroy(dms)); + } + + printf("Failures: %d\n", dms->fail_count); + + return 0; +} diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c new file mode 100644 index 0000000..8b564b8 --- /dev/null +++ b/test/dm/test-uclass.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <dm.h> +#include <errno.h> +#include <dm/test.h> +#include <dm/ut.h> +#include <asm/io.h> +#include <linux/list.h> + +static struct dm_test_state *dms = &global_test_state; + +int test_ping(struct device *dev, int pingval, int *pingret) +{ + const struct test_ops *ops = device_get_ops(dev); + + if (!ops->ping) + return -ENOSYS; + + return ops->ping(dev, pingval, pingret); +} + +static int test_post_bind(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++; + + return 0; +} + +static int test_pre_unbind(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]++; + + return 0; +} + +static int test_post_probe(struct device *dev) +{ + struct device *prev = list_entry(dev->uclass_node.prev, struct device, + uclass_node); + struct dm_test_uclass_perdev_priv *priv = dev->uclass_priv; + struct uclass *uc = dev->uclass; + + dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]++; + ut_assert(priv); + ut_assert(device_active(dev)); + priv->base_add = 0; + if (dms->skip_post_probe) + return 0; + if (&prev->uclass_node != &uc->dev_head) { + struct dm_test_uclass_perdev_priv *prev_uc_priv + = prev->uclass_priv; + struct dm_test_pdata *pdata = prev->platdata; + + ut_assert(pdata); + ut_assert(prev_uc_priv); + priv->base_add = prev_uc_priv->base_add + pdata->ping_add; + } + + return 0; +} + +static int test_pre_remove(struct device *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]++; + + return 0; +} + +static int test_init(struct uclass *uc) +{ + dm_testdrv_op_count[DM_TEST_OP_INIT]++; + ut_assert(uc->priv); + + return 0; +} + +static int test_destroy(struct uclass *uc) +{ + dm_testdrv_op_count[DM_TEST_OP_DESTROY]++; + + return 0; +} + +UCLASS_DRIVER(test) = { + .name = "test", + .id = UCLASS_TEST, + .post_bind = test_post_bind, + .pre_unbind = test_pre_unbind, + .post_probe = test_post_probe, + .pre_remove = test_pre_remove, + .init = test_init, + .destroy = test_destroy, + .priv_auto_alloc_size = sizeof(struct dm_test_uclass_priv), + .per_device_auto_alloc_size = sizeof(struct dm_test_uclass_perdev_priv), +}; diff --git a/test/dm/test.dts b/test/dm/test.dts new file mode 100644 index 0000000..ec5364f --- /dev/null +++ b/test/dm/test.dts @@ -0,0 +1,59 @@ +/dts-v1/; + +/ { + model = "sandbox"; + compatible = "sandbox"; + #address-cells = <1>; + #size-cells = <0>; + + a-test { + reg = <0>; + compatible = "denx,u-boot-fdt-test"; + ping-add = <0>; + }; + + junk { + reg = <1>; + compatible = "not,compatible"; + }; + + no-compatible { + reg = <2>; + }; + + b-test { + reg = <3>; + compatible = "denx,u-boot-fdt-test"; + ping-add = <3>; + }; + + some-bus { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + ping-add = <4>; + c-test { + compatible = "denx,u-boot-fdt-test"; + reg = <5>; + ping-add = <5>; + }; + }; + + d-test { + reg = <6>; + ping-add = <6>; + compatible = "google,another-fdt-test"; + }; + + base-gpios { + compatible = "sandbox,gpio"; + gpio-bank-name = "a"; + num-gpios = <20>; + }; + + extra-gpios { + compatible = "sandbox,gpio"; + gpio-bank-name = "b"; + num-gpios = <10>; + }; +}; diff --git a/test/dm/ut.c b/test/dm/ut.c new file mode 100644 index 0000000..8b69bc2 --- /dev/null +++ b/test/dm/ut.c @@ -0,0 +1,33 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm/test.h> +#include <dm/ut.h> + +struct dm_test_state; + +void ut_fail(struct dm_test_state *dms, const char *fname, int line, + const char *func, const char *cond) +{ + printf("%s:%d, %s(): %s\n", fname, line, func, cond); + dms->fail_count++; +} + +void ut_failf(struct dm_test_state *dms, const char *fname, int line, + const char *func, const char *cond, const char *fmt, ...) +{ + va_list args; + + printf("%s:%d, %s(): %s: ", fname, line, func, cond); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putc('\n'); + dms->fail_count++; +} diff --git a/tools/Makefile b/tools/Makefile index dcd49f8..bac6aaf 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -157,10 +157,10 @@ ifeq ($(LOGO_BMP),) LOGO_BMP= $(srctree)/$(src)/logos/denx.bmp # Use board logo and fallback to vendor -ifneq ($(wildcard logos/$(BOARD).bmp),) +ifneq ($(wildcard $(srctree)/$(src)/logos/$(BOARD).bmp),) LOGO_BMP= $(srctree)/$(src)/logos/$(BOARD).bmp else -ifneq ($(wildcard logos/$(VENDOR).bmp),) +ifneq ($(wildcard $(srctree)/$(src)/logos/$(VENDOR).bmp),) LOGO_BMP= $(srctree)/$(src)/logos/$(VENDOR).bmp endif endif @@ -191,3 +191,15 @@ $(LOGO_DATA_H): $(obj)/bmp_logo $(LOGO_BMP) # Let clean descend into subdirs subdir- += env + +ifneq ($(CROSS_BUILD_TOOLS),) +HOSTCC = $(CC) + +quiet_cmd_crosstools_strip = STRIP $^ + cmd_crosstools_strip = $(STRIP) $^; touch $@ +$(obj)/.strip: $(call objectify,$(filter $(always),$(hostprogs-y))) + $(call cmd,crosstools_strip) + +always += .strip +endif +clean-files += .strip |