[U-Boot] [PATCH v3 00/54] dm: Introduce new driver model uclasses

This series adds several new uclasses. Some of these have been discussed for a while. All are fairly simple and just provide enough functionality for existing use cases. The following are included in this series:
- Clocks - setting and getting PLL and peripheral clocks - Pinctrl - adjusting pin multiplexing settings - Reset - reseting the board or SoC - RAM - setting up RAM controllers and detecting the available RAM - MMC - MMC controllers (using the existing block device framework) - LEDs - turning LEDs on and off, with only a GPIO driver so far
Trivial support is also added for regmap and syscon controllers, modelled on how the kernel does this.
This builds on the SPL device tree support which was recently added.
One problem with device tree is that U-Boot has no way of dropping features it does not need or use. For SPL this problem needs to be solved and this series uses a new 'fdtgrep' tool for this. The 45KB Firefly device tree reduces to a 6KB bytes when unused material is removed. SDRAM timings are also in the device tree.
This series includes some changes aimed at reduce code size in SPL, including: - dropping alias sequence support (the aliases node) since many boards just use a single UART in SPL - adding a smaller panic() function that does not support printf()-format strings - removing device unbind code which will never be used in SPL
Changes in v3: - Use case-insensitve compare for image params (fixes build break with m53evk) - Fix indentation in function comment - Convert hex number to upper case to keep bc happy - Split this series apart from the Rockchip series
Changes in v2: - Add new patch to remove unused strings from a device tree - Add new patch with fdt_first/next_region() functions - Tidy up commit message a little - Add new patch with fdtgrep tool - Add new patch to reduce SPL device tree size with fdtgrep
Simon Glass (54): Add a dhrystone benchmark command sandbox: Enable dhry command mkimage: Display a better list of available image types fdt: Add a function to remove unused strings from a device tree fdt: Add fdt_first/next_region() functions fdt: Add fdtgrep tool dm: Reduce SPL device tree size dm: arm: Put driver model I2C drivers before legacy ones Add a way of checking the position of a structure member debug_uart: Remove use of asmlinkage dm: Allow debug UART to support an early console sandbox: Drop special-case sandbox console code dm: Move the tree/uclass dump code into its own file dm: core: Use debug() instead of printf() for failures dm: core: Add a function to find any device from device tree dm: core: Correct device_get_child_by_of_offset() parameter dm: gpio: Allow GPIO uclass to be used in SPL dm: gpio: Add dm_gpio_lookup_name() to look up a GPIO name dm: gpio: Add dm_gpio_request() to manually request a GPIO dm: Add support for register maps (regmap) dm: Add support for generic system controllers (syscon) dm: Correct the missing method check in cpu_get_info() dm: Add support for LEDs dm: led: Add a driver for GPIO-controlled LEDs spl: Add debugging info for spl_mmc boot dm: mmc: Add an MMC uclass mmc: Avoid using printf() for errors mmc: Add debug() output on read errors dm: mmc: Allow driver model to be used for MMC in SPL mmc: Add structure comments for dwmmc mmc: Support bypass mode with the get_mmc_clk() method mmc: Calculate dwmmc FIFO threshold size if not provided dm: Add basic support for pin multiplexing (pinctrl) dm: power: Avoid case-insensitve match for child names dm: power: Add regulator flags to centralise auto-set logic dm: pmic: Split output from function dm: power: Add a function to set up all regulators dm: power: Use debug() for errors in regulator uclass dm: pmic: Add functions to adjust PMIC registers dm: power: Allow use of regulators in SPL Drop CONFIG_ERRNO_STR from SPL dm: Add support for RAM drivers dm: spi: Make local functions static ns16550: Improve debug UART so it can work with 32-bit access Add rivest cipher 4 (rc4) implementation lib: Add function to extract a number from the end of a string fdt: Provide debug info when a device tree cannot be found dm: spl: Allow device tree/driver model in board_init_f() spl: Add a debug string before the jump to U-Boot mkimage: Set up a file size parameter and keep it updated dm: Add a system reset uclass zynq: Rename struct clk_ops to zynq_clk_ops dm: Add a clock uclass power: pmic: Use trailing_strtol() instead of a local function
Makefile | 2 +- arch/arm/cpu/u-boot-spl.lds | 12 +- arch/arm/mach-zynq/clk.c | 6 +- common/console.c | 25 +- common/image.c | 58 +- common/spl/spl.c | 36 +- common/spl/spl_mmc.c | 26 +- configs/sandbox_defconfig | 1 + doc/device-tree-bindings/leds/common.txt | 23 + doc/device-tree-bindings/leds/leds-gpio.txt | 52 ++ drivers/Kconfig | 8 + drivers/Makefile | 4 + drivers/clk/Kconfig | 19 + drivers/clk/Makefile | 8 + drivers/clk/clk-uclass.c | 58 ++ drivers/core/Makefile | 3 + drivers/core/device.c | 29 +- drivers/core/dump.c | 96 +++ drivers/core/lists.c | 6 +- drivers/core/regmap.c | 86 ++ drivers/core/syscon-uclass.c | 73 ++ drivers/cpu/cpu-uclass.c | 2 +- drivers/gpio/Makefile | 4 - drivers/gpio/gpio-uclass.c | 36 +- drivers/led/Kconfig | 26 + drivers/led/Makefile | 9 + drivers/led/led-uclass.c | 48 ++ drivers/led/led_gpio.c | 101 +++ drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/reset-uclass.c | 62 ++ drivers/mmc/Kconfig | 10 + drivers/mmc/Makefile | 2 + drivers/mmc/dw_mmc.c | 33 +- drivers/mmc/exynos_dw_mmc.c | 2 +- drivers/mmc/mmc-uclass.c | 34 + drivers/mmc/mmc.c | 10 +- drivers/pinctrl/Kconfig | 19 + drivers/pinctrl/Makefile | 8 + drivers/pinctrl/pinctrl-uclass.c | 51 ++ drivers/power/pmic/pmic-uclass.c | 57 +- drivers/power/regulator/regulator-uclass.c | 134 +-- drivers/ram/Kconfig | 18 + drivers/ram/Makefile | 7 + drivers/ram/ram-uclass.c | 28 + drivers/serial/ns16550.c | 36 +- drivers/spi/spi-uclass.c | 8 +- dts/Kconfig | 12 + include/asm-generic/global_data.h | 1 + include/asm-generic/gpio.h | 25 + include/clk.h | 80 ++ include/common.h | 11 + include/debug_uart.h | 22 +- include/dm/device.h | 16 +- include/dm/uclass-id.h | 7 + include/dm/util.h | 6 + include/dwmmc.h | 34 +- include/image.h | 11 + include/led.h | 51 ++ include/libfdt.h | 256 +++++- include/mmc.h | 22 + include/pinctrl.h | 74 ++ include/power/pmic.h | 34 + include/power/regulator.h | 53 +- include/power/sandbox_pmic.h | 4 +- include/ram.h | 38 + include/rc4.h | 21 + include/regmap.h | 72 ++ include/reset.h | 62 ++ include/spl.h | 12 + include/syscon.h | 56 ++ include/vsprintf.h | 26 + lib/Kconfig | 2 + lib/Makefile | 5 +- lib/dhry/Kconfig | 7 + lib/dhry/Makefile | 7 + lib/dhry/cmd_dhry.c | 34 + lib/dhry/dhry.h | 442 ++++++++++ lib/dhry/dhry_1.c | 421 +++++++++ lib/dhry/dhry_2.c | 217 +++++ lib/fdtdec.c | 21 +- lib/libfdt/Makefile | 2 +- lib/libfdt/fdt_region.c | 492 +++++++++++ lib/libfdt/fdt_rw.c | 32 + lib/rc4.c | 49 ++ lib/vsprintf.c | 19 + scripts/Makefile.spl | 33 + test/dm/cmd_dm.c | 82 +- test/dm/regulator.c | 2 +- tools/Makefile | 6 +- tools/fdtgrep.c | 1234 +++++++++++++++++++++++++++ tools/imagetool.h | 1 + tools/mkimage.c | 77 +- 93 files changed, 5259 insertions(+), 318 deletions(-) create mode 100644 doc/device-tree-bindings/leds/common.txt create mode 100644 doc/device-tree-bindings/leds/leds-gpio.txt create mode 100644 drivers/clk/Kconfig create mode 100644 drivers/clk/Makefile create mode 100644 drivers/clk/clk-uclass.c create mode 100644 drivers/core/dump.c create mode 100644 drivers/core/regmap.c create mode 100644 drivers/core/syscon-uclass.c create mode 100644 drivers/led/Kconfig create mode 100644 drivers/led/Makefile create mode 100644 drivers/led/led-uclass.c create mode 100644 drivers/led/led_gpio.c create mode 100644 drivers/misc/reset-uclass.c create mode 100644 drivers/mmc/mmc-uclass.c create mode 100644 drivers/pinctrl/Kconfig create mode 100644 drivers/pinctrl/Makefile create mode 100644 drivers/pinctrl/pinctrl-uclass.c create mode 100644 drivers/ram/Kconfig create mode 100644 drivers/ram/Makefile create mode 100644 drivers/ram/ram-uclass.c create mode 100644 include/led.h create mode 100644 include/pinctrl.h create mode 100644 include/ram.h create mode 100644 include/rc4.h create mode 100644 include/regmap.h create mode 100644 include/reset.h create mode 100644 include/syscon.h create mode 100644 lib/dhry/Kconfig create mode 100644 lib/dhry/Makefile create mode 100644 lib/dhry/cmd_dhry.c create mode 100644 lib/dhry/dhry.h create mode 100644 lib/dhry/dhry_1.c create mode 100644 lib/dhry/dhry_2.c create mode 100644 lib/libfdt/fdt_region.c create mode 100644 lib/rc4.c create mode 100644 tools/fdtgrep.c

Drystone provides a convenient sanity check that the CPU is running at full speed. Add this as a command which can be enabled as needed.
Note: I investigated using Coremark for this but there was a license agreement and I could not work out if it was GPL-compatible.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
lib/Kconfig | 2 + lib/Makefile | 2 + lib/dhry/Kconfig | 7 + lib/dhry/Makefile | 7 + lib/dhry/cmd_dhry.c | 34 ++++ lib/dhry/dhry.h | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dhry/dhry_1.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/dhry/dhry_2.c | 217 ++++++++++++++++++++++++++ 8 files changed, 1132 insertions(+) create mode 100644 lib/dhry/Kconfig create mode 100644 lib/dhry/Makefile create mode 100644 lib/dhry/cmd_dhry.c create mode 100644 lib/dhry/dhry.h create mode 100644 lib/dhry/dhry_1.c create mode 100644 lib/dhry/dhry_2.c
diff --git a/lib/Kconfig b/lib/Kconfig index 7ec8c98..f75dff1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -49,6 +49,8 @@ config LIB_RAND help This library provides pseudo-random number generator functions.
+source lib/dhry/Kconfig + source lib/rsa/Kconfig
menu "Hashing Support" diff --git a/lib/Makefile b/lib/Makefile index 97ed398..ca72187 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ obj-$(CONFIG_OF_LIBFDT) += libfdt/ obj-$(CONFIG_FIT) += libfdt/ +obj-$(CONFIG_FIT) += libfdt/ +obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
obj-$(CONFIG_AES) += aes.o obj-$(CONFIG_USB_TTY) += circbuf.o diff --git a/lib/dhry/Kconfig b/lib/dhry/Kconfig new file mode 100644 index 0000000..641b806 --- /dev/null +++ b/lib/dhry/Kconfig @@ -0,0 +1,7 @@ +config CMD_DHRYSTONE + bool "Support the 'dhry' command to run the dhrystone benchmark" + help + Dhrystone is an old benchmark in the public domain that gives a + rough idea of CPU performance. This enables a 'dhry' command + which runs this benchmark within U-Boot and reports the performance. + The number of 'Dhrystone MIPS' is also reported. diff --git a/lib/dhry/Makefile b/lib/dhry/Makefile new file mode 100644 index 0000000..926c0d6 --- /dev/null +++ b/lib/dhry/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += cmd_dhry.o dhry_1.o dhry_2.o diff --git a/lib/dhry/cmd_dhry.c b/lib/dhry/cmd_dhry.c new file mode 100644 index 0000000..5dc191e --- /dev/null +++ b/lib/dhry/cmd_dhry.c @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include "dhry.h" + +static int do_dhry(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong start, duration, dhry_per_sec, vax_mips; + int iterations = 1000000; + + if (argc > 1) + iterations = simple_strtoul(argv[1], NULL, 10); + + start = get_timer(0); + dhry(iterations); + duration = get_timer(start); + dhry_per_sec = iterations * 1000 / duration; + vax_mips = dhry_per_sec / 1757; + printf("%d iterations in %lu ms: %lu/s, %lu DMIPS\n", iterations, + duration, dhry_per_sec, vax_mips); + + return 0; +} + +U_BOOT_CMD( + dhry, 2, 1, do_dhry, + "[iterations] - run dhrystone benchmark", + "\n - run the Dhrystone 2.1 benchmark, a rough measure of CPU speed\n" +); diff --git a/lib/dhry/dhry.h b/lib/dhry/dhry.h new file mode 100644 index 0000000..49d4223 --- /dev/null +++ b/lib/dhry/dhry.h @@ -0,0 +1,442 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry.h SID: 3.4 5/15/91 19:30:21 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * Modification Log: + * addapted from: + * + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry.h (part 1 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * Siemens AG, AUT E 51 + * Postfach 3220 + * 8520 Erlangen + * Germany (West) + * Phone: [+49]-9131-7-20330 + * (8-17 Central European Time) + * Usenet: ..!mcvax!unido!estevax!weicker + * + * Original Version (in Ada) published in + * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984), + * pp. 1013 - 1030, together with the statistics + * on which the distribution of statements etc. is based. + * + * In this C version, the following C library functions are used: + * - strcpy, strcmp (inside the measurement loop) + * - printf, scanf (outside the measurement loop) + * In addition, Berkeley UNIX system calls "times ()" or "time ()" + * are used for execution time measurement. For measurements + * on other systems, these calls have to be changed. + * + * Collection of Results: + * Reinhold Weicker (address see above) and + * + * Rick Richardson + * PC Research. Inc. + * 94 Apple Orchard Drive + * Tinton Falls, NJ 07724 + * Phone: (201) 834-1378 (9-17 EST) + * Usenet: ...!seismo!uunet!pcrat!rick + * + * Please send results to Rick Richardson and/or Reinhold Weicker. + * Complete information should be given on hardware and software used. + * Hardware information includes: Machine type, CPU, type and size + * of caches; for microprocessors: clock frequency, memory speed + * (number of wait states). + * Software information includes: Compiler (and runtime library) + * manufacturer and version, compilation switches, OS version. + * The Operating System version may give an indication about the + * compiler; Dhrystone itself performs no OS calls in the measurement loop. + * + * The complete output generated by the program should be mailed + * such that at least some checks for correctness can be made. + * + *************************************************************************** + * + * History: This version C/2.1 has been made for two reasons: + * + * 1) There is an obvious need for a common C version of + * Dhrystone, since C is at present the most popular system + * programming language for the class of processors + * (microcomputers, minicomputers) where Dhrystone is used most. + * There should be, as far as possible, only one C version of + * Dhrystone such that results can be compared without + * restrictions. In the past, the C versions distributed + * by Rick Richardson (Version 1.1) and by Reinhold Weicker + * had small (though not significant) differences. + * + * 2) As far as it is possible without changes to the Dhrystone + * statistics, optimizing compilers should be prevented from + * removing significant statements. + * + * This C version has been developed in cooperation with + * Rick Richardson (Tinton Falls, NJ), it incorporates many + * ideas from the "Version 1.1" distributed previously by + * him over the UNIX network Usenet. + * I also thank Chaim Benedelac (National Semiconductor), + * David Ditzel (SUN), Earl Killian and John Mashey (MIPS), + * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley) + * for their help with comments on earlier versions of the + * benchmark. + * + * Changes: In the initialization part, this version follows mostly + * Rick Richardson's version distributed via Usenet, not the + * version distributed earlier via floppy disk by Reinhold Weicker. + * As a concession to older compilers, names have been made + * unique within the first 8 characters. + * Inside the measurement loop, this version follows the + * version previously distributed by Reinhold Weicker. + * + * At several places in the benchmark, code has been added, + * but within the measurement loop only in branches that + * are not executed. The intention is that optimizing compilers + * should be prevented from moving code out of the measurement + * loop, or from removing code altogether. Since the statements + * that are executed within the measurement loop have NOT been + * changed, the numbers defining the "Dhrystone distribution" + * (distribution of statements, operand types and locality) + * still hold. Except for sophisticated optimizing compilers, + * execution times for this version should be the same as + * for previous versions. + * + * Since it has proven difficult to subtract the time for the + * measurement loop overhead in a correct way, the loop check + * has been made a part of the benchmark. This does have + * an impact - though a very minor one - on the distribution + * statistics which have been updated for this version. + * + * All changes within the measurement loop are described + * and discussed in the companion paper "Rationale for + * Dhrystone version 2". + * + * Because of the self-imposed limitation that the order and + * distribution of the executed statements should not be + * changed, there are still cases where optimizing compilers + * may not generate code for some statements. To a certain + * degree, this is unavoidable for small synthetic benchmarks. + * Users of the benchmark are advised to check code listings + * whether code is generated for all statements of Dhrystone. + * + * Version 2.1 is identical to version 2.0 distributed via + * the UNIX network Usenet in March 1988 except that it corrects + * some minor deficiencies that were found by users of version 2.0. + * The only change within the measurement loop is that a + * non-executed "else" part was added to the "if" statement in + * Func_3, and a non-executed "else" part removed from Proc_3. + * + *************************************************************************** + * + * Defines: The following "Defines" are possible: + * -DREG=register (default: Not defined) + * As an approximation to what an average C programmer + * might do, the "register" storage class is applied + * (if enabled by -DREG=register) + * - for local variables, if they are used (dynamically) + * five or more times + * - for parameters if they are used (dynamically) + * six or more times + * Note that an optimal "register" strategy is + * compiler-dependent, and that "register" declarations + * do not necessarily lead to faster execution. + * -DNOSTRUCTASSIGN (default: Not defined) + * Define if the C compiler does not support + * assignment of structures. + * -DNOENUMS (default: Not defined) + * Define if the C compiler does not support + * enumeration types. + * -DTIMES (default) + * -DTIME + * The "times" function of UNIX (returning process times) + * or the "time" function (returning wallclock time) + * is used for measurement. + * For single user machines, "time ()" is adequate. For + * multi-user machines where you cannot get single-user + * access, use the "times ()" function. If you have + * neither, use a stopwatch in the dead of night. + * "printf"s are provided marking the points "Start Timer" + * and "Stop Timer". DO NOT use the UNIX "time(1)" + * command, as this will measure the total time to + * run this program, which will (erroneously) include + * the time to allocate storage (malloc) and to perform + * the initialization. + * -DHZ=nnn + * In Berkeley UNIX, the function "times" returns process + * time in 1/HZ seconds, with HZ = 60 for most systems. + * CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY + * A VALUE. + * + *************************************************************************** + * + * Compilation model and measurement (IMPORTANT): + * + * This C version of Dhrystone consists of three files: + * - dhry.h (this file, containing global definitions and comments) + * - dhry_1.c (containing the code corresponding to Ada package Pack_1) + * - dhry_2.c (containing the code corresponding to Ada package Pack_2) + * + * The following "ground rules" apply for measurements: + * - Separate compilation + * - No procedure merging + * - Otherwise, compiler optimizations are allowed but should be indicated + * - Default results are those without register declarations + * See the companion paper "Rationale for Dhrystone Version 2" for a more + * detailed discussion of these ground rules. + * + * For 16-Bit processors (e.g. 80186, 80286), times for all compilation + * models ("small", "medium", "large" etc.) should be given if possible, + * together with a definition of these models for the compiler system used. + * + ************************************************************************** + * + * Dhrystone (C version) statistics: + * + * [Comment from the first distribution, updated for version 2. + * Note that because of language differences, the numbers are slightly + * different from the Ada version.] + * + * The following program contains statements of a high level programming + * language (here: C) in a distribution considered representative: + * + * assignments 52 (51.0 %) + * control statements 33 (32.4 %) + * procedure, function calls 17 (16.7 %) + * + * 103 statements are dynamically executed. The program is balanced with + * respect to the three aspects: + * + * - statement type + * - operand type + * - operand locality + * operand global, local, parameter, or constant. + * + * The combination of these three aspects is balanced only approximately. + * + * 1. Statement Type: + * ----------------- number + * + * V1 = V2 9 + * (incl. V1 = F(..) + * V = Constant 12 + * Assignment, 7 + * with array element + * Assignment, 6 + * with record component + * -- + * 34 34 + * + * X = Y +|-|"&&"|"|" Z 5 + * X = Y +|-|"==" Constant 6 + * X = X +|- 1 3 + * X = Y *|/ Z 2 + * X = Expression, 1 + * two operators + * X = Expression, 1 + * three operators + * -- + * 18 18 + * + * if .... 14 + * with "else" 7 + * without "else" 7 + * executed 3 + * not executed 4 + * for ... 7 | counted every time + * while ... 4 | the loop condition + * do ... while 1 | is evaluated + * switch ... 1 + * break 1 + * declaration with 1 + * initialization + * -- + * 34 34 + * + * P (...) procedure call 11 + * user procedure 10 + * library procedure 1 + * X = F (...) + * function call 6 + * user function 5 + * library function 1 + * -- + * 17 17 + * --- + * 103 + * + * The average number of parameters in procedure or function calls + * is 1.82 (not counting the function values as implicit parameters). + * + * + * 2. Operators + * ------------ + * number approximate + * percentage + * + * Arithmetic 32 50.8 + * + * + 21 33.3 + * - 7 11.1 + * * 3 4.8 + * / (int div) 1 1.6 + * + * Comparison 27 42.8 + * + * == 9 14.3 + * /= 4 6.3 + * > 1 1.6 + * < 3 4.8 + * >= 1 1.6 + * <= 9 14.3 + * + * Logic 4 6.3 + * + * && (AND-THEN) 1 1.6 + * | (OR) 1 1.6 + * ! (NOT) 2 3.2 + * + * -- ----- + * 63 100.1 + * + * + * 3. Operand Type (counted once per operand reference): + * --------------- + * number approximate + * percentage + * + * Integer 175 72.3 % + * Character 45 18.6 % + * Pointer 12 5.0 % + * String30 6 2.5 % + * Array 2 0.8 % + * Record 2 0.8 % + * --- ------- + * 242 100.0 % + * + * When there is an access path leading to the final operand (e.g. a record + * component), only the final data type on the access path is counted. + * + * + * 4. Operand Locality: + * ------------------- + * number approximate + * percentage + * + * local variable 114 47.1 % + * global variable 22 9.1 % + * parameter 45 18.6 % + * value 23 9.5 % + * reference 22 9.1 % + * function result 6 2.5 % + * constant 55 22.7 % + * --- ------- + * 242 100.0 % + * + * + * The program does not compute anything meaningful, but it is syntactically + * and semantically correct. All variables have a value assigned to them + * before they are used as a source operand. + * + * There has been no explicit effort to account for the effects of a + * cache, or to balance the use of long or short displacements for code or + * data. + * + *************************************************************************** + */ + + +/* Compiler and system dependent definitions: */ + +#ifndef TIME +#define TIMES +#endif + /* Use times(2) time function unless */ + /* explicitly defined otherwise */ + +#define Mic_secs_Per_Second 1000000.0 + /* Berkeley UNIX C returns process times in seconds/HZ */ + +#ifdef NOSTRUCTASSIGN +#define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) +#else +#define structassign(d, s) d = s +#endif + +#ifdef NOENUM +#define Ident_1 0 +#define Ident_2 1 +#define Ident_3 2 +#define Ident_4 3 +#define Ident_5 4 + typedef int Enumeration; +#else + typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5} + Enumeration; +#endif + /* for boolean and enumeration types in Ada, Pascal */ + +/* General definitions: */ + +#define Null 0 + /* Value of a Null pointer */ +#define true 1 +#define false 0 + +typedef int One_Thirty; +typedef int One_Fifty; +typedef char Capital_Letter; +typedef int Boolean; +typedef char Str_30 [31]; +typedef int Arr_1_Dim [50]; +typedef int Arr_2_Dim [50] [50]; + +typedef struct record + { + struct record *Ptr_Comp; + Enumeration Discr; + union { + struct { + Enumeration Enum_Comp; + int Int_Comp; + char Str_Comp [31]; + } var_1; + struct { + Enumeration E_Comp_2; + char Str_2_Comp [31]; + } var_2; + struct { + char Ch_1_Comp; + char Ch_2_Comp; + } var_3; + } variant; + } Rec_Type, *Rec_Pointer; + + +/* + * dhry() - run dhrystone for a given number of iterations + * + * @iterations: Number of iterations to run + */ +void dhry(int iterations); diff --git a/lib/dhry/dhry_1.c b/lib/dhry/dhry_1.c new file mode 100644 index 0000000..be63710 --- /dev/null +++ b/lib/dhry/dhry_1.c @@ -0,0 +1,421 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry_1.c SID: 3.4 5/15/91 19:30:21 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * + * *** WARNING **** With BYTE's modifications applied, results obtained with + * ******* this version of the Dhrystone program may not be applicable + * to other versions. + * + * Modification Log: + * 10/22/97 - code cleanup to remove ANSI C compiler warnings + * Andy Kahn kahn@zk3.dec.com + * + * Adapted from: + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry_1.c (part 2 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + ***************************************************************************/ +char SCCSid[] = "@(#) @(#)dhry_1.c:3.4 -- 5/15/91 19:30:21"; + +#include <common.h> +#include <malloc.h> + +#include "dhry.h" + +unsigned long Run_Index; + +void report(void) +{ + printf("%ld loops\n", Run_Index); +} + +/* Global Variables: */ + +Rec_Pointer Ptr_Glob, + Next_Ptr_Glob; +int Int_Glob; +Boolean Bool_Glob; +char Ch_1_Glob, + Ch_2_Glob; +int Arr_1_Glob [50]; +int Arr_2_Glob [50] [50]; + +Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val); + /* forward declaration necessary since Enumeration may not simply be int */ + +#ifndef REG + Boolean Reg = false; +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else + Boolean Reg = true; +#endif + +/* variables for time measurement: */ + +#ifdef TIMES +#define Too_Small_Time 120 + /* Measurements should last at least about 2 seconds */ +#endif +#ifdef TIME +extern long time(); + /* see library function "time" */ +#define Too_Small_Time 2 + /* Measurements should last at least 2 seconds */ +#endif + +long Begin_Time, + End_Time, + User_Time; + +/* end of variables for time measurement */ + +void Proc_1 (REG Rec_Pointer Ptr_Val_Par); +void Proc_2 (One_Fifty *Int_Par_Ref); +void Proc_3 (Rec_Pointer *Ptr_Ref_Par); +void Proc_4 (void); +void Proc_5 (void); + + +extern Boolean Func_2(Str_30, Str_30); +extern void Proc_6(Enumeration, Enumeration *); +extern void Proc_7(One_Fifty, One_Fifty, One_Fifty *); +extern void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int); + +void dhry(int Number_Of_Runs) + /* main program, corresponds to procedures */ + /* Main and Proc_0 in the Ada version */ +{ + One_Fifty Int_1_Loc; + REG One_Fifty Int_2_Loc; + One_Fifty Int_3_Loc; + REG char Ch_Index; + Enumeration Enum_Loc; + Str_30 Str_1_Loc; + Str_30 Str_2_Loc; + + /* Initializations */ + + Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + + Ptr_Glob->Ptr_Comp = Next_Ptr_Glob; + Ptr_Glob->Discr = Ident_1; + Ptr_Glob->variant.var_1.Enum_Comp = Ident_3; + Ptr_Glob->variant.var_1.Int_Comp = 40; + strcpy (Ptr_Glob->variant.var_1.Str_Comp, + "DHRYSTONE PROGRAM, SOME STRING"); + strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); + + Arr_2_Glob [8][7] = 10; + /* Was missing in published program. Without this statement, */ + /* Arr_2_Glob [8][7] would have an undefined value. */ + /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */ + /* overflow may occur for this array element. */ + +#ifdef PRATTLE + printf ("\n"); + printf ("Dhrystone Benchmark, Version 2.1 (Language: C)\n"); + printf ("\n"); + if (Reg) + { + printf ("Program compiled with 'register' attribute\n"); + printf ("\n"); + } + else + { + printf ("Program compiled without 'register' attribute\n"); + printf ("\n"); + } + printf ("Please give the number of runs through the benchmark: "); + { + int n; + scanf ("%d", &n); + Number_Of_Runs = n; + } + printf ("\n"); + + printf ("Execution starts, %d runs through Dhrystone\n", Number_Of_Runs); +#endif /* PRATTLE */ + + Run_Index = 0; + + /***************/ + /* Start timer */ + /***************/ + +#ifdef SELF_TIMED +#ifdef TIMES + times (&time_info); + Begin_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + Begin_Time = time ( (long *) 0); +#endif +#endif /* SELF_TIMED */ + + for (Run_Index = 1; Run_Index < Number_Of_Runs; ++Run_Index) + { + + Proc_5(); + Proc_4(); + /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */ + Int_1_Loc = 2; + Int_2_Loc = 3; + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + Enum_Loc = Ident_2; + Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc); + /* Bool_Glob == 1 */ + while (Int_1_Loc < Int_2_Loc) /* loop body executed once */ + { + Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc; + /* Int_3_Loc == 7 */ + Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc); + /* Int_3_Loc == 7 */ + Int_1_Loc += 1; + } /* while */ + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc); + /* Int_Glob == 5 */ + Proc_1 (Ptr_Glob); + for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index) + /* loop body executed twice */ + { + if (Enum_Loc == Func_1 (Ch_Index, 'C')) + /* then, not executed */ + { + Proc_6 (Ident_1, &Enum_Loc); + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING"); + Int_2_Loc = Run_Index; + Int_Glob = Run_Index; + } + } + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Int_2_Loc = Int_2_Loc * Int_1_Loc; + Int_1_Loc = Int_2_Loc / Int_3_Loc; + Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc; + /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */ + Proc_2 (&Int_1_Loc); + /* Int_1_Loc == 5 */ + + } /* loop "for Run_Index" */ + + /**************/ + /* Stop timer */ + /**************/ +#ifdef SELF_TIMED +#ifdef TIMES + times (&time_info); + End_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + End_Time = time ( (long *) 0); +#endif +#endif /* SELF_TIMED */ + + /* BYTE version never executes this stuff */ +#ifdef SELF_TIMED + printf ("Execution ends\n"); + printf ("\n"); + printf ("Final values of the variables used in the benchmark:\n"); + printf ("\n"); + printf ("Int_Glob: %d\n", Int_Glob); + printf (" should be: %d\n", 5); + printf ("Bool_Glob: %d\n", Bool_Glob); + printf (" should be: %d\n", 1); + printf ("Ch_1_Glob: %c\n", Ch_1_Glob); + printf (" should be: %c\n", 'A'); + printf ("Ch_2_Glob: %c\n", Ch_2_Glob); + printf (" should be: %c\n", 'B'); + printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]); + printf (" should be: %d\n", 7); + printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]); + printf (" should be: Number_Of_Runs + 10\n"); + printf ("Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent)\n"); + printf (" Discr: %d\n", Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 2); + printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 17); + printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Next_Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent), same as above\n"); + printf (" Discr: %d\n", Next_Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 1); + printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 18); + printf (" Str_Comp: %s\n", + Next_Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Int_1_Loc: %d\n", Int_1_Loc); + printf (" should be: %d\n", 5); + printf ("Int_2_Loc: %d\n", Int_2_Loc); + printf (" should be: %d\n", 13); + printf ("Int_3_Loc: %d\n", Int_3_Loc); + printf (" should be: %d\n", 7); + printf ("Enum_Loc: %d\n", Enum_Loc); + printf (" should be: %d\n", 1); + printf ("Str_1_Loc: %s\n", Str_1_Loc); + printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n"); + printf ("Str_2_Loc: %s\n", Str_2_Loc); + printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n"); + printf ("\n"); + + User_Time = End_Time - Begin_Time; + + if (User_Time < Too_Small_Time) + { + printf ("Measured time too small to obtain meaningful results\n"); + printf ("Please increase number of runs\n"); + printf ("\n"); + } + else + { +#ifdef TIME + Microseconds = (float) User_Time * Mic_secs_Per_Second + / (float) Number_Of_Runs; + Dhrystones_Per_Second = (float) Number_Of_Runs / (float) User_Time; +#else + Microseconds = (float) User_Time * Mic_secs_Per_Second + / ((float) HZ * ((float) Number_Of_Runs)); + Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs) + / (float) User_Time; +#endif + printf ("Microseconds for one run through Dhrystone: "); + printf ("%6.1f \n", Microseconds); + printf ("Dhrystones per Second: "); + printf ("%6.1f \n", Dhrystones_Per_Second); + printf ("\n"); + } +#endif /* SELF_TIMED */ +} + + +void Proc_1 (REG Rec_Pointer Ptr_Val_Par) + /* executed once */ +{ + REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp; + /* == Ptr_Glob_Next */ + /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */ + /* corresponds to "rename" in Ada, "with" in Pascal */ + + structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob); + Ptr_Val_Par->variant.var_1.Int_Comp = 5; + Next_Record->variant.var_1.Int_Comp + = Ptr_Val_Par->variant.var_1.Int_Comp; + Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp; + Proc_3 (&Next_Record->Ptr_Comp); + /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp + == Ptr_Glob->Ptr_Comp */ + if (Next_Record->Discr == Ident_1) + /* then, executed */ + { + Next_Record->variant.var_1.Int_Comp = 6; + Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp, + &Next_Record->variant.var_1.Enum_Comp); + Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp; + Proc_7 (Next_Record->variant.var_1.Int_Comp, 10, + &Next_Record->variant.var_1.Int_Comp); + } + else /* not executed */ + structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp); +} /* Proc_1 */ + + +void Proc_2 (One_Fifty *Int_Par_Ref) + /* executed once */ + /* *Int_Par_Ref == 1, becomes 4 */ +{ + One_Fifty Int_Loc; + Enumeration Enum_Loc; + + Enum_Loc = 0; + + Int_Loc = *Int_Par_Ref + 10; + do /* executed once */ + if (Ch_1_Glob == 'A') + /* then, executed */ + { + Int_Loc -= 1; + *Int_Par_Ref = Int_Loc - Int_Glob; + Enum_Loc = Ident_1; + } /* if */ + while (Enum_Loc != Ident_1); /* true */ +} /* Proc_2 */ + + +void Proc_3 (Rec_Pointer *Ptr_Ref_Par) + /* executed once */ + /* Ptr_Ref_Par becomes Ptr_Glob */ +{ + if (Ptr_Glob != Null) + /* then, executed */ + *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp; + Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp); +} /* Proc_3 */ + + +void Proc_4 (void) /* without parameters */ + /* executed once */ +{ + Boolean Bool_Loc; + + Bool_Loc = Ch_1_Glob == 'A'; + Bool_Glob = Bool_Loc | Bool_Glob; + Ch_2_Glob = 'B'; +} /* Proc_4 */ + +void Proc_5 (void) /* without parameters */ +/*******/ + /* executed once */ +{ + Ch_1_Glob = 'A'; + Bool_Glob = false; +} /* Proc_5 */ + + + /* Procedure for the assignment of structures, */ + /* if the C compiler doesn't support this feature */ +#ifdef NOSTRUCTASSIGN +memcpy (d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} +#endif diff --git a/lib/dhry/dhry_2.c b/lib/dhry/dhry_2.c new file mode 100644 index 0000000..59aa458 --- /dev/null +++ b/lib/dhry/dhry_2.c @@ -0,0 +1,217 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry_2.c SID: 3.4 5/15/91 19:30:22 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * Modification Log: + * 10/22/97 - code cleanup to remove ANSI C compiler warnings + * Andy Kahn kahn@zk3.dec.com + * + * Adapted from: + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * **** WARNING **** See warning in n.dhry_1.c + * + * Version: C, Version 2.1 + * + * File: dhry_2.c (part 3 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + ****************************************************************************/ +/* SCCSid is defined in dhry_1.c */ + +#include <common.h> +#include "dhry.h" + +#ifndef REG +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#endif + +extern int Int_Glob; +extern char Ch_1_Glob; + +void Proc_6(Enumeration, Enumeration *); +void Proc_7(One_Fifty, One_Fifty, One_Fifty *); +void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int); +Enumeration Func_1(Capital_Letter, Capital_Letter); +Boolean Func_2(Str_30, Str_30); +Boolean Func_3(Enumeration); + +void Proc_6 (Enumeration Enum_Val_Par, Enumeration *Enum_Ref_Par) + /* executed once */ + /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */ +{ + *Enum_Ref_Par = Enum_Val_Par; + if (! Func_3 (Enum_Val_Par)) + /* then, not executed */ + *Enum_Ref_Par = Ident_4; + switch (Enum_Val_Par) + { + case Ident_1: + *Enum_Ref_Par = Ident_1; + break; + case Ident_2: + if (Int_Glob > 100) + /* then */ + *Enum_Ref_Par = Ident_1; + else *Enum_Ref_Par = Ident_4; + break; + case Ident_3: /* executed */ + *Enum_Ref_Par = Ident_2; + break; + case Ident_4: break; + case Ident_5: + *Enum_Ref_Par = Ident_3; + break; + } /* switch */ +} /* Proc_6 */ + +void Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref) +One_Fifty Int_1_Par_Val; +One_Fifty Int_2_Par_Val; +One_Fifty *Int_Par_Ref; +/**********************************************/ + /* executed three times */ + /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */ + /* Int_Par_Ref becomes 7 */ + /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */ + /* Int_Par_Ref becomes 17 */ + /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */ + /* Int_Par_Ref becomes 18 */ +{ + One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 2; + *Int_Par_Ref = Int_2_Par_Val + Int_Loc; +} /* Proc_7 */ + + +void Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val) +/*********************************************************************/ + /* executed once */ + /* Int_Par_Val_1 == 3 */ + /* Int_Par_Val_2 == 7 */ +Arr_1_Dim Arr_1_Par_Ref; +Arr_2_Dim Arr_2_Par_Ref; +int Int_1_Par_Val; +int Int_2_Par_Val; +{ + REG One_Fifty Int_Index; + REG One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 5; + Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val; + Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc]; + Arr_1_Par_Ref [Int_Loc+30] = Int_Loc; + for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index) + Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc; + Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1; + Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc]; + Int_Glob = 5; +} /* Proc_8 */ + + +Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val) +/*************************************************/ + /* executed three times */ + /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */ + /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */ + /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */ +{ + Capital_Letter Ch_1_Loc; + Capital_Letter Ch_2_Loc; + + Ch_1_Loc = Ch_1_Par_Val; + Ch_2_Loc = Ch_1_Loc; + if (Ch_2_Loc != Ch_2_Par_Val) + /* then, executed */ + return (Ident_1); + else /* not executed */ + { + Ch_1_Glob = Ch_1_Loc; + return (Ident_2); + } +} /* Func_1 */ + + + +Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref) +/*************************************************/ + /* executed once */ + /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */ + /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */ + +Str_30 Str_1_Par_Ref; +Str_30 Str_2_Par_Ref; +{ + REG One_Thirty Int_Loc; + Capital_Letter Ch_Loc; + + Ch_Loc = 'A'; + Int_Loc = 2; + while (Int_Loc <= 2) /* loop body executed once */ + if (Func_1 (Str_1_Par_Ref[Int_Loc], + Str_2_Par_Ref[Int_Loc+1]) == Ident_1) + /* then, executed */ + { + Ch_Loc = 'A'; + Int_Loc += 1; + } /* if, while */ + if (Ch_Loc >= 'W' && Ch_Loc < 'Z') + /* then, not executed */ + Int_Loc = 7; + if (Ch_Loc == 'R') + /* then, not executed */ + return (true); + else /* executed */ + { + if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0) + /* then, not executed */ + { + Int_Loc += 7; + Int_Glob = Int_Loc; + return (true); + } + else /* executed */ + return (false); + } /* if Ch_Loc */ +} /* Func_2 */ + + +Boolean Func_3 (Enum_Par_Val) +/***************************/ + /* executed once */ + /* Enum_Par_Val == Ident_3 */ +Enumeration Enum_Par_Val; +{ + Enumeration Enum_Loc; + + Enum_Loc = Enum_Par_Val; + if (Enum_Loc == Ident_3) + /* then, executed */ + return (true); + else /* not executed */ + return (false); +} /* Func_3 */

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Drystone provides a convenient sanity check that the CPU is running at full speed. Add this as a command which can be enabled as needed.
Note: I investigated using Coremark for this but there was a license agreement and I could not work out if it was GPL-compatible.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
lib/Kconfig | 2 + lib/Makefile | 2 + lib/dhry/Kconfig | 7 + lib/dhry/Makefile | 7 + lib/dhry/cmd_dhry.c | 34 ++++ lib/dhry/dhry.h | 442 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/dhry/dhry_1.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/dhry/dhry_2.c | 217 ++++++++++++++++++++++++++ 8 files changed, 1132 insertions(+) create mode 100644 lib/dhry/Kconfig create mode 100644 lib/dhry/Makefile create mode 100644 lib/dhry/cmd_dhry.c create mode 100644 lib/dhry/dhry.h create mode 100644 lib/dhry/dhry_1.c create mode 100644 lib/dhry/dhry_2.c
Applied to u-boot-dm.

Provide access to the dhrystone benchmark command.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 31fe2f9..3953ec3 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -19,6 +19,7 @@ CONFIG_DM_PCI=y CONFIG_PCI_SANDBOX=y CONFIG_SPI_FLASH_SANDBOX=y CONFIG_CMD_CROS_EC=y +CONFIG_CMD_DHRYSTONE=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SANDBOX=y CONFIG_CROS_EC_KEYB=y

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Provide access to the dhrystone benchmark command.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
configs/sandbox_defconfig | 1 + 1 file changed, 1 insertion(+)
Applied to u-boot-dm.

Offer to display the available image types in help. Also, rather than hacking the genimg_get_type_id() function to display a list of types, do this in the tool. Also, sort the list.
The list of image types is quite long, and hard to discover. Print it out when we show help information.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: - Use case-insensitve compare for image params (fixes build break with m53evk)
Changes in v2: None
common/image.c | 58 +++++++++++++++++++++++++++++++------------------------- include/image.h | 11 +++++++++++ tools/mkimage.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 33 deletions(-)
diff --git a/common/image.c b/common/image.c index f0f0135..9efacf8 100644 --- a/common/image.c +++ b/common/image.c @@ -543,6 +543,15 @@ void genimg_print_time(time_t timestamp) } #endif
+const table_entry_t *get_table_entry(const table_entry_t *table, int id) +{ + for (; table->id >= 0; ++table) { + if (table->id == id) + return table; + } + return NULL; +} + /** * get_table_entry_name - translate entry id to long name * @table: pointer to a translation table for entries of a specific type @@ -559,15 +568,14 @@ void genimg_print_time(time_t timestamp) */ char *get_table_entry_name(const table_entry_t *table, char *msg, int id) { - for (; table->id >= 0; ++table) { - if (table->id == id) + table = get_table_entry(table, id); + if (!table) + return msg; #if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) - return table->lname; + return table->lname; #else - return table->lname + gd->reloc_off; + return table->lname + gd->reloc_off; #endif - } - return (msg); }
const char *genimg_get_os_name(uint8_t os) @@ -586,6 +594,20 @@ const char *genimg_get_type_name(uint8_t type) return (get_table_entry_name(uimage_type, "Unknown Image", type)); }
+const char *genimg_get_type_short_name(uint8_t type) +{ + const table_entry_t *table; + + table = get_table_entry(uimage_type, type); + if (!table) + return "unknown"; +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return table->sname; +#else + return table->sname + gd->reloc_off; +#endif +} + const char *genimg_get_comp_name(uint8_t comp) { return (get_table_entry_name(uimage_comp, "Unknown Compression", @@ -610,34 +632,18 @@ int get_table_entry_id(const table_entry_t *table, const char *table_name, const char *name) { const table_entry_t *t; -#ifdef USE_HOSTCC - int first = 1; - - for (t = table; t->id >= 0; ++t) { - if (t->sname && strcasecmp(t->sname, name) == 0) - return(t->id); - }
- fprintf(stderr, "\nInvalid %s Type - valid names are", table_name); - for (t = table; t->id >= 0; ++t) { - if (t->sname == NULL) - continue; - fprintf(stderr, "%c %s", (first) ? ':' : ',', t->sname); - first = 0; - } - fprintf(stderr, "\n"); -#else for (t = table; t->id >= 0; ++t) { #ifdef CONFIG_NEEDS_MANUAL_RELOC - if (t->sname && strcmp(t->sname + gd->reloc_off, name) == 0) + if (t->sname && strcasecmp(t->sname + gd->reloc_off, name) == 0) #else - if (t->sname && strcmp(t->sname, name) == 0) + if (t->sname && strcasecmp(t->sname, name) == 0) #endif return (t->id); } debug("Invalid %s Type: %s\n", table_name, name); -#endif /* USE_HOSTCC */ - return (-1); + + return -1; }
int genimg_get_os_id(const char *name) diff --git a/include/image.h b/include/image.h index b6eb57e..63c3d37 100644 --- a/include/image.h +++ b/include/image.h @@ -246,6 +246,8 @@ struct lmb; #define IH_TYPE_LPC32XXIMAGE 21 /* x86 setup.bin Image */ #define IH_TYPE_LOADABLE 22 /* A list of typeless images */
+#define IH_TYPE_COUNT 23 /* Number of image types */ + /* * Compression Types */ @@ -411,6 +413,15 @@ char *get_table_entry_name(const table_entry_t *table, char *msg, int id); const char *genimg_get_os_name(uint8_t os); const char *genimg_get_arch_name(uint8_t arch); const char *genimg_get_type_name(uint8_t type); + +/** + * genimg_get_type_short_name() - get the short name for an image type + * + * @param type Image type (IH_TYPE_...) + * @return image short name, or "unknown" if unknown + */ +const char *genimg_get_type_short_name(uint8_t type); + const char *genimg_get_comp_name(uint8_t comp); int genimg_get_os_id(const char *name); int genimg_get_arch_id(const char *name); diff --git a/tools/mkimage.c b/tools/mkimage.c index 5ccd951..8808d70 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -26,8 +26,48 @@ struct image_tool_params params = { .imagename2 = "", };
-int -main (int argc, char **argv) +static int h_compare_image_name(const void *vtype1, const void *vtype2) +{ + const int *type1 = vtype1; + const int *type2 = vtype2; + const char *name1 = genimg_get_type_short_name(*type1); + const char *name2 = genimg_get_type_short_name(*type2); + + return strcmp(name1, name2); +} + +/* Show all image types supported by mkimage */ +static void show_image_types(void) +{ + struct image_type_params *tparams; + int order[IH_TYPE_COUNT]; + int count; + int type; + int i; + + /* Sort the names in order of short name for easier reading */ + memset(order, '\0', sizeof(order)); + for (count = 0, type = 0; type < IH_TYPE_COUNT; type++) { + tparams = imagetool_get_type(type); + if (tparams) + order[count++] = type; + } + qsort(order, count, sizeof(int), h_compare_image_name); + + fprintf(stderr, "\nInvalid image type. Supported image types:\n"); + for (i = 0; i < count; i++) { + type = order[i]; + tparams = imagetool_get_type(type); + if (tparams) { + fprintf(stderr, "\t%-15s %s\n", + genimg_get_type_short_name(type), + genimg_get_type_name(type)); + } + } + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) { int ifd = -1; struct stat sbuf; @@ -75,12 +115,16 @@ main (int argc, char **argv) usage (); goto NXTARG; case 'T': - if ((--argc <= 0) || - (params.type = - genimg_get_type_id (*++argv)) < 0) - usage (); + params.type = -1; + if (--argc >= 0 && argv[1]) { + params.type = + genimg_get_type_id(*++argv); + } + if (params.type < 0) { + show_image_types(); + usage(); + } goto NXTARG; - case 'a': if (--argc <= 0) usage (); @@ -546,6 +590,7 @@ static void usage(void) #endif fprintf (stderr, " %s -V ==> print version information and exit\n", params.cmdname); + fprintf(stderr, "Use -T to see a list of available image types\n");
exit (EXIT_FAILURE); }

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Offer to display the available image types in help. Also, rather than hacking the genimg_get_type_id() function to display a list of types, do this in the tool. Also, sort the list.
The list of image types is quite long, and hard to discover. Print it out when we show help information.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Use case-insensitve compare for image params (fixes build break with m53evk)
Changes in v2: None
common/image.c | 58 +++++++++++++++++++++++++++++++------------------------- include/image.h | 11 +++++++++++ tools/mkimage.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 33 deletions(-)
Applied to u-boot-dm.
(If people have time to test this out it would be great!)

Property names are stored in a string table. When a node property is removed, the string table is not updated since other nodes may have a property with the same name.
Thus it is possible for the string table to build up a number of unused strings. Add a function to remove these. This works by building a new device tree from the old one, adding strings one by one as needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: - Fix indentation in function comment
Changes in v2: - Add new patch to remove unused strings from a device tree
include/libfdt.h | 17 +++++++++++++++++ lib/libfdt/fdt_rw.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+)
diff --git a/include/libfdt.h b/include/libfdt.h index f3cbb63..610c507 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1646,6 +1646,23 @@ int fdt_del_node(void *fdt, int nodeoffset);
const char *fdt_strerror(int errval);
+/** + * fdt_remove_unused_strings() - Remove any unused strings from an FDT + * + * This creates a new device tree in @new with unused strings removed. The + * called can then use fdt_pack() to minimise the space consumed. + * + * @old: Old device tree blog + * @new: Place to put new device tree blob, which must be as large as + * @old + * @return + * 0, on success + * -FDT_ERR_BADOFFSET, corrupt device tree + * -FDT_ERR_NOSPACE, out of space, which should not happen unless there + * is something very wrong with the device tree input + */ +int fdt_remove_unused_strings(const void *old, void *new); + struct fdt_region { int offset; int size; diff --git a/lib/libfdt/fdt_rw.c b/lib/libfdt/fdt_rw.c index bec8b8a..1a358a8 100644 --- a/lib/libfdt/fdt_rw.c +++ b/lib/libfdt/fdt_rw.c @@ -449,3 +449,35 @@ int fdt_pack(void *fdt)
return 0; } + +int fdt_remove_unused_strings(const void *old, void *new) +{ + const struct fdt_property *old_prop; + struct fdt_property *new_prop; + int size = fdt_totalsize(old); + int next_offset, offset; + const char *str; + int ret; + int tag = FDT_PROP; + + /* Make a copy and remove the strings */ + memcpy(new, old, size); + fdt_set_size_dt_strings(new, 0); + + /* Add every property name back into the new string table */ + for (offset = 0; tag != FDT_END; offset = next_offset) { + tag = fdt_next_tag(old, offset, &next_offset); + if (tag != FDT_PROP) + continue; + old_prop = fdt_get_property_by_offset(old, offset, NULL); + new_prop = (struct fdt_property *)(unsigned long) + fdt_get_property_by_offset(new, offset, NULL); + str = fdt_string(old, fdt32_to_cpu(old_prop->nameoff)); + ret = _fdt_find_add_string(new, str); + if (ret < 0) + return ret; + new_prop->nameoff = cpu_to_fdt32(ret); + } + + return 0; +}

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Property names are stored in a string table. When a node property is removed, the string table is not updated since other nodes may have a property with the same name.
Thus it is possible for the string table to build up a number of unused strings. Add a function to remove these. This works by building a new device tree from the old one, adding strings one by one as needed.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Fix indentation in function comment
Changes in v2:
- Add new patch to remove unused strings from a device tree
include/libfdt.h | 17 +++++++++++++++++ lib/libfdt/fdt_rw.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+)
Applied to u-boot-dm.

These have been sent upstream but not accepted to libfdt. For now, bring these into U-Boot to enable fdtgrep to operate. We will use fdtgrep to cut device tree files down for SPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: - Add new patch with fdt_first/next_region() functions - Tidy up commit message a little
include/libfdt.h | 239 ++++++++++++++++++++++- lib/libfdt/Makefile | 2 +- lib/libfdt/fdt_region.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 lib/libfdt/fdt_region.c
diff --git a/include/libfdt.h b/include/libfdt.h index 610c507..b8b1866 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -121,7 +121,12 @@ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells * or similar property with a bad format or value */
-#define FDT_ERR_MAX 14 +#define FDT_ERR_TOODEEP 15 + /* FDT_ERR_TOODEEP: The depth of a node has exceeded the internal + * libfdt limit. This can happen if you have more than + * FDT_MAX_DEPTH nested nodes. */ + +#define FDT_ERR_MAX 15
/**********************************************************************/ /* Low-level functions (you probably don't need these) */ @@ -1668,6 +1673,77 @@ struct fdt_region { int size; };
+/* + * Flags for fdt_find_regions() + * + * Add a region for the string table (always the last region) + */ +#define FDT_REG_ADD_STRING_TAB (1 << 0) + +/* + * Add all supernodes of a matching node/property, useful for creating a + * valid subset tree + */ +#define FDT_REG_SUPERNODES (1 << 1) + +/* Add the FDT_BEGIN_NODE tags of subnodes, including their names */ +#define FDT_REG_DIRECT_SUBNODES (1 << 2) + +/* Add all subnodes of a matching node */ +#define FDT_REG_ALL_SUBNODES (1 << 3) + +/* Add a region for the mem_rsvmap table (always the first region) */ +#define FDT_REG_ADD_MEM_RSVMAP (1 << 4) + +/* Indicates what an fdt part is (node, property, value) */ +#define FDT_IS_NODE (1 << 0) +#define FDT_IS_PROP (1 << 1) +#define FDT_IS_VALUE (1 << 2) /* not supported */ +#define FDT_IS_COMPAT (1 << 3) /* used internally */ +#define FDT_NODE_HAS_PROP (1 << 4) /* node contains prop */ + +#define FDT_ANY_GLOBAL (FDT_IS_NODE | FDT_IS_PROP | FDT_IS_VALUE | \ + FDT_IS_COMPAT) +#define FDT_IS_ANY 0x1f /* all the above */ + +/* We set a reasonable limit on the number of nested nodes */ +#define FDT_MAX_DEPTH 32 + +/* Decribes what we want to include from the current tag */ +enum want_t { + WANT_NOTHING, + WANT_NODES_ONLY, /* No properties */ + WANT_NODES_AND_PROPS, /* Everything for one level */ + WANT_ALL_NODES_AND_PROPS /* Everything for all levels */ +}; + +/* Keeps track of the state at parent nodes */ +struct fdt_subnode_stack { + int offset; /* Offset of node */ + enum want_t want; /* The 'want' value here */ + int included; /* 1 if we included this node, 0 if not */ +}; + +struct fdt_region_ptrs { + int depth; /* Current tree depth */ + int done; /* What we have completed scanning */ + enum want_t want; /* What we are currently including */ + char *end; /* Pointer to end of full node path */ + int nextoffset; /* Next node offset to check */ +}; + +/* The state of our finding algortihm */ +struct fdt_region_state { + struct fdt_subnode_stack stack[FDT_MAX_DEPTH]; /* node stack */ + struct fdt_region *region; /* Contains list of regions found */ + int count; /* Numnber of regions found */ + const void *fdt; /* FDT blob */ + int max_regions; /* Maximum regions to find */ + int can_merge; /* 1 if we can merge with previous region */ + int start; /* Start position of current region */ + struct fdt_region_ptrs ptrs; /* Pointers for what we are up to */ +}; + /** * fdt_find_regions() - find regions in device tree * @@ -1727,4 +1803,165 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab);
+/** + * fdt_first_region() - find regions in device tree + * + * Given a nodes and properties to include and properties to exclude, find + * the regions of the device tree which describe those included parts. + * + * The use for this function is twofold. Firstly it provides a convenient + * way of performing a structure-aware grep of the tree. For example it is + * possible to grep for a node and get all the properties associated with + * that node. Trees can be subsetted easily, by specifying the nodes that + * are required, and then writing out the regions returned by this function. + * This is useful for small resource-constrained systems, such as boot + * loaders, which want to use an FDT but do not need to know about all of + * it. + * + * Secondly it makes it easy to hash parts of the tree and detect changes. + * The intent is to get a list of regions which will be invariant provided + * those parts are invariant. For example, if you request a list of regions + * for all nodes but exclude the property "data", then you will get the + * same region contents regardless of any change to "data" properties. + * + * This function can be used to produce a byte-stream to send to a hashing + * function to verify that critical parts of the FDT have not changed. + * Note that semantically null changes in order could still cause false + * hash misses. Such reordering might happen if the tree is regenerated + * from source, and nodes are reordered (the bytes-stream will be emitted + * in a different order and mnay hash functions will detect this). However + * if an existing tree is modified using libfdt functions, such as + * fdt_add_subnode() and fdt_setprop(), then this problem is avoided. + * + * The nodes/properties to include/exclude are defined by a function + * provided by the caller. This function is called for each node and + * property, and must return: + * + * 0 - to exclude this part + * 1 - to include this part + * -1 - for FDT_IS_PROP only: no information is available, so include + * if its containing node is included + * + * The last case is only used to deal with properties. Often a property is + * included if its containing node is included - this is the case where + * -1 is returned.. However if the property is specifically required to be + * included/excluded, then 0 or 1 can be returned. Note that including a + * property when the FDT_REG_SUPERNODES flag is given will force its + * containing node to be included since it is not valid to have a property + * that is not in a node. + * + * Using the information provided, the inclusion of a node can be controlled + * either by a node name or its compatible string, or any other property + * that the function can determine. + * + * As an example, including node "/" means to include the root node and all + * root properties. A flag provides a way of also including supernodes (of + * which there is none for the root node), and another flag includes + * immediate subnodes, so in this case we would get the FDT_BEGIN_NODE and + * FDT_END_NODE of all subnodes of /. + * + * The subnode feature helps in a hashing situation since it prevents the + * root node from changing at all. Any change to non-excluded properties, + * names of subnodes or number of subnodes would be detected. + * + * When used with FITs this provides the ability to hash and sign parts of + * the FIT based on different configurations in the FIT. Then it is + * impossible to change anything about that configuration (include images + * attached to the configuration), but it may be possible to add new + * configurations, new images or new signatures within the existing + * framework. + * + * Adding new properties to a device tree may result in the string table + * being extended (if the new property names are different from those + * already added). This function can optionally include a region for + * the string table so that this can be part of the hash too. This is always + * the last region. + * + * The FDT also has a mem_rsvmap table which can also be included, and is + * always the first region if so. + * + * The device tree header is not included in the region list. Since the + * contents of the FDT are changing (shrinking, often), the caller will need + * to regenerate the header anyway. + * + * @fdt: Device tree to check + * @h_include: Function to call to determine whether to include a part or + * not: + * + * @priv: Private pointer as passed to fdt_find_regions() + * @fdt: Pointer to FDT blob + * @offset: Offset of this node / property + * @type: Type of this part, FDT_IS_... + * @data: Pointer to data (node name, property name, compatible + * string, value (not yet supported) + * @size: Size of data, or 0 if none + * @return 0 to exclude, 1 to include, -1 if no information is + * available + * @priv: Private pointer passed to h_include + * @region: Returns list of regions, sorted by offset + * @max_regions: Maximum length of region list + * @path: Pointer to a temporary string for the function to use for + * building path names + * @path_len: Length of path, must be large enough to hold the longest + * path in the tree + * @flags: Various flags that control the region algortihm, see + * FDT_REG_... + * @return number of regions in list. If this is >max_regions then the + * region array was exhausted. You should increase max_regions and try + * the call again. Only the first max_regions elements are available in the + * array. + * + * On error a -ve value is return, which can be: + * + * -FDT_ERR_BADSTRUCTURE (too deep or more END tags than BEGIN tags + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_NOSPACE (path area is too small) + */ +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info); + +/** fdt_next_region() - find next region + * + * See fdt_first_region() for full description. This function finds the + * next region according to the provided parameters, which must be the same + * as passed to fdt_first_region(). + * + * This function can additionally return -FDT_ERR_NOTFOUND when there are no + * more regions + */ +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info); + +/** + * fdt_add_alias_regions() - find aliases that point to existing regions + * + * Once a device tree grep is complete some of the nodes will be present + * and some will have been dropped. This function checks all the alias nodes + * to figure out which points point to nodes which are still present. These + * aliases need to be kept, along with the nodes they reference. + * + * Given a list of regions function finds the aliases that still apply and + * adds more regions to the list for these. This function is called after + * fdt_next_region() has finished returning regions and requires the same + * state. + * + * @fdt: Device tree file to reference + * @region: List of regions that will be kept + * @count: Number of regions + * @max_regions: Number of entries that can fit in @region + * @info: Region state as returned from fdt_next_region() + * @return new number of regions in @region (i.e. count + the number added) + * or -FDT_ERR_NOSPACE if there was not enough space. + */ +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info); + #endif /* _LIBFDT_H */ diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile index 2f5413f..934d614 100644 --- a/lib/libfdt/Makefile +++ b/lib/libfdt/Makefile @@ -6,4 +6,4 @@ #
obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \ - fdt_empty_tree.o fdt_addresses.o + fdt_empty_tree.o fdt_addresses.o fdt_region.o diff --git a/lib/libfdt/fdt_region.c b/lib/libfdt/fdt_region.c new file mode 100644 index 0000000..9fea775 --- /dev/null +++ b/lib/libfdt/fdt_region.c @@ -0,0 +1,492 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2013 Google, Inc + * Written by Simon Glass sjg@chromium.org + * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause + */ + +#include "libfdt_env.h" + +#ifndef USE_HOSTCC +#include <fdt.h> +#include <libfdt.h> +#else +#include "fdt_host.h" +#endif + +#include "libfdt_internal.h" + +/** + * fdt_add_region() - Add a new region to our list + * + * The region is added if there is space, but in any case we increment the + * count. If permitted, and the new region overlaps the last one, we merge + * them. + * + * @info: State information + * @offset: Start offset of region + * @size: Size of region + */ +static int fdt_add_region(struct fdt_region_state *info, int offset, int size) +{ + struct fdt_region *reg; + + reg = info->region ? &info->region[info->count - 1] : NULL; + if (info->can_merge && info->count && + info->count <= info->max_regions && + reg && offset <= reg->offset + reg->size) { + reg->size = offset + size - reg->offset; + } else if (info->count++ < info->max_regions) { + if (reg) { + reg++; + reg->offset = offset; + reg->size = size; + } + } else { + return -1; + } + + return 0; +} + +static int region_list_contains_offset(struct fdt_region_state *info, + const void *fdt, int target) +{ + struct fdt_region *reg; + int num; + + target += fdt_off_dt_struct(fdt); + for (reg = info->region, num = 0; num < info->count; reg++, num++) { + if (target >= reg->offset && target < reg->offset + reg->size) + return 1; + } + + return 0; +} + +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int node, node_end, offset; + int did_alias_header; + + node = fdt_subnode_offset(fdt, 0, "aliases"); + if (node < 0) + return -FDT_ERR_NOTFOUND; + + /* The aliases node must come before the others */ + node_end = fdt_next_subnode(fdt, node); + if (node_end <= 0) + return -FDT_ERR_BADLAYOUT; + node_end -= sizeof(fdt32_t); + + did_alias_header = 0; + info->region = region; + info->count = count; + info->can_merge = 0; + info->max_regions = max_regions; + + for (offset = fdt_first_property_offset(fdt, node); + offset >= 0; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *name; + int target, next; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + target = fdt_path_offset(fdt, name); + if (!region_list_contains_offset(info, fdt, target)) + continue; + next = fdt_next_property_offset(fdt, offset); + if (next < 0) + next = node_end - sizeof(fdt32_t); + + if (!did_alias_header) { + fdt_add_region(info, base + node, 12); + did_alias_header = 1; + } + fdt_add_region(info, base + offset, next - offset); + } + + /* Add the 'end' tag */ + if (did_alias_header) + fdt_add_region(info, base + node_end, sizeof(fdt32_t)); + + return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE; +} + +/** + * fdt_include_supernodes() - Include supernodes required by this node + * + * When we decided to include a node or property which is not at the top + * level, this function forces the inclusion of higher level nodes. For + * example, given this tree: + * + * / { + * testing { + * } + * } + * + * If we decide to include testing then we need the root node to have a valid + * tree. This function adds those regions. + * + * @info: State information + * @depth: Current stack depth + */ +static int fdt_include_supernodes(struct fdt_region_state *info, int depth) +{ + int base = fdt_off_dt_struct(info->fdt); + int start, stop_at; + int i; + + /* + * Work down the stack looking for supernodes that we didn't include. + * The algortihm here is actually pretty simple, since we know that + * no previous subnode had to include these nodes, or if it did, we + * marked them as included (on the stack) already. + */ + for (i = 0; i <= depth; i++) { + if (!info->stack[i].included) { + start = info->stack[i].offset; + + /* Add the FDT_BEGIN_NODE tag of this supernode */ + fdt_next_tag(info->fdt, start, &stop_at); + if (fdt_add_region(info, base + start, stop_at - start)) + return -1; + + /* Remember that this supernode is now included */ + info->stack[i].included = 1; + info->can_merge = 1; + } + + /* Force (later) generation of the FDT_END_NODE tag */ + if (!info->stack[i].want) + info->stack[i].want = WANT_NODES_ONLY; + } + + return 0; +} + +enum { + FDT_DONE_NOTHING, + FDT_DONE_MEM_RSVMAP, + FDT_DONE_STRUCT, + FDT_DONE_END, + FDT_DONE_STRINGS, + FDT_DONE_ALL, +}; + +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + struct fdt_region_ptrs *p = &info->ptrs; + + /* Set up our state */ + info->fdt = fdt; + info->can_merge = 1; + info->max_regions = 1; + info->start = -1; + p->want = WANT_NOTHING; + p->end = path; + *p->end = '\0'; + p->nextoffset = 0; + p->depth = -1; + p->done = FDT_DONE_NOTHING; + + return fdt_next_region(fdt, h_include, priv, region, + path, path_len, flags, info); +} + +/* + * Theory of operation + * + * + * + +Note: in this description 'included' means that a node (or other part of +the tree) should be included in the region list, i.e. it will have a region +which covers its part of the tree. + +This function maintains some state from the last time it is called. It +checks the next part of the tree that it is supposed to look at +(p.nextoffset) to see if that should be included or not. When it finds +something to include, it sets info->start to its offset. This marks the +start of the region we want to include. + +Once info->start is set to the start (i.e. not -1), we continue scanning +until we find something that we don't want included. This will be the end +of a region. At this point we can close off the region and add it to the +list. So we do so, and reset info->start to -1. + +One complication here is that we want to merge regions. So when we come to +add another region later, we may in fact merge it with the previous one if +one ends where the other starts. + +The function fdt_add_region() will return -1 if it fails to add the region, +because we already have a region ready to be returned, and the new one +cannot be merged in with it. In this case, we must return the region we +found, and wait for another call to this function. When it comes, we will +repeat the processing of the tag and again try to add a region. This time it +will succeed. + +The current state of the pointers (stack, offset, etc.) is maintained in +a ptrs member. At the start of every loop iteration we make a copy of it. +The copy is then updated as the tag is processed. Only if we get to the end +of the loop iteration (and successfully call fdt_add_region() if we need +to) can we commit the changes we have made to these pointers. For example, +if we see an FDT_END_NODE tag we will decrement the depth value. But if we +need to add a region for this tag (let's say because the previous tag is +included and this FDT_END_NODE tag is not included) then we will only commit +the result if we were able to add the region. That allows us to retry again +next time. + +We keep track of a variable called 'want' which tells us what we want to +include when there is no specific information provided by the h_include +function for a particular property. This basically handles the inclusion of +properties which are pulled in by virtue of the node they are in. So if you +include a node, its properties are also included. In this case 'want' will +be WANT_NODES_AND_PROPS. The FDT_REG_DIRECT_SUBNODES feature also makes use +of 'want'. While we are inside the subnode, 'want' will be set to +WANT_NODES_ONLY, so that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE +tags will be included, and properties will be skipped. If WANT_NOTHING is +selected, then we will just rely on what the h_include() function tells us. + +Using 'want' we work out 'include', which tells us whether this current tag +should be included or not. As you can imagine, if the value of 'include' +changes, that means we are on a boundary between nodes to include and nodes +to exclude. At this point we either close off a previous region and add it +to the list, or mark the start of a new region. + +Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the string +list. Each of these dealt with as a whole (i.e. we create a region for each +if it is to be included). For mem_rsvmap we don't allow it to merge with +the first struct region. For the stringlist we don't allow it to merge with +the last struct region (which contains at minimum the FDT_END tag). +*/ +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int last_node = 0; + const char *str; + + info->region = region; + info->count = 0; + if (info->ptrs.done < FDT_DONE_MEM_RSVMAP && + (flags & FDT_REG_ADD_MEM_RSVMAP)) { + /* Add the memory reserve map into its own region */ + if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt), + fdt_off_dt_struct(fdt) - + fdt_off_mem_rsvmap(fdt))) + return 0; + info->can_merge = 0; /* Don't allow merging with this */ + info->ptrs.done = FDT_DONE_MEM_RSVMAP; + } + + /* + * Work through the tags one by one, deciding whether each needs to + * be included or not. We set the variable 'include' to indicate our + * decision. 'want' is used to track what we want to include - it + * allows us to pick up all the properties (and/or subnode tags) of + * a node. + */ + while (info->ptrs.done < FDT_DONE_STRUCT) { + const struct fdt_property *prop; + struct fdt_region_ptrs p; + const char *name; + int include = 0; + int stop_at = 0; + uint32_t tag; + int offset; + int val; + int len; + + /* + * Make a copy of our pointers. If we make it to the end of + * this block then we will commit them back to info->ptrs. + * Otherwise we can try again from the same starting state + * next time we are called. + */ + p = info->ptrs; + + /* + * Find the tag, and the offset of the next one. If we need to + * stop including tags, then by default we stop *after* + * including the current tag + */ + offset = p.nextoffset; + tag = fdt_next_tag(fdt, offset, &p.nextoffset); + stop_at = p.nextoffset; + + switch (tag) { + case FDT_PROP: + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + val = h_include(priv, fdt, last_node, FDT_IS_PROP, str, + strlen(str) + 1); + if (val == -1) { + include = p.want >= WANT_NODES_AND_PROPS; + } else { + include = val; + /* + * Make sure we include the } for this block. + * It might be more correct to have this done + * by the call to fdt_include_supernodes() in + * the case where it adds the node we are + * currently in, but this is equivalent. + */ + if ((flags & FDT_REG_SUPERNODES) && val && + !p.want) + p.want = WANT_NODES_ONLY; + } + + /* Value grepping is not yet supported */ + break; + + case FDT_NOP: + include = p.want >= WANT_NODES_AND_PROPS; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + last_node = offset; + p.depth++; + if (p.depth == FDT_MAX_DEPTH) + return -FDT_ERR_TOODEEP; + name = fdt_get_name(fdt, offset, &len); + if (p.end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + + /* Build the full path of this node */ + if (p.end != path + 1) + *p.end++ = '/'; + strcpy(p.end, name); + p.end += len; + info->stack[p.depth].want = p.want; + info->stack[p.depth].offset = offset; + + /* + * If we are not intending to include this node unless + * it matches, make sure we stop *before* its tag. + */ + if (p.want == WANT_NODES_ONLY || + !(flags & (FDT_REG_DIRECT_SUBNODES | + FDT_REG_ALL_SUBNODES))) { + stop_at = offset; + p.want = WANT_NOTHING; + } + val = h_include(priv, fdt, offset, FDT_IS_NODE, path, + p.end - path + 1); + + /* Include this if requested */ + if (val) { + p.want = (flags & FDT_REG_ALL_SUBNODES) ? + WANT_ALL_NODES_AND_PROPS : + WANT_NODES_AND_PROPS; + } + + /* If not requested, decay our 'p.want' value */ + else if (p.want) { + if (p.want != WANT_ALL_NODES_AND_PROPS) + p.want--; + + /* Not including this tag, so stop now */ + } else { + stop_at = offset; + } + + /* + * Decide whether to include this tag, and update our + * stack with the state for this node + */ + include = p.want; + info->stack[p.depth].included = include; + break; + + case FDT_END_NODE: + include = p.want; + if (p.depth < 0) + return -FDT_ERR_BADSTRUCTURE; + + /* + * If we don't want this node, stop right away, unless + * we are including subnodes + */ + if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES)) + stop_at = offset; + p.want = info->stack[p.depth].want; + p.depth--; + while (p.end > path && *--p.end != '/') + ; + *p.end = '\0'; + break; + + case FDT_END: + /* We always include the end tag */ + include = 1; + p.done = FDT_DONE_STRUCT; + break; + } + + /* If this tag is to be included, mark it as region start */ + if (include && info->start == -1) { + /* Include any supernodes required by this one */ + if (flags & FDT_REG_SUPERNODES) { + if (fdt_include_supernodes(info, p.depth)) + return 0; + } + info->start = offset; + } + + /* + * If this tag is not to be included, finish up the current + * region. + */ + if (!include && info->start != -1) { + if (fdt_add_region(info, base + info->start, + stop_at - info->start)) + return 0; + info->start = -1; + info->can_merge = 1; + } + + /* If we have made it this far, we can commit our pointers */ + info->ptrs = p; + } + + /* Add a region for the END tag and a separate one for string table */ + if (info->ptrs.done < FDT_DONE_END) { + if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTRUCTURE; + + if (fdt_add_region(info, base + info->start, + info->ptrs.nextoffset - info->start)) + return 0; + info->ptrs.done++; + } + if (info->ptrs.done < FDT_DONE_STRINGS) { + if (flags & FDT_REG_ADD_STRING_TAB) { + info->can_merge = 0; + if (fdt_off_dt_strings(fdt) < + base + info->ptrs.nextoffset) + return -FDT_ERR_BADLAYOUT; + if (fdt_add_region(info, fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return 0; + } + info->ptrs.done++; + } + + return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND; +}

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
These have been sent upstream but not accepted to libfdt. For now, bring these into U-Boot to enable fdtgrep to operate. We will use fdtgrep to cut device tree files down for SPL.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2:
- Add new patch with fdt_first/next_region() functions
- Tidy up commit message a little
include/libfdt.h | 239 ++++++++++++++++++++++- lib/libfdt/Makefile | 2 +- lib/libfdt/fdt_region.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 lib/libfdt/fdt_region.c
Applied to u-boot-dm.

This tool allows us to extract subsets of a device tree file. It is used by the SPL vuild, which needs to cut down the device tree size for use in limited memory.
This tool was originally written for libfdt but it has not been accepted upstream, so for now, include it in U-Boot. Several utilfdt library functions been included inline here.
If fdtgrep is eventually accepted in libfdt then we can bring that version of libfdt in here, and drop fdtgrep (requiring that fdtgrep is provided by the user).
If it is not accepted then another approach would be to write a special tool for chopping down device tree files for SPL. While it would use the same libfdt support, it would be less code than fdtgrep.c because it would not have general-purpose functions.
Another approach (which was used with v1 of this series) is to sprinkler all the device tree files with #ifdef. I don't like that idea.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: - Add new patch with fdtgrep tool
tools/Makefile | 6 +- tools/fdtgrep.c | 1234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1239 insertions(+), 1 deletion(-) create mode 100644 tools/fdtgrep.c
diff --git a/tools/Makefile b/tools/Makefile index 8ff9c2e..98414f7 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -58,7 +58,8 @@ hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o # Flattened device tree objects LIBFDT_OBJS := $(addprefix lib/libfdt/, \ - fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_wip.o) + fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_wip.o \ + fdt_region.o) RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ rsa-sign.o rsa-verify.o rsa-checksum.o \ rsa-mod-exp.o) @@ -155,6 +156,9 @@ hostprogs-$(CONFIG_ARMADA_XP) += kwboot hostprogs-y += proftool hostprogs-$(CONFIG_STATIC_RELA) += relocate-rela
+hostprogs-y += fdtgrep +fdtgrep-objs += $(LIBFDT_OBJS) fdtgrep.o + # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/fdtgrep.c b/tools/fdtgrep.c new file mode 100644 index 0000000..caaf600 --- /dev/null +++ b/tools/fdtgrep.c @@ -0,0 +1,1234 @@ +/* + * Copyright (c) 2013, Google Inc. + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Perform a grep of an FDT either displaying the source subset or producing + * a new .dtb subset which can be used as required. + */ + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <../include/libfdt.h> +#include <libfdt_internal.h> + +/* Define DEBUG to get some debugging output on stderr */ +#ifdef DEBUG +#define debug(a, b...) fprintf(stderr, a, ## b) +#else +#define debug(a, b...) +#endif + +/* A linked list of values we are grepping for */ +struct value_node { + int type; /* Types this value matches (FDT_IS... mask) */ + int include; /* 1 to include matches, 0 to exclude */ + const char *string; /* String to match */ + struct value_node *next; /* Pointer to next node, or NULL */ +}; + +/* Output formats we support */ +enum output_t { + OUT_DTS, /* Device tree source */ + OUT_DTB, /* Valid device tree binary */ + OUT_BIN, /* Fragment of .dtb, for hashing */ +}; + +/* Holds information which controls our output and options */ +struct display_info { + enum output_t output; /* Output format */ + int add_aliases; /* Add aliases node to output */ + int all; /* Display all properties/nodes */ + int colour; /* Display output in ANSI colour */ + int region_list; /* Output a region list */ + int flags; /* Flags (FDT_REG_...) */ + int list_strings; /* List strings in string table */ + int show_offset; /* Show offset */ + int show_addr; /* Show address */ + int header; /* Output an FDT header */ + int diff; /* Show +/- diff markers */ + int include_root; /* Include the root node and all properties */ + int remove_strings; /* Remove unused strings */ + int show_dts_version; /* Put '/dts-v1/;' on the first line */ + int types_inc; /* Mask of types that we include (FDT_IS...) */ + int types_exc; /* Mask of types that we exclude (FDT_IS...) */ + int invert; /* Invert polarity of match */ + struct value_node *value_head; /* List of values to match */ + const char *output_fname; /* Output filename */ + FILE *fout; /* File to write dts/dtb output */ +}; + +static void report_error(const char *where, int err) +{ + fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); +} + +/* Supported ANSI colours */ +enum { + COL_BLACK, + COL_RED, + COL_GREEN, + COL_YELLOW, + COL_BLUE, + COL_MAGENTA, + COL_CYAN, + COL_WHITE, + + COL_NONE = -1, +}; + +/** + * print_ansi_colour() - Print out the ANSI sequence for a colour + * + * @fout: Output file + * @col: Colour to output (COL_...), or COL_NONE to reset colour + */ +static void print_ansi_colour(FILE *fout, int col) +{ + if (col == COL_NONE) + fprintf(fout, "\033[0m"); + else + fprintf(fout, "\033[1;%dm", col + 30); +} + + +/** + * value_add() - Add a new value to our list of things to grep for + * + * @disp: Display structure, holding info about our options + * @headp: Pointer to header pointer of list + * @type: Type of this value (FDT_IS_...) + * @include: 1 if we want to include matches, 0 to exclude + * @str: String value to match + */ +static int value_add(struct display_info *disp, struct value_node **headp, + int type, int include, const char *str) +{ + struct value_node *node; + + /* + * Keep track of which types we are excluding/including. We don't + * allow both including and excluding things, because it doesn't make + * sense. 'Including' means that everything not mentioned is + * excluded. 'Excluding' means that everything not mentioned is + * included. So using the two together would be meaningless. + */ + if (include) + disp->types_inc |= type; + else + disp->types_exc |= type; + if (disp->types_inc & disp->types_exc & type) { + fprintf(stderr, + "Cannot use both include and exclude for '%s'\n", str); + return -1; + } + + str = strdup(str); + node = malloc(sizeof(*node)); + if (!str || !node) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + node->next = *headp; + node->type = type; + node->include = include; + node->string = str; + *headp = node; + + return 0; +} + +static bool util_is_printable_string(const void *data, int len) +{ + const char *s = data; + const char *ss, *se; + + /* zero length is not */ + if (len == 0) + return 0; + + /* must terminate with zero */ + if (s[len - 1] != '\0') + return 0; + + se = s + len; + + while (s < se) { + ss = s; + while (s < se && *s && isprint((unsigned char)*s)) + s++; + + /* not zero, or not done yet */ + if (*s != '\0' || s == ss) + return 0; + + s++; + } + + return 1; +} + +static void utilfdt_print_data(const char *data, int len) +{ + int i; + const char *p = data; + const char *s; + + /* no data, don't print */ + if (len == 0) + return; + + if (util_is_printable_string(data, len)) { + printf(" = "); + + s = data; + do { + printf(""%s"", s); + s += strlen(s) + 1; + if (s < data + len) + printf(", "); + } while (s < data + len); + + } else if ((len % 4) == 0) { + const uint32_t *cell = (const uint32_t *)data; + + printf(" = <"); + for (i = 0, len /= 4; i < len; i++) + printf("0x%08x%s", fdt32_to_cpu(cell[i]), + i < (len - 1) ? " " : ""); + printf(">"); + } else { + printf(" = ["); + for (i = 0; i < len; i++) + printf("%02x%s", *p++, i < len - 1 ? " " : ""); + printf("]"); + } +} + +/** + * display_fdt_by_regions() - Display regions of an FDT source + * + * This dumps an FDT as source, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to display, + * and this function displays them. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + */ +static int display_fdt_by_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count) +{ + struct fdt_region *reg = region, *reg_end = region + count; + uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(blob); + int base = fdt_off_dt_struct(blob); + int version = fdt_version(blob); + int offset, nextoffset; + int tag, depth, shift; + FILE *f = disp->fout; + uint64_t addr, size; + int in_region; + int file_ofs; + int i; + + if (disp->show_dts_version) + fprintf(f, "/dts-v1/;\n"); + + if (disp->header) { + fprintf(f, "// magic:\t\t0x%x\n", fdt_magic(blob)); + fprintf(f, "// totalsize:\t\t0x%x (%d)\n", fdt_totalsize(blob), + fdt_totalsize(blob)); + fprintf(f, "// off_dt_struct:\t0x%x\n", + fdt_off_dt_struct(blob)); + fprintf(f, "// off_dt_strings:\t0x%x\n", + fdt_off_dt_strings(blob)); + fprintf(f, "// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap); + fprintf(f, "// version:\t\t%d\n", version); + fprintf(f, "// last_comp_version:\t%d\n", + fdt_last_comp_version(blob)); + if (version >= 2) { + fprintf(f, "// boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(blob)); + } + if (version >= 3) { + fprintf(f, "// size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(blob)); + } + if (version >= 17) { + fprintf(f, "// size_dt_struct:\t0x%x\n", + fdt_size_dt_struct(blob)); + } + fprintf(f, "\n"); + } + + if (disp->flags & FDT_REG_ADD_MEM_RSVMAP) { + const struct fdt_reserve_entry *p_rsvmap; + + p_rsvmap = (const struct fdt_reserve_entry *) + ((const char *)blob + off_mem_rsvmap); + for (i = 0; ; i++) { + addr = fdt64_to_cpu(p_rsvmap[i].address); + size = fdt64_to_cpu(p_rsvmap[i].size); + if (addr == 0 && size == 0) + break; + + fprintf(f, "/memreserve/ %llx %llx;\n", + (unsigned long long)addr, + (unsigned long long)size); + } + } + + depth = 0; + nextoffset = 0; + shift = 4; /* 4 spaces per indent */ + do { + const struct fdt_property *prop; + const char *name; + int show; + int len; + + offset = nextoffset; + + /* + * Work out the file offset of this offset, and decide + * whether it is in the region list or not + */ + file_ofs = base + offset; + if (reg < reg_end && file_ofs >= reg->offset + reg->size) + reg++; + in_region = reg < reg_end && file_ofs >= reg->offset && + file_ofs < reg->offset + reg->size; + tag = fdt_next_tag(blob, offset, &nextoffset); + + if (tag == FDT_END) + break; + show = in_region || disp->all; + if (show && disp->diff) + fprintf(f, "%c", in_region ? '+' : '-'); + + if (!show) { + /* Do this here to avoid 'if (show)' in every 'case' */ + if (tag == FDT_BEGIN_NODE) + depth++; + else if (tag == FDT_END_NODE) + depth--; + continue; + } + if (tag != FDT_END) { + if (disp->show_addr) + fprintf(f, "%4x: ", file_ofs); + if (disp->show_offset) + fprintf(f, "%4x: ", file_ofs - base); + } + + /* Green means included, red means excluded */ + if (disp->colour) + print_ansi_colour(f, in_region ? COL_GREEN : COL_RED); + + switch (tag) { + case FDT_PROP: + prop = fdt_get_property_by_offset(blob, offset, NULL); + name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); + fprintf(f, "%*s%s", depth * shift, "", name); + utilfdt_print_data(prop->data, + fdt32_to_cpu(prop->len)); + fprintf(f, ";"); + break; + + case FDT_NOP: + fprintf(f, "%*s// [NOP]", depth * shift, ""); + break; + + case FDT_BEGIN_NODE: + name = fdt_get_name(blob, offset, &len); + fprintf(f, "%*s%s {", depth++ * shift, "", + *name ? name : "/"); + break; + + case FDT_END_NODE: + fprintf(f, "%*s};", --depth * shift, ""); + break; + } + + /* Reset colour back to normal before end of line */ + if (disp->colour) + print_ansi_colour(f, COL_NONE); + fprintf(f, "\n"); + } while (1); + + /* Print a list of strings if requested */ + if (disp->list_strings) { + const char *str; + int str_base = fdt_off_dt_strings(blob); + + for (offset = 0; offset < fdt_size_dt_strings(blob); + offset += strlen(str) + 1) { + str = fdt_string(blob, offset); + int len = strlen(str) + 1; + int show; + + /* Only print strings that are in the region */ + file_ofs = str_base + offset; + in_region = reg < reg_end && + file_ofs >= reg->offset && + file_ofs + len < reg->offset + + reg->size; + show = in_region || disp->all; + if (show && disp->diff) + printf("%c", in_region ? '+' : '-'); + if (disp->show_addr) + printf("%4x: ", file_ofs); + if (disp->show_offset) + printf("%4x: ", offset); + printf("%s\n", str); + } + } + + return 0; +} + +/** + * dump_fdt_regions() - Dump regions of an FDT as binary data + * + * This dumps an FDT as binary, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to dump, + * and this function dumps them. + * + * The output of this function may or may not be a valid FDT. To ensure it + * is, these disp->flags must be set: + * + * FDT_REG_SUPERNODES: ensures that subnodes are preceeded by their + * parents. Without this option, fragments of subnode data may be + * output without the supernodes above them. This is useful for + * hashing but cannot produce a valid FDT. + * FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT. + * Without this none of the properties will have names + * FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid + * without this. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + * @out: Output destination + */ +static int dump_fdt_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count, char *out) +{ + struct fdt_header *fdt; + int size, struct_start; + int ptr; + int i; + + /* Set up a basic header (even if we don't actually write it) */ + fdt = (struct fdt_header *)out; + memset(fdt, '\0', sizeof(*fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + struct_start = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + fdt_set_off_mem_rsvmap(fdt, struct_start); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + + /* + * Calculate the total size of the regions we are writing out. The + * first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag + * is set. The last will be the string table if FDT_REG_ADD_STRING_TAB + * is set. + */ + for (i = size = 0; i < count; i++) + size += region[i].size; + + /* Bring in the mem_rsvmap section from the old file if requested */ + if (count > 0 && (disp->flags & FDT_REG_ADD_MEM_RSVMAP)) { + struct_start += region[0].size; + size -= region[0].size; + } + fdt_set_off_dt_struct(fdt, struct_start); + + /* Update the header to have the correct offsets/sizes */ + if (count >= 2 && (disp->flags & FDT_REG_ADD_STRING_TAB)) { + int str_size; + + str_size = region[count - 1].size; + fdt_set_size_dt_struct(fdt, size - str_size); + fdt_set_off_dt_strings(fdt, struct_start + size - str_size); + fdt_set_size_dt_strings(fdt, str_size); + fdt_set_totalsize(fdt, struct_start + size); + } + + /* Write the header if required */ + ptr = 0; + if (disp->header) { + ptr = sizeof(*fdt); + while (ptr < fdt_off_mem_rsvmap(fdt)) + out[ptr++] = '\0'; + } + + /* Output all the nodes including any mem_rsvmap/string table */ + for (i = 0; i < count; i++) { + struct fdt_region *reg = ®ion[i]; + + memcpy(out + ptr, (const char *)blob + reg->offset, reg->size); + ptr += reg->size; + } + + return ptr; +} + +/** + * show_region_list() - Print out a list of regions + * + * The list includes the region offset (absolute offset from start of FDT + * blob in bytes) and size + * + * @reg: List of regions to print + * @count: Number of regions + */ +static void show_region_list(struct fdt_region *reg, int count) +{ + int i; + + printf("Regions: %d\n", count); + for (i = 0; i < count; i++, reg++) { + printf("%d: %-10x %-10x\n", i, reg->offset, + reg->offset + reg->size); + } +} + +static int check_type_include(void *priv, int type, const char *data, int size) +{ + struct display_info *disp = priv; + struct value_node *val; + int match, none_match = FDT_IS_ANY; + + /* If none of our conditions mention this type, we know nothing */ + debug("type=%x, data=%s\n", type, data ? data : "(null)"); + if (!((disp->types_inc | disp->types_exc) & type)) { + debug(" - not in any condition\n"); + return -1; + } + + /* + * Go through the list of conditions. For inclusive conditions, we + * return 1 at the first match. For exclusive conditions, we must + * check that there are no matches. + */ + for (val = disp->value_head; val; val = val->next) { + if (!(type & val->type)) + continue; + match = fdt_stringlist_contains(data, size, val->string); + debug(" - val->type=%x, str='%s', match=%d\n", + val->type, val->string, match); + if (match && val->include) { + debug(" - match inc %s\n", val->string); + return 1; + } + if (match) + none_match &= ~val->type; + } + + /* + * If this is an exclusive condition, and nothing matches, then we + * should return 1. + */ + if ((type & disp->types_exc) && (none_match & type)) { + debug(" - match exc\n"); + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type + */ + if (type == FDT_IS_NODE && disp->types_exc == FDT_ANY_GLOBAL) { + debug(" - supressed exc node\n"); + return -1; + } + return 1; + } + + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type (inclusive) + */ + if (type == FDT_IS_NODE && disp->types_inc == FDT_ANY_GLOBAL) + return -1; + + debug(" - no match, types_inc=%x, types_exc=%x, none_match=%x\n", + disp->types_inc, disp->types_exc, none_match); + + return 0; +} + +/** + * h_include() - Include handler function for fdt_find_regions() + * + * This function decides whether to include or exclude a node, property or + * compatible string. The function is defined by fdt_find_regions(). + * + * The algorithm is documented in the code - disp->invert is 0 for normal + * operation, and 1 to invert the sense of all matches. + * + * See + */ +static int h_include(void *priv, const void *fdt, int offset, int type, + const char *data, int size) +{ + struct display_info *disp = priv; + int inc, len; + + inc = check_type_include(priv, type, data, size); + if (disp->include_root && type == FDT_IS_PROP && offset == 0 && inc) + return 1; + + /* + * If the node name does not tell us anything, check the + * compatible string + */ + if (inc == -1 && type == FDT_IS_NODE) { + debug(" - checking compatible2\n"); + data = fdt_getprop(fdt, offset, "compatible", &len); + inc = check_type_include(priv, FDT_IS_COMPAT, data, len); + } + + /* If we still have no idea, check for properties in the node */ + if (inc != 1 && type == FDT_IS_NODE && + (disp->types_inc & FDT_NODE_HAS_PROP)) { + debug(" - checking node '%s'\n", + fdt_get_name(fdt, offset, NULL)); + for (offset = fdt_first_property_offset(fdt, offset); + offset > 0 && inc != 1; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *str; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + if (!prop) + continue; + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + inc = check_type_include(priv, FDT_NODE_HAS_PROP, str, + strlen(str)); + } + if (inc == -1) + inc = 0; + } + + switch (inc) { + case 1: + inc = !disp->invert; + break; + case 0: + inc = disp->invert; + break; + } + debug(" - returning %d\n", inc); + + return inc; +} + +static int h_cmp_region(const void *v1, const void *v2) +{ + const struct fdt_region *region1 = v1, *region2 = v2; + + return region1->offset - region2->offset; +} + +static int fdtgrep_find_regions(const void *fdt, + int (*include_func)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + struct display_info *disp, struct fdt_region *region, + int max_regions, char *path, int path_len, int flags) +{ + struct fdt_region_state state; + int count; + int ret; + + count = 0; + ret = fdt_first_region(fdt, include_func, disp, + ®ion[count++], path, path_len, + disp->flags, &state); + while (ret == 0) { + ret = fdt_next_region(fdt, include_func, disp, + count < max_regions ? ®ion[count] : NULL, + path, path_len, disp->flags, &state); + if (!ret) + count++; + } + + /* Find all the aliases and add those regions back in */ + if (disp->add_aliases && count < max_regions) { + int new_count; + + new_count = fdt_add_alias_regions(fdt, region, count, + max_regions, &state); + if (new_count > max_regions) { + region = malloc(new_count * sizeof(struct fdt_region)); + if (!region) { + fprintf(stderr, + "Out of memory for %d regions\n", + count); + return -1; + } + memcpy(region, state.region, + count * sizeof(struct fdt_region)); + free(state.region); + new_count = fdt_add_alias_regions(fdt, region, count, + max_regions, &state); + } + + /* + * The alias regions will now be at the end of the list. Sort + * the regions by offset to get things into the right order + */ + qsort(region, new_count, sizeof(struct fdt_region), + h_cmp_region); + count = new_count; + } + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + + return count; +} + +int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len) +{ + int fd = 0; /* assume stdin */ + char *buf = NULL; + off_t bufsize = 1024, offset = 0; + int ret = 0; + + *buffp = NULL; + if (strcmp(filename, "-") != 0) { + fd = open(filename, O_RDONLY); + if (fd < 0) + return errno; + } + + /* Loop until we have read everything */ + buf = malloc(bufsize); + if (!buf) + return -ENOMEM; + do { + /* Expand the buffer to hold the next chunk */ + if (offset == bufsize) { + bufsize *= 2; + buf = realloc(buf, bufsize); + if (!buf) + return -ENOMEM; + } + + ret = read(fd, &buf[offset], bufsize - offset); + if (ret < 0) { + ret = errno; + break; + } + offset += ret; + } while (ret != 0); + + /* Clean up, including closing stdin; return errno on error */ + close(fd); + if (ret) + free(buf); + else + *buffp = buf; + *len = bufsize; + return ret; +} + +int utilfdt_read_err(const char *filename, char **buffp) +{ + off_t len; + return utilfdt_read_err_len(filename, buffp, &len); +} + +char *utilfdt_read_len(const char *filename, off_t *len) +{ + char *buff; + int ret = utilfdt_read_err_len(filename, &buff, len); + + if (ret) { + fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, + strerror(ret)); + return NULL; + } + /* Successful read */ + return buff; +} + +char *utilfdt_read(const char *filename) +{ + off_t len; + return utilfdt_read_len(filename, &len); +} + +/** + * Run the main fdtgrep operation, given a filename and valid arguments + * + * @param disp Display information / options + * @param filename Filename of blob file + * @param return 0 if ok, -ve on error + */ +static int do_fdtgrep(struct display_info *disp, const char *filename) +{ + struct fdt_region *region; + int max_regions; + int count = 100; + char path[1024]; + char *blob; + int i, ret; + + blob = utilfdt_read(filename); + if (!blob) + return -1; + ret = fdt_check_header(blob); + if (ret) { + fprintf(stderr, "Error: %s\n", fdt_strerror(ret)); + return ret; + } + + /* Allow old files, but they are untested */ + if (fdt_version(blob) < 17 && disp->value_head) { + fprintf(stderr, + "Warning: fdtgrep does not fully support version %d files\n", + fdt_version(blob)); + } + + /* + * We do two passes, since we don't know how many regions we need. + * The first pass will count the regions, but if it is too many, + * we do another pass to actually record them. + */ + for (i = 0; i < 2; i++) { + region = malloc(count * sizeof(struct fdt_region)); + if (!region) { + fprintf(stderr, "Out of memory for %d regions\n", + count); + return -1; + } + max_regions = count; + count = fdtgrep_find_regions(blob, + h_include, disp, + region, max_regions, path, sizeof(path), + disp->flags); + if (count < 0) { + report_error("fdt_find_regions", count); + return -1; + } + if (count <= max_regions) + break; + free(region); + } + + /* Optionally print a list of regions */ + if (disp->region_list) + show_region_list(region, count); + + /* Output either source .dts or binary .dtb */ + if (disp->output == OUT_DTS) { + ret = display_fdt_by_regions(disp, blob, region, count); + } else { + void *fdt; + /* Allow reserved memory section to expand slightly */ + int size = fdt_totalsize(blob) + 16; + + fdt = malloc(size); + if (!fdt) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + size = dump_fdt_regions(disp, blob, region, count, fdt); + if (disp->remove_strings) { + void *out; + + out = malloc(size); + if (!out) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + ret = fdt_remove_unused_strings(fdt, out); + if (ret < 0) { + fprintf(stderr, + "Failed to remove unused strings: err=%d\n", + ret); + goto err; + } + free(fdt); + fdt = out; + ret = fdt_pack(fdt); + if (ret < 0) { + fprintf(stderr, "Failed to pack: err=%d\n", + ret); + goto err; + } + size = fdt_totalsize(fdt); + } + + if (size != fwrite(fdt, 1, size, disp->fout)) { + fprintf(stderr, "Write failure, %d bytes\n", size); + free(fdt); + ret = 1; + goto err; + } + free(fdt); + } +err: + free(blob); + free(region); + + return ret; +} + +static const char usage_synopsis[] = + "fdtgrep - extract portions from device tree\n" + "\n" + "Usage:\n" + " fdtgrep <options> <dt file>|-\n\n" + "Output formats are:\n" + "\tdts - device tree soure text\n" + "\tdtb - device tree blob (sets -Hmt automatically)\n" + "\tbin - device tree fragment (may not be a valid .dtb)"; + +/* Helper for usage_short_opts string constant */ +#define USAGE_COMMON_SHORT_OPTS "hV" + +/* Helper for aligning long_opts array */ +#define a_argument required_argument + +/* Helper for usage_long_opts option array */ +#define USAGE_COMMON_LONG_OPTS \ + {"help", no_argument, NULL, 'h'}, \ + {"version", no_argument, NULL, 'V'}, \ + {NULL, no_argument, NULL, 0x0} + +/* Helper for usage_opts_help array */ +#define USAGE_COMMON_OPTS_HELP \ + "Print this help and exit", \ + "Print version and exit", \ + NULL + +/* Helper for getopt case statements */ +#define case_USAGE_COMMON_FLAGS \ + case 'h': usage(NULL); \ + case 'V': util_version(); \ + case '?': usage("unknown option"); + +static const char usage_short_opts[] = + "haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv" + USAGE_COMMON_SHORT_OPTS; +static struct option const usage_long_opts[] = { + {"show-address", no_argument, NULL, 'a'}, + {"colour", no_argument, NULL, 'A'}, + {"include-node-with-prop", a_argument, NULL, 'b'}, + {"include-compat", a_argument, NULL, 'c'}, + {"exclude-compat", a_argument, NULL, 'C'}, + {"diff", no_argument, NULL, 'd'}, + {"enter-node", no_argument, NULL, 'e'}, + {"show-offset", no_argument, NULL, 'f'}, + {"include-match", a_argument, NULL, 'g'}, + {"exclude-match", a_argument, NULL, 'G'}, + {"show-header", no_argument, NULL, 'H'}, + {"show-version", no_argument, NULL, 'I'}, + {"list-regions", no_argument, NULL, 'l'}, + {"list-strings", no_argument, NULL, 'L'}, + {"include-mem", no_argument, NULL, 'm'}, + {"include-node", a_argument, NULL, 'n'}, + {"exclude-node", a_argument, NULL, 'N'}, + {"include-prop", a_argument, NULL, 'p'}, + {"exclude-prop", a_argument, NULL, 'P'}, + {"remove-strings", no_argument, NULL, 'r'}, + {"include-root", no_argument, NULL, 'R'}, + {"show-subnodes", no_argument, NULL, 's'}, + {"skip-supernodes", no_argument, NULL, 'S'}, + {"show-stringtab", no_argument, NULL, 't'}, + {"show-aliases", no_argument, NULL, 'T'}, + {"out", a_argument, NULL, 'o'}, + {"out-format", a_argument, NULL, 'O'}, + {"invert-match", no_argument, NULL, 'v'}, + USAGE_COMMON_LONG_OPTS, +}; +static const char * const usage_opts_help[] = { + "Display address", + "Show all nodes/tags, colour those that match", + "Include contains containing property", + "Compatible nodes to include in grep", + "Compatible nodes to exclude in grep", + "Diff: Mark matching nodes with +, others with -", + "Enter direct subnode names of matching nodes", + "Display offset", + "Node/property/compatible string to include in grep", + "Node/property/compatible string to exclude in grep", + "Output a header", + "Put "/dts-v1/;" on first line of dts output", + "Output a region list", + "List strings in string table", + "Include mem_rsvmap section in binary output", + "Node to include in grep", + "Node to exclude in grep", + "Property to include in grep", + "Property to exclude in grep", + "Remove unused strings from string table", + "Include root node and all properties", + "Show all subnodes matching nodes", + "Don't include supernodes of matching nodes", + "Include string table in binary output", + "Include matching aliases in output", + "-o <output file>", + "-O <output format>", + "Invert the sense of matching (select non-matching lines)", + USAGE_COMMON_OPTS_HELP +}; + +/** + * Call getopt_long() with standard options + * + * Since all util code runs getopt in the same way, provide a helper. + */ +#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \ + usage_long_opts, NULL) + +void util_usage(const char *errmsg, const char *synopsis, + const char *short_opts, struct option const long_opts[], + const char * const opts_help[]) +{ + FILE *fp = errmsg ? stderr : stdout; + const char a_arg[] = "<arg>"; + size_t a_arg_len = strlen(a_arg) + 1; + size_t i; + int optlen; + + fprintf(fp, + "Usage: %s\n" + "\n" + "Options: -[%s]\n", synopsis, short_opts); + + /* prescan the --long opt length to auto-align */ + optlen = 0; + for (i = 0; long_opts[i].name; ++i) { + /* +1 is for space between --opt and help text */ + int l = strlen(long_opts[i].name) + 1; + if (long_opts[i].has_arg == a_argument) + l += a_arg_len; + if (optlen < l) + optlen = l; + } + + for (i = 0; long_opts[i].name; ++i) { + /* helps when adding new applets or options */ + assert(opts_help[i] != NULL); + + /* first output the short flag if it has one */ + if (long_opts[i].val > '~') + fprintf(fp, " "); + else + fprintf(fp, " -%c, ", long_opts[i].val); + + /* then the long flag */ + if (long_opts[i].has_arg == no_argument) { + fprintf(fp, "--%-*s", optlen, long_opts[i].name); + } else { + fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg, + (int)(optlen - strlen(long_opts[i].name) - + a_arg_len), ""); + } + + /* finally the help text */ + fprintf(fp, "%s\n", opts_help[i]); + } + + if (errmsg) { + fprintf(fp, "\nError: %s\n", errmsg); + exit(EXIT_FAILURE); + } else { + exit(EXIT_SUCCESS); + } +} + +/** + * Show usage and exit + * + * If you name all your usage variables with usage_xxx, then you can call this + * help macro rather than expanding all arguments yourself. + * + * @param errmsg If non-NULL, an error message to display + */ +#define usage(errmsg) \ + util_usage(errmsg, usage_synopsis, usage_short_opts, \ + usage_long_opts, usage_opts_help) + +void util_version(void) +{ + printf("Version: %s\n", "(U-Boot)"); + exit(0); +} + +static void scan_args(struct display_info *disp, int argc, char *argv[]) +{ + int opt; + + while ((opt = util_getopt_long()) != EOF) { + int type = 0; + int inc = 1; + + switch (opt) { + case_USAGE_COMMON_FLAGS + case 'a': + disp->show_addr = 1; + break; + case 'A': + disp->all = 1; + break; + case 'b': + type = FDT_NODE_HAS_PROP; + break; + case 'C': + inc = 0; + /* no break */ + case 'c': + type = FDT_IS_COMPAT; + break; + case 'd': + disp->diff = 1; + break; + case 'e': + disp->flags |= FDT_REG_DIRECT_SUBNODES; + break; + case 'f': + disp->show_offset = 1; + break; + case 'G': + inc = 0; + /* no break */ + case 'g': + type = FDT_ANY_GLOBAL; + break; + case 'H': + disp->header = 1; + break; + case 'l': + disp->region_list = 1; + break; + case 'L': + disp->list_strings = 1; + break; + case 'm': + disp->flags |= FDT_REG_ADD_MEM_RSVMAP; + break; + case 'N': + inc = 0; + /* no break */ + case 'n': + type = FDT_IS_NODE; + break; + case 'o': + disp->output_fname = optarg; + break; + case 'O': + if (!strcmp(optarg, "dtb")) + disp->output = OUT_DTB; + else if (!strcmp(optarg, "dts")) + disp->output = OUT_DTS; + else if (!strcmp(optarg, "bin")) + disp->output = OUT_BIN; + else + usage("Unknown output format"); + break; + case 'P': + inc = 0; + /* no break */ + case 'p': + type = FDT_IS_PROP; + break; + case 'r': + disp->remove_strings = 1; + break; + case 'R': + disp->include_root = 1; + break; + case 's': + disp->flags |= FDT_REG_ALL_SUBNODES; + break; + case 'S': + disp->flags &= ~FDT_REG_SUPERNODES; + break; + case 't': + disp->flags |= FDT_REG_ADD_STRING_TAB; + break; + case 'T': + disp->add_aliases = 1; + break; + case 'v': + disp->invert = 1; + break; + case 'I': + disp->show_dts_version = 1; + break; + } + + if (type && value_add(disp, &disp->value_head, type, inc, + optarg)) + usage("Cannot add value"); + } + + if (disp->invert && disp->types_exc) + usage("-v has no meaning when used with 'exclude' conditions"); +} + +int main(int argc, char *argv[]) +{ + char *filename = NULL; + struct display_info disp; + int ret; + + /* set defaults */ + memset(&disp, '\0', sizeof(disp)); + disp.flags = FDT_REG_SUPERNODES; /* Default flags */ + + scan_args(&disp, argc, argv); + + /* Show matched lines in colour if we can */ + disp.colour = disp.all && isatty(0); + + /* Any additional arguments can match anything, just like -g */ + while (optind < argc - 1) { + if (value_add(&disp, &disp.value_head, FDT_IS_ANY, 1, + argv[optind++])) + usage("Cannot add value"); + } + + if (optind < argc) + filename = argv[optind++]; + if (!filename) + usage("Missing filename"); + + /* If a valid .dtb is required, set flags to ensure we get one */ + if (disp.output == OUT_DTB) { + disp.header = 1; + disp.flags |= FDT_REG_ADD_MEM_RSVMAP | FDT_REG_ADD_STRING_TAB; + } + + if (disp.output_fname) { + disp.fout = fopen(disp.output_fname, "w"); + if (!disp.fout) + usage("Cannot open output file"); + } else { + disp.fout = stdout; + } + + /* Run the grep and output the results */ + ret = do_fdtgrep(&disp, filename); + if (disp.output_fname) + fclose(disp.fout); + if (ret) + return 1; + + return 0; +}

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This tool allows us to extract subsets of a device tree file. It is used by the SPL vuild, which needs to cut down the device tree size for use in limited memory.
This tool was originally written for libfdt but it has not been accepted upstream, so for now, include it in U-Boot. Several utilfdt library functions been included inline here.
If fdtgrep is eventually accepted in libfdt then we can bring that version of libfdt in here, and drop fdtgrep (requiring that fdtgrep is provided by the user).
If it is not accepted then another approach would be to write a special tool for chopping down device tree files for SPL. While it would use the same libfdt support, it would be less code than fdtgrep.c because it would not have general-purpose functions.
Another approach (which was used with v1 of this series) is to sprinkler all the device tree files with #ifdef. I don't like that idea.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2:
- Add new patch with fdtgrep tool
tools/Makefile | 6 +- tools/fdtgrep.c | 1234 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1239 insertions(+), 1 deletion(-) create mode 100644 tools/fdtgrep.c
Applied to u-boot-dm.

The SPL device tree size must be minimised to save memory. Only include properties that are needed by SPL - this is determined by the presence of the "u-boot,dm-pre-reloc" property. Also remove a predefined list of unused properties from the nodes that remain.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: - Convert hex number to upper case to keep bc happy
Changes in v2: - Add new patch to reduce SPL device tree size with fdtgrep
Makefile | 2 +- arch/arm/cpu/u-boot-spl.lds | 2 +- dts/Kconfig | 12 ++++++++++++ scripts/Makefile.spl | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index 0a674bf..4276f13 100644 --- a/Makefile +++ b/Makefile @@ -1258,7 +1258,7 @@ u-boot.lds: $(LDSCRIPT) prepare FORCE
spl/u-boot-spl.bin: spl/u-boot-spl @: -spl/u-boot-spl: tools prepare +spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb) $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all
spl/sunxi-spl.bin: spl/u-boot-spl diff --git a/arch/arm/cpu/u-boot-spl.lds b/arch/arm/cpu/u-boot-spl.lds index a8be204..4b6e0f6 100644 --- a/arch/arm/cpu/u-boot-spl.lds +++ b/arch/arm/cpu/u-boot-spl.lds @@ -66,7 +66,7 @@ SECTIONS . = ALIGN(4); __bss_end = .; } - + __bss_size = __bss_end - __bss_start; .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } diff --git a/dts/Kconfig b/dts/Kconfig index 957f5c7..09cfefb 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -56,4 +56,16 @@ config DEFAULT_DEVICE_TREE It can be overridden from the command line: $ make DEVICE_TREE=<device-tree-name>
+config OF_SPL_REMOVE_PROPS + string "List of device tree properties to drop for SPL" + depends on OF_CONTROL && SPL + default "pinctrl-0 pinctrl-names clocks clock-names interrupt-parent" + help + Since SPL normally runs in a reduced memory space, the device tree + is cut down to only what is needed to load and start U-Boot. Only + nodes marked with the property "u-boot,dm-pre-reloc" will be + included. In addition, some properties are not used by U-Boot and + can be discarded. This option defines the list of properties to + discard. + endmenu diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index fd572f4..bcc7ca2 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -151,6 +151,8 @@ boot.bin: $(obj)/u-boot-spl.bin
ALL-y += $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN).cfg
+ALL-$(CONFIG_OF_SEPARATE) += $(obj)/$(SPL_BIN)-pad.bin $(obj)/$(SPL_BIN)-dtb.bin + ifdef CONFIG_SAMSUNG ALL-y += $(obj)/$(BOARD)-spl.bin endif @@ -165,6 +167,32 @@ endif
all: $(ALL-y)
+quiet_cmd_cat = CAT $@ +cmd_cat = cat $(filter-out $(PHONY), $^) > $@ + +$(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN)-pad.bin \ + $(obj)/$(SPL_BIN).dtb FORCE + $(call if_changed,cat) + +# Create a file that pads from the end of u-boot-spl.bin to bss_end +$(obj)/$(SPL_BIN)-pad.bin: $(obj)/$(SPL_BIN) + @bss_size_str=$(shell $(NM) $< | awk 'BEGIN {size = 0} /__bss_size/ {size = $$1} END {print "ibase=16; " toupper(size)}' | bc); \ + dd if=/dev/zero of=$@ bs=1 count=$${bss_size_str} 2>/dev/null; + +# Pass the original device tree file through fdtgrep twice. The first pass +# removes any unwanted nodes (i.e. those which don't have the +# 'u-boot,dm-pre-reloc' property and thus are not needed by SPL. The second +# pass removes various unused properties from the remaining nodes. +# The output is typically a much smaller device tree file. +quiet_cmd_fdtgrep = FDTGREP $@ + cmd_fdtgrep = $(objtree)/tools/fdtgrep -b u-boot,dm-pre-reloc -RT $< \ + -n /chosen -O dtb | \ + $(objtree)/tools/fdtgrep -r -O dtb - -o $@ \ + $(addprefix -P ,$(subst $",,$(CONFIG_OF_SPL_REMOVE_PROPS))) + +$(obj)/$(SPL_BIN).dtb: dts/dt.dtb + $(call cmd,fdtgrep) + quiet_cmd_cpp_cfg = CFG $@ cmd_cpp_cfg = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \ -DDO_DEPS_ONLY -D__ASSEMBLY__ -x assembler-with-cpp -P -dM -E -o $@ $<

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
The SPL device tree size must be minimised to save memory. Only include properties that are needed by SPL - this is determined by the presence of the "u-boot,dm-pre-reloc" property. Also remove a predefined list of unused properties from the nodes that remain.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Convert hex number to upper case to keep bc happy
Changes in v2:
- Add new patch to reduce SPL device tree size with fdtgrep
Makefile | 2 +- arch/arm/cpu/u-boot-spl.lds | 2 +- dts/Kconfig | 12 ++++++++++++ scripts/Makefile.spl | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-)
Applied to u-boot-dm.

Driver-model I2C drivers can be picked up by the linker script rule for legacy drivers. Change the order to avoid this.
We could make the legacy code depend on !CONFIG_DM_I2C but that is not necessary and it is good to keep conditions to a minimum.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
arch/arm/cpu/u-boot-spl.lds | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm/cpu/u-boot-spl.lds b/arch/arm/cpu/u-boot-spl.lds index 4b6e0f6..c5b4f7c 100644 --- a/arch/arm/cpu/u-boot-spl.lds +++ b/arch/arm/cpu/u-boot-spl.lds @@ -32,17 +32,17 @@ SECTIONS }
. = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*_i2c_*))); - } - - . = .; #ifdef CONFIG_SPL_DM .u_boot_list : { KEEP(*(SORT(.u_boot_list_*_driver_*))); KEEP(*(SORT(.u_boot_list_*_uclass_*))); } #endif + . = .; + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*_i2c_*))); + } + . = ALIGN(4);
__image_copy_end = .;

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Driver-model I2C drivers can be picked up by the linker script rule for legacy drivers. Change the order to avoid this.
We could make the legacy code depend on !CONFIG_DM_I2C but that is not necessary and it is good to keep conditions to a minimum.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
arch/arm/cpu/u-boot-spl.lds | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
Applied to u-boot-dm.

U-Boot uses structures for hardware access so it is important that these structures are correct. Add a way of asserting that a structure member is at a particular offset. This can be created using the datasheet for the hardware.
This implementation uses Static_assert() since BUILD_BUG_ON() only works within functions.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
include/common.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/include/common.h b/include/common.h index 8f4b2ec..4566bd1 100644 --- a/include/common.h +++ b/include/common.h @@ -1010,6 +1010,17 @@ int cpu_release(int nr, int argc, char * const argv[]); #define DEFINE_CACHE_ALIGN_BUFFER(type, name, size) \ DEFINE_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
+/* + * check_member() - Check the offset of a structure member + * + * @structure: Name of structure (e.g. global_data) + * @member: Name of member (e.g. baudrate) + * @offset: Expected offset in bytes + */ +#define check_member(structure, member, offset) _Static_assert( \ + offsetof(struct structure, member) == offset, \ + "`struct " #structure "` offset for `" #member "` is not " #offset) + /* Pull in stuff for the build system */ #ifdef DO_DEPS_ONLY # include <environment.h>

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
U-Boot uses structures for hardware access so it is important that these structures are correct. Add a way of asserting that a structure member is at a particular offset. This can be created using the datasheet for the hardware.
This implementation uses Static_assert() since BUILD_BUG_ON() only works within functions.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
include/common.h | 11 +++++++++++ 1 file changed, 11 insertions(+)
Applied to u-boot-dm.

This does not actually help any current arch. For x86 it makes it harder to call (requires stack) and for ARM it has no effect. Drop it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
include/debug_uart.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/include/debug_uart.h b/include/debug_uart.h index f56797b..a75e377 100644 --- a/include/debug_uart.h +++ b/include/debug_uart.h @@ -10,8 +10,6 @@ #ifndef _DEBUG_UART_H #define _DEBUG_UART_H
-#include <linux/linkage.h> - /* * The debug UART is intended for use very early in U-Boot to debug problems * when an ICE or other debug mechanism is not available. @@ -64,46 +62,46 @@ void debug_uart_init(void); * * @ch: Character to output */ -asmlinkage void printch(int ch); +void printch(int ch);
/** * printascii() - Output an ASCII string to the debug UART * * @str: String to output */ -asmlinkage void printascii(const char *str); +void printascii(const char *str);
/** * printhex2() - Output a 2-digit hex value * * @value: Value to output */ -asmlinkage void printhex2(uint value); +void printhex2(uint value);
/** * printhex4() - Output a 4-digit hex value * * @value: Value to output */ -asmlinkage void printhex4(uint value); +void printhex4(uint value);
/** * printhex8() - Output a 8-digit hex value * * @value: Value to output */ -asmlinkage void printhex8(uint value); +void printhex8(uint value);
/* * Now define some functions - this should be inserted into the serial driver */ #define DEBUG_UART_FUNCS \ - asmlinkage void printch(int ch) \ + void printch(int ch) \ { \ _debug_uart_putc(ch); \ } \ \ - asmlinkage void printascii(const char *str) \ + void printascii(const char *str) \ { \ while (*str) \ _debug_uart_putc(*str++); \ @@ -121,17 +119,17 @@ asmlinkage void printhex8(uint value); printhex1(value >> (4 * digits)); \ } \ \ - asmlinkage void printhex2(uint value) \ + void printhex2(uint value) \ { \ printhex(value, 2); \ } \ \ - asmlinkage void printhex4(uint value) \ + void printhex4(uint value) \ { \ printhex(value, 4); \ } \ \ - asmlinkage void printhex8(uint value) \ + void printhex8(uint value) \ { \ printhex(value, 8); \ }

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This does not actually help any current arch. For x86 it makes it harder to call (requires stack) and for ARM it has no effect. Drop it.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
include/debug_uart.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-)
Applied to u-boot-dm.

When there is no console ready, allow the debug UART to be used for output. This makes debugging of early code considerably easier.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/console.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/common/console.c b/common/console.c index 0058222..acad8bd 100644 --- a/common/console.c +++ b/common/console.c @@ -6,6 +6,7 @@ */
#include <common.h> +#include <debug_uart.h> #include <stdarg.h> #include <iomux.h> #include <malloc.h> @@ -460,6 +461,13 @@ void putc(const char c) return; } #endif +#ifdef CONFIG_DEBUG_UART + /* if we don't have a console yet, use the debug UART */ + if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { + printch(c); + return; + } +#endif #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) return; @@ -491,7 +499,18 @@ void puts(const char *s) return; } #endif +#ifdef CONFIG_DEBUG_UART + if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { + while (*s) { + int ch = *s++;
+ printch(ch); + if (ch == '\n') + printch('\r'); + } + return; + } +#endif #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) return;

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
When there is no console ready, allow the debug UART to be used for output. This makes debugging of early code considerably easier.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/console.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
Applied to u-boot-dm.

At present printf() skips output if it can see there is no console. This is really just an optimisation, and is not necessary. Also it is currently incorrect in some cases. Rather than update the logic, just remove it so that we don't need to keep it in sync.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/console.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/common/console.c b/common/console.c index acad8bd..ace206c 100644 --- a/common/console.c +++ b/common/console.c @@ -456,6 +456,7 @@ static inline void print_pre_console_buffer(int flushpoint) {} void putc(const char c) { #ifdef CONFIG_SANDBOX + /* sandbox can send characters to stdout before it has a console */ if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { os_putc(c); return; @@ -540,11 +541,6 @@ int printf(const char *fmt, ...) uint i; char printbuffer[CONFIG_SYS_PBSIZE];
-#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER) - if (!gd->have_console) - return 0; -#endif - va_start(args, fmt);
/* For this to work, printbuffer must be larger than

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
At present printf() skips output if it can see there is no console. This is really just an optimisation, and is not necessary. Also it is currently incorrect in some cases. Rather than update the logic, just remove it so that we don't need to keep it in sync.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/console.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
Applied to u-boot-dm.

In SPL it is sometimes useful to be able to obtain a dump of the current driver model state. Since commands are not available, provide a way to directly call the functions to output this information.
Adjust the existing commands to use these functions.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/dump.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/util.h | 6 ++++ test/dm/cmd_dm.c | 82 ++----------------------------------------- 4 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 drivers/core/dump.c
diff --git a/drivers/core/Makefile b/drivers/core/Makefile index a3fec38..ed21fed 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -9,3 +9,4 @@ ifndef CONFIG_SPL_BUILD obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o +obj-$(CONFIG_DM) += dump.o diff --git a/drivers/core/dump.c b/drivers/core/dump.c new file mode 100644 index 0000000..fd4596e --- /dev/null +++ b/drivers/core/dump.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <dm/root.h> + +static void show_devices(struct udevice *dev, int depth, int last_flag) +{ + int i, is_last; + struct udevice *child; + char class_name[12]; + + /* print the first 11 characters to not break the tree-format. */ + strlcpy(class_name, dev->uclass->uc_drv->name, sizeof(class_name)); + printf(" %-11s [ %c ] ", class_name, + dev->flags & DM_FLAG_ACTIVATED ? '+' : ' '); + + for (i = depth; i >= 0; i--) { + is_last = (last_flag >> i) & 1; + if (i) { + if (is_last) + printf(" "); + else + printf("| "); + } else { + if (is_last) + printf("`-- "); + else + printf("|-- "); + } + } + + printf("%s\n", dev->name); + + list_for_each_entry(child, &dev->child_head, sibling_node) { + is_last = list_is_last(&child->sibling_node, &dev->child_head); + show_devices(child, depth + 1, (last_flag << 1) | is_last); + } +} + +void dm_dump_all(void) +{ + struct udevice *root; + + root = dm_root(); + if (root) { + printf(" Class Probed Name\n"); + printf("----------------------------------------\n"); + show_devices(root, -1, 0); + } +} + +/** + * dm_display_line() - Display information about a single device + * + * Displays a single line of information with an option prefix + * + * @dev: Device to display + */ +static void dm_display_line(struct udevice *dev) +{ + printf("- %c %s @ %08lx", + dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ', + dev->name, (ulong)map_to_sysmem(dev)); + if (dev->seq != -1 || dev->req_seq != -1) + printf(", seq %d, (req %d)", dev->seq, dev->req_seq); + puts("\n"); +} + +void dm_dump_uclass(void) +{ + struct uclass *uc; + int ret; + int id; + + for (id = 0; id < UCLASS_COUNT; id++) { + struct udevice *dev; + + ret = uclass_get(id, &uc); + if (ret) + continue; + + printf("uclass %d: %s\n", id, uc->uc_drv->name); + if (list_empty(&uc->dev_head)) + continue; + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + dm_display_line(dev); + } + puts("\n"); + } +} diff --git a/include/dm/util.h b/include/dm/util.h index 0cec17b..7dbed67 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -33,4 +33,10 @@ struct list_head; */ int list_count_items(struct list_head *head);
+/* Dump out a tree of all devices */ +void dm_dump_all(void); + +/* Dump out a list of uclasses and their devices */ +void dm_dump_uclass(void); + #endif diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c index 5bb2a99..5c501ec 100644 --- a/test/dm/cmd_dm.c +++ b/test/dm/cmd_dm.c @@ -14,96 +14,20 @@ #include <errno.h> #include <asm/io.h> #include <dm/root.h> -#include <dm/uclass-internal.h> - -static void show_devices(struct udevice *dev, int depth, int last_flag) -{ - int i, is_last; - struct udevice *child; - char class_name[12]; - - /* print the first 11 characters to not break the tree-format. */ - strlcpy(class_name, dev->uclass->uc_drv->name, sizeof(class_name)); - printf(" %-11s [ %c ] ", class_name, - dev->flags & DM_FLAG_ACTIVATED ? '+' : ' '); - - for (i = depth; i >= 0; i--) { - is_last = (last_flag >> i) & 1; - if (i) { - if (is_last) - printf(" "); - else - printf("| "); - } else { - if (is_last) - printf("`-- "); - else - printf("|-- "); - } - } - - printf("%s\n", dev->name); - - list_for_each_entry(child, &dev->child_head, sibling_node) { - is_last = list_is_last(&child->sibling_node, &dev->child_head); - show_devices(child, depth + 1, (last_flag << 1) | is_last); - } -} +#include <dm/util.h>
static int do_dm_dump_all(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - struct udevice *root; - - root = dm_root(); - if (root) { - printf(" Class Probed Name\n"); - printf("----------------------------------------\n"); - show_devices(root, -1, 0); - } + dm_dump_all();
return 0; }
-/** - * dm_display_line() - Display information about a single device - * - * Displays a single line of information with an option prefix - * - * @dev: Device to display - */ -static void dm_display_line(struct udevice *dev) -{ - printf("- %c %s @ %08lx", - dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ', - dev->name, (ulong)map_to_sysmem(dev)); - if (dev->seq != -1 || dev->req_seq != -1) - printf(", seq %d, (req %d)", dev->seq, dev->req_seq); - puts("\n"); -} - 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 udevice *dev; - - ret = uclass_get(id, &uc); - if (ret) - continue; - - printf("uclass %d: %s\n", id, uc->uc_drv->name); - if (list_empty(&uc->dev_head)) - continue; - list_for_each_entry(dev, &uc->dev_head, uclass_node) { - dm_display_line(dev); - } - puts("\n"); - } + dm_dump_uclass();
return 0; }

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
In SPL it is sometimes useful to be able to obtain a dump of the current driver model state. Since commands are not available, provide a way to directly call the functions to output this information.
Adjust the existing commands to use these functions.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/dump.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/util.h | 6 ++++ test/dm/cmd_dm.c | 82 ++----------------------------------------- 4 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 drivers/core/dump.c
Applied to u-boot-dm.

To avoid bloating SPL code, use debug() where possible in the driver model core code. The error code is already returned, and can be investigated as needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/lists.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 0c49d99..2e52500 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -86,13 +86,13 @@ int device_bind_driver_to_node(struct udevice *parent, const char *drv_name,
drv = lists_driver_lookup_name(drv_name); if (!drv) { - printf("Cannot find driver '%s'\n", drv_name); + debug("Cannot find driver '%s'\n", drv_name); return -ENOENT; } ret = device_bind(parent, drv, dev_name, NULL, node, devp); if (ret) { - printf("Cannot create device named '%s' (err=%d)\n", - dev_name, ret); + debug("Cannot create device named '%s' (err=%d)\n", + dev_name, ret); return ret; }

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
To avoid bloating SPL code, use debug() where possible in the driver model core code. The error code is already returned, and can be investigated as needed.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/lists.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
Applied to u-boot-dm.

In some rare cases it is useful to be able to locate a device given a device tree node offset. An example is when you have an alias that points to a node and you want to find the associated device. The device may be SPI, MMC or something else, but you don't need to know the uclass to find it.
Add a function to do a global search for a device, given its device tree offset.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/device.c | 25 +++++++++++++++++++++++++ include/dm/device.h | 14 ++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 85fd1fc..43aff54 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -470,6 +470,31 @@ int device_get_child_by_of_offset(struct udevice *parent, int seq, return device_get_device_tail(dev, ret, devp); }
+static struct udevice *_device_find_global_by_of_offset(struct udevice *parent, + int of_offset) +{ + struct udevice *dev, *found; + + if (parent->of_offset == of_offset) + return parent; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + found = _device_find_global_by_of_offset(dev, of_offset); + if (found) + return found; + } + + return NULL; +} + +int device_get_global_by_of_offset(int of_offset, struct udevice **devp) +{ + struct udevice *dev; + + dev = _device_find_global_by_of_offset(gd->dm_root, of_offset); + return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); +} + int device_find_first_child(struct udevice *parent, struct udevice **devp) { if (list_empty(&parent->child_head)) { diff --git a/include/dm/device.h b/include/dm/device.h index 18296bb..9a94ee1 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -390,6 +390,20 @@ int device_get_child_by_of_offset(struct udevice *parent, int seq, struct udevice **devp);
/** + * device_get_global_by_of_offset() - Get a device based on FDT offset + * + * Locates a device by its device tree offset, searching globally throughout + * the all driver model devices. + * + * The device is probed to activate it ready for use. + * + * @of_offset: Device tree offset to find + * @devp: Returns pointer to device if found, otherwise this is set to NULL + * @return 0 if OK, -ve on error + */ +int device_get_global_by_of_offset(int of_offset, struct udevice **devp); + +/** * device_find_first_child() - Find the first child of a device * * @parent: Parent device to search

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
In some rare cases it is useful to be able to locate a device given a device tree node offset. An example is when you have an alias that points to a node and you want to find the associated device. The device may be SPI, MMC or something else, but you don't need to know the uclass to find it.
Add a function to do a global search for a device, given its device tree offset.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/device.c | 25 +++++++++++++++++++++++++ include/dm/device.h | 14 ++++++++++++++ 2 files changed, 39 insertions(+)
Applied to u-boot-dm.

This parameter is named 'seq' but should be named 'of_offset'.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/device.c | 4 ++-- include/dm/device.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/core/device.c b/drivers/core/device.c index 43aff54..456426a 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -459,14 +459,14 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset, return -ENODEV; }
-int device_get_child_by_of_offset(struct udevice *parent, int seq, +int device_get_child_by_of_offset(struct udevice *parent, int node, struct udevice **devp) { struct udevice *dev; int ret;
*devp = NULL; - ret = device_find_child_by_of_offset(parent, seq, &dev); + ret = device_find_child_by_of_offset(parent, node, &dev); return device_get_device_tail(dev, ret, devp); }
diff --git a/include/dm/device.h b/include/dm/device.h index 9a94ee1..9fa0048 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -386,7 +386,7 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset, * @devp: Returns pointer to device if found, otherwise this is set to NULL * @return 0 if OK, -ve on error */ -int device_get_child_by_of_offset(struct udevice *parent, int seq, +int device_get_child_by_of_offset(struct udevice *parent, int of_offset, struct udevice **devp);
/**

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This parameter is named 'seq' but should be named 'of_offset'.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/device.c | 4 ++-- include/dm/device.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
Applied to u-boot-dm.

Now that we support driver model in SPL, allow GPIO drivers to be used there also.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/gpio/Makefile | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 5864850..67c6374 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,13 +6,9 @@ #
ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_DM_GPIO) += gpio-uclass.o obj-$(CONFIG_AXP_GPIO) += axp_gpio.o endif -/* TODO(sjg@chromium.org): Only tegra supports driver model in SPL */ -ifdef CONFIG_TEGRA_GPIO obj-$(CONFIG_DM_GPIO) += gpio-uclass.o -endif
obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Now that we support driver model in SPL, allow GPIO drivers to be used there also.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/gpio/Makefile | 4 ---- 1 file changed, 4 deletions(-)
Applied to u-boot-dm.

Provide a driver-model function to look up a GPIO name. Make the standard function use it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/gpio/gpio-uclass.c | 34 ++++++++++++++++++++++++++-------- include/asm-generic/gpio.h | 13 +++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index bf982b9..c6fd580 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -48,8 +48,7 @@ static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc) return ret ? ret : -ENOENT; }
-int gpio_lookup_name(const char *name, struct udevice **devp, - unsigned int *offsetp, unsigned int *gpiop) +int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc) { struct gpio_dev_priv *uc_priv = NULL; struct udevice *dev; @@ -57,8 +56,6 @@ int gpio_lookup_name(const char *name, struct udevice **devp, int numeric; int ret;
- if (devp) - *devp = NULL; numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1; for (ret = uclass_first_device(UCLASS_GPIO, &dev); dev; @@ -84,12 +81,33 @@ int gpio_lookup_name(const char *name, struct udevice **devp, if (!dev) return ret ? ret : -EINVAL;
+ desc->dev = dev; + desc->offset = offset; + + return 0; +} + +int gpio_lookup_name(const char *name, struct udevice **devp, + unsigned int *offsetp, unsigned int *gpiop) +{ + struct gpio_desc desc; + int ret; + + if (devp) + *devp = NULL; + ret = dm_gpio_lookup_name(name, &desc); + if (ret) + return ret; + if (devp) - *devp = dev; + *devp = desc.dev; if (offsetp) - *offsetp = offset; - if (gpiop) - *gpiop = uc_priv->gpio_base + offset; + *offsetp = desc.offset; + if (gpiop) { + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc.dev); + + *gpiop = uc_priv->gpio_base + desc.offset; + }
return 0; } diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index de91e57..b1cf95c 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -322,6 +322,19 @@ struct gpio_dev_priv { const char *gpio_get_bank_info(struct udevice *dev, int *offset_count);
/** + * dm_gpio_lookup_name() - Look up a named GPIO and return its description + * + * The name of a GPIO is typically its bank name followed by a number from 0. + * For example A0 is the first GPIO in bank A. Each bank is a separate driver + * model device. + * + * @name: Name to look up + * @desc: Returns description, on success + * @return 0 if OK, -ve on error + */ +int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc); + +/** * 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

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Provide a driver-model function to look up a GPIO name. Make the standard function use it.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/gpio/gpio-uclass.c | 34 ++++++++++++++++++++++++++-------- include/asm-generic/gpio.h | 13 +++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-)
Applied to u-boot-dm.

This function can be used for testing to manually request a GPIO for use, without resorting to the legacy GPIO API.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/gpio/gpio-uclass.c | 2 +- include/asm-generic/gpio.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index c6fd580..4efda31 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -127,7 +127,7 @@ static int gpio_find_and_xlate(struct gpio_desc *desc, return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0; }
-static int dm_gpio_request(struct gpio_desc *desc, const char *label) +int dm_gpio_request(struct gpio_desc *desc, const char *label) { struct udevice *dev = desc->dev; struct gpio_dev_priv *uc_priv; diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index b1cf95c..0af599f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -434,6 +434,18 @@ int gpio_request_list_by_name(struct udevice *dev, const char *list_name, int flags);
/** + * dm_gpio_request() - manually request a GPIO + * + * Note: This function should only be used for testing / debugging. Instead. + * use gpio_request_by_name() to pull GPIOs from the device tree. + * + * @desc: GPIO description of GPIO to request (see dm_gpio_lookup_name()) + * @label: Label to attach to the GPIO while claimed + * @return 0 if OK, -ve on error + */ +int dm_gpio_request(struct gpio_desc *desc, const char *label); + +/** * gpio_get_list_count() - Returns the number of GPIOs in a list * * Counts the GPIOs in a list. See gpio_request_by_name() for additional

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This function can be used for testing to manually request a GPIO for use, without resorting to the legacy GPIO API.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/gpio/gpio-uclass.c | 2 +- include/asm-generic/gpio.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-)
Applied to u-boot-dm.

Add a simple implementaton of register maps, supporting only direct I/O for now. This can be enhanced later to support buses which have registers, such as I2C, SPI and PCI.
It allows drivers which can operate with multiple buses to avoid dealing with the particulars of register access on that bus.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/regmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/regmap.h | 72 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/core/regmap.c create mode 100644 include/regmap.h
diff --git a/drivers/core/Makefile b/drivers/core/Makefile index ed21fed..7851824 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o obj-$(CONFIG_DM) += dump.o +obj-$(CONFIG_OF_CONTROL) += regmap.o diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c new file mode 100644 index 0000000..519832f --- /dev/null +++ b/drivers/core/regmap.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <libfdt.h> +#include <malloc.h> +#include <mapmem.h> +#include <regmap.h> + +DECLARE_GLOBAL_DATA_PTR; + +int regmap_init_mem(struct udevice *dev, struct regmap **mapp) +{ + const void *blob = gd->fdt_blob; + struct regmap_range *range; + const fdt32_t *cell; + struct regmap *map; + int count; + int addr_len, size_len, both_len; + int parent; + int len; + + parent = dev->parent->of_offset; + addr_len = fdt_address_cells(blob, parent); + size_len = fdt_size_cells(blob, parent); + both_len = addr_len + size_len; + + cell = fdt_getprop(blob, dev->of_offset, "reg", &len); + len /= sizeof(*cell); + count = len / both_len; + if (!cell || !count) + return -EINVAL; + + map = malloc(sizeof(struct regmap)); + if (!map) + return -ENOMEM; + + if (count <= 1) { + map->range = &map->base_range; + } else { + map->range = malloc(count * sizeof(struct regmap_range)); + if (!map->range) { + free(map); + return -ENOMEM; + } + } + + map->base = fdtdec_get_number(cell, addr_len); + map->range_count = count; + + for (range = map->range; count > 0; + count--, cell += both_len, range++) { + range->start = fdtdec_get_number(cell, addr_len); + range->size = fdtdec_get_number(cell + addr_len, size_len); + } + + *mapp = map; + + return 0; +} + +void *regmap_get_range(struct regmap *map, unsigned int range_num) +{ + struct regmap_range *range; + + if (range_num >= map->range_count) + return NULL; + range = &map->range[range_num]; + + return map_sysmem(range->start, range->size); +} + +int regmap_uninit(struct regmap *map) +{ + if (map->range_count > 1) + free(map->range); + free(map); + + return 0; +} diff --git a/include/regmap.h b/include/regmap.h new file mode 100644 index 0000000..eccf770 --- /dev/null +++ b/include/regmap.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __REGMAP_H +#define __REGMAP_H + +/** + * struct regmap_range - a register map range + * + * @start: Start address + * @size: Size in bytes + */ +struct regmap_range { + ulong start; + ulong size; +}; + +/** + * struct regmap - a way of accessing hardware/bus registers + * + * @base: Base address of register map + * @range_count: Number of ranges available within the map + * @range: Pointer to the list of ranges, allocated if @range_count > 1 + * @base_range: If @range_count is <= 1, @range points here + */ +struct regmap { + phys_addr_t base; + int range_count; + struct regmap_range *range, base_range; +}; + +/* + * Interface to provide access to registers either through a direct memory + * bus or through a peripheral bus like I2C, SPI. + */ +int regmap_write(struct regmap *map, uint offset, uint val); +int regmap_read(struct regmap *map, uint offset, uint *valp); + +#define regmap_write32(map, ptr, member, val) \ + regmap_write(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), val) + +#define regmap_read32(map, ptr, member, valp) \ + regmap_read(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), valp) + +/** + * regmap_init_mem() - Set up a new register map that uses memory access + * + * Use regmap_uninit() to free it. + * + * @dev: Device that uses this map + * @mapp: Returns allocated map + */ +int regmap_init_mem(struct udevice *dev, struct regmap **mapp); + +/** + * regmap_get_range() - Obtain the base memory address of a regmap range + * + * @map: Regmap to query + * @range_num: Range to look up + */ +void *regmap_get_range(struct regmap *map, unsigned int range_num); + +/** + * regmap_uninit() - free a previously inited regmap + */ +int regmap_uninit(struct regmap *map); + +#endif

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Add a simple implementaton of register maps, supporting only direct I/O for now. This can be enhanced later to support buses which have registers, such as I2C, SPI and PCI.
It allows drivers which can operate with multiple buses to avoid dealing with the particulars of register access on that bus.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/regmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/regmap.h | 72 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/core/regmap.c create mode 100644 include/regmap.h
Applied to u-boot-dm.

Many SoCs have a number of system controllers which are dealt with as a group by a single driver. It is a pain to have to add lots of compatible strings and/or separate drivers for each. Instead we can identify the controllers by a number and request the address of the one we want.
Add a simple implementation of this which can be used by SoC driver code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/syscon-uclass.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/syscon.h | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 drivers/core/syscon-uclass.c create mode 100644 include/syscon.h
diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 7851824..54d57e5 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -11,3 +11,4 @@ endif obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o obj-$(CONFIG_DM) += dump.o obj-$(CONFIG_OF_CONTROL) += regmap.o +obj-$(CONFIG_OF_CONTROL) += syscon-uclass.o diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c new file mode 100644 index 0000000..4d66bb5 --- /dev/null +++ b/drivers/core/syscon-uclass.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <syscon.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/err.h> + +struct regmap *syscon_get_regmap(struct udevice *dev) +{ + struct syscon_uc_info *priv = dev_get_uclass_priv(dev); + + return priv->regmap; +} + +static int syscon_pre_probe(struct udevice *dev) +{ + struct syscon_uc_info *priv = dev_get_uclass_priv(dev); + + return regmap_init_mem(dev, &priv->regmap); +} + +struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_SYSCON, &uc); + if (ret) + return ERR_PTR(ret); + uclass_foreach_dev(dev, uc) { + if (dev->driver_data == driver_data) { + struct syscon_uc_info *priv; + int ret; + + ret = device_probe(dev); + if (ret) + return ERR_PTR(ret); + priv = dev_get_uclass_priv(dev); + + return priv->regmap; + } + } + + return ERR_PTR(-ENOENT); +} + +void *syscon_get_first_range(ulong driver_data) +{ + struct regmap *map; + + map = syscon_get_regmap_by_driver_data(driver_data); + if (IS_ERR(map)) + return map; + return regmap_get_range(map, 0); +} + +UCLASS_DRIVER(syscon) = { + .id = UCLASS_SYSCON, + .name = "syscon", + .per_device_auto_alloc_size = sizeof(struct syscon_uc_info), + .pre_probe = syscon_pre_probe, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c7310d7..24baa5c 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -46,6 +46,7 @@ enum uclass_id { UCLASS_SPI, /* SPI bus */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ + UCLASS_SYSCON, /* System configuration device */ UCLASS_THERMAL, /* Thermal sensor */ UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ diff --git a/include/syscon.h b/include/syscon.h new file mode 100644 index 0000000..c62ccd6 --- /dev/null +++ b/include/syscon.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SYSCON_H +#define __SYSCON_H + +/** + * struct syscon_uc_info - Information stored by the syscon UCLASS_UCLASS + * + * @regmap: Register map for this controller + */ +struct syscon_uc_info { + struct regmap *regmap; +}; + +/* So far there are no ops so this is a placeholder */ +struct syscon_ops { +}; + +#define syscon_get_ops(dev) ((struct syscon_ops *)(dev)->driver->ops) + +/** + * syscon_get_regmap() - Get access to a register map + * + * @dev: Device to check (UCLASS_SCON) + * @info: Returns regmap for the device + * @return 0 if OK, -ve on error + */ +struct regmap *syscon_get_regmap(struct udevice *dev); + +/** + * syscon_get_regmap_by_driver_data() - Look up a controller by its ID + * + * Each system controller can be accessed by its driver data, which is + * assumed to be unique through the scope of all system controllers that + * are in use. This function looks up the regmap given this driver data. + * + * @driver_data: Driver data value to look up + * @return register map correponding to @driver_data, or -ve error code + */ +struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data); + +/** + * syscon_get_first_range() - get the first memory range from a syscon regmap + * + * @driver_data: Driver data value to look up + * @return first region of register map correponding to @driver_data, or + * -ve error code + */ +void *syscon_get_first_range(ulong driver_data); + +#endif

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Many SoCs have a number of system controllers which are dealt with as a group by a single driver. It is a pain to have to add lots of compatible strings and/or separate drivers for each. Instead we can identify the controllers by a number and request the address of the one we want.
Add a simple implementation of this which can be used by SoC driver code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/core/Makefile | 1 + drivers/core/syscon-uclass.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/syscon.h | 56 +++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 drivers/core/syscon-uclass.c create mode 100644 include/syscon.h
Applied to u-boot-dm.

This is checking the wrong method. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/cpu/cpu-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c index ab18ee2..ffe250d 100644 --- a/drivers/cpu/cpu-uclass.c +++ b/drivers/cpu/cpu-uclass.c @@ -25,7 +25,7 @@ int cpu_get_info(struct udevice *dev, struct cpu_info *info) { struct cpu_ops *ops = cpu_get_ops(dev);
- if (!ops->get_desc) + if (!ops->get_info) return -ENOSYS;
return ops->get_info(dev, info);

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This is checking the wrong method. Fix it.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/cpu/cpu-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c index ab18ee2..ffe250d 100644 --- a/drivers/cpu/cpu-uclass.c +++ b/drivers/cpu/cpu-uclass.c @@ -25,7 +25,7 @@ int cpu_get_info(struct udevice *dev, struct cpu_info *info) { struct cpu_ops *ops = cpu_get_ops(dev);
if (!ops->get_desc)
if (!ops->get_info) return -ENOSYS; return ops->get_info(dev, info);
-- 2.4.3.573.g4eafbef
Applied to u-boot-x86.

Add a simple uclass for LEDs, so that these can be controlled by the device tree and activated when needed. LEDs are referred to by their label.
This implementation requires a driver for each type of LED (e.g GPIO, I2C).
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
doc/device-tree-bindings/leds/common.txt | 23 ++++++++++++++ drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/led/Kconfig | 17 +++++++++++ drivers/led/Makefile | 8 +++++ drivers/led/led-uclass.c | 48 ++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/led.h | 51 ++++++++++++++++++++++++++++++++ scripts/Makefile.spl | 1 + 9 files changed, 152 insertions(+) create mode 100644 doc/device-tree-bindings/leds/common.txt create mode 100644 drivers/led/Kconfig create mode 100644 drivers/led/Makefile create mode 100644 drivers/led/led-uclass.c create mode 100644 include/led.h
diff --git a/doc/device-tree-bindings/leds/common.txt b/doc/device-tree-bindings/leds/common.txt new file mode 100644 index 0000000..2d88816 --- /dev/null +++ b/doc/device-tree-bindings/leds/common.txt @@ -0,0 +1,23 @@ +Common leds properties. + +Optional properties for child nodes: +- label : The label for this LED. If omitted, the label is + taken from the node name (excluding the unit address). + +- linux,default-trigger : This parameter, if present, is a + string defining the trigger assigned to the LED. Current triggers are: + "backlight" - LED will act as a back-light, controlled by the framebuffer + system + "default-on" - LED will turn on (but for leds-gpio see "default-state" + property in Documentation/devicetree/bindings/gpio/led.txt) + "heartbeat" - LED "double" flashes at a load average based rate + "ide-disk" - LED indicates disk activity + "timer" - LED flashes at a fixed, configurable rate + +Examples: + +system-status { + label = "Status"; + linux,default-trigger = "heartbeat"; + ... +}; diff --git a/drivers/Kconfig b/drivers/Kconfig index 1f40887..ee942e2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -20,6 +20,8 @@ source "drivers/net/Kconfig"
source "drivers/input/Kconfig"
+source "drivers/led/Kconfig" + source "drivers/serial/Kconfig"
source "drivers/tpm/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 405b64b..c090aba 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ +obj-$(CONFIG_LED) += led/ obj-y += misc/ obj-y += pcmcia/ obj-y += dfu/ diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig new file mode 100644 index 0000000..e4d9a84 --- /dev/null +++ b/drivers/led/Kconfig @@ -0,0 +1,17 @@ +config LED + bool "Enable LED support" + depends on DM + help + Many boards have LEDs which can be used to signal status or alerts. + U-Boot provides a uclass API to implement this feature. LED drivers + can provide access to board-specific LEDs. Use of the device tree + for configuration is encouraged. + +config SPL_LED_SUPPORT + bool "Enable LED support in SPL" + depends on LED + help + The LED subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use LEDs in SPL, + enable this option. You will need to enable device tree in SPL + for this to work. diff --git a/drivers/led/Makefile b/drivers/led/Makefile new file mode 100644 index 0000000..b39b205 --- /dev/null +++ b/drivers/led/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# Written by Simon Glass sjg@chromium.org +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_LED) += led-uclass.o diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c new file mode 100644 index 0000000..a80ae93 --- /dev/null +++ b/drivers/led/led-uclass.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +int led_get_by_label(const char *label, struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_LED, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + + if (!strcmp(label, uc_plat->label)) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENOENT; +} + +int led_set_on(struct udevice *dev, int on) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->set_on) + return -ENOSYS; + + return ops->set_on(dev, on); +} + +UCLASS_DRIVER(led) = { + .id = UCLASS_LED, + .name = "led", + .per_device_platdata_auto_alloc_size = sizeof(struct led_uclass_plat), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 24baa5c..a961648 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -33,6 +33,7 @@ enum uclass_id { UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ + UCLASS_LED, /* Light-emitting diode (LED) */ UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MASS_STORAGE, /* Mass storage device */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ diff --git a/include/led.h b/include/led.h new file mode 100644 index 0000000..8925d75 --- /dev/null +++ b/include/led.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LED_H +#define __LED_H + +/** + * struct led_uclass_plat - Platform data the uclass stores about each device + * + * @label: LED label + */ +struct led_uclass_plat { + const char *label; +}; + +struct led_ops { + /** + * set_on() - set the state of an LED + * + * @dev: LED device to change + * @on: 1 to turn the LED on, 0 to turn it off + * @return 0 if OK, -ve on error + */ + int (*set_on)(struct udevice *dev, int on); +}; + +#define led_get_ops(dev) ((struct led_ops *)(dev)->driver->ops) + +/** + * led_get_by_label() - Find an LED device by label + * + * @label: LED label to look up + * @devp: Returns the associated device, if found + * @return 0 if found, -ve on error + */ +int led_get_by_label(const char *label, struct udevice **devp); + +/** + * led_set_on() - set the state of an LED + * + * @dev: LED device to change + * @on: 1 to turn the LED on, 0 to turn it off + * @return 0 if OK, -ve on error + */ +int led_set_on(struct udevice *dev, int on); + +#endif diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index bcc7ca2..6b32618 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -64,6 +64,7 @@ libs-$(CONFIG_SPL_SERIAL_SUPPORT) += drivers/serial/ libs-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/ libs-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/ libs-y += fs/ +libs-$(CONFIG_SPL_LED_SUPPORT) += drivers/led/ libs-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/ libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/ drivers/power/pmic/ libs-$(CONFIG_SPL_MTD_SUPPORT) += drivers/mtd/

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Add a simple uclass for LEDs, so that these can be controlled by the device tree and activated when needed. LEDs are referred to by their label.
This implementation requires a driver for each type of LED (e.g GPIO, I2C).
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
doc/device-tree-bindings/leds/common.txt | 23 ++++++++++++++ drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/led/Kconfig | 17 +++++++++++ drivers/led/Makefile | 8 +++++ drivers/led/led-uclass.c | 48 ++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/led.h | 51 ++++++++++++++++++++++++++++++++ scripts/Makefile.spl | 1 + 9 files changed, 152 insertions(+) create mode 100644 doc/device-tree-bindings/leds/common.txt create mode 100644 drivers/led/Kconfig create mode 100644 drivers/led/Makefile create mode 100644 drivers/led/led-uclass.c create mode 100644 include/led.h
Applied to u-boot-dm.

Add a simple driver which allows use of LEDs attached to GPIOs. The linux device tree binding is used.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
doc/device-tree-bindings/leds/leds-gpio.txt | 52 ++++++++++++++ drivers/led/Kconfig | 9 +++ drivers/led/Makefile | 1 + drivers/led/led_gpio.c | 101 ++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 doc/device-tree-bindings/leds/leds-gpio.txt create mode 100644 drivers/led/led_gpio.c
diff --git a/doc/device-tree-bindings/leds/leds-gpio.txt b/doc/device-tree-bindings/leds/leds-gpio.txt new file mode 100644 index 0000000..df1b308 --- /dev/null +++ b/doc/device-tree-bindings/leds/leds-gpio.txt @@ -0,0 +1,52 @@ +LEDs connected to GPIO lines + +Required properties: +- compatible : should be "gpio-leds". + +Each LED is represented as a sub-node of the gpio-leds device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: +- gpios : Should specify the LED's GPIO, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be + indicated using flags in the GPIO specifier. +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- default-state: (optional) The initial state of the LED. Valid + values are "on", "off", and "keep". If the LED is already on or off + and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or + on). The "keep" setting will keep the LED at whatever its current + state is, without producing a glitch. The default is off if this + property is not present. + +Examples: + +leds { + compatible = "gpio-leds"; + hdd { + label = "IDE Activity"; + gpios = <&mcu_pio 0 1>; /* Active low */ + linux,default-trigger = "ide-disk"; + }; + + fault { + gpios = <&mcu_pio 1 0>; + /* Keep LED on if BIOS detected hardware fault */ + default-state = "keep"; + }; +}; + +run-control { + compatible = "gpio-leds"; + red { + gpios = <&mpc8572 6 0>; + default-state = "off"; + }; + green { + gpios = <&mpc8572 7 0>; + default-state = "on"; + }; +}; diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index e4d9a84..de5feea 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -15,3 +15,12 @@ config SPL_LED_SUPPORT If this is acceptable and you have a need to use LEDs in SPL, enable this option. You will need to enable device tree in SPL for this to work. + +config LED_GPIO + bool "LED support for GPIO-connected LEDs" + depends on LED && DM_GPIO + help + Enable support for LEDs which are connected to GPIO lines. These + GPIOs may be on the SoC or some other device which provides GPIOs. + The GPIO driver must used driver model. LEDs are configured using + the device tree. diff --git a/drivers/led/Makefile b/drivers/led/Makefile index b39b205..990129e 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -6,3 +6,4 @@ #
obj-$(CONFIG_LED) += led-uclass.o +obj-$(CONFIG_LED_GPIO) += led_gpio.o diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c new file mode 100644 index 0000000..a4cd618 --- /dev/null +++ b/drivers/led/led_gpio.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/gpio.h> +#include <dm/lists.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct led_gpio_priv { + struct gpio_desc gpio; +}; + +static int gpio_led_set_on(struct udevice *dev, int on) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (!dm_gpio_is_valid(&priv->gpio)) + return -EREMOTEIO; + + return dm_gpio_set_value(&priv->gpio, on); +} + +static int led_gpio_probe(struct udevice *dev) +{ + struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + struct led_gpio_priv *priv = dev_get_priv(dev); + + /* Ignore the top-level LED node */ + if (!uc_plat->label) + return 0; + return gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); +} + +static int led_gpio_remove(struct udevice *dev) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->gpio)) + dm_gpio_free(dev, &priv->gpio); + + return 0; +} + +static int led_gpio_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + struct udevice *dev; + int node; + int ret; + + for (node = fdt_first_subnode(blob, parent->of_offset); + node > 0; + node = fdt_next_subnode(blob, node)) { + struct led_uclass_plat *uc_plat; + const char *label; + + label = fdt_getprop(blob, node, "label", NULL); + if (!label) { + debug("%s: node %s has no label\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + ret = device_bind_driver_to_node(parent, "gpio_led", + fdt_get_name(blob, node, NULL), + node, &dev); + if (ret) + return ret; + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct led_ops gpio_led_ops = { + .set_on = gpio_led_set_on, +}; + +static const struct udevice_id led_gpio_ids[] = { + { .compatible = "gpio-leds" }, + { } +}; + +U_BOOT_DRIVER(led_gpio) = { + .name = "gpio_led", + .id = UCLASS_LED, + .of_match = led_gpio_ids, + .ops = &gpio_led_ops, + .priv_auto_alloc_size = sizeof(struct led_gpio_priv), + .bind = led_gpio_bind, + .probe = led_gpio_probe, + .remove = led_gpio_remove, +};

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Add a simple driver which allows use of LEDs attached to GPIOs. The linux device tree binding is used.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
doc/device-tree-bindings/leds/leds-gpio.txt | 52 ++++++++++++++ drivers/led/Kconfig | 9 +++ drivers/led/Makefile | 1 + drivers/led/led_gpio.c | 101 ++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 doc/device-tree-bindings/leds/leds-gpio.txt create mode 100644 drivers/led/led_gpio.c
Applied to u-boot-dm.

Add a few messages to indicate progress and failure.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/spl/spl_mmc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index 552f80d..51add39 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -26,11 +26,14 @@ static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector)
/* read image header to find the image size & load address */ count = mmc->block_dev.block_read(0, sector, 1, header); + debug("read sector %lx, count=%lu\n", sector, count); if (count == 0) goto end;
- if (image_get_magic(header) != IH_MAGIC) + if (image_get_magic(header) != IH_MAGIC) { + puts("bad magic\n"); return -1; + }
spl_parse_image_header(header);
@@ -40,7 +43,9 @@ static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector)
/* Read the header too to avoid extra memcpy */ count = mmc->block_dev.block_read(0, sector, image_size_sectors, - (void *) spl_image.load_addr); + (void *)spl_image.load_addr); + debug("read %x sectors to %x\n", image_size_sectors, + spl_image.load_addr);
end: if (count == 0) {

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Add a few messages to indicate progress and failure.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/spl/spl_mmc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
Applied to u-boot-dm.

Add basic support for MMC, providing a uclass which can set up an MMC device. This allows MMC drivers to move to using driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/mmc/Kconfig | 10 ++++++++++ drivers/mmc/Makefile | 2 ++ drivers/mmc/mmc-uclass.c | 34 ++++++++++++++++++++++++++++++++++ drivers/mmc/mmc.c | 2 ++ include/dm/uclass-id.h | 1 + include/mmc.h | 22 ++++++++++++++++++++++ 6 files changed, 71 insertions(+) create mode 100644 drivers/mmc/mmc-uclass.c
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 7ba85a2..3e835f7 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -1,5 +1,15 @@ menu "MMC Host controller Support"
+config DM_MMC + bool "Enable MMC controllers using Driver Model" + depends on DM + help + This enables the MultiMediaCard (MMC) uclass which suports MMC and + Secure Digital I/O (SDIO) cards. Both removable (SD, micro-SD, etc.) + and non-removable (e.g. eMMC chip) devices are supported. These + appear as block devices in U-Boot and can support filesystems such + as EXT4 and FAT. + config SH_SDHI bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" depends on RMOBILE diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index ed73687..2680c63 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -5,6 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ #
+obj-$(CONFIG_DM_MMC) += mmc-uclass.o + obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o obj-$(CONFIG_BFIN_SDH) += bfin_sdh.o diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c new file mode 100644 index 0000000..777489f --- /dev/null +++ b/drivers/mmc/mmc-uclass.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mmc.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> + +struct mmc *mmc_get_mmc_dev(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv; + + if (!device_active(dev)) + return NULL; + upriv = dev_get_uclass_priv(dev); + return upriv->mmc; +} + +U_BOOT_DRIVER(mmc) = { + .name = "mmc", + .id = UCLASS_MMC, +}; + +UCLASS_DRIVER(mmc) = { + .id = UCLASS_MMC, + .name = "mmc", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct mmc_uclass_priv), +}; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 79e6fee..4eab274 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1761,8 +1761,10 @@ int mmc_initialize(bd_t *bis) INIT_LIST_HEAD (&mmc_devices); cur_dev_num = 0;
+#ifndef CONFIG_DM_MMC if (board_mmc_init(bis) < 0) cpu_mmc_init(bis); +#endif
#ifndef CONFIG_SPL_BUILD print_mmc_devices(','); diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a961648..cba7c0a 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -36,6 +36,7 @@ enum uclass_id { UCLASS_LED, /* Light-emitting diode (LED) */ UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MASS_STORAGE, /* Mass storage device */ + UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ diff --git a/include/mmc.h b/include/mmc.h index dd98b3b..cda9a19 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -266,6 +266,28 @@ #define MMC_NUM_BOOT_PARTITION 2 #define MMC_PART_RPMB 3 /* RPMB partition number */
+/* Driver model support */ + +/** + * struct mmc_uclass_priv - Holds information about a device used by the uclass + */ +struct mmc_uclass_priv { + struct mmc *mmc; +}; + +/** + * mmc_get_mmc_dev() - get the MMC struct pointer for a device + * + * Provided that the device is already probed and ready for use, this value + * will be available. + * + * @dev: Device + * @return associated mmc struct pointer if available, else NULL + */ +struct mmc *mmc_get_mmc_dev(struct udevice *dev); + +/* End of driver model support */ + struct mmc_cid { unsigned long psn; unsigned short oid;

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Add basic support for MMC, providing a uclass which can set up an MMC device. This allows MMC drivers to move to using driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/Kconfig | 10 ++++++++++ drivers/mmc/Makefile | 2 ++ drivers/mmc/mmc-uclass.c | 34 ++++++++++++++++++++++++++++++++++ drivers/mmc/mmc.c | 2 ++ include/dm/uclass-id.h | 1 + include/mmc.h | 22 ++++++++++++++++++++++ 6 files changed, 71 insertions(+) create mode 100644 drivers/mmc/mmc-uclass.c
Applied to u-boot-dm.

These bloat the code and cause problems for SPL. Use debug() where possible and try to return a useful error code instead.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 53a8aca..8f28d7e 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -8,6 +8,7 @@
#include <bouncebuf.h> #include <common.h> +#include <errno.h> #include <malloc.h> #include <mmc.h> #include <dwmmc.h> @@ -119,7 +120,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { if (get_timer(start) > timeout) { - printf("%s: Timeout on data busy\n", __func__); + debug("%s: Timeout on data busy\n", __func__); return TIMEOUT; } } @@ -178,7 +179,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, }
if (i == retry) { - printf("%s: Timeout.\n", __func__); + debug("%s: Timeout.\n", __func__); return TIMEOUT; }
@@ -194,8 +195,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, debug("%s: Response Timeout.\n", __func__); return TIMEOUT; } else if (mask & DWMCI_INTMSK_RE) { - printf("%s: Response Error.\n", __func__); - return -1; + debug("%s: Response Error.\n", __func__); + return -EIO; }
@@ -214,7 +215,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, do { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { - printf("%s: DATA ERROR!\n", __func__); + debug("%s: DATA ERROR!\n", __func__); return -1; } } while (!(mask & DWMCI_INTMSK_DTO)); @@ -251,7 +252,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else if (host->bus_hz) sclk = host->bus_hz; else { - printf("%s: Didn't get source clock value.\n", __func__); + debug("%s: Didn't get source clock value.\n", __func__); return -EINVAL; }
@@ -270,7 +271,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) { - printf("%s: Timeout!\n", __func__); + debug("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START); @@ -285,7 +286,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) { - printf("%s: Timeout!\n", __func__); + debug("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START); @@ -339,8 +340,8 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_PWREN, 1);
if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { - printf("%s[%d] Fail-reset!!\n", __func__, __LINE__); - return -1; + debug("%s[%d] Fail-reset!!\n", __func__, __LINE__); + return -EIO; }
/* Enumerate at 400KHz */

On Wed, Jun 24, 2015 at 5:38 AM, Simon Glass sjg@chromium.org wrote:
These bloat the code and cause problems for SPL. Use debug() where possible and try to return a useful error code instead.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 21 +++++++++++----------
Subject line and description should show this is dwmmc specific.
ChenYu
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 53a8aca..8f28d7e 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -8,6 +8,7 @@
#include <bouncebuf.h> #include <common.h> +#include <errno.h> #include <malloc.h> #include <mmc.h> #include <dwmmc.h> @@ -119,7 +120,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { if (get_timer(start) > timeout) {
printf("%s: Timeout on data busy\n", __func__);
debug("%s: Timeout on data busy\n", __func__); return TIMEOUT; } }
@@ -178,7 +179,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, }
if (i == retry) {
printf("%s: Timeout.\n", __func__);
debug("%s: Timeout.\n", __func__); return TIMEOUT; }
@@ -194,8 +195,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, debug("%s: Response Timeout.\n", __func__); return TIMEOUT; } else if (mask & DWMCI_INTMSK_RE) {
printf("%s: Response Error.\n", __func__);
return -1;
debug("%s: Response Error.\n", __func__);
return -EIO; }
@@ -214,7 +215,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, do { mask = dwmci_readl(host, DWMCI_RINTSTS); if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) {
printf("%s: DATA ERROR!\n", __func__);
debug("%s: DATA ERROR!\n", __func__); return -1; } } while (!(mask & DWMCI_INTMSK_DTO));
@@ -251,7 +252,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) else if (host->bus_hz) sclk = host->bus_hz; else {
printf("%s: Didn't get source clock value.\n", __func__);
debug("%s: Didn't get source clock value.\n", __func__); return -EINVAL; }
@@ -270,7 +271,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) {
printf("%s: Timeout!\n", __func__);
debug("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START);
@@ -285,7 +286,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) do { status = dwmci_readl(host, DWMCI_CMD); if (timeout-- < 0) {
printf("%s: Timeout!\n", __func__);
debug("%s: Timeout!\n", __func__); return -ETIMEDOUT; } } while (status & DWMCI_CMD_START);
@@ -339,8 +340,8 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_PWREN, 1);
if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) {
printf("%s[%d] Fail-reset!!\n", __func__, __LINE__);
return -1;
debug("%s[%d] Fail-reset!!\n", __func__, __LINE__);
return -EIO; } /* Enumerate at 400KHz */
-- 2.4.3.573.g4eafbef
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Allow read errors to be diagnosed more easily.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/mmc/mmc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 4eab274..da47037 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -250,14 +250,18 @@ static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) return 0; }
- if (mmc_set_blocklen(mmc, mmc->read_bl_len)) + if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { + debug("%s: Failed to set blocklen\n", __func__); return 0; + }
do { cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo; - if(mmc_read_blocks(mmc, dst, start, cur) != cur) + if (mmc_read_blocks(mmc, dst, start, cur) != cur) { + debug("%s: Failed to read blocks\n", __func__); return 0; + } blocks_todo -= cur; start += cur; dst += cur * mmc->read_bl_len;

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Allow read errors to be diagnosed more easily.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/mmc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
Applied to u-boot-dm.

Enable MMC using driver model in SPL for consistency with U-Boot proper.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/spl/spl_mmc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index 51add39..5f1cfbf 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -7,6 +7,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <dm.h> #include <spl.h> #include <linux/compiler.h> #include <asm/u-boot.h> @@ -101,9 +102,18 @@ void spl_mmc_load_image(void) { struct mmc *mmc; u32 boot_mode; - int err; + int err = 0; __maybe_unused int part;
+#ifdef CONFIG_DM_MMC + struct udevice *dev; + + mmc_initialize(NULL); + err = uclass_get_device(UCLASS_MMC, 0, &dev); + mmc = NULL; + if (!err) + mmc = mmc_get_mmc_dev(dev); +#else mmc_initialize(gd->bd);
/* We register only one device. So, the dev id is always 0 */ @@ -114,8 +124,11 @@ void spl_mmc_load_image(void) #endif hang(); } +#endif + + if (!err) + err = mmc_init(mmc);
- err = mmc_init(mmc); if (err) { #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT printf("spl: mmc init failed with error: %d\n", err);

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
Enable MMC using driver model in SPL for consistency with U-Boot proper.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/spl/spl_mmc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
Applied to u-boot-dm.

It took a little while to figure this out, so this patch adds documentation to help the next person who needs to do this.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
include/dwmmc.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/include/dwmmc.h b/include/dwmmc.h index 86a5491..7a7555a 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -129,8 +129,24 @@ /* quirks */ #define DWMCI_QUIRK_DISABLE_SMU (1 << 0)
+/** + * struct dwmci_host - Information about a designware MMC host + * + * @name: Device name + * @ioaddr: Base I/O address of controller + * @quirks: Quick flags - see DWMCI_QUIRK_... + * @caps: Capabilities - see MMC_MODE_... + * @bus_hz: Bus speed in Hz, if @get_mmc_clk() is NULL + * @div: Arbitrary clock divider value for use by controller + * @dev_index: Arbitrary device index for use by controller + * @dev_id: Arbitrary device ID for use by controller + * @buswidth: Bus width in bits (8 or 4) + * @fifoth_val: Value for FIFOTH register (or 0 to leave unset) + * @mmc: Pointer to generic MMC structure for this device + * @priv: Private pointer for use by controller + */ struct dwmci_host { - char *name; + const char *name; void *ioaddr; unsigned int quirks; unsigned int caps;

On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
It took a little while to figure this out, so this patch adds documentation to help the next person who needs to do this.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
include/dwmmc.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
Applied to u-boot-dm.

Some SoCs want to adjust the input clock to the DWMMC block as a way of controlling the MMC bus clock. Update the get_mmc_clk() method to support this.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/exynos_dw_mmc.c | 2 +- include/dwmmc.h | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 8f28d7e..a034c3f 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -248,7 +248,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) * host->bus_hz should be set by user. */ if (host->get_mmc_clk) - sclk = host->get_mmc_clk(host); + sclk = host->get_mmc_clk(host, freq); else if (host->bus_hz) sclk = host->bus_hz; else { diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index e083745..3f702ba 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -39,7 +39,7 @@ static void exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); }
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) +unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; int8_t clk_div; diff --git a/include/dwmmc.h b/include/dwmmc.h index 7a7555a..25cf42c 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -163,7 +163,21 @@ struct dwmci_host {
void (*clksel)(struct dwmci_host *host); void (*board_init)(struct dwmci_host *host); - unsigned int (*get_mmc_clk)(struct dwmci_host *host); + + /** + * Get / set a particular MMC clock frequency + * + * This is used to request the current clock frequency of the clock + * that drives the DWMMC peripheral. The caller will then use this + * information to work out the divider it needs to achieve the + * required MMC bus clock frequency. If you want to handle the + * clock external to DWMMC, use @freq to select the frequency and + * return that value too. Then DWMMC will put itself in bypass mode. + * + * @host: DWMMC host + * @freq: Frequency the host is trying to achieve + */ + unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq);
struct mmc_config cfg; };

Hi,
On Wed, Jun 24, 2015 at 5:38 AM, Simon Glass sjg@chromium.org wrote:
Some SoCs want to adjust the input clock to the DWMMC block as a way of controlling the MMC bus clock. Update the get_mmc_clk() method to support this.
The subject line should probably reflect this is a DWMMC only patch? There are systems that have MMC and don't use the DWMMC controller.
ChenYu
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/exynos_dw_mmc.c | 2 +- include/dwmmc.h | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 8f28d7e..a034c3f 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -248,7 +248,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) * host->bus_hz should be set by user. */ if (host->get_mmc_clk)
sclk = host->get_mmc_clk(host);
sclk = host->get_mmc_clk(host, freq); else if (host->bus_hz) sclk = host->bus_hz; else {
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index e083745..3f702ba 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -39,7 +39,7 @@ static void exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); }
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) +unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; int8_t clk_div; diff --git a/include/dwmmc.h b/include/dwmmc.h index 7a7555a..25cf42c 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -163,7 +163,21 @@ struct dwmci_host {
void (*clksel)(struct dwmci_host *host); void (*board_init)(struct dwmci_host *host);
unsigned int (*get_mmc_clk)(struct dwmci_host *host);
/**
* Get / set a particular MMC clock frequency
*
* This is used to request the current clock frequency of the clock
* that drives the DWMMC peripheral. The caller will then use this
* information to work out the divider it needs to achieve the
* required MMC bus clock frequency. If you want to handle the
* clock external to DWMMC, use @freq to select the frequency and
* return that value too. Then DWMMC will put itself in bypass mode.
*
* @host: DWMMC host
* @freq: Frequency the host is trying to achieve
*/
unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq); struct mmc_config cfg;
};
2.4.3.573.g4eafbef
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Hi, Simon.
On 06/24/2015 06:38 AM, Simon Glass wrote:
Some SoCs want to adjust the input clock to the DWMMC block as a way of controlling the MMC bus clock. Update the get_mmc_clk() method to support this.
I didn't see your other patches. But i don't know this patch's purpose. I think that @freq usage seems like host->bus_hz ("bus_hz" property), doesn't it?
Best Regards, Jaehoon Chung
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/exynos_dw_mmc.c | 2 +- include/dwmmc.h | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 8f28d7e..a034c3f 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -248,7 +248,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) * host->bus_hz should be set by user. */ if (host->get_mmc_clk)
sclk = host->get_mmc_clk(host);
else if (host->bus_hz) sclk = host->bus_hz; else {sclk = host->get_mmc_clk(host, freq);
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index e083745..3f702ba 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -39,7 +39,7 @@ static void exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); }
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) +unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; int8_t clk_div; diff --git a/include/dwmmc.h b/include/dwmmc.h index 7a7555a..25cf42c 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -163,7 +163,21 @@ struct dwmci_host {
void (*clksel)(struct dwmci_host *host); void (*board_init)(struct dwmci_host *host);
- unsigned int (*get_mmc_clk)(struct dwmci_host *host);
/**
* Get / set a particular MMC clock frequency
*
* This is used to request the current clock frequency of the clock
* that drives the DWMMC peripheral. The caller will then use this
* information to work out the divider it needs to achieve the
* required MMC bus clock frequency. If you want to handle the
* clock external to DWMMC, use @freq to select the frequency and
* return that value too. Then DWMMC will put itself in bypass mode.
*
* @host: DWMMC host
* @freq: Frequency the host is trying to achieve
*/
unsigned int (*get_mmc_clk)(struct dwmci_host *host, uint freq);
struct mmc_config cfg;
};

We can calculate this. Add code to do this if it is not provided.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a034c3f..cce2a5d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -355,9 +355,15 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1);
- if (host->fifoth_val) { - dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); + if (!host->fifoth_val) { + uint32_t fifo_size; + + fifo_size = dwmci_readl(host, DWMCI_FIFOTH); + fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1; + host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | + TX_WMARK(fifo_size / 2); } + dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);

Hi, Simon.
On 06/24/2015 06:38 AM, Simon Glass wrote:
We can calculate this. Add code to do this if it is not provided.
Did you consider the kernel side?
Best Regards, Jaehoon Chung
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a034c3f..cce2a5d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -355,9 +355,15 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1);
- if (host->fifoth_val) {
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
if (!host->fifoth_val) {
uint32_t fifo_size;
fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
TX_WMARK(fifo_size / 2);
}
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);

Hi Jaehoon,
On 24 June 2015 at 19:58, Jaehoon Chung jh80.chung@gmail.com wrote:
Hi, Simon.
On 06/24/2015 06:38 AM, Simon Glass wrote:
We can calculate this. Add code to do this if it is not provided.
Did you consider the kernel side?
Can you please be more specific?
Regards, Simon
Best Regards, Jaehoon Chung
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a034c3f..cce2a5d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -355,9 +355,15 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1);
if (host->fifoth_val) {
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
if (!host->fifoth_val) {
uint32_t fifo_size;
fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
TX_WMARK(fifo_size / 2); }
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);

Hi, Simon.
On 06/26/2015 04:26 AM, Simon Glass wrote:
Hi Jaehoon,
On 24 June 2015 at 19:58, Jaehoon Chung jh80.chung@gmail.com wrote:
Hi, Simon.
On 06/24/2015 06:38 AM, Simon Glass wrote:
We can calculate this. Add code to do this if it is not provided.
Did you consider the kernel side?
Can you please be more specific?
I didn't check now for fifoth value. But as i know (in my experiment), if it's not defined fifoth_val into kernel dt file, fifoth value is calculated with value of reading register.
Well, i think your patch is right. (It needs to calculate the exactly fifoth_val.)
The below case should be problem.
1. Calculate and set the fifoth value at bootloader. fifoth register = 0x203f0040 2. If fifoth_val doesn't set into dt-file(kernel), on kernel side re-calculate fifoth value. Should be fifo_size = 3f, since fifoth register's value is 0x203f0040. and fifoth register should be set to 0x201e001f.(just example.)
So i will try to find more generic solution for this problem. If i missed something, let me know, plz. :)
Actually, this case is complicated case.
Best Regards, Jaehoon Chung
Regards,1 Simon
Best Regards, Jaehoon Chung
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/mmc/dw_mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a034c3f..cce2a5d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -355,9 +355,15 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1);
if (host->fifoth_val) {
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val);
if (!host->fifoth_val) {
uint32_t fifo_size;
fifo_size = dwmci_readl(host, DWMCI_FIFOTH);
fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1;
host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) |
TX_WMARK(fifo_size / 2); }
dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); dwmci_writel(host, DWMCI_CLKENA, 0); dwmci_writel(host, DWMCI_CLKSRC, 0);

Add a uclass which permits pin multiplexing to be configured for a particular function. It uses the concept of a peripheral ID to specify the peripheral to adjust. Typically peripheral IDs are SPI0, SPI1, MMC0, etc.
The uclass provides two methods:
- get_periph_id() - returns the peripheral ID for a particular driver model device. This can be used to look up (say) an I2C device, and then find its peripheral ID so that the pins for that device can be requested.
- request() - allows a particular function to be requested. This change may impact multiple pins on the chip. For example, an I2C bus requires two pins (clock and data) and the request() method will set up both pins.
This also allows GPIO drivers which sit under the 'pinctrl' device tree node to be detected and used.
At some point this could be expanded to support a full interface with support for the full device tree bindings. In that case the concept of peripheral ID might not be needed.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/pinctrl/Kconfig | 19 +++++++++++ drivers/pinctrl/Makefile | 8 +++++ drivers/pinctrl/pinctrl-uclass.c | 51 +++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/pinctrl.h | 74 ++++++++++++++++++++++++++++++++++++++++ scripts/Makefile.spl | 1 + 8 files changed, 157 insertions(+) create mode 100644 drivers/pinctrl/Kconfig create mode 100644 drivers/pinctrl/Makefile create mode 100644 drivers/pinctrl/pinctrl-uclass.c create mode 100644 include/pinctrl.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index ee942e2..17a61e4 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -32,6 +32,8 @@ source "drivers/spi/Kconfig"
source "drivers/gpio/Kconfig"
+source "drivers/pinctrl/Kconfig" + source "drivers/power/Kconfig"
source "drivers/hwmon/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index c090aba..b68c4ee 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_LED) += led/ obj-y += misc/ obj-y += pcmcia/ obj-y += dfu/ +obj-$(CONFIG_PINCTRL) += pinctrl/ obj-y += rtc/ obj-y += sound/ obj-y += tpm/ diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig new file mode 100644 index 0000000..e667361 --- /dev/null +++ b/drivers/pinctrl/Kconfig @@ -0,0 +1,19 @@ +config PINCTRL + bool "Pin multiplexing driver support" + help + Support pin multiplexing control in U-Boot. Most SoCs have their own + own multiplexing arrangement where a single pin can be used for + several functions. An SoC pinctrl driver allows the required + function to be selected for each pin. The driver is typically + controlled by the device tree. + +config SPL_PINCTRL_SUPPORT + bool "Enable pin multiplexing (pinctrl) support in SPL" + depends on PINCTRL + help + The pinctrl subsystem can add a substantial overhead to the SPL + image since it typically requires quite a few tables either in the + driver or in the device tree. If this is acceptable and you need + to adjust pin multiplexing in SPL in order to boot into U-Boot, + enable this option. You will need to enable device tree in SPL + for this to work. diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile new file mode 100644 index 0000000..6e63ee7 --- /dev/null +++ b/drivers/pinctrl/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# Written by Simon Glass sjg@chromium.org +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_PINCTRL) += pinctrl-uclass.o diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c new file mode 100644 index 0000000..82bd980 --- /dev/null +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <pinctrl.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +int pinctrl_request(struct udevice *dev, int func, int flags) +{ + struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(dev, func, flags); +} + +int pinctrl_request_noflags(struct udevice *dev, int func) +{ + return pinctrl_request(dev, func, 0); +} + +int pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph) +{ + struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (!ops->get_periph_id) + return -ENOSYS; + + return ops->get_periph_id(dev, periph); +} + +static int pinctrl_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +UCLASS_DRIVER(pinctrl) = { + .id = UCLASS_PINCTRL, + .name = "pinctrl", + .post_bind = pinctrl_post_bind, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index cba7c0a..14a1614 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -41,6 +41,7 @@ enum uclass_id { UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ UCLASS_PCI_GENERIC, /* Generic PCI bus device */ + UCLASS_PINCTRL, /* Pin multiplexing control */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_RTC, /* Real time clock device */ diff --git a/include/pinctrl.h b/include/pinctrl.h new file mode 100644 index 0000000..e7aca2e --- /dev/null +++ b/include/pinctrl.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __PINCTRL_H +#define __PINCTRL_H + +struct pinctrl_ops { + /** + * request() - Request a particular pinctrl function + * + * This activates the selected function. + * + * @dev: Device to adjust (UCLASS_PINCTRL) + * @func: Function number (driver-specific) + * @return 0 if OK, -ve on error + */ + int (*request)(struct udevice *dev, int func, int flags); + + /** + * get_periph_id() - get the peripheral ID for a device + * + * This generally looks at the peripheral's device tree node to work + * out the peripheral ID. The return value is normally interpreted as + * enum periph_id. so long as this is defined by the platform (which it + * should be). + * + * @dev: Pinctrl device to use for decoding + * @periph: Device to check + * @return peripheral ID of @periph, or -ENOENT on error + */ + int (*get_periph_id)(struct udevice *dev, struct udevice *periph); +}; + +#define pinctrl_get_ops(dev) ((struct pinctrl_ops *)(dev)->driver->ops) + +/** + * pinctrl_request() - Request a particular pinctrl function + * + * @dev: Device to check (UCLASS_PINCTRL) + * @func: Function number (driver-specific) + * @flags: Flags (driver-specific) + * @return 0 if OK, -ve on error + */ +int pinctrl_request(struct udevice *dev, int func, int flags); + +/** + * pinctrl_request_noflags() - Request a particular pinctrl function + * + * This is similar to pinctrl_request() but uses 0 for @flags. + * + * @dev: Device to check (UCLASS_PINCTRL) + * @func: Function number (driver-specific) + * @return 0 if OK, -ve on error + */ +int pinctrl_request_noflags(struct udevice *dev, int func); + +/** + * pinctrl_get_periph_id() - get the peripheral ID for a device + * + * This generally looks at the peripheral's device tree node to work out the + * peripheral ID. The return value is normally interpreted as enum periph_id. + * so long as this is defined by the platform (which it should be). + * + * @dev: Pinctrl device to use for decoding + * @periph: Device to check + * @return peripheral ID of @periph, or -ENOENT on error + */ +int pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph); + +#endif diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 6b32618..24ca58b 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -77,6 +77,7 @@ libs-$(CONFIG_SPL_NET_SUPPORT) += net/ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/phy/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/net/phy/ +libs-$(CONFIG_SPL_PINCTRL_SUPPORT) += drivers/pinctrl/ libs-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/ libs-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/

This is not user input (i.e. from the command line). It should be possible to get the case correct and avoid the case-insensitive match. This will help avoid sloppy device tree setups.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 812ac13..40b5135 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -55,7 +55,7 @@ int pmic_bind_children(struct udevice *pmic, int offset, child = NULL; for (info = child_info; info->prefix && info->driver; info++) { prefix_len = strlen(info->prefix); - if (strncasecmp(info->prefix, node_name, prefix_len)) + if (strncmp(info->prefix, node_name, prefix_len)) continue;
debug(" - compatible prefix: '%s'\n", info->prefix);

Hello,
On 06/23/2015 11:38 PM, Simon Glass wrote:
This is not user input (i.e. from the command line). It should be possible to get the case correct and avoid the case-insensitive match. This will help avoid sloppy device tree setups.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 812ac13..40b5135 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -55,7 +55,7 @@ int pmic_bind_children(struct udevice *pmic, int offset, child = NULL; for (info = child_info; info->prefix && info->driver; info++) { prefix_len = strlen(info->prefix);
if (strncasecmp(info->prefix, node_name, prefix_len))
if (strncmp(info->prefix, node_name, prefix_len)) continue; debug(" - compatible prefix: '%s'\n", info->prefix);
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello,
On 06/23/2015 11:38 PM, Simon Glass wrote:
This is not user input (i.e. from the command line). It should be possible to get the case correct and avoid the case-insensitive match. This will help avoid sloppy device tree setups.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 812ac13..40b5135 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -55,7 +55,7 @@ int pmic_bind_children(struct udevice *pmic, int offset, child = NULL; for (info = child_info; info->prefix && info->driver; info++) { prefix_len = strlen(info->prefix);
if (strncasecmp(info->prefix, node_name,
prefix_len))
if (strncmp(info->prefix, node_name, prefix_len)) continue; debug(" - compatible prefix: '%s'\n",
info->prefix);
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Thanks for all the testing and review!
Applied to u-boot-dm.

Decide when the regulator is set up whether we want to auto-set the voltage or current. This avoids the complex logic spilling into the processing code.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 12 ++++++++++++ include/power/regulator.h | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 31ffd44..0f1ca77 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -319,6 +319,18 @@ static int regulator_pre_probe(struct udevice *dev) uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, "regulator-boot-on");
+ /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uV != -ENODATA) && + (uc_pdata->max_uV != -ENODATA) && + (uc_pdata->min_uV == uc_pdata->max_uV)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uA != -ENODATA) && + (uc_pdata->max_uA != -ENODATA) && + (uc_pdata->min_uA == uc_pdata->max_uA)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; + return 0; }
diff --git a/include/power/regulator.h b/include/power/regulator.h index 03a2cef..79ce0a4 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -128,6 +128,11 @@ struct dm_regulator_mode { const char *name; };
+enum regulator_flag { + REGULATOR_FLAG_AUTOSET_UV = 1 << 0, + REGULATOR_FLAG_AUTOSET_UA = 1 << 1, +}; + /** * struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and * allocated on each regulator bind. This structure holds an information @@ -143,6 +148,8 @@ struct dm_regulator_mode { * @max_uA* - maximum amperage (micro Amps) * @always_on* - bool type, true or false * @boot_on* - bool type, true or false + * TODO(sjg@chromium.org): Consider putting the above two into @flags + * @flags: - flags value (see REGULATOR_FLAG_...) * @name** - fdt regulator name - should be taken from the device tree * * Note: @@ -162,6 +169,7 @@ struct dm_regulator_uclass_platdata { bool always_on; bool boot_on; const char *name; + int flags; };
/* Regulator device operations */

Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
Decide when the regulator is set up whether we want to auto-set the voltage or current. This avoids the complex logic spilling into the processing code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 12 ++++++++++++ include/power/regulator.h | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 31ffd44..0f1ca77 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -319,6 +319,18 @@ static int regulator_pre_probe(struct udevice *dev) uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, "regulator-boot-on");
- /* Those values are optional (-ENODATA if unset) */
- if ((uc_pdata->min_uV != -ENODATA) &&
(uc_pdata->max_uV != -ENODATA) &&
(uc_pdata->min_uV == uc_pdata->max_uV))
uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV;
- /* Those values are optional (-ENODATA if unset) */
- if ((uc_pdata->min_uA != -ENODATA) &&
(uc_pdata->max_uA != -ENODATA) &&
(uc_pdata->min_uA == uc_pdata->max_uA))
uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA;
- return 0; }
diff --git a/include/power/regulator.h b/include/power/regulator.h index 03a2cef..79ce0a4 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -128,6 +128,11 @@ struct dm_regulator_mode { const char *name; };
+enum regulator_flag {
- REGULATOR_FLAG_AUTOSET_UV = 1 << 0,
- REGULATOR_FLAG_AUTOSET_UA = 1 << 1,
+};
- /**
- struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and
- allocated on each regulator bind. This structure holds an information
@@ -143,6 +148,8 @@ struct dm_regulator_mode {
- @max_uA* - maximum amperage (micro Amps)
- @always_on* - bool type, true or false
- @boot_on* - bool type, true or false
- TODO(sjg@chromium.org): Consider putting the above two into @flags
- @flags: - flags value (see REGULATOR_FLAG_...)
- @name** - fdt regulator name - should be taken from the device tree
- Note:
@@ -162,6 +169,7 @@ struct dm_regulator_uclass_platdata { bool always_on; bool boot_on; const char *name;
int flags; };
/* Regulator device operations */
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
Decide when the regulator is set up whether we want to auto-set the voltage or current. This avoids the complex logic spilling into the processing code.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 12 ++++++++++++ include/power/regulator.h | 8 ++++++++ 2 files changed, 20 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 31ffd44..0f1ca77 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -319,6 +319,18 @@ static int regulator_pre_probe(struct udevice *dev) uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, "regulator-boot-on");
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uV != -ENODATA) &&
(uc_pdata->max_uV != -ENODATA) &&
(uc_pdata->min_uV == uc_pdata->max_uV))
uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV;
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uA != -ENODATA) &&
(uc_pdata->max_uA != -ENODATA) &&
(uc_pdata->min_uA == uc_pdata->max_uA))
uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA;
}return 0;
diff --git a/include/power/regulator.h b/include/power/regulator.h index 03a2cef..79ce0a4 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -128,6 +128,11 @@ struct dm_regulator_mode { const char *name; };
+enum regulator_flag {
REGULATOR_FLAG_AUTOSET_UV = 1 << 0,
REGULATOR_FLAG_AUTOSET_UA = 1 << 1,
+};
- /**
- struct dm_regulator_uclass_platdata - pointed by
dev->uclass_platdata, and
- allocated on each regulator bind. This structure holds an information
@@ -143,6 +148,8 @@ struct dm_regulator_mode {
- @max_uA* - maximum amperage (micro Amps)
- @always_on* - bool type, true or false
- @boot_on* - bool type, true or false
- TODO(sjg@chromium.org): Consider putting the above two into @flags
- @flags: - flags value (see REGULATOR_FLAG_...)
- @name** - fdt regulator name - should be taken from the device
tree
- Note:
@@ -162,6 +169,7 @@ struct dm_regulator_uclass_platdata { bool always_on; bool boot_on; const char *name;
int flags;
};
/* Regulator device operations */
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

The regulator_autoset() function mixes printf() output and PMIC adjustment code. It provides a boolean to control the output. It is better to avoid missing logic and output, and this permits a smaller SPL code size. So split the output into a separate function.
Also rename the function to have a by_name() suffix, since we would like to be able to pass a device when we know it, and thus avoid the name search.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 98 +++++++++++------------------- include/power/regulator.h | 34 ++++++++--- include/power/sandbox_pmic.h | 4 +- test/dm/regulator.c | 2 +- 4 files changed, 63 insertions(+), 75 deletions(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 0f1ca77..687d3b1 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -138,87 +138,57 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp) return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); }
-static int failed(int ret, bool verbose, const char *fmt, ...) +int regulator_autoset(struct udevice *dev) { - va_list args; - char buf[64]; - - if (verbose == false) - return ret; + struct dm_regulator_uclass_platdata *uc_pdata; + int ret = 0;
- va_start(args, fmt); - vscnprintf(buf, sizeof(buf), fmt, args); - va_end(args); + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata->always_on && !uc_pdata->boot_on) + return -EMEDIUMTYPE;
- printf(buf); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + ret = regulator_set_value(dev, uc_pdata->min_uV); + if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) + ret = regulator_set_current(dev, uc_pdata->min_uA);
if (!ret) - return 0; - - printf(" (ret: %d)", ret); + ret = regulator_set_enable(dev, true);
return ret; }
-int regulator_autoset(const char *platname, - struct udevice **devp, - bool verbose) +static void regulator_show(struct udevice *dev, int ret) { struct dm_regulator_uclass_platdata *uc_pdata; - struct udevice *dev; - int ret; - - if (devp) - *devp = NULL; - - ret = regulator_get_by_platname(platname, &dev); - if (ret) { - error("Can get the regulator: %s!", platname); - return ret; - }
uc_pdata = dev_get_uclass_platdata(dev); - if (!uc_pdata) { - error("Can get the regulator %s uclass platdata!", platname); - return -ENXIO; - } - - if (!uc_pdata->always_on && !uc_pdata->boot_on) - goto retdev;
- if (verbose) - printf("%s@%s: ", dev->name, uc_pdata->name); - - /* Those values are optional (-ENODATA if unset) */ - if ((uc_pdata->min_uV != -ENODATA) && - (uc_pdata->max_uV != -ENODATA) && - (uc_pdata->min_uV == uc_pdata->max_uV)) { - ret = regulator_set_value(dev, uc_pdata->min_uV); - if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV)) - goto exit; - } - - /* Those values are optional (-ENODATA if unset) */ - if ((uc_pdata->min_uA != -ENODATA) && - (uc_pdata->max_uA != -ENODATA) && - (uc_pdata->min_uA == uc_pdata->max_uA)) { - ret = regulator_set_current(dev, uc_pdata->min_uA); - if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA)) - goto exit; - } + printf("%s@%s: ", dev->name, uc_pdata->name); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + printf("set %d uV", uc_pdata->min_uV); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) + printf("; set %d uA", uc_pdata->min_uA); + printf("; enabling"); + if (ret) + printf(" (ret: %d)\n", ret); + printf("\n"); +}
- ret = regulator_set_enable(dev, true); - if (failed(ret, verbose, "; enabling", uc_pdata->min_uA)) - goto exit; +int regulator_autoset_by_name(const char *platname, struct udevice **devp) +{ + struct udevice *dev; + int ret;
-retdev: + ret = regulator_get_by_platname(platname, &dev); if (devp) *devp = dev; -exit: - if (verbose) - printf("\n"); + if (ret) { + debug("Can get the regulator: %s!", platname); + return ret; + }
- return ret; + return regulator_autoset(dev); }
int regulator_list_autoset(const char *list_platname[], @@ -229,7 +199,9 @@ int regulator_list_autoset(const char *list_platname[], int error = 0, i = 0, ret;
while (list_platname[i]) { - ret = regulator_autoset(list_platname[i], &dev, verbose); + ret = regulator_autoset_by_name(list_platname[i], &dev); + if (ret != -EMEDIUMTYPE && verbose) + regulator_show(dev, ret); if (ret & !error) error = ret;
diff --git a/include/power/regulator.h b/include/power/regulator.h index 79ce0a4..86e9c3b 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,9 +316,28 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/** - * regulator_autoset: setup the regulator given by its uclass's platform data - * name field. The setup depends on constraints found in device's uclass's - * platform data (struct dm_regulator_uclass_platdata): + * regulator_autoset: setup the the voltage/current on a regulator + * + * The setup depends on constraints found in device's uclass's platform data + * (struct dm_regulator_uclass_platdata): + * + * - Enable - will set - if any of: 'always_on' or 'boot_on' is set to true, + * or if both are unset, then the function returns + * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal + * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal + * + * The function returns on the first-encountered error. + * + * @platname - expected string for dm_regulator_uclass_platdata .name field + * @devp - returned pointer to the regulator device - if non-NULL passed + * @return: 0 on success or negative value of errno. + */ +int regulator_autoset(struct udevice *dev); + +/** + * regulator_autoset_by_name: setup the regulator given by its uclass's + * platform data name field. The setup depends on constraints found in device's + * uclass's platform data (struct dm_regulator_uclass_platdata): * - Enable - will set - if any of: 'always_on' or 'boot_on' is set to true, * or if both are unset, then the function returns * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal @@ -328,21 +347,18 @@ int regulator_set_mode(struct udevice *dev, int mode_id); * * @platname - expected string for dm_regulator_uclass_platdata .name field * @devp - returned pointer to the regulator device - if non-NULL passed - * @verbose - (true/false) print regulator setup info, or be quiet * @return: 0 on success or negative value of errno. * * The returned 'regulator' device can be used with: * - regulator_get/set_* */ -int regulator_autoset(const char *platname, - struct udevice **devp, - bool verbose); +int regulator_autoset_by_name(const char *platname, struct udevice **devp);
/** * regulator_list_autoset: setup the regulators given by list of their uclass's * platform data name field. The setup depends on constraints found in device's * uclass's platform data. The function loops with calls to: - * regulator_autoset() for each name from the list. + * regulator_autoset_by_name() for each name from the list. * * @list_platname - an array of expected strings for .name field of each * regulator's uclass platdata @@ -383,7 +399,7 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp); * Search by name, found in regulator uclass platdata. * * @platname - expected string for uc_pdata->name of regulator uclass platdata - * @devp - returned pointer to the regulator device + * @devp - returns pointer to the regulator device or NULL on error * @return 0 on success or negative value of errno. * * The returned 'regulator' device is probed and can be used with: diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h index ae14292..8547674 100644 --- a/include/power/sandbox_pmic.h +++ b/include/power/sandbox_pmic.h @@ -117,11 +117,11 @@ enum {
/* * Expected regulators setup after call of: - * - regulator_autoset() + * - regulator_autoset_by_name() * - regulator_list_autoset() */
-/* BUCK1: for testing regulator_autoset() */ +/* BUCK1: for testing regulator_autoset_by_name() */ #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UV 1200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UA 200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE true diff --git a/test/dm/regulator.c b/test/dm/regulator.c index d279c04..3d0056f 100644 --- a/test/dm/regulator.c +++ b/test/dm/regulator.c @@ -210,7 +210,7 @@ static int dm_test_power_regulator_autoset(struct unit_test_state *uts) * Expected output state: uV=1200000; uA=200000; output enabled */ platname = regulator_names[BUCK1][PLATNAME]; - ut_assertok(regulator_autoset(platname, &dev_autoset, false)); + ut_assertok(regulator_autoset_by_name(platname, &dev_autoset));
/* Check, that the returned device is proper */ ut_assertok(regulator_get_by_platname(platname, &dev));

Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
The regulator_autoset() function mixes printf() output and PMIC adjustment code. It provides a boolean to control the output. It is better to avoid missing logic and output, and this permits a smaller SPL code size. So split the output into a separate function.
Also rename the function to have a by_name() suffix, since we would like to be able to pass a device when we know it, and thus avoid the name search.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 98 +++++++++++------------------- include/power/regulator.h | 34 ++++++++--- include/power/sandbox_pmic.h | 4 +- test/dm/regulator.c | 2 +- 4 files changed, 63 insertions(+), 75 deletions(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 0f1ca77..687d3b1 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -138,87 +138,57 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp) return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); }
-static int failed(int ret, bool verbose, const char *fmt, ...) +int regulator_autoset(struct udevice *dev) {
- va_list args;
- char buf[64];
- if (verbose == false)
return ret;
- struct dm_regulator_uclass_platdata *uc_pdata;
- int ret = 0;
- va_start(args, fmt);
- vscnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
- uc_pdata = dev_get_uclass_platdata(dev);
- if (!uc_pdata->always_on && !uc_pdata->boot_on)
return -EMEDIUMTYPE;
- printf(buf);
if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV)
ret = regulator_set_value(dev, uc_pdata->min_uV);
if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA))
ret = regulator_set_current(dev, uc_pdata->min_uA);
if (!ret)
return 0;
- printf(" (ret: %d)", ret);
ret = regulator_set_enable(dev, true);
return ret; }
-int regulator_autoset(const char *platname,
struct udevice **devp,
bool verbose)
+static void regulator_show(struct udevice *dev, int ret) { struct dm_regulator_uclass_platdata *uc_pdata;
struct udevice *dev;
int ret;
if (devp)
*devp = NULL;
ret = regulator_get_by_platname(platname, &dev);
if (ret) {
error("Can get the regulator: %s!", platname);
return ret;
}
uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata) {
error("Can get the regulator %s uclass platdata!", platname);
return -ENXIO;
}
if (!uc_pdata->always_on && !uc_pdata->boot_on)
goto retdev;
if (verbose)
printf("%s@%s: ", dev->name, uc_pdata->name);
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uV != -ENODATA) &&
(uc_pdata->max_uV != -ENODATA) &&
(uc_pdata->min_uV == uc_pdata->max_uV)) {
ret = regulator_set_value(dev, uc_pdata->min_uV);
if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV))
goto exit;
}
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uA != -ENODATA) &&
(uc_pdata->max_uA != -ENODATA) &&
(uc_pdata->min_uA == uc_pdata->max_uA)) {
ret = regulator_set_current(dev, uc_pdata->min_uA);
if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA))
goto exit;
}
- printf("%s@%s: ", dev->name, uc_pdata->name);
- if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV)
printf("set %d uV", uc_pdata->min_uV);
- if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)
printf("; set %d uA", uc_pdata->min_uA);
- printf("; enabling");
- if (ret)
printf(" (ret: %d)\n", ret);
- printf("\n");
+}
- ret = regulator_set_enable(dev, true);
- if (failed(ret, verbose, "; enabling", uc_pdata->min_uA))
goto exit;
+int regulator_autoset_by_name(const char *platname, struct udevice **devp) +{
- struct udevice *dev;
- int ret;
-retdev:
- ret = regulator_get_by_platname(platname, &dev); if (devp) *devp = dev;
-exit:
- if (verbose)
printf("\n");
- if (ret) {
debug("Can get the regulator: %s!", platname);
return ret;
- }
- return ret;
return regulator_autoset(dev); }
int regulator_list_autoset(const char *list_platname[],
@@ -229,7 +199,9 @@ int regulator_list_autoset(const char *list_platname[], int error = 0, i = 0, ret;
while (list_platname[i]) {
ret = regulator_autoset(list_platname[i], &dev, verbose);
ret = regulator_autoset_by_name(list_platname[i], &dev);
if (ret != -EMEDIUMTYPE && verbose)
if (ret & !error) error = ret;regulator_show(dev, ret);
diff --git a/include/power/regulator.h b/include/power/regulator.h index 79ce0a4..86e9c3b 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,9 +316,28 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/**
- regulator_autoset: setup the regulator given by its uclass's platform data
- name field. The setup depends on constraints found in device's uclass's
- platform data (struct dm_regulator_uclass_platdata):
- regulator_autoset: setup the the voltage/current on a regulator
duplicated "the"
- The setup depends on constraints found in device's uclass's platform data
- (struct dm_regulator_uclass_platdata):
- Enable - will set - if any of: 'always_on' or 'boot_on' is set to true,
- or if both are unset, then the function returns
- Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
- Current limit - will set - if '.min_uA' and '.max_uA' values are equal
- The function returns on the first-encountered error.
- @platname - expected string for dm_regulator_uclass_platdata .name field
- @devp - returned pointer to the regulator device - if non-NULL passed
- @return: 0 on success or negative value of errno.
- */
+int regulator_autoset(struct udevice *dev);
+/**
- regulator_autoset_by_name: setup the regulator given by its uclass's
- platform data name field. The setup depends on constraints found in device's
- uclass's platform data (struct dm_regulator_uclass_platdata):
- Enable - will set - if any of: 'always_on' or 'boot_on' is set to true,
- or if both are unset, then the function returns
- Voltage value - will set - if '.min_uV' and '.max_uV' values are equal
@@ -328,21 +347,18 @@ int regulator_set_mode(struct udevice *dev, int mode_id);
- @platname - expected string for dm_regulator_uclass_platdata .name field
- @devp - returned pointer to the regulator device - if non-NULL passed
*/
- @verbose - (true/false) print regulator setup info, or be quiet
- @return: 0 on success or negative value of errno.
- The returned 'regulator' device can be used with:
- regulator_get/set_*
-int regulator_autoset(const char *platname,
struct udevice **devp,
bool verbose);
+int regulator_autoset_by_name(const char *platname, struct udevice **devp);
/**
- regulator_list_autoset: setup the regulators given by list of their uclass's
- platform data name field. The setup depends on constraints found in device's
- uclass's platform data. The function loops with calls to:
- regulator_autoset() for each name from the list.
- regulator_autoset_by_name() for each name from the list.
- @list_platname - an array of expected strings for .name field of each
regulator's uclass platdata
@@ -383,7 +399,7 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp);
- Search by name, found in regulator uclass platdata.
- @platname - expected string for uc_pdata->name of regulator uclass platdata
- @devp - returned pointer to the regulator device
- @devp - returns pointer to the regulator device or NULL on error
- @return 0 on success or negative value of errno.
- The returned 'regulator' device is probed and can be used with:
diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h index ae14292..8547674 100644 --- a/include/power/sandbox_pmic.h +++ b/include/power/sandbox_pmic.h @@ -117,11 +117,11 @@ enum {
/*
- Expected regulators setup after call of:
- regulator_autoset()
*/
- regulator_autoset_by_name()
- regulator_list_autoset()
-/* BUCK1: for testing regulator_autoset() */ +/* BUCK1: for testing regulator_autoset_by_name() */ #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UV 1200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UA 200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE true diff --git a/test/dm/regulator.c b/test/dm/regulator.c index d279c04..3d0056f 100644 --- a/test/dm/regulator.c +++ b/test/dm/regulator.c @@ -210,7 +210,7 @@ static int dm_test_power_regulator_autoset(struct unit_test_state *uts) * Expected output state: uV=1200000; uA=200000; output enabled */ platname = regulator_names[BUCK1][PLATNAME];
- ut_assertok(regulator_autoset(platname, &dev_autoset, false));
ut_assertok(regulator_autoset_by_name(platname, &dev_autoset));
/* Check, that the returned device is proper */ ut_assertok(regulator_get_by_platname(platname, &dev));
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
The regulator_autoset() function mixes printf() output and PMIC adjustment code. It provides a boolean to control the output. It is better to avoid missing logic and output, and this permits a smaller SPL code size. So split the output into a separate function.
Also rename the function to have a by_name() suffix, since we would like to be able to pass a device when we know it, and thus avoid the name search.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 98 +++++++++++------------------- include/power/regulator.h | 34 ++++++++--- include/power/sandbox_pmic.h | 4 +- test/dm/regulator.c | 2 +- 4 files changed, 63 insertions(+), 75 deletions(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 0f1ca77..687d3b1 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -138,87 +138,57 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp) return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); }
-static int failed(int ret, bool verbose, const char *fmt, ...) +int regulator_autoset(struct udevice *dev) {
va_list args;
char buf[64];
if (verbose == false)
return ret;
struct dm_regulator_uclass_platdata *uc_pdata;
int ret = 0;
va_start(args, fmt);
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata->always_on && !uc_pdata->boot_on)
return -EMEDIUMTYPE;
printf(buf);
if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV)
ret = regulator_set_value(dev, uc_pdata->min_uV);
if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA))
ret = regulator_set_current(dev, uc_pdata->min_uA); if (!ret)
return 0;
printf(" (ret: %d)", ret);
ret = regulator_set_enable(dev, true); return ret;
}
-int regulator_autoset(const char *platname,
struct udevice **devp,
bool verbose)
+static void regulator_show(struct udevice *dev, int ret) { struct dm_regulator_uclass_platdata *uc_pdata;
struct udevice *dev;
int ret;
if (devp)
*devp = NULL;
ret = regulator_get_by_platname(platname, &dev);
if (ret) {
error("Can get the regulator: %s!", platname);
return ret;
} uc_pdata = dev_get_uclass_platdata(dev);
if (!uc_pdata) {
error("Can get the regulator %s uclass platdata!",
platname);
return -ENXIO;
}
if (!uc_pdata->always_on && !uc_pdata->boot_on)
goto retdev;
if (verbose)
printf("%s@%s: ", dev->name, uc_pdata->name);
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uV != -ENODATA) &&
(uc_pdata->max_uV != -ENODATA) &&
(uc_pdata->min_uV == uc_pdata->max_uV)) {
ret = regulator_set_value(dev, uc_pdata->min_uV);
if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV))
goto exit;
}
/* Those values are optional (-ENODATA if unset) */
if ((uc_pdata->min_uA != -ENODATA) &&
(uc_pdata->max_uA != -ENODATA) &&
(uc_pdata->min_uA == uc_pdata->max_uA)) {
ret = regulator_set_current(dev, uc_pdata->min_uA);
if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA))
goto exit;
}
printf("%s@%s: ", dev->name, uc_pdata->name);
if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV)
printf("set %d uV", uc_pdata->min_uV);
if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)
printf("; set %d uA", uc_pdata->min_uA);
printf("; enabling");
if (ret)
printf(" (ret: %d)\n", ret);
printf("\n");
+}
ret = regulator_set_enable(dev, true);
if (failed(ret, verbose, "; enabling", uc_pdata->min_uA))
goto exit;
+int regulator_autoset_by_name(const char *platname, struct udevice **devp) +{
struct udevice *dev;
int ret;
-retdev:
ret = regulator_get_by_platname(platname, &dev); if (devp) *devp = dev;
-exit:
if (verbose)
printf("\n");
if (ret) {
debug("Can get the regulator: %s!", platname);
return ret;
}
return ret;
return regulator_autoset(dev);
}
int regulator_list_autoset(const char *list_platname[],
@@ -229,7 +199,9 @@ int regulator_list_autoset(const char *list_platname[], int error = 0, i = 0, ret;
while (list_platname[i]) {
ret = regulator_autoset(list_platname[i], &dev, verbose);
ret = regulator_autoset_by_name(list_platname[i], &dev);
if (ret != -EMEDIUMTYPE && verbose)
regulator_show(dev, ret); if (ret & !error) error = ret;
diff --git a/include/power/regulator.h b/include/power/regulator.h index 79ce0a4..86e9c3b 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,9 +316,28 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/**
- regulator_autoset: setup the regulator given by its uclass's platform
data
- name field. The setup depends on constraints found in device's
uclass's
- platform data (struct dm_regulator_uclass_platdata):
- regulator_autoset: setup the the voltage/current on a regulator
duplicated "the"
- The setup depends on constraints found in device's uclass's platform
data
- (struct dm_regulator_uclass_platdata):
- Enable - will set - if any of: 'always_on' or 'boot_on' is set to
true,
- or if both are unset, then the function returns
- Voltage value - will set - if '.min_uV' and '.max_uV' values are
equal
- Current limit - will set - if '.min_uA' and '.max_uA' values are
equal
- The function returns on the first-encountered error.
- @platname - expected string for dm_regulator_uclass_platdata .name
field
- @devp - returned pointer to the regulator device - if non-NULL
passed
- @return: 0 on success or negative value of errno.
- */
+int regulator_autoset(struct udevice *dev);
+/**
- regulator_autoset_by_name: setup the regulator given by its uclass's
- platform data name field. The setup depends on constraints found in
device's
- uclass's platform data (struct dm_regulator_uclass_platdata):
- Enable - will set - if any of: 'always_on' or 'boot_on' is set to
true,
- or if both are unset, then the function returns
- Voltage value - will set - if '.min_uV' and '.max_uV' values are
equal @@ -328,21 +347,18 @@ int regulator_set_mode(struct udevice *dev, int mode_id);
- @platname - expected string for dm_regulator_uclass_platdata .name
field
- @devp - returned pointer to the regulator device - if non-NULL
passed
*/
- @verbose - (true/false) print regulator setup info, or be quiet
- @return: 0 on success or negative value of errno.
- The returned 'regulator' device can be used with:
- regulator_get/set_*
-int regulator_autoset(const char *platname,
struct udevice **devp,
bool verbose);
+int regulator_autoset_by_name(const char *platname, struct udevice **devp);
/**
- regulator_list_autoset: setup the regulators given by list of their
uclass's
- platform data name field. The setup depends on constraints found in
device's
- uclass's platform data. The function loops with calls to:
- regulator_autoset() for each name from the list.
- regulator_autoset_by_name() for each name from the list.
- @list_platname - an array of expected strings for .name field of each
regulator's uclass platdata
@@ -383,7 +399,7 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp);
- Search by name, found in regulator uclass platdata.
- @platname - expected string for uc_pdata->name of regulator uclass
platdata
- @devp - returned pointer to the regulator device
- @devp - returns pointer to the regulator device or NULL on error
- @return 0 on success or negative value of errno.
- The returned 'regulator' device is probed and can be used with:
diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h index ae14292..8547674 100644 --- a/include/power/sandbox_pmic.h +++ b/include/power/sandbox_pmic.h @@ -117,11 +117,11 @@ enum {
/*
- Expected regulators setup after call of:
- regulator_autoset()
*/
- regulator_autoset_by_name()
- regulator_list_autoset()
-/* BUCK1: for testing regulator_autoset() */ +/* BUCK1: for testing regulator_autoset_by_name() */ #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UV 1200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UA 200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE true diff --git a/test/dm/regulator.c b/test/dm/regulator.c index d279c04..3d0056f 100644 --- a/test/dm/regulator.c +++ b/test/dm/regulator.c @@ -210,7 +210,7 @@ static int dm_test_power_regulator_autoset(struct unit_test_state *uts) * Expected output state: uV=1200000; uA=200000; output enabled */ platname = regulator_names[BUCK1][PLATNAME];
ut_assertok(regulator_autoset(platname, &dev_autoset, false));
ut_assertok(regulator_autoset_by_name(platname, &dev_autoset)); /* Check, that the returned device is proper */ ut_assertok(regulator_get_by_platname(platname, &dev));
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

The device tree provides information about which regulators should be on at boot, or always on. Use this to set them up automatically.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 22 ++++++++++++++++++++++ include/power/regulator.h | 11 +++++++++++ 2 files changed, 33 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 687d3b1..a2d0b9f 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -306,6 +306,28 @@ static int regulator_pre_probe(struct udevice *dev) return 0; }
+int regulators_enable_boot_on(bool verbose) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_REGULATOR, &uc); + if (ret) + return ret; + for (uclass_first_device(UCLASS_REGULATOR, &dev); + dev && !ret; + uclass_next_device(&dev)) { + ret = regulator_autoset(dev); + if (ret == -EMEDIUMTYPE) + continue; + if (verbose) + regulator_show(dev, ret); + } + + return ret; +} + UCLASS_DRIVER(regulator) = { .id = UCLASS_REGULATOR, .name = "regulator", diff --git a/include/power/regulator.h b/include/power/regulator.h index 86e9c3b..0bdb496 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,6 +316,17 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/** + * regulators_enable_boot_on() - enable regulators needed for boot + * + * This enables all regulators which are marked to be on at boot time. This + * only works for regulators which don't have a range for voltage/current, + * since in that case it is not possible to know which value to use. + * + * This effectively caslls regulator_autoset() for every regulator. + */ +int regulators_enable_boot_on(bool verbose); + +/** * regulator_autoset: setup the the voltage/current on a regulator * * The setup depends on constraints found in device's uclass's platform data

Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
The device tree provides information about which regulators should be on at boot, or always on. Use this to set them up automatically.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 22 ++++++++++++++++++++++ include/power/regulator.h | 11 +++++++++++ 2 files changed, 33 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 687d3b1..a2d0b9f 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -306,6 +306,28 @@ static int regulator_pre_probe(struct udevice *dev) return 0; }
+int regulators_enable_boot_on(bool verbose) +{
- struct udevice *dev;
- struct uclass *uc;
- int ret;
- ret = uclass_get(UCLASS_REGULATOR, &uc);
- if (ret)
return ret;
- for (uclass_first_device(UCLASS_REGULATOR, &dev);
dev && !ret;
uclass_next_device(&dev)) {
ret = regulator_autoset(dev);
if (ret == -EMEDIUMTYPE)
continue;
if (verbose)
regulator_show(dev, ret);
- }
- return ret;
+}
- UCLASS_DRIVER(regulator) = { .id = UCLASS_REGULATOR, .name = "regulator",
diff --git a/include/power/regulator.h b/include/power/regulator.h index 86e9c3b..0bdb496 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,6 +316,17 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/**
- regulators_enable_boot_on() - enable regulators needed for boot
- This enables all regulators which are marked to be on at boot time. This
- only works for regulators which don't have a range for voltage/current,
- since in that case it is not possible to know which value to use.
- This effectively caslls regulator_autoset() for every regulator.
"calls"
- */
+int regulators_enable_boot_on(bool verbose);
+/**
- regulator_autoset: setup the the voltage/current on a regulator
- The setup depends on constraints found in device's uclass's platform data
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:38 PM, Simon Glass wrote:
The device tree provides information about which regulators should be on at boot, or always on. Use this to set them up automatically.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 22 ++++++++++++++++++++++ include/power/regulator.h | 11 +++++++++++ 2 files changed, 33 insertions(+)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 687d3b1..a2d0b9f 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -306,6 +306,28 @@ static int regulator_pre_probe(struct udevice *dev) return 0; }
+int regulators_enable_boot_on(bool verbose) +{
struct udevice *dev;
struct uclass *uc;
int ret;
ret = uclass_get(UCLASS_REGULATOR, &uc);
if (ret)
return ret;
for (uclass_first_device(UCLASS_REGULATOR, &dev);
dev && !ret;
uclass_next_device(&dev)) {
ret = regulator_autoset(dev);
if (ret == -EMEDIUMTYPE)
continue;
if (verbose)
regulator_show(dev, ret);
}
return ret;
+}
- UCLASS_DRIVER(regulator) = { .id = UCLASS_REGULATOR, .name = "regulator",
diff --git a/include/power/regulator.h b/include/power/regulator.h index 86e9c3b..0bdb496 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -316,6 +316,17 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id);
/**
- regulators_enable_boot_on() - enable regulators needed for boot
- This enables all regulators which are marked to be on at boot time.
This
- only works for regulators which don't have a range for
voltage/current,
- since in that case it is not possible to know which value to use.
- This effectively caslls regulator_autoset() for every regulator.
"calls"
- */
+int regulators_enable_boot_on(bool verbose);
+/**
- regulator_autoset: setup the the voltage/current on a regulator
- The setup depends on constraints found in device's uclass's platform
data
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

To reduce unnecessary code size in an uncommon code path, use debug() where possible(). The driver returns an error which indicates failure.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index a2d0b9f..12e141b 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -262,7 +262,7 @@ static int regulator_post_bind(struct udevice *dev) if (regulator_name_is_unique(dev, uc_pdata->name)) return 0;
- error(""%s" of dev: "%s", has nonunique value: "%s"", + debug(""%s" of dev: "%s", has nonunique value: "%s"", property, dev->name, uc_pdata->name);
return -EINVAL;

Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
To reduce unnecessary code size in an uncommon code path, use debug() where possible(). The driver returns an error which indicates failure.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index a2d0b9f..12e141b 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -262,7 +262,7 @@ static int regulator_post_bind(struct udevice *dev) if (regulator_name_is_unique(dev, uc_pdata->name)) return 0;
- error(""%s" of dev: "%s", has nonunique value: "%s"",
debug(""%s" of dev: "%s", has nonunique value: "%s"", property, dev->name, uc_pdata->name);
return -EINVAL;
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
To reduce unnecessary code size in an uncommon code path, use debug() where possible(). The driver returns an error which indicates failure.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/regulator/regulator-uclass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index a2d0b9f..12e141b 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -262,7 +262,7 @@ static int regulator_post_bind(struct udevice *dev) if (regulator_name_is_unique(dev, uc_pdata->name)) return 0;
error("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"",
debug("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"", property, dev->name, uc_pdata->name); return -EINVAL;
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

It is a common requirement to update some PMIC registers. Provide some simple convenience functions to do this.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 32 ++++++++++++++++++++++++++++++++ include/power/pmic.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 40b5135..dbab3e3 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -139,6 +139,38 @@ int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len) return ops->write(dev, reg, buffer, len); }
+int pmic_reg_read(struct udevice *dev, uint reg) +{ + u8 byte; + int ret; + + ret = pmic_read(dev, reg, &byte, 1); + debug("%s: reg=%x, value=%x\n", __func__, reg, byte); + + return ret ? ret : byte; +} + +int pmic_reg_write(struct udevice *dev, uint reg, uint value) +{ + u8 byte = value; + + debug("%s: reg=%x, value=%x\n", __func__, reg, value); + return pmic_read(dev, reg, &byte, 1); +} + +int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) +{ + u8 byte; + int ret; + + ret = pmic_reg_read(dev, reg); + if (ret < 0) + return ret; + byte = (ret & ~clr) | set; + + return pmic_reg_write(dev, reg, byte); +} + UCLASS_DRIVER(pmic) = { .id = UCLASS_PMIC, .name = "pmic", diff --git a/include/power/pmic.h b/include/power/pmic.h index eb152ef..6ba4b6e 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -264,6 +264,40 @@ int pmic_reg_count(struct udevice *dev); */ int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len); int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len); + +/** + * pmic_reg_read() - read a PMIC register value + * + * @dev: PMIC device to read + * @reg: Register to read + * @return value read on success or negative value of errno. + */ +int pmic_reg_read(struct udevice *dev, uint reg); + +/** + * pmic_reg_write() - write a PMIC register value + * + * @dev: PMIC device to write + * @reg: Register to write + * @value: Value to write + * @return 0 on success or negative value of errno. + */ +int pmic_reg_write(struct udevice *dev, uint reg, uint value); + +/** + * pmic_clrsetbits() - clear and set bits in a PMIC register + * + * This reads a register, optionally clears some bits, optionally sets some + * bits, then writes the register. + * + * @dev: PMIC device to update + * @reg: Register to update + * @clr: Bit mask to clear (set those bits that you want cleared) + * @set: Bit mask to set (set those bits that you want set) + * @return 0 on success or negative value of errno. + */ +int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set); + #endif /* CONFIG_DM_PMIC */
#ifdef CONFIG_POWER

Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
It is a common requirement to update some PMIC registers. Provide some simple convenience functions to do this.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 32 ++++++++++++++++++++++++++++++++ include/power/pmic.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 40b5135..dbab3e3 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -139,6 +139,38 @@ int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len) return ops->write(dev, reg, buffer, len); }
+int pmic_reg_read(struct udevice *dev, uint reg) +{
- u8 byte;
- int ret;
- ret = pmic_read(dev, reg, &byte, 1);
- debug("%s: reg=%x, value=%x\n", __func__, reg, byte);
- return ret ? ret : byte;
+}
+int pmic_reg_write(struct udevice *dev, uint reg, uint value) +{
- u8 byte = value;
- debug("%s: reg=%x, value=%x\n", __func__, reg, value);
- return pmic_read(dev, reg, &byte, 1);
+}
+int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) +{
- u8 byte;
- int ret;
- ret = pmic_reg_read(dev, reg);
- if (ret < 0)
return ret;
- byte = (ret & ~clr) | set;
- return pmic_reg_write(dev, reg, byte);
+}
- UCLASS_DRIVER(pmic) = { .id = UCLASS_PMIC, .name = "pmic",
diff --git a/include/power/pmic.h b/include/power/pmic.h index eb152ef..6ba4b6e 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -264,6 +264,40 @@ int pmic_reg_count(struct udevice *dev); */ int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len); int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len);
+/**
- pmic_reg_read() - read a PMIC register value
- @dev: PMIC device to read
- @reg: Register to read
- @return value read on success or negative value of errno.
- */
+int pmic_reg_read(struct udevice *dev, uint reg);
+/**
- pmic_reg_write() - write a PMIC register value
- @dev: PMIC device to write
- @reg: Register to write
- @value: Value to write
- @return 0 on success or negative value of errno.
- */
+int pmic_reg_write(struct udevice *dev, uint reg, uint value);
+/**
- pmic_clrsetbits() - clear and set bits in a PMIC register
- This reads a register, optionally clears some bits, optionally sets some
- bits, then writes the register.
- @dev: PMIC device to update
- @reg: Register to update
- @clr: Bit mask to clear (set those bits that you want cleared)
- @set: Bit mask to set (set those bits that you want set)
- @return 0 on success or negative value of errno.
- */
+int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set);
#endif /* CONFIG_DM_PMIC */
#ifdef CONFIG_POWER
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
It is a common requirement to update some PMIC registers. Provide some simple convenience functions to do this.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 32 ++++++++++++++++++++++++++++++++ include/power/pmic.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 40b5135..dbab3e3 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -139,6 +139,38 @@ int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len) return ops->write(dev, reg, buffer, len); }
+int pmic_reg_read(struct udevice *dev, uint reg) +{
u8 byte;
int ret;
ret = pmic_read(dev, reg, &byte, 1);
debug("%s: reg=%x, value=%x\n", __func__, reg, byte);
return ret ? ret : byte;
+}
+int pmic_reg_write(struct udevice *dev, uint reg, uint value) +{
u8 byte = value;
debug("%s: reg=%x, value=%x\n", __func__, reg, value);
return pmic_read(dev, reg, &byte, 1);
+}
+int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) +{
u8 byte;
int ret;
ret = pmic_reg_read(dev, reg);
if (ret < 0)
return ret;
byte = (ret & ~clr) | set;
return pmic_reg_write(dev, reg, byte);
+}
- UCLASS_DRIVER(pmic) = { .id = UCLASS_PMIC, .name = "pmic",
diff --git a/include/power/pmic.h b/include/power/pmic.h index eb152ef..6ba4b6e 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -264,6 +264,40 @@ int pmic_reg_count(struct udevice *dev); */ int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len); int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len);
+/**
- pmic_reg_read() - read a PMIC register value
- @dev: PMIC device to read
- @reg: Register to read
- @return value read on success or negative value of errno.
- */
+int pmic_reg_read(struct udevice *dev, uint reg);
+/**
- pmic_reg_write() - write a PMIC register value
- @dev: PMIC device to write
- @reg: Register to write
- @value: Value to write
- @return 0 on success or negative value of errno.
- */
+int pmic_reg_write(struct udevice *dev, uint reg, uint value);
+/**
- pmic_clrsetbits() - clear and set bits in a PMIC register
- This reads a register, optionally clears some bits, optionally sets
some
- bits, then writes the register.
- @dev: PMIC device to update
- @reg: Register to update
- @clr: Bit mask to clear (set those bits that you want cleared)
- @set: Bit mask to set (set those bits that you want set)
- @return 0 on success or negative value of errno.
- */
+int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set);
#endif /* CONFIG_DM_PMIC */
#ifdef CONFIG_POWER
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

This functionality may be useful for setting up regulators early during boot.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
scripts/Makefile.spl | 1 + 1 file changed, 1 insertion(+)
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 24ca58b..1e58be9 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -67,6 +67,7 @@ libs-y += fs/ libs-$(CONFIG_SPL_LED_SUPPORT) += drivers/led/ libs-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/ libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/ drivers/power/pmic/ +libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/regulator/ libs-$(CONFIG_SPL_MTD_SUPPORT) += drivers/mtd/ libs-$(CONFIG_SPL_NAND_SUPPORT) += drivers/mtd/nand/ libs-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += drivers/misc/

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
his functionality may be useful for setting up regulators early during boot.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
scripts/Makefile.spl | 1 + 1 file changed, 1 insertion(+)
Applied to u-boot-dm.

This bloats the code size quite a bit and is less useful in SPL where there is no command line.
Avoid including this code in SPL.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
lib/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/Makefile b/lib/Makefile index ca72187..1139f9b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_USB_TTY) += circbuf.o obj-y += crc7.o obj-y += crc8.o obj-y += crc16.o +obj-$(CONFIG_ERRNO_STR) += errno_str.o obj-$(CONFIG_FIT) += fdtdec_common.o obj-$(CONFIG_OF_CONTROL) += fdtdec_common.o obj-$(CONFIG_OF_CONTROL) += fdtdec.o @@ -59,7 +60,6 @@ endif obj-$(CONFIG_ADDR_MAP) += addr_map.o obj-y += hashtable.o obj-y += errno.o -obj-$(CONFIG_ERRNO_STR) += errno_str.o obj-y += display_options.o obj-$(CONFIG_BCH) += bch.o obj-y += crc32.o

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
This bloats the code size quite a bit and is less useful in SPL where there is no command line.
Avoid including this code in SPL.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
lib/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Applied to u-boot-dm.

Add support for a driver which sets up DRAM and can return information about the amount of RAM available. This is a first step towards moving RAM init to driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/ram/Kconfig | 18 ++++++++++++++++++ drivers/ram/Makefile | 7 +++++++ drivers/ram/ram-uclass.c | 28 ++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/ram.h | 38 ++++++++++++++++++++++++++++++++++++++ scripts/Makefile.spl | 1 + 8 files changed, 96 insertions(+) create mode 100644 drivers/ram/Kconfig create mode 100644 drivers/ram/Makefile create mode 100644 drivers/ram/ram-uclass.c create mode 100644 include/ram.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index 17a61e4..7c9eefc 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -36,6 +36,8 @@ source "drivers/pinctrl/Kconfig"
source "drivers/power/Kconfig"
+source "drivers/ram/Kconfig" + source "drivers/hwmon/Kconfig"
source "drivers/watchdog/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index b68c4ee..39b7919 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -12,6 +12,7 @@ obj-y += misc/ obj-y += pcmcia/ obj-y += dfu/ obj-$(CONFIG_PINCTRL) += pinctrl/ +obj-$(CONFIG_RAM) += ram/ obj-y += rtc/ obj-y += sound/ obj-y += tpm/ diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig new file mode 100644 index 0000000..642a2d8 --- /dev/null +++ b/drivers/ram/Kconfig @@ -0,0 +1,18 @@ +config RAM + bool "Enable RAM drivers using Driver Model" + depends on DM + help + This allows drivers to be provided for SDRAM and other RAM + controllers and their type to be specified in the board's device + tree. Generally some parameters are required to set up the RAM and + the RAM size can either be statically defined or dynamically + detected. + +config SPL_RAM_SUPPORT + bool "Enable RAM support in SPL" + depends on RAM + help + The RAM subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use RAM drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up RAM (e.g. SDRAM / DDR) within SPL. diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile new file mode 100644 index 0000000..4494d81 --- /dev/null +++ b/drivers/ram/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2015 Google, Inc +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_RAM) += ram-uclass.o diff --git a/drivers/ram/ram-uclass.c b/drivers/ram/ram-uclass.c new file mode 100644 index 0000000..2f1fbe7 --- /dev/null +++ b/drivers/ram/ram-uclass.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <ram.h> +#include <dm.h> +#include <errno.h> +#include <dm/lists.h> +#include <dm/root.h> + +int ram_get_info(struct udevice *dev, struct ram_info *info) +{ + struct ram_ops *ops = ram_get_ops(dev); + + if (!ops->get_info) + return -ENOSYS; + + return ops->get_info(dev, info); +} + +UCLASS_DRIVER(ram) = { + .id = UCLASS_RAM, + .name = "ram", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 14a1614..5bb602f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -28,6 +28,7 @@ enum uclass_id { UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY_PORT, /* Display port video */ + UCLASS_RAM, /* RAM controller */ UCLASS_ETH, /* Ethernet device */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ UCLASS_I2C, /* I2C bus */ diff --git a/include/ram.h b/include/ram.h new file mode 100644 index 0000000..e2172a8 --- /dev/null +++ b/include/ram.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RAM_H +#define __RAM_H + +struct ram_info { + phys_addr_t base; + size_t size; +}; + +struct ram_ops { + /** + * get_info() - Get basic memory info + * + * @dev: Device to check (UCLASS_RAM) + * @info: Place to put info + * @return 0 if OK, -ve on error + */ + int (*get_info)(struct udevice *dev, struct ram_info *info); +}; + +#define ram_get_ops(dev) ((struct ram_ops *)(dev)->driver->ops) + +/** + * ram_get_info() - Get information about a RAM device + * + * @dev: Device to check (UCLASS_RAM) + * @info: Returns RAM info + * @return 0 if OK, -ve on error + */ +int ram_get_info(struct udevice *dev, struct ram_info *info); + +#endif diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index 1e58be9..c4e83bb 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -79,6 +79,7 @@ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/phy/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/net/phy/ libs-$(CONFIG_SPL_PINCTRL_SUPPORT) += drivers/pinctrl/ +libs-$(CONFIG_SPL_RAM_SUPPORT) += drivers/ram/ libs-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/ libs-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Add support for a driver which sets up DRAM and can return information about the amount of RAM available. This is a first step towards moving RAM init to driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/ram/Kconfig | 18 ++++++++++++++++++ drivers/ram/Makefile | 7 +++++++ drivers/ram/ram-uclass.c | 28 ++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/ram.h | 38 ++++++++++++++++++++++++++++++++++++++ scripts/Makefile.spl | 1 + 8 files changed, 96 insertions(+) create mode 100644 drivers/ram/Kconfig create mode 100644 drivers/ram/Makefile create mode 100644 drivers/ram/ram-uclass.c create mode 100644 include/ram.h
Applied to u-boot-dm.

Several functions in this file should be marked as static. Update them.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/spi/spi-uclass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 737ae64..d666272 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -95,13 +95,13 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags); }
-int spi_post_bind(struct udevice *dev) +static int spi_post_bind(struct udevice *dev) { /* Scan the bus for devices */ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); }
-int spi_child_post_bind(struct udevice *dev) +static int spi_child_post_bind(struct udevice *dev) { struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
@@ -111,7 +111,7 @@ int spi_child_post_bind(struct udevice *dev) return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); }
-int spi_post_probe(struct udevice *bus) +static int spi_post_probe(struct udevice *bus) { struct dm_spi_bus *spi = dev_get_uclass_priv(bus);
@@ -121,7 +121,7 @@ int spi_post_probe(struct udevice *bus) return 0; }
-int spi_child_pre_probe(struct udevice *dev) +static int spi_child_pre_probe(struct udevice *dev) { struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_slave *slave = dev_get_parentdata(dev);

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Several functions in this file should be marked as static. Update them.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/spi/spi-uclass.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
Applied to u-boot-dm.

Since Rockchip requires 32-bit serial access, add this to the driver. Refactor a little to make this easier.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/serial/ns16550.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 9b044a3..c8a77e2 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -246,6 +246,17 @@ int NS16550_tstc(NS16550_t com_port)
#include <debug_uart.h>
+#define serial_dout(reg, value) \ + serial_out_shift((char *)com_port + \ + ((char *)reg - (char *)com_port) * \ + (1 << CONFIG_DEBUG_UART_SHIFT), \ + CONFIG_DEBUG_UART_SHIFT, value) +#define serial_din(reg) \ + serial_in_shift((char *)com_port + \ + ((char *)reg - (char *)com_port) * \ + (1 << CONFIG_DEBUG_UART_SHIFT), \ + CONFIG_DEBUG_UART_SHIFT) + void debug_uart_init(void) { struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; @@ -259,28 +270,23 @@ void debug_uart_init(void) */ baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); - serial_out_shift(&com_port->ier, CONFIG_DEBUG_UART_SHIFT, - CONFIG_SYS_NS16550_IER); - serial_out_shift(&com_port->mcr, CONFIG_DEBUG_UART_SHIFT, UART_MCRVAL); - serial_out_shift(&com_port->fcr, CONFIG_DEBUG_UART_SHIFT, UART_FCRVAL); - - serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT, - UART_LCR_BKSE | UART_LCRVAL); - serial_out_shift(&com_port->dll, CONFIG_DEBUG_UART_SHIFT, - baud_divisor & 0xff); - serial_out_shift(&com_port->dlm, CONFIG_DEBUG_UART_SHIFT, - (baud_divisor >> 8) & 0xff); - serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT, - UART_LCRVAL); + serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); + serial_dout(&com_port->mcr, UART_MCRVAL); + serial_dout(&com_port->fcr, UART_FCRVAL); + + serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); + serial_dout(&com_port->dll, baud_divisor & 0xff); + serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); + serial_dout(&com_port->lcr, UART_LCRVAL); }
static inline void _debug_uart_putc(int ch) { struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
- while (!(serial_in_shift(&com_port->lsr, 0) & UART_LSR_THRE)) + while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) ; - serial_out_shift(&com_port->thr, CONFIG_DEBUG_UART_SHIFT, ch); + serial_dout(&com_port->thr, ch); }
DEBUG_UART_FUNCS

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Since Rockchip requires 32-bit serial access, add this to the driver. Refactor a little to make this easier.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/serial/ns16550.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-)
Applied to u-boot-dm.

Add an implementation of RC4. This will be used by Rockchip booting but may be useful in other situations.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
include/rc4.h | 21 +++++++++++++++++++++ lib/Makefile | 1 + lib/rc4.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 include/rc4.h create mode 100644 lib/rc4.c
diff --git a/include/rc4.h b/include/rc4.h new file mode 100644 index 0000000..ea409c2 --- /dev/null +++ b/include/rc4.h @@ -0,0 +1,21 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RC4_H +#define __RC4_H + +/** + * rc4_encode() - encode a buf with the RC4 cipher + * + * @buf: Buffer to encode (it is overwrite in the process + * @len: Length of buffer in bytes + * @key: 16-byte key to use + */ +void rc4_encode(unsigned char *buf, unsigned int len, unsigned char key[16]); + +#endif diff --git a/lib/Makefile b/lib/Makefile index 1139f9b..fd106b9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_MD5) += md5.o obj-y += net_utils.o obj-$(CONFIG_PHYSMEM) += physmem.o obj-y += qsort.o +obj-y += rc4.o obj-$(CONFIG_SHA1) += sha1.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o obj-$(CONFIG_SHA256) += sha256.o diff --git a/lib/rc4.c b/lib/rc4.c new file mode 100644 index 0000000..89d15f3 --- /dev/null +++ b/lib/rc4.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * + * Rivest Cipher 4 (RC4) implementation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef USE_HOSTCC +#include <common.h> +#endif +#include <rc4.h> + +void rc4_encode(unsigned char *buf, unsigned int len, unsigned char key[16]) +{ + unsigned char s[256], k[256], temp; + unsigned short i, j, t; + int ptr; + + j = 0; + for (i = 0; i < 256; i++) { + s[i] = (unsigned char)i; + j &= 0x0f; + k[i] = key[j]; + j++; + } + + j = 0; + for (i = 0; i < 256; i++) { + j = (j + s[i] + k[i]) % 256; + temp = s[i]; + s[i] = s[j]; + s[j] = temp; + } + + i = 0; + j = 0; + for (ptr = 0; ptr < len; ptr++) { + i = (i + 1) % 256; + j = (j + s[i]) % 256; + temp = s[i]; + s[i] = s[j]; + s[j] = temp; + t = (s[i] + (s[j] % 256)) % 256; + buf[ptr] = buf[ptr] ^ s[t]; + } +}

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Add an implementation of RC4. This will be used by Rockchip booting but may be useful in other situations.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
include/rc4.h | 21 +++++++++++++++++++++ lib/Makefile | 1 + lib/rc4.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 include/rc4.h create mode 100644 lib/rc4.c
Applied to u-boot-dm.

Split out the code in fdtdec which finds a number at the end of a string. It can be useful in other situations.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
include/vsprintf.h | 26 ++++++++++++++++++++++++++ lib/fdtdec.c | 14 ++++++-------- lib/vsprintf.c | 19 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/include/vsprintf.h b/include/vsprintf.h index 09c8abd..3af8a4f 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -41,6 +41,32 @@ unsigned long long simple_strtoull(const char *cp, char **endp, long simple_strtol(const char *cp, char **endp, unsigned int base);
/** + * trailing_strtol() - extract a trailing integer from a string + * + * Given a string this finds a trailing number on the string and returns it. + * For example, "abc123" would return 123. + * + * @str: String to exxamine + * @return training number if found, else -1 + */ +long trailing_strtol(const char *str); + +/** + * trailing_strtoln() - extract a trailing integer from a fixed-length string + * + * Given a fixed-length string this finds a trailing number on the string + * and returns it. For example, "abc123" would return 123. Only the + * characters between @str and @end - 1 are examined. If @end is NULL, it is + * set to str + strlen(str). + * + * @str: String to exxamine + * @end: Pointer to end of string to examine, or NULL to use the + * whole string + * @return training number if found, else -1 + */ +long trailing_strtoln(const char *str, const char *end); + +/** * panic() - Print a message and reset/hang * * Prints a message on the console(s) and then resets. If CONFIG_PANIC_HANG is diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 9877849..add9adc 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -505,8 +505,7 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, const char *prop; const char *name; const char *slash; - const char *p; - int len; + int len, val;
prop = fdt_getprop_by_offset(blob, prop_offset, &name, &len); debug(" - %s, %s\n", name, prop); @@ -517,12 +516,11 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, slash = strrchr(prop, '/'); if (strcmp(slash + 1, find_name)) continue; - for (p = name + strlen(name) - 1; p > name; p--) { - if (!isdigit(*p)) { - *seqp = simple_strtoul(p + 1, NULL, 10); - debug("Found seq %d\n", *seqp); - return 0; - } + val = trailing_strtol(name); + if (val != -1) { + *seqp = val; + debug("Found seq %d\n", *seqp); + return 0; } }
diff --git a/lib/vsprintf.c b/lib/vsprintf.c index bedc865..e7e569e 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -166,6 +166,25 @@ unsigned long long simple_strtoull(const char *cp, char **endp, return result; }
+long trailing_strtoln(const char *str, const char *end) +{ + const char *p; + + if (!end) + end = str + strlen(str); + for (p = end - 1; p > str; p--) { + if (!isdigit(*p)) + return simple_strtoul(p + 1, NULL, 10); + } + + return -1; +} + +long trailing_strtol(const char *str) +{ + return trailing_strtoln(str, NULL); +} + /* we use this so that we can do without the ctype library */ #define is_digit(c) ((c) >= '0' && (c) <= '9')

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Split out the code in fdtdec which finds a number at the end of a string. It can be useful in other situations.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
include/vsprintf.h | 26 ++++++++++++++++++++++++++ lib/fdtdec.c | 14 ++++++-------- lib/vsprintf.c | 19 +++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-)
Applied to u-boot-dm.

It can be quite confusing with a new platform to figure out why the device tree cannot be located. Add some debug information for this case.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
lib/fdtdec.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/lib/fdtdec.c b/lib/fdtdec.c index add9adc..a78d577 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -568,6 +568,13 @@ int fdtdec_prepare_fdt(void) puts("Missing DTB\n"); #else puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n"); +# ifdef DEBUG + if (gd->fdt_blob) { + printf("fdt_blob=%p\n", gd->fdt_blob); + print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4, + 32, 0); + } +# endif #endif return -1; }

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
It can be quite confusing with a new platform to figure out why the device tree cannot be located. Add some debug information for this case.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
lib/fdtdec.c | 7 +++++++ 1 file changed, 7 insertions(+)
Applied to u-boot-dm.

Add an spl_init() function that does basic init such that board_init_f() can use simple malloc(), device tree and driver model. Each one is set up only if enabled for SPL.
Note: We really should refactor SPL such that there is a single board_init_f() and rename the existing weak board_init_f() functions provided by boards, calling them from the single board_init_f().
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/spl/spl.c | 35 ++++++++++++++++++++++++----------- include/asm-generic/global_data.h | 1 + include/spl.h | 12 ++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/common/spl/spl.c b/common/spl/spl.c index aeb0645..074c41d 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -148,18 +148,12 @@ static void spl_ram_load_image(void) } #endif
-void board_init_r(gd_t *dummy1, ulong dummy2) +int spl_init(void) { - u32 boot_device; int ret;
- debug(">>spl:board_init_r()\n"); - -#if defined(CONFIG_SYS_SPL_MALLOC_START) - mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, - CONFIG_SYS_SPL_MALLOC_SIZE); - gd->flags |= GD_FLG_FULL_MALLOC_INIT; -#elif defined(CONFIG_SYS_MALLOC_F_LEN) + debug("spl_init()\n"); +#if defined(CONFIG_SYS_MALLOC_F_LEN) gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN; gd->malloc_ptr = 0; #endif @@ -168,17 +162,36 @@ void board_init_r(gd_t *dummy1, ulong dummy2) ret = fdtdec_setup(); if (ret) { debug("fdtdec_setup() returned error %d\n", ret); - hang(); + return ret; } } if (IS_ENABLED(CONFIG_SPL_DM)) { ret = dm_init_and_scan(true); if (ret) { debug("dm_init_and_scan() returned error %d\n", ret); - hang(); + return ret; } } + gd->flags |= GD_FLG_SPL_INIT; + + return 0; +}
+void board_init_r(gd_t *dummy1, ulong dummy2) +{ + u32 boot_device; + + debug(">>spl:board_init_r()\n"); + +#if defined(CONFIG_SYS_SPL_MALLOC_START) + mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, + CONFIG_SYS_SPL_MALLOC_SIZE); + gd->flags |= GD_FLG_FULL_MALLOC_INIT; +#endif + if (!(gd->flags & GD_FLG_SPL_INIT)) { + if (spl_init()) + hang(); + } #ifndef CONFIG_PPC /* * timer_init() does not exist on PPC systems. The timer is initialized diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 6747619..73c61e6 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -115,5 +115,6 @@ typedef struct global_data { #define GD_FLG_ENV_READY 0x00080 /* Env. imported into hash table */ #define GD_FLG_SERIAL_READY 0x00100 /* Pre-reloc serial console ready */ #define GD_FLG_FULL_MALLOC_INIT 0x00200 /* Full malloc() is ready */ +#define GD_FLG_SPL_INIT 0x00400 /* spl_init() has been called */
#endif /* __ASM_GENERIC_GBL_DATA_H */ diff --git a/include/spl.h b/include/spl.h index d19940f..8e53426 100644 --- a/include/spl.h +++ b/include/spl.h @@ -81,6 +81,18 @@ void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image); int spl_load_image_ext(block_dev_desc_t *block_dev, int partition, const char *filename); int spl_load_image_ext_os(block_dev_desc_t *block_dev, int partition);
+/** + * spl_init() - Set up device tree and driver model in SPL if enabled + * + * Call this function in board_init_f() if you want to use device tree and + * driver model early, before board_init_r() is called. This function will + * be called from board_init_r() if not called earlier. + * + * If this is not called, then driver model will be inactive in SPL's + * board_init_f(), and no device tree will be available. + */ +int spl_init(void); + #ifdef CONFIG_SPL_BOARD_INIT void spl_board_init(void); #endif

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Add an spl_init() function that does basic init such that board_init_f() can use simple malloc(), device tree and driver model. Each one is set up only if enabled for SPL.
Note: We really should refactor SPL such that there is a single board_init_f() and rename the existing weak board_init_f() functions provided by boards, calling them from the single board_init_f().
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/spl/spl.c | 35 ++++++++++++++++++++++++----------- include/asm-generic/global_data.h | 1 + include/spl.h | 12 ++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-)
Applied to u-boot-dm.

As a debug option, add positive confirmation that SPL has completed execution. This can help with diagnosing the location of unexpected hangs.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
common/spl/spl.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/common/spl/spl.c b/common/spl/spl.c index 074c41d..94b01da 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -298,6 +298,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2) gd->malloc_ptr / 1024); #endif
+ debug("loaded - jumping to U-Boot..."); jump_to_image_no_args(&spl_image); }

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
As a debug option, add positive confirmation that SPL has completed execution. This can help with diagnosing the location of unexpected hangs.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
common/spl/spl.c | 1 + 1 file changed, 1 insertion(+)
Applied to u-boot-dm.

Some functions called by mkimage would like to know the output file size. Initially this is the same as the input file size, but it may be affected by adding headers, etc.
Add this information to the image parameters.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
tools/imagetool.h | 1 + tools/mkimage.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+)
diff --git a/tools/imagetool.h b/tools/imagetool.h index b7874f4..99bbf2f 100644 --- a/tools/imagetool.h +++ b/tools/imagetool.h @@ -59,6 +59,7 @@ struct image_tool_params { const char *keydest; /* Destination .dtb for public key */ const char *comment; /* Comment to add to signature node */ int require_keys; /* 1 to mark signing keys as 'required' */ + int file_size; /* Total size of output file */ };
/* diff --git a/tools/mkimage.c b/tools/mkimage.c index 8808d70..e81d455 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -75,6 +75,7 @@ int main(int argc, char **argv) int retval = 0; struct image_type_params *tparams = NULL; int pad_len = 0; + int dfd;
params.cmdname = *argv; params.addr = params.ep = 0; @@ -310,6 +311,22 @@ NXTARG: ; exit (retval); }
+ dfd = open(params.datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params.cmdname, params.datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params.cmdname, params.datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + params.file_size = sbuf.st_size + tparams->header_size; + close(dfd); + /* * In case there an header with a variable * length will be added, the corresponding @@ -409,6 +426,7 @@ NXTARG: ; params.cmdname, params.imagefile, strerror(errno)); exit (EXIT_FAILURE); } + params.file_size = sbuf.st_size;
ptr = mmap(0, sbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, ifd, 0); if (ptr == MAP_FAILED) {

Hi Simon,
On Tue, Jun 23, 2015 at 4:39 PM, Simon Glass sjg@chromium.org wrote:
Some functions called by mkimage would like to know the output file size. Initially this is the same as the input file size, but it may be affected by adding headers, etc.
Add this information to the image parameters.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Joe Hershberger joe.hershberger@ni.com

On 25 June 2015 at 16:51, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Jun 23, 2015 at 4:39 PM, Simon Glass sjg@chromium.org wrote:
Some functions called by mkimage would like to know the output file size. Initially this is the same as the input file size, but it may be affected by adding headers, etc.
Add this information to the image parameters.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Joe Hershberger joe.hershberger@ni.com
Applied to u-boot-dm.

On Saturday, July 18, 2015 at 01:58:09 AM, Simon Glass wrote:
On 25 June 2015 at 16:51, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Jun 23, 2015 at 4:39 PM, Simon Glass sjg@chromium.org wrote:
Some functions called by mkimage would like to know the output file size. Initially this is the same as the input file size, but it may be affected by adding headers, etc.
Add this information to the image parameters.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Joe Hershberger joe.hershberger@ni.com
Applied to u-boot-dm.
Hi,
This breaks all i.MX23 and i.MX28 boards, try building for example the mx28evk board, u-boot.sb target:
$ make mx28evk_defconfig $ make u-boot.sb [...] LD spl/lib/built-in.o LDS spl/u-boot-spl.lds LD spl/u-boot-spl OBJCOPY spl/u-boot-spl.bin CFG spl/u-boot-spl.cfg MKIMAGE u-boot.sb ./tools/mkimage: Can't open (null): Bad address arch/arm/cpu/arm926ejs/mxs/Makefile:82: recipe for target 'u-boot.sb' failed make[1]: *** [u-boot.sb] Error 1 Makefile:989: recipe for target 'u-boot.sb' failed make: *** [u-boot.sb] Error 2
Shall I look into it or do you already have a patch ? :)
Best regards, Marek Vasut

On Tuesday, August 11, 2015 at 01:43:05 AM, Marek Vasut wrote:
On Saturday, July 18, 2015 at 01:58:09 AM, Simon Glass wrote:
On 25 June 2015 at 16:51, Joe Hershberger joe.hershberger@gmail.com wrote:
Hi Simon,
On Tue, Jun 23, 2015 at 4:39 PM, Simon Glass sjg@chromium.org wrote:
Some functions called by mkimage would like to know the output file size. Initially this is the same as the input file size, but it may be affected by adding headers, etc.
Add this information to the image parameters.
Signed-off-by: Simon Glass sjg@chromium.org
Acked-by: Joe Hershberger joe.hershberger@ni.com
Applied to u-boot-dm.
Hi,
This breaks all i.MX23 and i.MX28 boards, try building for example the mx28evk board, u-boot.sb target:
$ make mx28evk_defconfig $ make u-boot.sb [...] LD spl/lib/built-in.o LDS spl/u-boot-spl.lds LD spl/u-boot-spl OBJCOPY spl/u-boot-spl.bin CFG spl/u-boot-spl.cfg MKIMAGE u-boot.sb ./tools/mkimage: Can't open (null): Bad address arch/arm/cpu/arm926ejs/mxs/Makefile:82: recipe for target 'u-boot.sb' failed make[1]: *** [u-boot.sb] Error 1 Makefile:989: recipe for target 'u-boot.sb' failed make: *** [u-boot.sb] Error 2
Shall I look into it or do you already have a patch ? :)
Patch is out and the patch is most certainly correct.
On the other hand, is my understanding correct that this patch makes passing the -d option to mkimage mandatory ? It was optional before this patch I believe.
Best regards, Marek Vasut

It is common for system reset to be available at multiple levels in modern hardware. For example, an SoC may provide a reset option, and a board may provide its own reset for reasons of security or thoroughness. It is useful to be able to model this hardware without hard-coding the behaviour in the SoC or board. Also there is a distinction sometimes between resetting just the CPU (leaving GPIO state alone) and resetting all the PMICs, just cutting power.
To achieve this, add a simple system reset uclass. It allows multiple devices to provide reset functionality and provides a way to walk through them, requesting a particular reset type until is it provided.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/misc/Kconfig | 9 +++++++ drivers/misc/Makefile | 1 + drivers/misc/reset-uclass.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/reset.h | 62 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 drivers/misc/reset-uclass.c create mode 100644 include/reset.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 64b07a3..3b7f76a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -73,3 +73,12 @@ config PCA9551_I2C_ADDR default 0x60 help The I2C address of the PCA9551 LED controller. + +config RESET + bool "Enable support for reset drivers" + depends on DM + help + Enable reset drivers which can be used to reset the CPU or board. + Each driver can provide a reset method which will be called to + effect a reset. The uclass will try all available drivers when + reset_walk() is called. diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 120babc..5da5178 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o +obj-$(CONFIG_RESET) += reset-uclass.o diff --git a/drivers/misc/reset-uclass.c b/drivers/misc/reset-uclass.c new file mode 100644 index 0000000..ba27757 --- /dev/null +++ b/drivers/misc/reset-uclass.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <reset.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/err.h> + +int reset_request(struct udevice *dev, enum reset_t type) +{ + struct reset_ops *ops = reset_get_ops(dev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(dev, type); +} + +void reset_walk(enum reset_t type) +{ + struct udevice *dev; + int ret = 0; + + while (ret != -EINPROGRESS && type < RESET_COUNT) { + for (uclass_first_device(UCLASS_RESET, &dev); + dev; + uclass_next_device(&dev)) { + ret = reset_request(dev, type); + if (ret == -EINPROGRESS) + break; + } + } + + /* Wait for the reset to take effect */ + mdelay(100); + + /* Still no reset? Give up */ + printf("Reset not supported on this platform\n"); + hang(); +} + +/** + * reset_cpu() - calls reset_walk(RESET_WARM) + */ +void reset_cpu(ulong addr) +{ + reset_walk(RESET_WARM); +} + +UCLASS_DRIVER(reset) = { + .id = UCLASS_RESET, + .name = "reset", +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 5bb602f..fc486f2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -45,6 +45,7 @@ enum uclass_id { UCLASS_PINCTRL, /* Pin multiplexing control */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_REGULATOR, /* Regulator device */ + UCLASS_RESET, /* Reset device */ UCLASS_RTC, /* Real time clock device */ UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ diff --git a/include/reset.h b/include/reset.h new file mode 100644 index 0000000..d29e108 --- /dev/null +++ b/include/reset.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RESET_H +#define __RESET_H + +enum reset_t { + RESET_WARM, /* Reset CPU, keep GPIOs active */ + RESET_COLD, /* Reset CPU and GPIOs */ + RESET_POWER, /* Reset PMIC (remove and restore power) */ + + RESET_COUNT, +}; + +struct reset_ops { + /** + * request() - request a reset of the given type + * + * Note that this function may return before the reset takes effect. + * + * @type: Reset type to request + * @return -EINPROGRESS if the reset has been started and + * will complete soon, -EPROTONOSUPPORT if not supported + * by this device, 0 if the reset has already happened + * (in which case this method will not actually return) + */ + int (*request)(struct udevice *dev, enum reset_t type); +}; + +#define reset_get_ops(dev) ((struct reset_ops *)(dev)->driver->ops) + +/** + * reset_request() - request a reset + * + * @type: Reset type to request + * @return 0 if OK, -EPROTONOSUPPORT if not supported by this device + */ +int reset_request(struct udevice *dev, enum reset_t type); + +/** + * reset_walk() - cause a reset + * + * This works through the available reset devices until it finds one that can + * perform a reset. If the provided reset type is not available, the next one + * will be tried. + * + * If this function fails to reset, it will display a message and halt + * + * @type: Reset type to request + */ +void reset_walk(enum reset_t type); + +/** + * reset_cpu() - calls reset_walk(RESET_WARM) + */ +void reset_cpu(ulong addr); + +#endif

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
It is common for system reset to be available at multiple levels in modern hardware. For example, an SoC may provide a reset option, and a board may provide its own reset for reasons of security or thoroughness. It is useful to be able to model this hardware without hard-coding the behaviour in the SoC or board. Also there is a distinction sometimes between resetting just the CPU (leaving GPIO state alone) and resetting all the PMICs, just cutting power.
To achieve this, add a simple system reset uclass. It allows multiple devices to provide reset functionality and provides a way to walk through them, requesting a particular reset type until is it provided.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/misc/Kconfig | 9 +++++++ drivers/misc/Makefile | 1 + drivers/misc/reset-uclass.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/reset.h | 62 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 drivers/misc/reset-uclass.c create mode 100644 include/reset.h
Applied to u-boot-dm.

Since we want clk_ops to be used in U-Boot as a whole, rename the Zynq version until it can be converted to driver model.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
arch/arm/mach-zynq/clk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c index d2885dc..6444be8 100644 --- a/arch/arm/mach-zynq/clk.c +++ b/arch/arm/mach-zynq/clk.c @@ -48,11 +48,11 @@ DECLARE_GLOBAL_DATA_PTR; struct clk;
/** - * struct clk_ops: + * struct zynq_clk_ops: * @set_rate: Function pointer to set_rate() implementation * @get_rate: Function pointer to get_rate() implementation */ -struct clk_ops { +struct zynq_clk_ops { int (*set_rate)(struct clk *clk, unsigned long rate); unsigned long (*get_rate)(struct clk *clk); }; @@ -72,7 +72,7 @@ struct clk { enum zynq_clk parent; unsigned int flags; u32 *reg; - struct clk_ops ops; + struct zynq_clk_ops ops; }; #define ZYNQ_CLK_FLAGS_HAS_2_DIVS 1

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Since we want clk_ops to be used in U-Boot as a whole, rename the Zynq version until it can be converted to driver model.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
arch/arm/mach-zynq/clk.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
Applied to u-boot-dm.

Clocks are an important feature of platforms and have become increasing complex with time. Most modern SoCs have multiple PLLs and dozens of clock dividers which distribute clocks to on-chip peripherals.
Some SoC implementations have a clock API which is private to that SoC family, e.g. Tegra and Exynos. This is useful but it would be better to have a common API that can be understood and used throughout U-Boot.
Add a simple clock API as a starting point. It supports querying and setting the rate of a clock. Each clock is a device. To reduce memory and processing overhead the concept of peripheral clocks is provided. These do not need to be explicit devices - it is possible to write a driver that can adjust the I2C clock (for example) without an explicit I2C clock device. This can dramatically reduce the number of devices (and associated overhead) in a complex SoC.
Clocks are referenced by a number, and it is expected that SoCs will define that numbering themselves via an enum.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/clk/Kconfig | 19 ++++++++++++ drivers/clk/Makefile | 8 +++++ drivers/clk/clk-uclass.c | 58 +++++++++++++++++++++++++++++++++++ include/clk.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + scripts/Makefile.spl | 1 + 8 files changed, 170 insertions(+) create mode 100644 drivers/clk/Kconfig create mode 100644 drivers/clk/Makefile create mode 100644 drivers/clk/clk-uclass.c
diff --git a/drivers/Kconfig b/drivers/Kconfig index 7c9eefc..619d93a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,5 +1,7 @@ menu "Device Drivers"
+source "drivers/clk/Kconfig" + source "drivers/core/Kconfig"
source "drivers/cpu/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 39b7919..9f5cec7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_CLK) += clk/ obj-$(CONFIG_DM) += core/ obj-$(CONFIG_DM_DEMO) += demo/ obj-$(CONFIG_BIOSEMU) += bios_emulator/ diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig new file mode 100644 index 0000000..07eb54c --- /dev/null +++ b/drivers/clk/Kconfig @@ -0,0 +1,19 @@ +config CLK + bool "Enable clock driver support" + depends on DM + help + This allows drivers to be provided for clock generators, including + oscillators and PLLs. Devices can use a common clock API to request + a particular clock rate and check on available clocks. Clocks can + feed into other clocks in a tree structure, with multiplexers to + choose the source for each clock. + +config SPL_CLK_SUPPORT + bool "Enable clock support in SPL" + depends on CLK + help + The clock subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use clock drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up clocks within SPL, and allows the same drivers to be + used as U-Boot proper. diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile new file mode 100644 index 0000000..b51cf23 --- /dev/null +++ b/drivers/clk/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CLK) += clk-uclass.o diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c new file mode 100644 index 0000000..73dfd7d --- /dev/null +++ b/drivers/clk/clk-uclass.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <dm/lists.h> +#include <dm/root.h> + +ulong clk_get_rate(struct udevice *dev) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->get_rate) + return -ENOSYS; + + return ops->get_rate(dev); +} + +ulong clk_set_rate(struct udevice *dev, ulong rate) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->set_rate) + return -ENOSYS; + + return ops->set_rate(dev, rate); +} + +ulong clk_get_periph_rate(struct udevice *dev, int periph) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->get_periph_rate) + return -ENOSYS; + + return ops->get_periph_rate(dev, periph); +} + +ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->set_periph_rate) + return -ENOSYS; + + return ops->set_periph_rate(dev, periph, rate); +} + +UCLASS_DRIVER(clk) = { + .id = UCLASS_CLK, + .name = "clk", +}; diff --git a/include/clk.h b/include/clk.h index df4570c..254ad2b 100644 --- a/include/clk.h +++ b/include/clk.h @@ -1,6 +1,86 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass sjg@chromium.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + #ifndef _CLK_H_ #define _CLK_H_
int soc_clk_dump(void);
+struct clk_ops { + /** + * get_rate() - Get current clock rate + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, or -ve error code + */ + ulong (*get_rate)(struct udevice *dev); + + /** + * set_rate() - Set current clock rate + * + * @dev: Device to adjust + * @rate: New clock rate in Hz + * @return new rate, or -ve error code + */ + ulong (*set_rate)(struct udevice *dev, ulong rate); + + /** + * clk_set_periph_rate() - Set clock rate for a peripheral + * + * @dev: Device to adjust (UCLASS_CLK) + * @rate: New clock rate in Hz + * @return new clock rate in Hz, or -ve error code + */ + ulong (*get_periph_rate)(struct udevice *dev, int periph); + + /** + * clk_set_periph_rate() - Set current clock rate for a peripheral + * + * @dev: Device to update (UCLASS_CLK) + * @periph: Peripheral ID to cupdate + * @return new clock rate in Hz, or -ve error code + */ + ulong (*set_periph_rate)(struct udevice *dev, int periph, ulong rate); +}; + +#define clk_get_ops(dev) ((struct clk_ops *)(dev)->driver->ops) + +/** + * clk_get_rate() - Get current clock rate + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, or -ve error code + */ +ulong clk_get_rate(struct udevice *dev); + +/** + * set_rate() - Set current clock rate + * + * @dev: Device to adjust + * @rate: New clock rate in Hz + * @return new rate, or -ve error code + */ +ulong clk_set_rate(struct udevice *dev, ulong rate); + +/** + * clk_get_periph_rate() - Get current clock rate for a peripheral + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, -ve error code + */ +ulong clk_get_periph_rate(struct udevice *dev, int periph); + +/** + * clk_set_periph_rate() - Set current clock rate for a peripheral + * + * @dev: Device to update (UCLASS_CLK) + * @periph: Peripheral ID to cupdate + * @return new clock rate in Hz, or -ve error code + */ +ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate); + #endif /* _CLK_H_ */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index fc486f2..72512b5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -25,6 +25,7 @@ enum uclass_id { UCLASS_SIMPLE_BUS, /* bus with child devices */
/* U-Boot uclasses start here - in alphabetical order */ + UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY_PORT, /* Display port video */ diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index c4e83bb..9d7ac99 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -54,6 +54,7 @@ libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/ libs-$(CONFIG_SPL_FRAMEWORK) += common/spl/ libs-$(CONFIG_SPL_LIBCOMMON_SUPPORT) += common/ libs-$(CONFIG_SPL_LIBDISK_SUPPORT) += disk/ +libs-$(CONFIG_SPL_CLK_SUPPORT) += drivers/clk/ libs-$(CONFIG_SPL_DM) += drivers/core/ libs-$(CONFIG_SPL_I2C_SUPPORT) += drivers/i2c/ libs-$(CONFIG_SPL_GPIO_SUPPORT) += drivers/gpio/

On 23 June 2015 at 15:39, Simon Glass sjg@chromium.org wrote:
Clocks are an important feature of platforms and have become increasing complex with time. Most modern SoCs have multiple PLLs and dozens of clock dividers which distribute clocks to on-chip peripherals.
Some SoC implementations have a clock API which is private to that SoC family, e.g. Tegra and Exynos. This is useful but it would be better to have a common API that can be understood and used throughout U-Boot.
Add a simple clock API as a starting point. It supports querying and setting the rate of a clock. Each clock is a device. To reduce memory and processing overhead the concept of peripheral clocks is provided. These do not need to be explicit devices - it is possible to write a driver that can adjust the I2C clock (for example) without an explicit I2C clock device. This can dramatically reduce the number of devices (and associated overhead) in a complex SoC.
Clocks are referenced by a number, and it is expected that SoCs will define that numbering themselves via an enum.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/clk/Kconfig | 19 ++++++++++++ drivers/clk/Makefile | 8 +++++ drivers/clk/clk-uclass.c | 58 +++++++++++++++++++++++++++++++++++ include/clk.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + scripts/Makefile.spl | 1 + 8 files changed, 170 insertions(+) create mode 100644 drivers/clk/Kconfig create mode 100644 drivers/clk/Makefile create mode 100644 drivers/clk/clk-uclass.c
Applied to u-boot-dm.

Use the common function to obtain the number from the end of the string, instead of a local function. Also tweak the position of a debug() statement.
Signed-off-by: Simon Glass sjg@chromium.org ---
Changes in v3: - Split this series apart from the Rockchip series
Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index dbab3e3..d99cb9a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -9,6 +9,7 @@ #include <fdtdec.h> #include <errno.h> #include <dm.h> +#include <vsprintf.h> #include <dm/lists.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -17,16 +18,6 @@
DECLARE_GLOBAL_DATA_PTR;
-static ulong str_get_num(const char *ptr, const char *maxptr) -{ - if (!ptr || !maxptr) - return 0; - - while (!isdigit(*ptr) && ptr++ < maxptr); - - return simple_strtoul(ptr, NULL, 0); -} - int pmic_bind_children(struct udevice *pmic, int offset, const struct pmic_child_info *child_info) { @@ -35,7 +26,6 @@ int pmic_bind_children(struct udevice *pmic, int offset, struct driver *drv; struct udevice *child; const char *node_name; - int node_name_len; int bind_count = 0; int node; int prefix_len; @@ -47,19 +37,19 @@ int pmic_bind_children(struct udevice *pmic, int offset, for (node = fdt_first_subnode(blob, offset); node > 0; node = fdt_next_subnode(blob, node)) { - node_name = fdt_get_name(blob, node, &node_name_len); + node_name = fdt_get_name(blob, node, NULL);
debug("* Found child node: '%s' at offset:%d\n", node_name, node);
child = NULL; for (info = child_info; info->prefix && info->driver; info++) { + debug(" - compatible prefix: '%s'\n", info->prefix); + prefix_len = strlen(info->prefix); if (strncmp(info->prefix, node_name, prefix_len)) continue;
- debug(" - compatible prefix: '%s'\n", info->prefix); - drv = lists_driver_lookup_name(info->driver); if (!drv) { debug(" - driver: '%s' not found!\n", @@ -78,10 +68,7 @@ int pmic_bind_children(struct udevice *pmic, int offset,
debug(" - bound child device: '%s'\n", child->name);
- child->driver_data = str_get_num(node_name + - prefix_len, - node_name + - node_name_len); + child->driver_data = trailing_strtol(node_name);
debug(" - set 'child->driver_data': %lu\n", child->driver_data);

Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
Use the common function to obtain the number from the end of the string, instead of a local function. Also tweak the position of a debug() statement.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Split this series apart from the Rockchip series
Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index dbab3e3..d99cb9a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -9,6 +9,7 @@ #include <fdtdec.h> #include <errno.h> #include <dm.h> +#include <vsprintf.h> #include <dm/lists.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -17,16 +18,6 @@
DECLARE_GLOBAL_DATA_PTR;
-static ulong str_get_num(const char *ptr, const char *maxptr) -{
- if (!ptr || !maxptr)
return 0;
- while (!isdigit(*ptr) && ptr++ < maxptr);
- return simple_strtoul(ptr, NULL, 0);
-}
- int pmic_bind_children(struct udevice *pmic, int offset, const struct pmic_child_info *child_info) {
@@ -35,7 +26,6 @@ int pmic_bind_children(struct udevice *pmic, int offset, struct driver *drv; struct udevice *child; const char *node_name;
- int node_name_len; int bind_count = 0; int node; int prefix_len;
@@ -47,19 +37,19 @@ int pmic_bind_children(struct udevice *pmic, int offset, for (node = fdt_first_subnode(blob, offset); node > 0; node = fdt_next_subnode(blob, node)) {
node_name = fdt_get_name(blob, node, &node_name_len);
node_name = fdt_get_name(blob, node, NULL);
debug("* Found child node: '%s' at offset:%d\n", node_name, node);
child = NULL; for (info = child_info; info->prefix && info->driver; info++) {
debug(" - compatible prefix: '%s'\n", info->prefix);
prefix_len = strlen(info->prefix); if (strncmp(info->prefix, node_name, prefix_len)) continue;
debug(" - compatible prefix: '%s'\n", info->prefix);
drv = lists_driver_lookup_name(info->driver); if (!drv) { debug(" - driver: '%s' not found!\n",
@@ -78,10 +68,7 @@ int pmic_bind_children(struct udevice *pmic, int offset,
debug(" - bound child device: '%s'\n", child->name);
child->driver_data = str_get_num(node_name +
prefix_len,
node_name +
node_name_len);
child->driver_data = trailing_strtol(node_name); debug(" - set 'child->driver_data': %lu\n", child->driver_data);
Tested on: - Odroid U3 (odroid_defconfig) - Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,

On 1 July 2015 at 03:44, Przemyslaw Marczak p.marczak@samsung.com wrote:
Hello Simon,
On 06/23/2015 11:39 PM, Simon Glass wrote:
Use the common function to obtain the number from the end of the string, instead of a local function. Also tweak the position of a debug() statement.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v3:
- Split this series apart from the Rockchip series
Changes in v2: None
drivers/power/pmic/pmic-uclass.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-)
diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index dbab3e3..d99cb9a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -9,6 +9,7 @@ #include <fdtdec.h> #include <errno.h> #include <dm.h> +#include <vsprintf.h> #include <dm/lists.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -17,16 +18,6 @@
DECLARE_GLOBAL_DATA_PTR;
-static ulong str_get_num(const char *ptr, const char *maxptr) -{
if (!ptr || !maxptr)
return 0;
while (!isdigit(*ptr) && ptr++ < maxptr);
return simple_strtoul(ptr, NULL, 0);
-}
- int pmic_bind_children(struct udevice *pmic, int offset, const struct pmic_child_info *child_info) {
@@ -35,7 +26,6 @@ int pmic_bind_children(struct udevice *pmic, int offset, struct driver *drv; struct udevice *child; const char *node_name;
int node_name_len; int bind_count = 0; int node; int prefix_len;
@@ -47,19 +37,19 @@ int pmic_bind_children(struct udevice *pmic, int offset, for (node = fdt_first_subnode(blob, offset); node > 0; node = fdt_next_subnode(blob, node)) {
node_name = fdt_get_name(blob, node, &node_name_len);
node_name = fdt_get_name(blob, node, NULL); debug("* Found child node: '%s' at offset:%d\n",
node_name, node);
child = NULL; for (info = child_info; info->prefix && info->driver;
info++) {
debug(" - compatible prefix: '%s'\n",
info->prefix);
prefix_len = strlen(info->prefix); if (strncmp(info->prefix, node_name, prefix_len)) continue;
debug(" - compatible prefix: '%s'\n",
info->prefix);
drv = lists_driver_lookup_name(info->driver); if (!drv) { debug(" - driver: '%s' not found!\n",
@@ -78,10 +68,7 @@ int pmic_bind_children(struct udevice *pmic, int offset,
debug(" - bound child device: '%s'\n",
child->name);
child->driver_data = str_get_num(node_name +
prefix_len,
node_name +
node_name_len);
child->driver_data = trailing_strtol(node_name); debug(" - set 'child->driver_data': %lu\n", child->driver_data);
Tested on:
- Odroid U3 (odroid_defconfig)
- Sandbox - ut pmic/regulator
Tested-by: Przemyslaw Marczak p.marczak@samsung.com Acked-by: Przemyslaw Marczak p.marczak@samsung.com
Best regards,
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak@samsung.com
Applied to u-boot-dm.

Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
York

Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Regards, Simon

On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
York

On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?

On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
York

On Tue, Jun 30, 2015 at 01:10:45PM -0700, York Sun wrote:
On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
OK. I think we'd agreed to that at ELC-E last year and it might have been in a few here-and-there emails but it's worth spelling out somewhere.
Hey Simon? doc/driver-model/README.txt has a pdata example, so maybe the answer here is it's time to update README.txt in a few ways :)

Hi Tom,
On 30 June 2015 at 14:31, Tom Rini trini@konsulko.com wrote:
On Tue, Jun 30, 2015 at 01:10:45PM -0700, York Sun wrote:
On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
OK. I think we'd agreed to that at ELC-E last year and it might have been in a few here-and-there emails but it's worth spelling out somewhere.
Hey Simon? doc/driver-model/README.txt has a pdata example, so maybe the answer here is it's time to update README.txt in a few ways :)
I'll prepare a patch.
Regards, Simon

On 1 July 2015 at 02:38, Simon Glass sjg@chromium.org wrote:
Hi Tom,
On 30 June 2015 at 14:31, Tom Rini trini@konsulko.com wrote:
On Tue, Jun 30, 2015 at 01:10:45PM -0700, York Sun wrote:
On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote: > Simon, > > Does the dm force using device tree? I was reviewing a patch set regarding SPI > and found OF_CONTROL has to be selected in order to get the driver model happy. > > My understanding of the driver model is both device tree and platform data are > allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
My observations with this approach (dm-spi)
1. We're planning to move spi driver with dm support but many of the boards which used spi drivers don't have dts support yet. 2. I think dm will progress only when dts support progresses.
The only solution for this - if we need to move any driver to dm then check for dts on particular board this driver uses and move that board to have dts support.
Any comments?
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
OK. I think we'd agreed to that at ELC-E last year and it might have been in a few here-and-there emails but it's worth spelling out somewhere.
Hey Simon? doc/driver-model/README.txt has a pdata example, so maybe the answer here is it's time to update README.txt in a few ways :)
I'll prepare a patch.
thanks!

On 2 July 2015 at 12:33, Jagan Teki jteki@openedev.com wrote:
On 1 July 2015 at 02:38, Simon Glass sjg@chromium.org wrote:
Hi Tom,
On 30 June 2015 at 14:31, Tom Rini trini@konsulko.com wrote:
On Tue, Jun 30, 2015 at 01:10:45PM -0700, York Sun wrote:
On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote: > Hi York, > > On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote: >> Simon, >> >> Does the dm force using device tree? I was reviewing a patch set regarding SPI >> and found OF_CONTROL has to be selected in order to get the driver model happy. >> >> My understanding of the driver model is both device tree and platform data are >> allowed, like Linux. Is that still true? > > For buses you need device tree. I was rather hoping that we could > avoid platform data on platforms that have device tree. What is the > point? >
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
My observations with this approach (dm-spi)
- We're planning to move spi driver with dm support but many of the
boards which used spi drivers don't have dts support yet. 2. I think dm will progress only when dts support progresses.
The only solution for this - if we need to move any driver to dm then check for dts on particular board this driver uses and move that board to have dts support.
Any comments?
Any suggestions?
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
OK. I think we'd agreed to that at ELC-E last year and it might have been in a few here-and-there emails but it's worth spelling out somewhere.
Hey Simon? doc/driver-model/README.txt has a pdata example, so maybe the answer here is it's time to update README.txt in a few ways :)
I'll prepare a patch.
thanks!

Hi Jagan,
On 9 July 2015 at 14:31, Jagan Teki jteki@openedev.com wrote:
On 2 July 2015 at 12:33, Jagan Teki jteki@openedev.com wrote:
On 1 July 2015 at 02:38, Simon Glass sjg@chromium.org wrote:
Hi Tom,
On 30 June 2015 at 14:31, Tom Rini trini@konsulko.com wrote:
On Tue, Jun 30, 2015 at 01:10:45PM -0700, York Sun wrote:
On 06/30/2015 12:01 PM, Tom Rini wrote:
On Tue, Jun 30, 2015 at 11:42:41AM -0700, York Sun wrote: > > > On 06/30/2015 11:33 AM, Simon Glass wrote: >> Hi York, >> >> On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote: >>> Simon, >>> >>> Does the dm force using device tree? I was reviewing a patch set regarding SPI >>> and found OF_CONTROL has to be selected in order to get the driver model happy. >>> >>> My understanding of the driver model is both device tree and platform data are >>> allowed, like Linux. Is that still true? >> >> For buses you need device tree. I was rather hoping that we could >> avoid platform data on platforms that have device tree. What is the >> point? >> > > Simon, > > It happens on a platform not using device tree, but DM will be used. > > I prefer DM to have both, rather than being forced to use device tree, unless we > are going to enforce using device tree on all new platforms. Since device tree > is still an option, I feel it is best to support platform data, like Linux > drivers do.
Well, to what end? My recollection is that in short, the kernel has both since platform data predates device tree (and converting platform data to device tree is still a thing that happens). But we're trying to skip that intermediate step. Are there platforms where you do not plan to use a device tree, ever?
My observations with this approach (dm-spi)
- We're planning to move spi driver with dm support but many of the
boards which used spi drivers don't have dts support yet. 2. I think dm will progress only when dts support progresses.
The only solution for this - if we need to move any driver to dm then check for dts on particular board this driver uses and move that board to have dts support.
Any comments?
Any suggestions?
Yes, or maybe enable DTS for the board? It's not that hard. E.g. see this for Raspberry Pi:
http://patchwork.ozlabs.org/patch/492694/ http://patchwork.ozlabs.org/patch/492698/ http://patchwork.ozlabs.org/patch/492700/
Tom,
I am not against using device tree at all. It is more dynamic and flexible. But I don't see any indication that we favor device tree over pdata (except in the code). If we are skipping pdata for new drivers, a clear message will be helpful. That's what I am trying to get clarification.
OK. I think we'd agreed to that at ELC-E last year and it might have been in a few here-and-there emails but it's worth spelling out somewhere.
Hey Simon? doc/driver-model/README.txt has a pdata example, so maybe the answer here is it's time to update README.txt in a few ways :)
I'll prepare a patch.
thanks!
Jagan | openedev.
Regards, Simon

On 1 July 2015 at 00:12, York Sun yorksun@freescale.com wrote:
On 06/30/2015 11:33 AM, Simon Glass wrote:
Hi York,
On 30 June 2015 at 10:08, York Sun yorksun@freescale.com wrote:
Simon,
Does the dm force using device tree? I was reviewing a patch set regarding SPI and found OF_CONTROL has to be selected in order to get the driver model happy.
My understanding of the driver model is both device tree and platform data are allowed, like Linux. Is that still true?
For buses you need device tree. I was rather hoping that we could avoid platform data on platforms that have device tree. What is the point?
Simon,
It happens on a platform not using device tree, but DM will be used.
I prefer DM to have both, rather than being forced to use device tree, unless we are going to enforce using device tree on all new platforms. Since device tree is still an option, I feel it is best to support platform data, like Linux drivers do.
I can understand your concern about pdata, but the dts is more dynamic approach to get the device data and at least some of the architecture in u-boot had a support for it. And if we start with dts boards will turn it on to use dts instead of going back to use legacy pdata which is so called static approach (which might increase the board code, some times increasing configs)
Using/adding/going by dts support is more generic and dynamic instead of static, IMHO
thanks!

Hi,
On 23 June 2015 at 15:38, Simon Glass sjg@chromium.org wrote:
This series adds several new uclasses. Some of these have been discussed for a while. All are fairly simple and just provide enough functionality for existing use cases. The following are included in this series:
- Clocks - setting and getting PLL and peripheral clocks
- Pinctrl - adjusting pin multiplexing settings
- Reset - reseting the board or SoC
- RAM - setting up RAM controllers and detecting the available RAM
- MMC - MMC controllers (using the existing block device framework)
- LEDs - turning LEDs on and off, with only a GPIO driver so far
Trivial support is also added for regmap and syscon controllers, modelled on how the kernel does this.
This builds on the SPL device tree support which was recently added.
I've started up a u-boot-dm/next tree again for the next release. I'm planning start applying patches from this soon, or at least the ones that don't need a respin. Please let me know if there are any more comments. I can still fix things after they are applied (basically drop patches and start again) but it is easier to get things right first time.
[snip]
Regards, Simon
participants (10)
-
Chen-Yu Tsai
-
Jaehoon Chung
-
Jaehoon Chung
-
Jagan Teki
-
Joe Hershberger
-
Marek Vasut
-
Przemyslaw Marczak
-
Simon Glass
-
Tom Rini
-
York Sun