From d1ceeeff6c2ee1e55b7140654c8d6de44b60dab6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:11 +1300 Subject: doc: Move UEFI under develop/ Much of the content here is useful only for development. Move it under that section. Signed-off-by: Simon Glass Reviewed-by: Heinrich Schuchardt --- doc/develop/index.rst | 1 + doc/develop/uefi/index.rst | 15 ++ doc/develop/uefi/iscsi.rst | 184 ++++++++++++++ doc/develop/uefi/u-boot_on_efi.rst | 235 +++++++++++++++++ doc/develop/uefi/uefi.rst | 498 +++++++++++++++++++++++++++++++++++++ 5 files changed, 933 insertions(+) create mode 100644 doc/develop/uefi/index.rst create mode 100644 doc/develop/uefi/iscsi.rst create mode 100644 doc/develop/uefi/u-boot_on_efi.rst create mode 100644 doc/develop/uefi/uefi.rst (limited to 'doc/develop') diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 41c0ba1ebd9..84914bb47bf 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -13,6 +13,7 @@ Implementation global_data logging menus + uefi/index version Debugging diff --git a/doc/develop/uefi/index.rst b/doc/develop/uefi/index.rst new file mode 100644 index 00000000000..7e65dbc5d5e --- /dev/null +++ b/doc/develop/uefi/index.rst @@ -0,0 +1,15 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Unified Extensible Firmware (UEFI) +================================== + +U-Boot provides an implementation of the UEFI API allowing to run UEFI +compliant software like Linux, GRUB, and iPXE. Furthermore U-Boot itself +can be run an UEFI payload. + +.. toctree:: + :maxdepth: 2 + + uefi.rst + u-boot_on_efi.rst + iscsi.rst diff --git a/doc/develop/uefi/iscsi.rst b/doc/develop/uefi/iscsi.rst new file mode 100644 index 00000000000..51d38cde243 --- /dev/null +++ b/doc/develop/uefi/iscsi.rst @@ -0,0 +1,184 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2018 Heinrich Schuchardt + +iSCSI booting with U-Boot and iPXE +================================== + +Motivation +---------- + +U-Boot has only a reduced set of supported network protocols. The focus for +network booting has been on UDP based protocols. A TCP stack and HTTP support +are expected to be integrated in 2018 together with a wget command. + +For booting a diskless computer this leaves us with BOOTP or DHCP to get the +address of a boot script. TFTP or NFS can be used to load the boot script, the +operating system kernel and the initial file system (initrd). + +These protocols are insecure. The client cannot validate the authenticity +of the contacted servers. And the server cannot verify the identity of the +client. + +Furthermore the services providing the operating system loader or kernel are +not the ones that the operating system typically will use. Especially in a SAN +environment this makes updating the operating system a hassle. After installing +a new kernel version the boot files have to be copied to the TFTP server +directory. + +The HTTPS protocol provides certificate based validation of servers. Sensitive +data like passwords can be securely transmitted. + +The iSCSI protocol is used for connecting storage attached networks. It +provides mutual authentication using the CHAP protocol. It typically runs on +a TCP transport. + +Thus a better solution than DHCP/TFTP/NFS boot would be to load a boot script +via HTTPS and to download any other files needed for booting via iSCSI from the +same target where the operating system is installed. + +An alternative to implementing these protocols in U-Boot is to use an existing +software that can run on top of U-Boot. iPXE[1] is the "swiss army knife" of +network booting. It supports both HTTPS and iSCSI. It has a scripting engine for +fine grained control of the boot process and can provide a command shell. + +iPXE can be built as an EFI application (named snp.efi) which can be loaded and +run by U-Boot. + +Boot sequence +------------- + +U-Boot loads the EFI application iPXE snp.efi using the bootefi command. This +application has network access via the simple network protocol offered by +U-Boot. + +iPXE executes its internal script. This script may optionally chain load a +secondary boot script via HTTPS or open a shell. + +For the further boot process iPXE connects to the iSCSI server. This includes +the mutual authentication using the CHAP protocol. After the authentication iPXE +has access to the iSCSI targets. + +For a selected iSCSI target iPXE sets up a handle with the block IO protocol. It +uses the ConnectController boot service of U-Boot to request U-Boot to connect a +file system driver. U-Boot reads from the iSCSI drive via the block IO protocol +offered by iPXE. It creates the partition handles and installs the simple file +protocol. Now iPXE can call the simple file protocol to load GRUB[2]. U-Boot +uses the block IO protocol offered by iPXE to fulfill the request. + +Once GRUB is started it uses the same block IO protocol to load Linux. Via +the EFI stub Linux is called as an EFI application:: + + +--------+ +--------+ + | | Runs | | + | U-Boot |========>| iPXE | + | EFI | | snp.efi| + +--------+ | | DHCP | | + | |<===|********|<========| | + | DHCP | | | Get IP | | + | Server | | | Address | | + | |===>|********|========>| | + +--------+ | | Response| | + | | | | + | | | | + +--------+ | | HTTPS | | + | |<===|********|<========| | + | HTTPS | | | Load | | + | Server | | | Script | | + | |===>|********|========>| | + +--------+ | | | | + | | | | + | | | | + +--------+ | | iSCSI | | + | |<===|********|<========| | + | iSCSI | | | Auth | | + | Server |===>|********|========>| | + | | | | | | + | | | | Loads | | + | |<===|********|<========| | +--------+ + | | | | GRUB | | Runs | | + | |===>|********|========>| |======>| GRUB | + | | | | | | | | + | | | | | | | | + | | | | | | Loads | | + | |<===|********|<========|********|<======| | +--------+ + | | | | | | Linux | | Runs | | + | |===>|********|========>|********|======>| |=====>| Linux | + | | | | | | | | | | + +--------+ +--------+ +--------+ +--------+ | | + | | + | | + | ~ ~ ~ ~| + +Security +-------- + +The iSCSI protocol is not encrypted. The traffic could be secured using IPsec +but neither U-Boot nor iPXE does support this. So we should at least separate +the iSCSI traffic from all other network traffic. This can be achieved using a +virtual local area network (VLAN). + +Configuration +------------- + +iPXE +~~~~ + +For running iPXE on arm64 the bin-arm64-efi/snp.efi build target is needed:: + + git clone http://git.ipxe.org/ipxe.git + cd ipxe/src + make bin-arm64-efi/snp.efi -j6 EMBED=myscript.ipxe + +The available commands for the boot script are documented at: + +http://ipxe.org/cmd + +Credentials are managed as environment variables. These are described here: + +http://ipxe.org/cfg + +iPXE by default will put the CPU to rest when waiting for input. U-Boot does +not wake it up due to missing interrupt support. To avoid this behavior create +file src/config/local/nap.h: + +.. code-block:: c + + /* nap.h */ + #undef NAP_EFIX86 + #undef NAP_EFIARM + #define NAP_NULL + +The supported commands in iPXE are controlled by an include, too. Putting the +following into src/config/local/general.h is sufficient for most use cases: + +.. code-block:: c + + /* general.h */ + #define NSLOOKUP_CMD /* Name resolution command */ + #define PING_CMD /* Ping command */ + #define NTP_CMD /* NTP commands */ + #define VLAN_CMD /* VLAN commands */ + #define IMAGE_EFI /* EFI image support */ + #define DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ + #define DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ + #define DOWNLOAD_PROTO_NFS /* Network File System Protocol */ + #define DOWNLOAD_PROTO_FILE /* Local file system access */ + +Open-iSCSI +~~~~~~~~~~ + +When the root file system is on an iSCSI drive you should disable pings and set +the replacement timer to a high value in the configuration file [3]:: + + node.conn[0].timeo.noop_out_interval = 0 + node.conn[0].timeo.noop_out_timeout = 0 + node.session.timeo.replacement_timeout = 86400 + +Links +----- + +* [1] https://ipxe.org - iPXE open source boot firmware +* [2] https://www.gnu.org/software/grub/ - + GNU GRUB (Grand Unified Bootloader) +* [3] https://github.com/open-iscsi/open-iscsi/blob/master/README - + Open-iSCSI README diff --git a/doc/develop/uefi/u-boot_on_efi.rst b/doc/develop/uefi/u-boot_on_efi.rst new file mode 100644 index 00000000000..c9a41bc919f --- /dev/null +++ b/doc/develop/uefi/u-boot_on_efi.rst @@ -0,0 +1,235 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2015 Google, Inc + +U-Boot on EFI +============= +This document provides information about U-Boot running on top of EFI, either +as an application or just as a means of getting U-Boot onto a new platform. + + +Motivation +---------- +Running U-Boot on EFI is useful in several situations: + +- You have EFI running on a board but U-Boot does not natively support it + fully yet. You can boot into U-Boot from EFI and use that until U-Boot is + fully ported + +- You need to use an EFI implementation (e.g. UEFI) because your vendor + requires it in order to provide support + +- You plan to use coreboot to boot into U-Boot but coreboot support does + not currently exist for your platform. In the meantime you can use U-Boot + on EFI and then move to U-Boot on coreboot when ready + +- You use EFI but want to experiment with a simpler alternative like U-Boot + + +Status +------ +Only x86 is supported at present. If you are using EFI on another architecture +you may want to reconsider. However, much of the code is generic so could be +ported. + +U-Boot supports running as an EFI application for 32-bit EFI only. This is +not very useful since only a serial port is provided. You can look around at +memory and type 'help' but that is about it. + +More usefully, U-Boot supports building itself as a payload for either 32-bit +or 64-bit EFI. U-Boot is packaged up and loaded in its entirety by EFI. Once +started, U-Boot changes to 32-bit mode (currently) and takes over the +machine. You can use devices, boot a kernel, etc. + + +Build Instructions +------------------ +First choose a board that has EFI support and obtain an EFI implementation +for that board. It will be either 32-bit or 64-bit. Alternatively, you can +opt for using QEMU [1] and the OVMF [2], as detailed below. + +To build U-Boot as an EFI application (32-bit EFI required), enable CONFIG_EFI +and CONFIG_EFI_APP. The efi-x86_app config (efi-x86_app_defconfig) is set up +for this. Just build U-Boot as normal, e.g.:: + + make efi-x86_app_defconfig + make + +To build U-Boot as an EFI payload (32-bit or 64-bit EFI can be used), enable +CONFIG_EFI, CONFIG_EFI_STUB, and select either CONFIG_EFI_STUB_32BIT or +CONFIG_EFI_STUB_64BIT. The efi-x86_payload configs (efi-x86_payload32_defconfig +and efi-x86_payload32_defconfig) are set up for this. Then build U-Boot as +normal, e.g.:: + + make efi-x86_payload32_defconfig (or efi-x86_payload64_defconfig) + make + +You will end up with one of these files depending on what you build for: + +* u-boot-app.efi - U-Boot EFI application +* u-boot-payload.efi - U-Boot EFI payload application + + +Trying it out +------------- +QEMU is an emulator and it can emulate an x86 machine. Please make sure your +QEMU version is 2.3.0 or above to test this. You can run the payload with +something like this:: + + mkdir /tmp/efi + cp /path/to/u-boot*.efi /tmp/efi + qemu-system-x86_64 -bios bios.bin -hda fat:/tmp/efi/ + +Add -nographic if you want to use the terminal for output. Once it starts +type 'fs0:u-boot-payload.efi' to run the payload or 'fs0:u-boot-app.efi' to +run the application. 'bios.bin' is the EFI 'BIOS'. Check [2] to obtain a +prebuilt EFI BIOS for QEMU or you can build one from source as well. + +To try it on real hardware, put u-boot-app.efi on a suitable boot medium, +such as a USB stick. Then you can type something like this to start it:: + + fs0:u-boot-payload.efi + +(or fs0:u-boot-app.efi for the application) + +This will start the payload, copy U-Boot into RAM and start U-Boot. Note +that EFI does not support booting a 64-bit application from a 32-bit +EFI (or vice versa). Also it will often fail to print an error message if +you get this wrong. + + +Inner workings +-------------- +Here follow a few implementation notes for those who want to fiddle with +this and perhaps contribute patches. + +The application and payload approaches sound similar but are in fact +implemented completely differently. + +EFI Application +~~~~~~~~~~~~~~~ +For the application the whole of U-Boot is built as a shared library. The +efi_main() function is in lib/efi/efi_app.c. It sets up some basic EFI +functions with efi_init(), sets up U-Boot global_data, allocates memory for +U-Boot's malloc(), etc. and enters the normal init sequence (board_init_f() +and board_init_r()). + +Since U-Boot limits its memory access to the allocated regions very little +special code is needed. The CONFIG_EFI_APP option controls a few things +that need to change so 'git grep CONFIG_EFI_APP' may be instructive. +The CONFIG_EFI option controls more general EFI adjustments. + +The only available driver is the serial driver. This calls back into EFI +'boot services' to send and receive characters. Although it is implemented +as a serial driver the console device is not necessarilly serial. If you +boot EFI with video output then the 'serial' device will operate on your +target devices's display instead and the device's USB keyboard will also +work if connected. If you have both serial and video output, then both +consoles will be active. Even though U-Boot does the same thing normally, +These are features of EFI, not U-Boot. + +Very little code is involved in implementing the EFI application feature. +U-Boot is highly portable. Most of the difficulty is in modifying the +Makefile settings to pass the right build flags. In particular there is very +little x86-specific code involved - you can find most of it in +arch/x86/cpu. Porting to ARM (which can also use EFI if you are brave +enough) should be straightforward. + +Use the 'reset' command to get back to EFI. + +EFI Payload +~~~~~~~~~~~ +The payload approach is a different kettle of fish. It works by building +U-Boot exactly as normal for your target board, then adding the entire +image (including device tree) into a small EFI stub application responsible +for booting it. The stub application is built as a normal EFI application +except that it has a lot of data attached to it. + +The stub application is implemented in lib/efi/efi_stub.c. The efi_main() +function is called by EFI. It is responsible for copying U-Boot from its +original location into memory, disabling EFI boot services and starting +U-Boot. U-Boot then starts as normal, relocates, starts all drivers, etc. + +The stub application is architecture-dependent. At present it has some +x86-specific code and a comment at the top of efi_stub.c describes this. + +While the stub application does allocate some memory from EFI this is not +used by U-Boot (the payload). In fact when U-Boot starts it has all of the +memory available to it and can operate as it pleases (but see the next +section). + +Tables +~~~~~~ +The payload can pass information to U-Boot in the form of EFI tables. At +present this feature is used to pass the EFI memory map, an inordinately +large list of memory regions. You can use the 'efi mem all' command to +display this list. U-Boot uses the list to work out where to relocate +itself. + +Although U-Boot can use any memory it likes, EFI marks some memory as used +by 'run-time services', code that hangs around while U-Boot is running and +is even present when Linux is running. This is common on x86 and provides +a way for Linux to call back into the firmware to control things like CPU +fan speed. U-Boot uses only 'conventional' memory, in EFI terminology. It +will relocate itself to the top of the largest block of memory it can find +below 4GB. + +Interrupts +~~~~~~~~~~ +U-Boot drivers typically don't use interrupts. Since EFI enables interrupts +it is possible that an interrupt will fire that U-Boot cannot handle. This +seems to cause problems. For this reason the U-Boot payload runs with +interrupts disabled at present. + +32/64-bit +~~~~~~~~~ +While the EFI application can in principle be built as either 32- or 64-bit, +only 32-bit is currently supported. This means that the application can only +be used with 32-bit EFI. + +The payload stub can be build as either 32- or 64-bits. Only a small amount +of code is built this way (see the extra- line in lib/efi/Makefile). +Everything else is built as a normal U-Boot, so is always 32-bit on x86 at +present. + +Future work +----------- +This work could be extended in a number of ways: + +- Add ARM support + +- Add 64-bit application support + +- Figure out how to solve the interrupt problem + +- Add more drivers to the application side (e.g. video, block devices, USB, + environment access). This would mostly be an academic exercise as a strong + use case is not readily apparent, but it might be fun. + +- Avoid turning off boot services in the stub. Instead allow U-Boot to make + use of boot services in case it wants to. It is unclear what it might want + though. + +Where is the code? +------------------ +lib/efi + payload stub, application, support code. Mostly arch-neutral + +arch/x86/cpu/efi + x86 support code for running as an EFI application and payload + +board/efi/efi-x86_app/efi.c + x86 board code for running as an EFI application + +board/efi/efi-x86_payload + generic x86 EFI payload board support code + +common/cmd_efi.c + the 'efi' command + +-- +Ben Stoltz, Simon Glass +Google, Inc +July 2015 + +* [1] http://www.qemu.org +* [2] http://www.tianocore.org/ovmf/ diff --git a/doc/develop/uefi/uefi.rst b/doc/develop/uefi/uefi.rst new file mode 100644 index 00000000000..5a67737c157 --- /dev/null +++ b/doc/develop/uefi/uefi.rst @@ -0,0 +1,498 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2018 Heinrich Schuchardt + +UEFI on U-Boot +============== + +The Unified Extensible Firmware Interface Specification (UEFI) [1] has become +the default for booting on AArch64 and x86 systems. It provides a stable API for +the interaction of drivers and applications with the firmware. The API comprises +access to block storage, network, and console to name a few. The Linux kernel +and boot loaders like GRUB or the FreeBSD loader can be executed. + +Development target +------------------ + +The implementation of UEFI in U-Boot strives to reach the requirements described +in the "Embedded Base Boot Requirements (EBBR) Specification - Release v1.0" +[2]. The "Server Base Boot Requirements System Software on ARM Platforms" [3] +describes a superset of the EBBR specification and may be used as further +reference. + +A full blown UEFI implementation would contradict the U-Boot design principle +"keep it small". + +Building U-Boot for UEFI +------------------------ + +The UEFI standard supports only little-endian systems. The UEFI support can be +activated for ARM and x86 by specifying:: + + CONFIG_CMD_BOOTEFI=y + CONFIG_EFI_LOADER=y + +in the .config file. + +Support for attaching virtual block devices, e.g. iSCSI drives connected by the +loaded UEFI application [4], requires:: + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +Executing a UEFI binary +~~~~~~~~~~~~~~~~~~~~~~~ + +The bootefi command is used to start UEFI applications or to install UEFI +drivers. It takes two parameters:: + + bootefi [fdt address] + +* image address - the memory address of the UEFI binary +* fdt address - the memory address of the flattened device tree + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:2 ${fdt_addr_r} boot/dtb + 29830 bytes read in 14 ms (2 MiB/s) + => load mmc 0:1 ${kernel_addr_r} efi/debian/grubaa64.efi + reading efi/debian/grubaa64.efi + 120832 bytes read in 7 ms (16.5 MiB/s) + => bootefi ${kernel_addr_r} ${fdt_addr_r} + +When booting from a memory location it is unknown from which file it was loaded. +Therefore the bootefi command uses the device path of the block device partition +or the network adapter and the file name of the most recently loaded PE-COFF +file when setting up the loaded image protocol. + +Launching a UEFI binary from a FIT image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A signed FIT image can be used to securely boot a UEFI image via the +bootm command. This feature is available if U-Boot is configured with:: + + CONFIG_BOOTM_EFI=y + +A sample configuration is provided as file doc/uImage.FIT/uefi.its. + +Below you find the output of an example session starting GRUB:: + + => load mmc 0:1 ${kernel_addr_r} image.fit + 4620426 bytes read in 83 ms (53.1 MiB/s) + => bootm ${kernel_addr_r}#config-grub-nofdt + ## Loading kernel from FIT Image at 40400000 ... + Using 'config-grub-nofdt' configuration + Verifying Hash Integrity ... sha256,rsa2048:dev+ OK + Trying 'efi-grub' kernel subimage + Description: GRUB EFI Firmware + Created: 2019-11-20 8:18:16 UTC + Type: Kernel Image (no loading done) + Compression: uncompressed + Data Start: 0x404000d0 + Data Size: 450560 Bytes = 440 KiB + Hash algo: sha256 + Hash value: 4dbee00021112df618f58b3f7cf5e1595533d543094064b9ce991e8b054a9eec + Verifying Hash Integrity ... sha256+ OK + XIP Kernel Image (no loading done) + ## Transferring control to EFI (at address 404000d0) ... + Welcome to GRUB! + +See doc/uImage.FIT/howto.txt for an introduction to FIT images. + +Configuring UEFI secure boot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The UEFI specification[1] defines a secure way of executing UEFI images +by verifying a signature (or message digest) of image with certificates. +This feature on U-Boot is enabled with:: + + CONFIG_UEFI_SECURE_BOOT=y + +To make the boot sequence safe, you need to establish a chain of trust; +In UEFI secure boot the chain trust is defined by the following UEFI variables + +* PK - Platform Key +* KEK - Key Exchange Keys +* db - white list database +* dbx - black list database + +An in depth description of UEFI secure boot is beyond the scope of this +document. Please, refer to the UEFI specification and available online +documentation. Here is a simple example that you can follow for your initial +attempt (Please note that the actual steps will depend on your system and +environment.): + +Install the required tools on your host + +* openssl +* efitools +* sbsigntool + +Create signing keys and the key database on your host: + +The platform key + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ \ + -keyout PK.key -out PK.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + PK.crt PK.esl; + sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth + +The key exchange keys + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ \ + -keyout KEK.key -out KEK.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + KEK.crt KEK.esl + sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth + +The whitelist database + +.. code-block:: bash + + openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ \ + -keyout db.key -out db.crt -nodes -days 365 + cert-to-efi-sig-list -g 11111111-2222-3333-4444-123456789abc \ + db.crt db.esl + sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth + +Copy the \*.auth files to media, say mmc, that is accessible from U-Boot. + +Sign an image with one of the keys in "db" on your host + +.. code-block:: bash + + sbsign --key db.key --cert db.crt helloworld.efi + +Now in U-Boot install the keys on your board:: + + fatload mmc 0:1 PK.auth + setenv -e -nv -bs -rt -at -i :$filesize PK + fatload mmc 0:1 KEK.auth + setenv -e -nv -bs -rt -at -i :$filesize KEK + fatload mmc 0:1 db.auth + setenv -e -nv -bs -rt -at -i :$filesize db + +Set up boot parameters on your board:: + + efidebug boot add 1 HELLO mmc 0:1 /helloworld.efi.signed "" + +Now your board can run the signed image via the boot manager (see below). +You can also try this sequence by running Pytest, test_efi_secboot, +on the sandbox + +.. code-block:: bash + + cd + pytest.py test/py/tests/test_efi_secboot/test_signed.py --bd sandbox + +UEFI binaries may be signed by Microsoft using the following certificates: + +* KEK: Microsoft Corporation KEK CA 2011 + http://go.microsoft.com/fwlink/?LinkId=321185. +* db: Microsoft Windows Production PCA 2011 + http://go.microsoft.com/fwlink/p/?linkid=321192. +* db: Microsoft Corporation UEFI CA 2011 + http://go.microsoft.com/fwlink/p/?linkid=321194. + +Using OP-TEE for EFI variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of implementing UEFI variable services inside U-Boot they can +also be provided in the secure world by a module for OP-TEE[1]. The +interface between U-Boot and OP-TEE for variable services is enabled by +CONFIG_EFI_MM_COMM_TEE=y. + +Tianocore EDK II's standalone management mode driver for variables can +be linked to OP-TEE for this purpose. This module uses the Replay +Protected Memory Block (RPMB) of an eMMC device for persisting +non-volatile variables. When calling the variable services via the +OP-TEE API U-Boot's OP-TEE supplicant relays calls to the RPMB driver +which has to be enabled via CONFIG_SUPPORT_EMMC_RPMB=y. + +[1] https://optee.readthedocs.io/ - OP-TEE documentation + +Executing the boot manager +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The UEFI specification foresees to define boot entries and boot sequence via +UEFI variables. Booting according to these variables is possible via:: + + bootefi bootmgr [fdt address] + +As of U-Boot v2020.10 UEFI variables cannot be set at runtime. The U-Boot +command 'efidebug' can be used to set the variables. + +Executing the built in hello world application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A hello world UEFI application can be built with:: + + CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y + +It can be embedded into the U-Boot binary with:: + + CONFIG_CMD_BOOTEFI_HELLO=y + +The bootefi command is used to start the embedded hello world application:: + + bootefi hello [fdt address] + +Below you find the output of an example session:: + + => bootefi hello ${fdtcontroladdr} + ## Starting EFI application at 01000000 ... + WARNING: using memory device/image path, this may confuse some payloads! + Hello, world! + Running on UEFI 2.7 + Have SMBIOS table + Have device tree + Load options: root=/dev/sdb3 init=/sbin/init rootwait ro + ## Application terminated, r = 0 + +The environment variable fdtcontroladdr points to U-Boot's internal device tree +(if available). + +Executing the built-in self-test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An UEFI self-test suite can be embedded in U-Boot by building with:: + + CONFIG_CMD_BOOTEFI_SELFTEST=y + +For testing the UEFI implementation the bootefi command can be used to start the +self-test:: + + bootefi selftest [fdt address] + +The environment variable 'efi_selftest' can be used to select a single test. If +it is not provided all tests are executed except those marked as 'on request'. +If the environment variable is set to 'list' a list of all tests is shown. + +Below you can find the output of an example session:: + + => setenv efi_selftest simple network protocol + => bootefi selftest + Testing EFI API implementation + Selected test: 'simple network protocol' + Setting up 'simple network protocol' + Setting up 'simple network protocol' succeeded + Executing 'simple network protocol' + DHCP Discover + DHCP reply received from 192.168.76.2 (52:55:c0:a8:4c:02) + as broadcast message. + Executing 'simple network protocol' succeeded + Tearing down 'simple network protocol' + Tearing down 'simple network protocol' succeeded + Boot services terminated + Summary: 0 failures + Preparing for reset. Press any key. + +The UEFI life cycle +------------------- + +After the U-Boot platform has been initialized the UEFI API provides two kinds +of services: + +* boot services +* runtime services + +The API can be extended by loading UEFI drivers which come in two variants: + +* boot drivers +* runtime drivers + +UEFI drivers are installed with U-Boot's bootefi command. With the same command +UEFI applications can be executed. + +Loaded images of UEFI drivers stay in memory after returning to U-Boot while +loaded images of applications are removed from memory. + +An UEFI application (e.g. an operating system) that wants to take full control +of the system calls ExitBootServices. After a UEFI application calls +ExitBootServices + +* boot services are not available anymore +* timer events are stopped +* the memory used by U-Boot except for runtime services is released +* the memory used by boot time drivers is released + +So this is a point of no return. Afterwards the UEFI application can only return +to U-Boot by rebooting. + +The UEFI object model +--------------------- + +UEFI offers a flexible and expandable object model. The objects in the UEFI API +are devices, drivers, and loaded images. These objects are referenced by +handles. + +The interfaces implemented by the objects are referred to as protocols. These +are identified by GUIDs. They can be installed and uninstalled by calling the +appropriate boot services. + +Handles are created by the InstallProtocolInterface or the +InstallMultipleProtocolinterfaces service if NULL is passed as handle. + +Handles are deleted when the last protocol has been removed with the +UninstallProtocolInterface or the UninstallMultipleProtocolInterfaces service. + +Devices offer the EFI_DEVICE_PATH_PROTOCOL. A device path is the concatenation +of device nodes. By their device paths all devices of a system are arranged in a +tree. + +Drivers offer the EFI_DRIVER_BINDING_PROTOCOL. This protocol is used to connect +a driver to devices (which are referenced as controllers in this context). + +Loaded images offer the EFI_LOADED_IMAGE_PROTOCOL. This protocol provides meta +information about the image and a pointer to the unload callback function. + +The UEFI events +--------------- + +In the UEFI terminology an event is a data object referencing a notification +function which is queued for calling when the event is signaled. The following +types of events exist: + +* periodic and single shot timer events +* exit boot services events, triggered by calling the ExitBootServices() service +* virtual address change events +* memory map change events +* read to boot events +* reset system events +* system table events +* events that are only triggered programmatically + +Events can be created with the CreateEvent service and deleted with CloseEvent +service. + +Events can be assigned to an event group. If any of the events in a group is +signaled, all other events in the group are also set to the signaled state. + +The UEFI driver model +--------------------- + +A driver is specific for a single protocol installed on a device. To install a +driver on a device the ConnectController service is called. In this context +controller refers to the device for which the driver is installed. + +The relevant drivers are identified using the EFI_DRIVER_BINDING_PROTOCOL. This +protocol has has three functions: + +* supported - determines if the driver is compatible with the device +* start - installs the driver by opening the relevant protocol with + attribute EFI_OPEN_PROTOCOL_BY_DRIVER +* stop - uninstalls the driver + +The driver may create child controllers (child devices). E.g. a driver for block +IO devices will create the device handles for the partitions. The child +controllers will open the supported protocol with the attribute +EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + +A driver can be detached from a device using the DisconnectController service. + +U-Boot devices mapped as UEFI devices +------------------------------------- + +Some of the U-Boot devices are mapped as UEFI devices + +* block IO devices +* console +* graphical output +* network adapter + +As of U-Boot 2018.03 the logic for doing this is hard coded. + +The development target is to integrate the setup of these UEFI devices with the +U-Boot driver model [5]. So when a U-Boot device is discovered a handle should +be created and the device path protocol and the relevant IO protocol should be +installed. The UEFI driver then would be attached by calling ConnectController. +When a U-Boot device is removed DisconnectController should be called. + +UEFI devices mapped as U-Boot devices +------------------------------------- + +UEFI drivers binaries and applications may create new (virtual) devices, install +a protocol and call the ConnectController service. Now the matching UEFI driver +is determined by iterating over the implementations of the +EFI_DRIVER_BINDING_PROTOCOL. + +It is the task of the UEFI driver to create a corresponding U-Boot device and to +proxy calls for this U-Boot device to the controller. + +In U-Boot 2018.03 this has only been implemented for block IO devices. + +UEFI uclass +~~~~~~~~~~~ + +An UEFI uclass driver (lib/efi_driver/efi_uclass.c) has been created that +takes care of initializing the UEFI drivers and providing the +EFI_DRIVER_BINDING_PROTOCOL implementation for the UEFI drivers. + +A linker created list is used to keep track of the UEFI drivers. To create an +entry in the list the UEFI driver uses the U_BOOT_DRIVER macro specifying +UCLASS_EFI as the ID of its uclass, e.g:: + + /* Identify as UEFI driver */ + U_BOOT_DRIVER(efi_block) = { + .name = "EFI block driver", + .id = UCLASS_EFI, + .ops = &driver_ops, + }; + +The available operations are defined via the structure struct efi_driver_ops:: + + struct efi_driver_ops { + const efi_guid_t *protocol; + const efi_guid_t *child_protocol; + int (*bind)(efi_handle_t handle, void *interface); + }; + +When the supported() function of the EFI_DRIVER_BINDING_PROTOCOL is called the +uclass checks if the protocol GUID matches the protocol GUID of the UEFI driver. +In the start() function the bind() function of the UEFI driver is called after +checking the GUID. +The stop() function of the EFI_DRIVER_BINDING_PROTOCOL disconnects the child +controllers created by the UEFI driver and the UEFI driver. (In U-Boot v2013.03 +this is not yet completely implemented.) + +UEFI block IO driver +~~~~~~~~~~~~~~~~~~~~ + +The UEFI block IO driver supports devices exposing the EFI_BLOCK_IO_PROTOCOL. + +When connected it creates a new U-Boot block IO device with interface type +IF_TYPE_EFI, adds child controllers mapping the partitions, and installs the +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on these. This can be used together with the +software iPXE to boot from iSCSI network drives [4]. + +This driver is only available if U-Boot is configured with:: + + CONFIG_BLK=y + CONFIG_PARTITIONS=y + +Miscellaneous +------------- + +Load file 2 protocol +~~~~~~~~~~~~~~~~~~~~ + +The load file 2 protocol can be used by the Linux kernel to load the initial +RAM disk. U-Boot can be configured to provide an implementation with:: + + EFI_LOAD_FILE2_INITRD=y + EFI_INITRD_FILESPEC=interface dev:part path_to_initrd + +Links +----- + +* [1] http://uefi.org/specifications - UEFI specifications +* [2] https://github.com/ARM-software/ebbr/releases/download/v1.0/ebbr-v1.0.pdf - + Embedded Base Boot Requirements (EBBR) Specification - Release v1.0 +* [3] https://developer.arm.com/docs/den0044/latest/server-base-boot-requirements-system-software-on-arm-platforms-version-11 - + Server Base Boot Requirements System Software on ARM Platforms - Version 1.1 +* [4] :doc:`iscsi` +* [5] :doc:`../driver-model/index` -- cgit v1.3.1 From 5ce319133b2364e3283c3cde7a269681ff8431af Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:12 +1300 Subject: doc: Move driver model docs under develop/ These docs are useful for developers, not users. Move them under that section. Suggested-by: Heinrich Schuchardt Signed-off-by: Simon Glass --- doc/develop/driver-model/bind.rst | 49 + doc/develop/driver-model/debugging.rst | 62 ++ doc/develop/driver-model/design.rst | 1016 +++++++++++++++++++++ doc/develop/driver-model/ethernet.rst | 321 +++++++ doc/develop/driver-model/fdt-fixup.rst | 132 +++ doc/develop/driver-model/fs_firmware_loader.rst | 154 ++++ doc/develop/driver-model/i2c-howto.rst | 56 ++ doc/develop/driver-model/index.rst | 29 + doc/develop/driver-model/livetree.rst | 286 ++++++ doc/develop/driver-model/migration.rst | 101 ++ doc/develop/driver-model/of-plat.rst | 913 ++++++++++++++++++ doc/develop/driver-model/pci-info.rst | 172 ++++ doc/develop/driver-model/pmic-framework.rst | 143 +++ doc/develop/driver-model/remoteproc-framework.rst | 169 ++++ doc/develop/driver-model/serial-howto.rst | 46 + doc/develop/driver-model/soc-framework.rst | 68 ++ doc/develop/driver-model/spi-howto.rst | 692 ++++++++++++++ doc/develop/driver-model/usb-info.rst | 423 +++++++++ doc/develop/index.rst | 1 + doc/driver-model/bind.rst | 49 - doc/driver-model/debugging.rst | 62 -- doc/driver-model/design.rst | 1016 --------------------- doc/driver-model/ethernet.rst | 321 ------- doc/driver-model/fdt-fixup.rst | 132 --- doc/driver-model/fs_firmware_loader.rst | 154 ---- doc/driver-model/i2c-howto.rst | 56 -- doc/driver-model/index.rst | 25 - doc/driver-model/livetree.rst | 286 ------ doc/driver-model/migration.rst | 101 -- doc/driver-model/of-plat.rst | 913 ------------------ doc/driver-model/pci-info.rst | 172 ---- doc/driver-model/pmic-framework.rst | 143 --- doc/driver-model/remoteproc-framework.rst | 169 ---- doc/driver-model/serial-howto.rst | 46 - doc/driver-model/soc-framework.rst | 68 -- doc/driver-model/spi-howto.rst | 692 -------------- doc/driver-model/usb-info.rst | 423 --------- doc/index.rst | 11 - 38 files changed, 4833 insertions(+), 4839 deletions(-) create mode 100644 doc/develop/driver-model/bind.rst create mode 100644 doc/develop/driver-model/debugging.rst create mode 100644 doc/develop/driver-model/design.rst create mode 100644 doc/develop/driver-model/ethernet.rst create mode 100644 doc/develop/driver-model/fdt-fixup.rst create mode 100644 doc/develop/driver-model/fs_firmware_loader.rst create mode 100644 doc/develop/driver-model/i2c-howto.rst create mode 100644 doc/develop/driver-model/index.rst create mode 100644 doc/develop/driver-model/livetree.rst create mode 100644 doc/develop/driver-model/migration.rst create mode 100644 doc/develop/driver-model/of-plat.rst create mode 100644 doc/develop/driver-model/pci-info.rst create mode 100644 doc/develop/driver-model/pmic-framework.rst create mode 100644 doc/develop/driver-model/remoteproc-framework.rst create mode 100644 doc/develop/driver-model/serial-howto.rst create mode 100644 doc/develop/driver-model/soc-framework.rst create mode 100644 doc/develop/driver-model/spi-howto.rst create mode 100644 doc/develop/driver-model/usb-info.rst delete mode 100644 doc/driver-model/bind.rst delete mode 100644 doc/driver-model/debugging.rst delete mode 100644 doc/driver-model/design.rst delete mode 100644 doc/driver-model/ethernet.rst delete mode 100644 doc/driver-model/fdt-fixup.rst delete mode 100644 doc/driver-model/fs_firmware_loader.rst delete mode 100644 doc/driver-model/i2c-howto.rst delete mode 100644 doc/driver-model/index.rst delete mode 100644 doc/driver-model/livetree.rst delete mode 100644 doc/driver-model/migration.rst delete mode 100644 doc/driver-model/of-plat.rst delete mode 100644 doc/driver-model/pci-info.rst delete mode 100644 doc/driver-model/pmic-framework.rst delete mode 100644 doc/driver-model/remoteproc-framework.rst delete mode 100644 doc/driver-model/serial-howto.rst delete mode 100644 doc/driver-model/soc-framework.rst delete mode 100644 doc/driver-model/spi-howto.rst delete mode 100644 doc/driver-model/usb-info.rst (limited to 'doc/develop') diff --git a/doc/develop/driver-model/bind.rst b/doc/develop/driver-model/bind.rst new file mode 100644 index 00000000000..b19661b5fe2 --- /dev/null +++ b/doc/develop/driver-model/bind.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Patrice Chotard + +Binding/unbinding a driver +========================== + +This document aims to describe the bind and unbind commands. + +For debugging purpose, it should be useful to bind or unbind a driver from +the U-boot command line. + +The unbind command calls the remove device driver callback and unbind the +device from its driver. + +The bind command binds a device to its driver. + +In some cases it can be useful to be able to bind a device to a driver from +the command line. +The obvious example is for versatile devices such as USB gadget. +Another use case is when the devices are not yet ready at startup and +require some setup before the drivers are bound (ex: FPGA which bitsream is +fetched from a mass storage or ethernet) + +usage: + +bind +bind + +unbind +unbind +unbind + +Where: + - is the node's device tree path + - is one of the class available in the list given by the "dm uclass" + command or first column of "dm tree" command. + - is the index of the parent's node (second column of "dm tree" output). + - is the driver name to bind given by the "dm drivers" command or the by + the fourth column of "dm tree" output. + +example: + +bind usb_dev_generic 0 usb_ether +unbind usb_dev_generic 0 usb_ether +or +unbind eth 1 + +bind /ocp/omap_dwc3@48380000/usb@48390000 usb_ether +unbind /ocp/omap_dwc3@48380000/usb@48390000 diff --git a/doc/develop/driver-model/debugging.rst b/doc/develop/driver-model/debugging.rst new file mode 100644 index 00000000000..bbb2794340f --- /dev/null +++ b/doc/develop/driver-model/debugging.rst @@ -0,0 +1,62 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Simon Glass + +Debugging driver model +====================== + +This document aims to provide help when you cannot work out why driver model is +not doing what you expect. + + +Useful techniques in general +---------------------------- + +Here are some useful debugging features generally. + + - If you are writing a new feature, consider doing it in sandbox instead of + on your board. Sandbox has no limits, allows easy debugging (e.g. gdb) and + you can write emulators for most common devices. + - Put '#define DEBUG' at the top of a file, to activate all the debug() and + log_debug() statements in that file. + - Where logging is used, change the logging level, e.g. in SPL with + CONFIG_SPL_LOG_MAX_LEVEL=7 (which is LOGL_DEBUG) and + CONFIG_LOG_DEFAULT_LEVEL=7 + - Where logging of return values is implemented with log_msg_ret(), set + CONFIG_LOG_ERROR_RETURN=y to see exactly where the error is happening + - Make sure you have a debug UART enabled - see CONFIG_DEBUG_UART. With this + you can get serial output (printf(), etc.) before the serial driver is + running. + - Use a JTAG emulator to set breakpoints and single-step through code + +Not that most of these increase code/data size somewhat when enabled. + + +Failure to locate a device +-------------------------- + +Let's say you have uclass_first_device_err() and it is not finding anything. + +If it is returning an error, then that gives you a clue. Look up linux/errno.h +to see errors. Common ones are: + + - -ENOMEM which indicates that memory is short. If it happens in SPL or + before relocation in U-Boot, check CONFIG_SPL_SYS_MALLOC_F_LEN and + CONFIG_SYS_MALLOC_F_LEN as they may need to be larger. Add '#define DEBUG' + at the very top of malloc_simple.c to get an idea of where your memory is + going. + - -EINVAL which typically indicates that something was missing or wrong in + the device tree node. Check that everything is correct and look at the + of_to_plat() method in the driver. + +If there is no error, you should check if the device is actually bound. Call +dm_dump_all() just before you locate the device to make sure it exists. + +If it does not exist, check your device tree compatible strings match up with +what the driver expects (in the struct udevice_id array). + +If you are using of-platdata (e.g. CONFIG_SPL_OF_PLATDATA), check that the +driver name is the same as the first compatible string in the device tree (with +invalid-variable characters converted to underscore). + +If you are really stuck, putting '#define LOG_DEBUG' at the top of +drivers/core/lists.c should show you what is going on. diff --git a/doc/develop/driver-model/design.rst b/doc/develop/driver-model/design.rst new file mode 100644 index 00000000000..4e5cecbab6a --- /dev/null +++ b/doc/develop/driver-model/design.rst @@ -0,0 +1,1016 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Simon Glass + +Design Details +============== + +This README contains high-level information about driver model, a unified +way of declaring and accessing drivers in U-Boot. The original work was done +by: + + * Marek Vasut + * Pavel Herrmann + * Viktor Křivák + * Tomas Hlavacek + +This has been both simplified and extended into the current implementation +by: + + * Simon Glass + + +Terminology +----------- + +Uclass + a group of devices which operate in the same way. A uclass provides + a way of accessing individual devices within the group, but always + using the same interface. For example a GPIO uclass provides + operations for get/set value. An I2C uclass may have 10 I2C ports, + 4 with one driver, and 6 with another. + +Driver + some code which talks to a peripheral and presents a higher-level + interface to it. + +Device + an instance of a driver, tied to a particular port or peripheral. + + +How to try it +------------- + +Build U-Boot sandbox and run it:: + + make sandbox_defconfig + make + ./u-boot -d u-boot.dtb + + (type 'reset' to exit U-Boot) + + +There is a uclass called 'demo'. This uclass handles +saying hello, and reporting its status. There are two drivers in this +uclass: + + - simple: Just prints a message for hello, doesn't implement status + - shape: Prints shapes and reports number of characters printed as status + +The demo class is pretty simple, but not trivial. The intention is that it +can be used for testing, so it will implement all driver model features and +provide good code coverage of them. It does have multiple drivers, it +handles parameter data and plat (data which tells the driver how +to operate on a particular platform) and it uses private driver data. + +To try it, see the example session below:: + + =>demo hello 1 + Hello '@' from 07981110: red 4 + =>demo status 2 + Status: 0 + =>demo hello 2 + g + r@ + e@@ + e@@@ + n@@@@ + g@@@@@ + =>demo status 2 + Status: 21 + =>demo hello 4 ^ + y^^^ + e^^^^^ + l^^^^^^^ + l^^^^^^^ + o^^^^^ + w^^^ + =>demo status 4 + Status: 36 + => + + +Running the tests +----------------- + +The intent with driver model is that the core portion has 100% test coverage +in sandbox, and every uclass has its own test. As a move towards this, tests +are provided in test/dm. To run them, try:: + + ./test/py/test.py --bd sandbox --build -k ut_dm -v + +You should see something like this:: + + (venv)$ ./test/py/test.py --bd sandbox --build -k ut_dm -v + +make O=/root/u-boot/build-sandbox -s sandbox_defconfig + +make O=/root/u-boot/build-sandbox -s -j8 + ============================= test session starts ============================== + platform linux2 -- Python 2.7.5, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- /root/u-boot/venv/bin/python + cachedir: .cache + rootdir: /root/u-boot, inifile: + collected 199 items + + test/py/tests/test_ut.py::test_ut_dm_init PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_bind] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_conversion] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_shot] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_conversion] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_shot] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_supply] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_adc_wrong_channel_selection] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_autobind] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_alloc] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_valid] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_autoprobe] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind_uclass] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_pre_probe_uclass] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_children] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_funcs] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_iterators] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data_uclass] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_ops] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata_uclass] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_children] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_clk_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_clk_periph] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_device_get_uclass_id] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_eth] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_eth_act] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_eth_alias] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_eth_prime] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_eth_rotate] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_fdt] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_fdt_offset] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_fdt_pre_reloc] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_fdt_uclass_seq] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio_anon] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio_copy] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio_leak] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio_phandles] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_gpio_requestf] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_bytewise] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_find] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset_len] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_probe_empty] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_read_write] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_i2c_speed] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_leak] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_led_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_led_gpio] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_led_label] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_lifecycle] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_mmc_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_net_retry] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_operations] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_ordering] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_pci_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_pci_busnum] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_pci_swapcase] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_platdata] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_get] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_io] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset_list] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_get] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_current] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_enable] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_mode] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_voltage] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_pre_reloc] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_ram_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_regmap_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_regmap_syscon] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_remoteproc_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_remove] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_reset_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_reset_walk] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_rtc_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_rtc_dual] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_rtc_reset] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_rtc_set_get] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_spi_find] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_spi_flash] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_spi_xfer] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_syscon_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_syscon_by_driver_data] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_timer_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass_before_ready] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find_by_name] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get_by_name] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_flash] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_keyb] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_multi] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_remove] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_remove] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_reorder] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_base] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp_comp] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_chars] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_context] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation1] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation2] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation3] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_text] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_bs] PASSED + test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_scroll] PASSED + + ======================= 84 tests deselected by '-kut_dm' ======================= + ================== 115 passed, 84 deselected in 3.77 seconds =================== + +What is going on? +----------------- + +Let's start at the top. The demo command is in cmd/demo.c. It does +the usual command processing and then: + +.. code-block:: c + + struct udevice *demo_dev; + + ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); + +UCLASS_DEMO means the class of devices which implement 'demo'. Other +classes might be MMC, or GPIO, hashing or serial. The idea is that the +devices in the class all share a particular way of working. The class +presents a unified view of all these devices to U-Boot. + +This function looks up a device for the demo uclass. Given a device +number we can find the device because all devices have registered with +the UCLASS_DEMO uclass. + +The device is automatically activated ready for use by uclass_get_device(). + +Now that we have the device we can do things like: + +.. code-block:: c + + return demo_hello(demo_dev, ch); + +This function is in the demo uclass. It takes care of calling the 'hello' +method of the relevant driver. Bearing in mind that there are two drivers, +this particular device may use one or other of them. + +The code for demo_hello() is in drivers/demo/demo-uclass.c: + +.. code-block:: c + + int demo_hello(struct udevice *dev, int ch) + { + const struct demo_ops *ops = device_get_ops(dev); + + if (!ops->hello) + return -ENOSYS; + + return ops->hello(dev, ch); + } + +As you can see it just calls the relevant driver method. One of these is +in drivers/demo/demo-simple.c: + +.. code-block:: c + + static int simple_hello(struct udevice *dev, int ch) + { + const struct dm_demo_pdata *pdata = dev_get_plat(dev); + + printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), + pdata->colour, pdata->sides); + + return 0; + } + + +So that is a trip from top (command execution) to bottom (driver action) +but it leaves a lot of topics to address. + + +Declaring Drivers +----------------- + +A driver declaration looks something like this (see +drivers/demo/demo-shape.c): + +.. code-block:: c + + static const struct demo_ops shape_ops = { + .hello = shape_hello, + .status = shape_status, + }; + + U_BOOT_DRIVER(demo_shape_drv) = { + .name = "demo_shape_drv", + .id = UCLASS_DEMO, + .ops = &shape_ops, + .priv_data_size = sizeof(struct shape_data), + }; + + +This driver has two methods (hello and status) and requires a bit of +private data (accessible through dev_get_priv(dev) once the driver has +been probed). It is a member of UCLASS_DEMO so will register itself +there. + +In U_BOOT_DRIVER it is also possible to specify special methods for bind +and unbind, and these are called at appropriate times. For many drivers +it is hoped that only 'probe' and 'remove' will be needed. + +The U_BOOT_DRIVER macro creates a data structure accessible from C, +so driver model can find the drivers that are available. + +The methods a device can provide are documented in the device.h header. +Briefly, they are: + + * bind - make the driver model aware of a device (bind it to its driver) + * unbind - make the driver model forget the device + * of_to_plat - convert device tree data to plat - see later + * probe - make a device ready for use + * remove - remove a device so it cannot be used until probed again + +The sequence to get a device to work is bind, of_to_plat (if using +device tree) and probe. + + +Platform Data +------------- + +Note: platform data is the old way of doing things. It is +basically a C structure which is passed to drivers to tell them about +platform-specific settings like the address of its registers, bus +speed, etc. Device tree is now the preferred way of handling this. +Unless you have a good reason not to use device tree (the main one +being you need serial support in SPL and don't have enough SRAM for +the cut-down device tree and libfdt libraries) you should stay away +from platform data. + +Platform data is like Linux platform data, if you are familiar with that. +It provides the board-specific information to start up a device. + +Why is this information not just stored in the device driver itself? The +idea is that the device driver is generic, and can in principle operate on +any board that has that type of device. For example, with modern +highly-complex SoCs it is common for the IP to come from an IP vendor, and +therefore (for example) the MMC controller may be the same on chips from +different vendors. It makes no sense to write independent drivers for the +MMC controller on each vendor's SoC, when they are all almost the same. +Similarly, we may have 6 UARTs in an SoC, all of which are mostly the same, +but lie at different addresses in the address space. + +Using the UART example, we have a single driver and it is instantiated 6 +times by supplying 6 lots of platform data. Each lot of platform data +gives the driver name and a pointer to a structure containing information +about this instance - e.g. the address of the register space. It may be that +one of the UARTS supports RS-485 operation - this can be added as a flag in +the platform data, which is set for this one port and clear for the rest. + +Think of your driver as a generic piece of code which knows how to talk to +a device, but needs to know where it is, any variant/option information and +so on. Platform data provides this link between the generic piece of code +and the specific way it is bound on a particular board. + +Examples of platform data include: + + - The base address of the IP block's register space + - Configuration options, like: + - the SPI polarity and maximum speed for a SPI controller + - the I2C speed to use for an I2C device + - the number of GPIOs available in a GPIO device + +Where does the platform data come from? It is either held in a structure +which is compiled into U-Boot, or it can be parsed from the Device Tree +(see 'Device Tree' below). + +For an example of how it can be compiled in, see demo-pdata.c which +sets up a table of driver names and their associated platform data. +The data can be interpreted by the drivers however they like - it is +basically a communication scheme between the board-specific code and +the generic drivers, which are intended to work on any board. + +Drivers can access their data via dev->info->plat. Here is +the declaration for the platform data, which would normally appear +in the board file. + +.. code-block:: c + + static const struct dm_demo_pdata red_square = { + .colour = "red", + .sides = 4. + }; + + static const struct driver_info info[] = { + { + .name = "demo_shape_drv", + .plat = &red_square, + }, + }; + + demo1 = driver_bind(root, &info[0]); + + +Device Tree +----------- + +While plat is useful, a more flexible way of providing device data is +by using device tree. In U-Boot you should use this where possible. Avoid +sending patches which make use of the U_BOOT_DRVINFO() macro unless strictly +necessary. + +With device tree we replace the above code with the following device tree +fragment: + +.. code-block:: c + + red-square { + compatible = "demo-shape"; + colour = "red"; + sides = <4>; + }; + +This means that instead of having lots of U_BOOT_DRVINFO() declarations in +the board file, we put these in the device tree. This approach allows a lot +more generality, since the same board file can support many types of boards +(e,g. with the same SoC) just by using different device trees. An added +benefit is that the Linux device tree can be used, thus further simplifying +the task of board-bring up either for U-Boot or Linux devs (whoever gets to +the board first!). + +The easiest way to make this work it to add a few members to the driver: + +.. code-block:: c + + .plat_auto = sizeof(struct dm_test_pdata), + .of_to_plat = testfdt_of_to_plat, + +The 'auto' feature allowed space for the plat to be allocated +and zeroed before the driver's of_to_plat() method is called. The +of_to_plat() method, which the driver write supplies, should parse +the device tree node for this device and place it in dev->plat. Thus +when the probe method is called later (to set up the device ready for use) +the platform data will be present. + +Note that both methods are optional. If you provide an of_to_plat +method then it will be called first (during activation). If you provide a +probe method it will be called next. See Driver Lifecycle below for more +details. + +If you don't want to have the plat automatically allocated then you +can leave out plat_auto. In this case you can use malloc +in your of_to_plat (or probe) method to allocate the required memory, +and you should free it in the remove method. + +The driver model tree is intended to mirror that of the device tree. The +root driver is at device tree offset 0 (the root node, '/'), and its +children are the children of the root node. + +In order for a device tree to be valid, the content must be correct with +respect to either device tree specification +(https://www.devicetree.org/specifications/) or the device tree bindings that +are found in the doc/device-tree-bindings directory. When not U-Boot specific +the bindings in this directory tend to come from the Linux Kernel. As such +certain design decisions may have been made already for us in terms of how +specific devices are described and bound. In most circumstances we wish to +retain compatibility without additional changes being made to the device tree +source files. + +Declaring Uclasses +------------------ + +The demo uclass is declared like this: + +.. code-block:: c + + UCLASS_DRIVER(demo) = { + .id = UCLASS_DEMO, + }; + +It is also possible to specify special methods for probe, etc. The uclass +numbering comes from include/dm/uclass-id.h. To add a new uclass, add to the +end of the enum there, then declare your uclass as above. + + +Device Sequence Numbers +----------------------- + +U-Boot numbers devices from 0 in many situations, such as in the command +line for I2C and SPI buses, and the device names for serial ports (serial0, +serial1, ...). Driver model supports this numbering and permits devices +to be locating by their 'sequence'. This numbering uniquely identifies a +device in its uclass, so no two devices within a particular uclass can have +the same sequence number. + +Sequence numbers start from 0 but gaps are permitted. For example, a board +may have I2C buses 1, 4, 5 but no 0, 2 or 3. The choice of how devices are +numbered is up to a particular board, and may be set by the SoC in some +cases. While it might be tempting to automatically renumber the devices +where there are gaps in the sequence, this can lead to confusion and is +not the way that U-Boot works. + +Where a device gets its sequence number is controlled by the DM_SEQ_ALIAS +Kconfig option, which can have a different value in U-Boot proper and SPL. +If this option is not set, aliases are ignored. + +Even if CONFIG_DM_SEQ_ALIAS is enabled, the uclass must still have the +DM_UC_FLAG_SEQ_ALIAS flag set, for its devices to be sequenced by aliases. + +With those options set, devices with an alias (e.g. "serial2") will get that +sequence number (e.g. 2). Other devices get the next available number after all +aliases and all existing numbers. This means that if there is just a single +alias "serial2", unaliased serial devices will be assigned 3 or more, with 0 and +1 being unused. + +If CONFIG_DM_SEQ_ALIAS or DM_UC_FLAG_SEQ_ALIAS are not set, all devices will get +sequence numbers in a simple ordering starting from 0. To find the next number +to allocate, driver model scans through to find the maximum existing number, +then uses the next one. It does not attempt to fill in gaps. + +.. code-block:: none + + aliases { + serial2 = "/serial@22230000"; + }; + +This indicates that in the uclass called "serial", the named node +("/serial@22230000") will be given sequence number 2. Any command or driver +which requests serial device 2 will obtain this device. + +More commonly you can use node references, which expand to the full path: + +.. code-block:: none + + aliases { + serial2 = &serial_2; + }; + ... + serial_2: serial@22230000 { + ... + }; + +The alias resolves to the same string in this case, but this version is +easier to read. + +Device sequence numbers are resolved when a device is bound and the number does +not change for the life of the device. + +There are some situations where the uclass must allocate sequence numbers, +since a strictly increase sequence (with devicetree nodes bound first) is not +suitable. An example of this is the PCI bus. In this case, you can set the +uclass DM_UC_FLAG_NO_AUTO_SEQ flag. With this flag set, only devices with an +alias will be assigned a number by driver model. The rest is left to the uclass +to sort out, e.g. when enumerating the bus. + +Note that changing the sequence number for a device (e.g. in a driver) is not +permitted. If it is felt to be necessary, ask on the mailing list. + +Bus Drivers +----------- + +A common use of driver model is to implement a bus, a device which provides +access to other devices. Example of buses include SPI and I2C. Typically +the bus provides some sort of transport or translation that makes it +possible to talk to the devices on the bus. + +Driver model provides some useful features to help with implementing buses. +Firstly, a bus can request that its children store some 'parent data' which +can be used to keep track of child state. Secondly, the bus can define +methods which are called when a child is probed or removed. This is similar +to the methods the uclass driver provides. Thirdly, per-child platform data +can be provided to specify things like the child's address on the bus. This +persists across child probe()/remove() cycles. + +For consistency and ease of implementation, the bus uclass can specify the +per-child platform data, so that it can be the same for all children of buses +in that uclass. There are also uclass methods which can be called when +children are bound and probed. + +Here an explanation of how a bus fits with a uclass may be useful. Consider +a USB bus with several devices attached to it, each from a different (made +up) uclass:: + + xhci_usb (UCLASS_USB) + eth (UCLASS_ETH) + camera (UCLASS_CAMERA) + flash (UCLASS_FLASH_STORAGE) + +Each of the devices is connected to a different address on the USB bus. +The bus device wants to store this address and some other information such +as the bus speed for each device. + +To achieve this, the bus device can use dev->parent_plat in each of its +three children. This can be auto-allocated if the bus driver (or bus uclass) +has a non-zero value for per_child_plat_auto. If not, then +the bus device or uclass can allocate the space itself before the child +device is probed. + +Also the bus driver can define the child_pre_probe() and child_post_remove() +methods to allow it to do some processing before the child is activated or +after it is deactivated. + +Similarly the bus uclass can define the child_post_bind() method to obtain +the per-child platform data from the device tree and set it up for the child. +The bus uclass can also provide a child_pre_probe() method. Very often it is +the bus uclass that controls these features, since it avoids each driver +having to do the same processing. Of course the driver can still tweak and +override these activities. + +Note that the information that controls this behaviour is in the bus's +driver, not the child's. In fact it is possible that child has no knowledge +that it is connected to a bus. The same child device may even be used on two +different bus types. As an example. the 'flash' device shown above may also +be connected on a SATA bus or standalone with no bus:: + + xhci_usb (UCLASS_USB) + flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by USB bus + + sata (UCLASS_AHCI) + flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by SATA bus + + flash (UCLASS_FLASH_STORAGE) - no parent data/methods (not on a bus) + +Above you can see that the driver for xhci_usb/sata controls the child's +bus methods. In the third example the device is not on a bus, and therefore +will not have these methods at all. Consider the case where the flash +device defines child methods. These would be used for *its* children, and +would be quite separate from the methods defined by the driver for the bus +that the flash device is connetced to. The act of attaching a device to a +parent device which is a bus, causes the device to start behaving like a +bus device, regardless of its own views on the matter. + +The uclass for the device can also contain data private to that uclass. +But note that each device on the bus may be a member of a different +uclass, and this data has nothing to do with the child data for each child +on the bus. It is the bus' uclass that controls the child with respect to +the bus. + + +Driver Lifecycle +---------------- + +Here are the stages that a device goes through in driver model. Note that all +methods mentioned here are optional - e.g. if there is no probe() method for +a device then it will not be called. A simple device may have very few +methods actually defined. + +Bind stage +^^^^^^^^^^ + +U-Boot discovers devices using one of these two methods: + +- Scan the U_BOOT_DRVINFO() definitions. U-Boot looks up the name specified + by each, to find the appropriate U_BOOT_DRIVER() definition. In this case, + there is no path by which driver_data may be provided, but the U_BOOT_DRVINFO() + may provide plat. + +- Scan through the device tree definitions. U-Boot looks at top-level + nodes in the the device tree. It looks at the compatible string in each node + and uses the of_match table of the U_BOOT_DRIVER() structure to find the + right driver for each node. In this case, the of_match table may provide a + driver_data value, but plat cannot be provided until later. + +For each device that is discovered, U-Boot then calls device_bind() to create a +new device, initializes various core fields of the device object such as name, +uclass & driver, initializes any optional fields of the device object that are +applicable such as of_offset, driver_data & plat, and finally calls the +driver's bind() method if one is defined. + +At this point all the devices are known, and bound to their drivers. There +is a 'struct udevice' allocated for all devices. However, nothing has been +activated (except for the root device). Each bound device that was created +from a U_BOOT_DRVINFO() declaration will hold the plat pointer specified +in that declaration. For a bound device created from the device tree, +plat will be NULL, but of_offset will be the offset of the device tree +node that caused the device to be created. The uclass is set correctly for +the device. + +The device's sequence number is assigned, either the requested one or the next +available one (after all aliases are processed) if nothing particular is +requested. + +The device's bind() method is permitted to perform simple actions, but +should not scan the device tree node, not initialise hardware, nor set up +structures or allocate memory. All of these tasks should be left for +the probe() method. + +Note that compared to Linux, U-Boot's driver model has a separate step of +probe/remove which is independent of bind/unbind. This is partly because in +U-Boot it may be expensive to probe devices and we don't want to do it until +they are needed, or perhaps until after relocation. + +Reading ofdata +^^^^^^^^^^^^^^ + +Most devices have data in the device tree which they can read to find out the +base address of hardware registers and parameters relating to driver +operation. This is called 'ofdata' (Open-Firmware data). + +The device's of_to_plat() implemnents allocation and reading of +plat. A parent's ofdata is always read before a child. + +The steps are: + + 1. If priv_auto is non-zero, then the device-private space + is allocated for the device and zeroed. It will be accessible as + dev->priv. The driver can put anything it likes in there, but should use + it for run-time information, not platform data (which should be static + and known before the device is probed). + + 2. If plat_auto is non-zero, then the platform data space + is allocated. This is only useful for device tree operation, since + otherwise you would have to specify the platform data in the + U_BOOT_DRVINFO() declaration. The space is allocated for the device and + zeroed. It will be accessible as dev->plat. + + 3. If the device's uclass specifies a non-zero per_device_auto, + then this space is allocated and zeroed also. It is allocated for and + stored in the device, but it is uclass data. owned by the uclass driver. + It is possible for the device to access it. + + 4. If the device's immediate parent specifies a per_child_auto + then this space is allocated. This is intended for use by the parent + device to keep track of things related to the child. For example a USB + flash stick attached to a USB host controller would likely use this + space. The controller can hold information about the USB state of each + of its children. + + 5. If the driver provides an of_to_plat() method, then this is + called to convert the device tree data into platform data. This should + do various calls like dev_read_u32(dev, ...) to access the node and store + the resulting information into dev->plat. After this point, the device + works the same way whether it was bound using a device tree node or + U_BOOT_DRVINFO() structure. In either case, the platform data is now stored + in the plat structure. Typically you will use the + plat_auto feature to specify the size of the platform data + structure, and U-Boot will automatically allocate and zero it for you before + entry to of_to_plat(). But if not, you can allocate it yourself in + of_to_plat(). Note that it is preferable to do all the device tree + decoding in of_to_plat() rather than in probe(). (Apart from the + ugliness of mixing configuration and run-time data, one day it is possible + that U-Boot will cache platform data for devices which are regularly + de/activated). + + 6. The device is marked 'plat valid'. + +Note that ofdata reading is always done (for a child and all its parents) +before probing starts. Thus devices go through two distinct states when +probing: reading platform data and actually touching the hardware to bring +the device up. + +Having probing separate from ofdata-reading helps deal with of-platdata, where +the probe() method is common to both DT/of-platdata operation, but the +of_to_plat() method is implemented differently. + +Another case has come up where this separate is useful. Generation of ACPI +tables uses the of-platdata but does not want to probe the device. Probing +would cause U-Boot to violate one of its design principles, viz that it +should only probe devices that are used. For ACPI we want to generate a +table for each device, even if U-Boot does not use it. In fact it may not +even be possible to probe the device - e.g. an SD card which is not +present will cause an error on probe, yet we still must tell Linux about +the SD card connector in case it is used while Linux is running. + +It is important that the of_to_plat() method does not actually probe +the device itself. However there are cases where other devices must be probed +in the of_to_plat() method. An example is where a device requires a +GPIO for it to operate. To select a GPIO obviously requires that the GPIO +device is probed. This is OK when used by common, core devices such as GPIO, +clock, interrupts, reset and the like. + +If your device relies on its parent setting up a suitable address space, so +that dev_read_addr() works correctly, then make sure that the parent device +has its setup code in of_to_plat(). If it has it in the probe method, +then you cannot call dev_read_addr() from the child device's +of_to_plat() method. Move it to probe() instead. Buses like PCI can +fall afoul of this rule. + +Activation/probe +^^^^^^^^^^^^^^^^ + +When a device needs to be used, U-Boot activates it, by first reading ofdata +as above and then following these steps (see device_probe()): + + 1. All parent devices are probed. It is not possible to activate a device + unless its predecessors (all the way up to the root device) are activated. + This means (for example) that an I2C driver will require that its bus + be activated. + + 2. The device's probe() method is called. This should do anything that + is required by the device to get it going. This could include checking + that the hardware is actually present, setting up clocks for the + hardware and setting up hardware registers to initial values. The code + in probe() can access: + + - platform data in dev->plat (for configuration) + - private data in dev->priv (for run-time state) + - uclass data in dev->uclass_priv (for things the uclass stores + about this device) + + Note: If you don't use priv_auto then you will need to + allocate the priv space here yourself. The same applies also to + plat_auto. Remember to free them in the remove() method. + + 3. The device is marked 'activated' + + 4. The uclass's post_probe() method is called, if one exists. This may + cause the uclass to do some housekeeping to record the device as + activated and 'known' by the uclass. + +Running stage +^^^^^^^^^^^^^ + +The device is now activated and can be used. From now until it is removed +all of the above structures are accessible. The device appears in the +uclass's list of devices (so if the device is in UCLASS_GPIO it will appear +as a device in the GPIO uclass). This is the 'running' state of the device. + +Removal stage +^^^^^^^^^^^^^ + +When the device is no-longer required, you can call device_remove() to +remove it. This performs the probe steps in reverse: + + 1. The uclass's pre_remove() method is called, if one exists. This may + cause the uclass to do some housekeeping to record the device as + deactivated and no-longer 'known' by the uclass. + + 2. All the device's children are removed. It is not permitted to have + an active child device with a non-active parent. This means that + device_remove() is called for all the children recursively at this point. + + 3. The device's remove() method is called. At this stage nothing has been + deallocated so platform data, private data and the uclass data will all + still be present. This is where the hardware can be shut down. It is + intended that the device be completely inactive at this point, For U-Boot + to be sure that no hardware is running, it should be enough to remove + all devices. + + 4. The device memory is freed (platform data, private data, uclass data, + parent data). + + Note: Because the platform data for a U_BOOT_DRVINFO() is defined with a + static pointer, it is not de-allocated during the remove() method. For + a device instantiated using the device tree data, the platform data will + be dynamically allocated, and thus needs to be deallocated during the + remove() method, either: + + - if the plat_auto is non-zero, the deallocation happens automatically + within the driver model core in the unbind stage; or + + - when plat_auto is 0, both the allocation (in probe() + or preferably of_to_plat()) and the deallocation in remove() + are the responsibility of the driver author. + + 5. The device is marked inactive. Note that it is still bound, so the + device structure itself is not freed at this point. Should the device be + activated again, then the cycle starts again at step 2 above. + +Unbind stage +^^^^^^^^^^^^ + +The device is unbound. This is the step that actually destroys the device. +If a parent has children these will be destroyed first. After this point +the device does not exist and its memory has be deallocated. + + +Special cases for removal +------------------------- + +Some devices need to do clean-up before the OS is called. For example, a USB +driver may want to stop the bus. This can be done in the remove() method. +Some special flags are used to determine whether to remove the device: + + DM_FLAG_OS_PREPARE - indicates that the device needs to get ready for OS + boot. The device will be removed just before the OS is booted + DM_REMOVE_ACTIVE_DMA - indicates that the device uses DMA. This is + effectively the same as DM_FLAG_OS_PREPARE, so the device is removed + before the OS is booted + DM_FLAG_VITAL - indicates that the device is 'vital' to the operation of + other devices. It is possible to remove this device after all regular + devices are removed. This is useful e.g. for a clock, which need to + be active during the device-removal phase. + +The dm_remove_devices_flags() function can be used to remove devices based on +their driver flags. + +Data Structures +--------------- + +Driver model uses a doubly-linked list as the basic data structure. Some +nodes have several lists running through them. Creating a more efficient +data structure might be worthwhile in some rare cases, once we understand +what the bottlenecks are. + + +Changes since v1 +---------------- + +For the record, this implementation uses a very similar approach to the +original patches, but makes at least the following changes: + +- Tried to aggressively remove boilerplate, so that for most drivers there + is little or no 'driver model' code to write. +- Moved some data from code into data structure - e.g. store a pointer to + the driver operations structure in the driver, rather than passing it + to the driver bind function. +- Rename some structures to make them more similar to Linux (struct udevice + instead of struct instance, struct plat, etc.) +- Change the name 'core' to 'uclass', meaning U-Boot class. It seems that + this concept relates to a class of drivers (or a subsystem). We shouldn't + use 'class' since it is a C++ reserved word, so U-Boot class (uclass) seems + better than 'core'. +- Remove 'struct driver_instance' and just use a single 'struct udevice'. + This removes a level of indirection that doesn't seem necessary. +- Built in device tree support, to avoid the need for plat +- Removed the concept of driver relocation, and just make it possible for + the new driver (created after relocation) to access the old driver data. + I feel that relocation is a very special case and will only apply to a few + drivers, many of which can/will just re-init anyway. So the overhead of + dealing with this might not be worth it. +- Implemented a GPIO system, trying to keep it simple + + +Pre-Relocation Support +---------------------- + +For pre-relocation we simply call the driver model init function. Only +drivers marked with DM_FLAG_PRE_RELOC or the device tree 'u-boot,dm-pre-reloc' +property are initialised prior to relocation. This helps to reduce the driver +model overhead. This flag applies to SPL and TPL as well, if device tree is +enabled (CONFIG_OF_CONTROL) there. + +Note when device tree is enabled, the device tree 'u-boot,dm-pre-reloc' +property can provide better control granularity on which device is bound +before relocation. While with DM_FLAG_PRE_RELOC flag of the driver all +devices with the same driver are bound, which requires allocation a large +amount of memory. When device tree is not used, DM_FLAG_PRE_RELOC is the +only way for statically declared devices via U_BOOT_DRVINFO() to be bound +prior to relocation. + +It is possible to limit this to specific relocation steps, by using +the more specialized 'u-boot,dm-spl' and 'u-boot,dm-tpl' flags +in the device tree node. For U-Boot proper you can use 'u-boot,dm-pre-proper' +which means that it will be processed (and a driver bound) in U-Boot proper +prior to relocation, but will not be available in SPL or TPL. + +To reduce the size of SPL and TPL, only the nodes with pre-relocation properties +('u-boot,dm-pre-reloc', 'u-boot,dm-spl' or 'u-boot,dm-tpl') are keept in their +device trees (see README.SPL for details); the remaining nodes are always bound. + +Then post relocation we throw that away and re-init driver model again. +For drivers which require some sort of continuity between pre- and +post-relocation devices, we can provide access to the pre-relocation +device pointers, but this is not currently implemented (the root device +pointer is saved but not made available through the driver model API). + + +SPL Support +----------- + +Driver model can operate in SPL. Its efficient implementation and small code +size provide for a small overhead which is acceptable for all but the most +constrained systems. + +To enable driver model in SPL, define CONFIG_SPL_DM. You might want to +consider the following option also. See the main README for more details. + + - CONFIG_SYS_MALLOC_SIMPLE + - CONFIG_DM_WARN + - CONFIG_DM_DEVICE_REMOVE + - CONFIG_DM_STDIO + + +Enabling Driver Model +--------------------- + +Driver model is being brought into U-Boot gradually. As each subsystems gets +support, a uclass is created and a CONFIG to enable use of driver model for +that subsystem. + +For example CONFIG_DM_SERIAL enables driver model for serial. With that +defined, the old serial support is not enabled, and your serial driver must +conform to driver model. With that undefined, the old serial support is +enabled and driver model is not available for serial. This means that when +you convert a driver, you must either convert all its boards, or provide for +the driver to be compiled both with and without driver model (generally this +is not very hard). + +See the main README for full details of the available driver model CONFIG +options. + + +Things to punt for later +------------------------ + +Uclasses are statically numbered at compile time. It would be possible to +change this to dynamic numbering, but then we would require some sort of +lookup service, perhaps searching by name. This is slightly less efficient +so has been left out for now. One small advantage of dynamic numbering might +be fewer merge conflicts in uclass-id.h. diff --git a/doc/develop/driver-model/ethernet.rst b/doc/develop/driver-model/ethernet.rst new file mode 100644 index 00000000000..cdbccca34d6 --- /dev/null +++ b/doc/develop/driver-model/ethernet.rst @@ -0,0 +1,321 @@ +Ethernet Driver Guide +======================= + +The networking stack in Das U-Boot is designed for multiple network devices +to be easily added and controlled at runtime. This guide is meant for people +who wish to review the net driver stack with an eye towards implementing your +own ethernet device driver. Here we will describe a new pseudo 'APE' driver. + +Most existing drivers do already - and new network driver MUST - use the +U-Boot core driver model. Generic information about this can be found in +doc/driver-model/design.rst, this document will thus focus on the network +specific code parts. +Some drivers are still using the old Ethernet interface, differences between +the two and hints about porting will be handled at the end. + +Driver framework +------------------ + +A network driver following the driver model must declare itself using +the UCLASS_ETH .id field in the U-Boot driver struct: + +.. code-block:: c + + U_BOOT_DRIVER(eth_ape) = { + .name = "eth_ape", + .id = UCLASS_ETH, + .of_match = eth_ape_ids, + .of_to_plat = eth_ape_of_to_plat, + .probe = eth_ape_probe, + .ops = ð_ape_ops, + .priv_auto = sizeof(struct eth_ape_priv), + .plat_auto = sizeof(struct eth_ape_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, + }; + +struct eth_ape_priv contains runtime per-instance data, like buffers, pointers +to current descriptors, current speed settings, pointers to PHY related data +(like struct mii_dev) and so on. Declaring its size in .priv_auto +will let the driver framework allocate it at the right time. +It can be retrieved using a dev_get_priv(dev) call. + +struct eth_ape_pdata contains static platform data, like the MMIO base address, +a hardware variant, the MAC address. ``struct eth_pdata eth_pdata`` +as the first member of this struct helps to avoid duplicated code. +If you don't need any more platform data beside the standard member, +just use sizeof(struct eth_pdata) for the plat_auto. + +PCI devices add a line pointing to supported vendor/device ID pairs: + +.. code-block:: c + + static struct pci_device_id supported[] = { + { PCI_DEVICE(PCI_VENDOR_ID_APE, 0x4223) }, + {} + }; + + U_BOOT_PCI_DEVICE(eth_ape, supported); + +It is also possible to declare support for a whole class of PCI devices:: + + { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI << 8, 0xffff00) }, + +Device probing and instantiation will be handled by the driver model framework, +so follow the guidelines there. The probe() function would initialise the +platform specific parts of the hardware, like clocks, resets, GPIOs, the MDIO +bus. Also it would take care of any special PHY setup (power rails, enable +bits for internal PHYs, etc.). + +Driver methods +---------------- + +The real work will be done in the driver method functions the driver provides +by defining the members of struct eth_ops: + +.. code-block:: c + + struct eth_ops { + int (*start)(struct udevice *dev); + int (*send)(struct udevice *dev, void *packet, int length); + int (*recv)(struct udevice *dev, int flags, uchar **packetp); + int (*free_pkt)(struct udevice *dev, uchar *packet, int length); + void (*stop)(struct udevice *dev); + int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); + int (*write_hwaddr)(struct udevice *dev); + int (*read_rom_hwaddr)(struct udevice *dev); + }; + +An up-to-date version of this struct together with more information can be +found in include/net.h. + +Only start, stop, send and recv are required, the rest are optional and are +handled by generic code or ignored if not provided. + +The **start** function initialises the hardware and gets it ready for send/recv +operations. You often do things here such as resetting the MAC +and/or PHY, and waiting for the link to autonegotiate. You should also take +the opportunity to program the device's MAC address with the enetaddr member +of the generic struct eth_pdata (which would be the first member of your +own plat struct). This allows the rest of U-Boot to dynamically change +the MAC address and have the new settings be respected. + +The **send** function does what you think -- transmit the specified packet +whose size is specified by length (in bytes). The packet buffer can (and +will!) be reused for subsequent calls to send(), so it must be no longer +used when the send() function returns. The easiest way to achieve this is +to wait until the transmission is complete. Alternatively, if supported by +the hardware, just waiting for the buffer to be consumed (by some DMA engine) +might be an option as well. +Another way of consuming the buffer could be to copy the data to be send, +then just queue the copied packet (for instance handing it over to a DMA +engine), and return immediately afterwards. +In any case you should leave the state such that the send function can be +called multiple times in a row. + +The **recv** function polls for availability of a new packet. If none is +available, it must return with -EAGAIN. +If a packet has been received, make sure it is accessible to the CPU +(invalidate caches if needed), then write its address to the packetp pointer, +and return the length. If there is an error (receive error, too short or too +long packet), return 0 if you require the packet to be cleaned up normally, +or a negative error code otherwise (cleanup not necessary or already done). +The U-Boot network stack will then process the packet. + +If **free_pkt** is defined, U-Boot will call it after a received packet has +been processed, so the packet buffer can be freed or recycled. Typically you +would hand it back to the hardware to acquire another packet. free_pkt() will +be called after recv(), for the same packet, so you don't necessarily need +to infer the buffer to free from the ``packet`` pointer, but can rely on that +being the last packet that recv() handled. +The common code sets up packet buffers for you already in the .bss +(net_rx_packets), so there should be no need to allocate your own. This doesn't +mean you must use the net_rx_packets array however; you're free to use any +buffer you wish. + +The **stop** function should turn off / disable the hardware and place it back +in its reset state. It can be called at any time (before any call to the +related start() function), so make sure it can handle this sort of thing. + +The (optional) **write_hwaddr** function should program the MAC address stored +in pdata->enetaddr into the Ethernet controller. + +So the call graph at this stage would look something like: + +.. code-block:: c + + (some net operation (ping / tftp / whatever...)) + eth_init() + ops->start() + eth_send() + ops->send() + eth_rx() + ops->recv() + (process packet) + if (ops->free_pkt) + ops->free_pkt() + eth_halt() + ops->stop() + + +CONFIG_PHYLIB / CONFIG_CMD_MII +-------------------------------- + +If your device supports banging arbitrary values on the MII bus (pretty much +every device does), you should add support for the mii command. Doing so is +fairly trivial and makes debugging mii issues a lot easier at runtime. + +In your driver's ``probe()`` function, add a call to mdio_alloc() and +mdio_register() like so: + +.. code-block:: c + + bus = mdio_alloc(); + if (!bus) { + ... + return -ENOMEM; + } + + bus->read = ape_mii_read; + bus->write = ape_mii_write; + mdio_register(bus); + +And then define the mii_read and mii_write functions if you haven't already. +Their syntax is straightforward:: + + int mii_read(struct mii_dev *bus, int addr, int devad, int reg); + int mii_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val); + +The read function should read the register 'reg' from the phy at address 'addr' +and return the result to its caller. The implementation for the write function +should logically follow. + +................................................................ + +Legacy network drivers +------------------------ + +!!! WARNING !!! + +This section below describes the old way of doing things. No new Ethernet +drivers should be implemented this way. All new drivers should be written +against the U-Boot core driver model, as described above. + +The actual callback functions are fairly similar, the differences are: + +- ``start()`` is called ``init()`` +- ``stop()`` is called ``halt()`` +- The ``recv()`` function must loop until all packets have been received, for + each packet it must call the net_process_received_packet() function, + handing it over the pointer and the length. Afterwards it should free + the packet, before checking for new data. + +For porting an old driver to the new driver model, split the existing recv() +function into the actual new recv() function, just fetching **one** packet, +remove the call to net_process_received_packet(), then move the packet +cleanup into the ``free_pkt()`` function. + +Registering the driver and probing a device is handled very differently, +follow the recommendations in the driver model design documentation for +instructions on how to port this over. For the records, the old way of +initialising a network driver is as follows: + +Old network driver registration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When U-Boot initializes, it will call the common function eth_initialize(). +This will in turn call the board-specific board_eth_init() (or if that fails, +the cpu-specific cpu_eth_init()). These board-specific functions can do random +system handling, but ultimately they will call the driver-specific register +function which in turn takes care of initializing that particular instance. + +Keep in mind that you should code the driver to avoid storing state in global +data as someone might want to hook up two of the same devices to one board. +Any such information that is specific to an interface should be stored in a +private, driver-defined data structure and pointed to by eth->priv (see below). + +So the call graph at this stage would look something like: + +.. code-block:: c + + board_init() + eth_initialize() + board_eth_init() / cpu_eth_init() + driver_register() + initialize eth_device + eth_register() + +At this point in time, the only thing you need to worry about is the driver's +register function. The pseudo code would look something like: + +.. code-block:: c + + int ape_register(struct bd_info *bis, int iobase) + { + struct ape_priv *priv; + struct eth_device *dev; + struct mii_dev *bus; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(priv); + return -ENOMEM; + } + + /* setup whatever private state you need */ + + memset(dev, 0, sizeof(*dev)); + sprintf(dev->name, "APE"); + + /* + * if your device has dedicated hardware storage for the + * MAC, read it and initialize dev->enetaddr with it + */ + ape_mac_read(dev->enetaddr); + + dev->iobase = iobase; + dev->priv = priv; + dev->init = ape_init; + dev->halt = ape_halt; + dev->send = ape_send; + dev->recv = ape_recv; + dev->write_hwaddr = ape_write_hwaddr; + + eth_register(dev); + + #ifdef CONFIG_PHYLIB + bus = mdio_alloc(); + if (!bus) { + free(priv); + free(dev); + return -ENOMEM; + } + + bus->read = ape_mii_read; + bus->write = ape_mii_write; + mdio_register(bus); + #endif + + return 1; + } + +The exact arguments needed to initialize your device are up to you. If you +need to pass more/less arguments, that's fine. You should also add the +prototype for your new register function to include/netdev.h. + +The return value for this function should be as follows: +< 0 - failure (hardware failure, not probe failure) +>=0 - number of interfaces detected + +You might notice that many drivers seem to use xxx_initialize() rather than +xxx_register(). This is the old naming convention and should be avoided as it +causes confusion with the driver-specific init function. + +Other than locating the MAC address in dedicated hardware storage, you should +not touch the hardware in anyway. That step is handled in the driver-specific +init function. Remember that we are only registering the device here, we are +not checking its state or doing random probing. diff --git a/doc/develop/driver-model/fdt-fixup.rst b/doc/develop/driver-model/fdt-fixup.rst new file mode 100644 index 00000000000..974c09031ed --- /dev/null +++ b/doc/develop/driver-model/fdt-fixup.rst @@ -0,0 +1,132 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. 2017-01-06, Mario Six + +Pre-relocation device tree manipulation +======================================= + +Purpose +------- + +In certain markets, it is beneficial for manufacturers of embedded devices to +offer certain ranges of products, where the functionality of the devices within +one series either don't differ greatly from another, or can be thought of as +"extensions" of each other, where one device only differs from another in the +addition of a small number of features (e.g. an additional output connector). + +To realize this in hardware, one method is to have a motherboard, and several +possible daughter boards that can be attached to this mother board. Different +daughter boards then either offer the slightly different functionality, or the +addition of the daughter board to the device realizes the "extension" of +functionality to the device described previously. + +For the software, we obviously want to reuse components for all these +variations of the device. This means that the software somehow needs to cope +with the situation that certain ICs may or may not be present on any given +system, depending on which daughter boards are connected to the motherboard. + +In the Linux kernel, one possible solution to this problem is to employ the +device tree overlay mechanism: There exists one "base" device tree, which +features only the components guaranteed to exist in all varieties of the +device. At the start of the kernel, the presence and type of the daughter +boards is then detected, and the corresponding device tree overlays are applied +to support the components on the daughter boards. + +Note that the components present on every variety of the board must, of course, +provide a way to find out if and which daughter boards are installed for this +mechanism to work. + +In the U-Boot boot loader, support for device tree overlays has recently been +integrated, and is used on some boards to alter the device tree that is later +passed to Linux. But since U-Boot's driver model, which is device tree-based as +well, is being used in more and more drivers, the same problem of altering the +device tree starts cropping up in U-Boot itself as well. + +An additional problem with the device tree in U-Boot is that it is read-only, +and the current mechanisms don't allow easy manipulation of the device tree +after the driver model has been initialized. While migrating to a live device +tree (at least after the relocation) would greatly simplify the solution of +this problem, it is a non-negligible task to implement it, an a interim +solution is needed to address the problem at least in the medium-term. + +Hence, we propose a solution to this problem by offering a board-specific +call-back function, which is passed a writeable pointer to the device tree. +This function is called before the device tree is relocated, and specifically +before the main U-Boot's driver model is instantiated, hence the main U-Boot +"sees" all modifications to the device tree made in this function. Furthermore, +we have the pre-relocation driver model at our disposal at this stage, which +means that we can query the hardware for the existence and variety of the +components easily. + +Implementation +-------------- + +To take advantage of the pre-relocation device tree manipulation mechanism, +boards have to implement the function board_fix_fdt, which has the following +signature: + +.. code-block:: c + + int board_fix_fdt (void *rw_fdt_blob) + +The passed-in void pointer is a writeable pointer to the device tree, which can +be used to manipulate the device tree using e.g. functions from +include/fdt_support.h. The return value should either be 0 in case of +successful execution of the device tree manipulation or something else for a +failure. Note that returning a non-null value from the function will +unrecoverably halt the boot process, as with any function from init_sequence_f +(in common/board_f.c). + +Furthermore, the Kconfig option OF_BOARD_FIXUP has to be set for the function +to be called:: + + Device Tree Control + -> [*] Board-specific manipulation of Device Tree + ++----------------------------------------------------------+ +| WARNING: The actual manipulation of the device tree has | +| to be the _last_ set of operations in board_fix_fdt! | +| Since the pre-relocation driver model does not adapt to | +| changes made to the device tree either, its references | +| into the device tree will be invalid after manipulating | +| it, and unpredictable behavior might occur when | +| functions that rely on them are executed! | ++----------------------------------------------------------+ + +Hence, the recommended layout of the board_fixup_fdt call-back function is the +following: + +.. code-block:: c + + int board_fix_fdt(void *rw_fdt_blob) + { + /* + * Collect information about device's hardware and store + * them in e.g. local variables + */ + + /* Do device tree manipulation using the values previously collected */ + + /* Return 0 on successful manipulation and non-zero otherwise */ + } + +If this convention is kept, both an "additive" approach, meaning that nodes for +detected components are added to the device tree, as well as a "subtractive" +approach, meaning that nodes for absent components are removed from the tree, +as well as a combination of both approaches should work. + +Example +------- + +The controlcenterdc board (board/gdsys/a38x/controlcenterdc.c) features a +board_fix_fdt function, in which six GPIO expanders (which might be present or +not, since they are on daughter boards) on a I2C bus are queried for, and +subsequently deactivated in the device tree if they are not present. + +Note that the dm_i2c_simple_probe function does not use the device tree, hence +it is safe to call it after the tree has already been manipulated. + +Work to be done +--------------- + +* The application of device tree overlay should be possible in board_fixup_fdt, + but has not been tested at this stage. diff --git a/doc/develop/driver-model/fs_firmware_loader.rst b/doc/develop/driver-model/fs_firmware_loader.rst new file mode 100644 index 00000000000..a44708cb4c5 --- /dev/null +++ b/doc/develop/driver-model/fs_firmware_loader.rst @@ -0,0 +1,154 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (C) 2018-2019 Intel Corporation + +File System Firmware Loader +=========================== + +This is file system firmware loader for U-Boot framework, which has very close +to some Linux Firmware API. For the details of Linux Firmware API, you can refer +to https://01.org/linuxgraphics/gfx-docs/drm/driver-api/firmware/index.html. + +File system firmware loader can be used to load whatever(firmware, image, +and binary) from the storage device in file system format into target location +such as memory, then consumer driver such as FPGA driver can program FPGA image +from the target location into FPGA. + +To enable firmware loader, CONFIG_FS_LOADER need to be set at +_defconfig such as "CONFIG_FS_LOADER=y". + +Firmware Loader API core features +--------------------------------- + +Firmware storage device described in device tree source +------------------------------------------------------- +For passing data like storage device phandle and partition where the +firmware loading from to the firmware loader driver, those data could be +defined in fs-loader node as shown in below: + +Example for block device:: + + fs_loader0: fs-loader { + u-boot,dm-pre-reloc; + compatible = "u-boot,fs-loader"; + phandlepart = <&mmc 1>; + }; + +<&mmc 1> means block storage device pointer and its partition. + +Above example is a description for block storage, but for UBI storage +device, it can be described in FDT as shown in below: + +Example for ubi:: + + fs_loader1: fs-loader { + u-boot,dm-pre-reloc; + compatible = "u-boot,fs-loader"; + mtdpart = "UBI", + ubivol = "ubi0"; + }; + +Then, firmware-loader property can be added with any device node, which +driver would use the firmware loader for loading. + +The value of the firmware-loader property should be set with phandle +of the fs-loader node. For example:: + + firmware-loader = <&fs_loader0>; + +If there are majority of devices using the same fs-loader node, then +firmware-loader property can be added under /chosen node instead of +adding to each of device node. + +For example:: + + /{ + chosen { + firmware-loader = <&fs_loader0>; + }; + }; + +In each respective driver of devices using firmware loader, the firmware +loaded instance should be created by DT phandle. + +For example of getting DT phandle from /chosen and creating instance: + +.. code-block:: c + + chosen_node = ofnode_path("/chosen"); + if (!ofnode_valid(chosen_node)) { + debug("/chosen node was not found.\n"); + return -ENOENT; + } + + phandle_p = ofnode_get_property(chosen_node, "firmware-loader", &size); + if (!phandle_p) { + debug("firmware-loader property was not found.\n"); + return -ENOENT; + } + + phandle = fdt32_to_cpu(*phandle_p); + ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, + phandle, &dev); + if (ret) + return ret; + +Firmware loader driver is also designed to support U-boot environment +variables, so all these data from FDT can be overwritten +through the U-boot environment variable during run time. + +For examples: + +storage_interface: + Storage interface, it can be "mmc", "usb", "sata" or "ubi". +fw_dev_part: + Block device number and its partition, it can be "0:1". +fw_ubi_mtdpart: + UBI device mtd partition, it can be "UBI". +fw_ubi_volume: + UBI volume, it can be "ubi0". + +When above environment variables are set, environment values would be +used instead of data from FDT. +The benefit of this design allows user to change storage attribute data +at run time through U-boot console and saving the setting as default +environment values in the storage for the next power cycle, so no +compilation is required for both driver and FDT. + +File system firmware Loader API +------------------------------- + +.. code-block:: c + + int request_firmware_into_buf(struct udevice *dev, + const char *name, + void *buf, size_t size, u32 offset) + +Load firmware into a previously allocated buffer + +Parameters: + +* struct udevice \*dev: An instance of a driver +* const char \*name: name of firmware file +* void \*buf: address of buffer to load firmware into +* size_t size: size of buffer +* u32 offset: offset of a file for start reading into buffer + +Returns: + size of total read + -ve when error + +Description: + The firmware is loaded directly into the buffer pointed to by buf + +Example of calling request_firmware_into_buf API after creating firmware loader +instance: + +.. code-block:: c + + ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, + phandle, &dev); + if (ret) + return ret; + + request_firmware_into_buf(dev, filename, buffer_location, buffer_size, + offset_ofreading); diff --git a/doc/develop/driver-model/i2c-howto.rst b/doc/develop/driver-model/i2c-howto.rst new file mode 100644 index 00000000000..27e7440cd46 --- /dev/null +++ b/doc/develop/driver-model/i2c-howto.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +How to port an I2C driver to driver model +========================================= + +Over half of the I2C drivers have been converted as at November 2016. These +ones remain: + + * adi_i2c + * davinci_i2c + * fti2c010 + * ihs_i2c + * kona_i2c + * lpc32xx_i2c + * pca9564_i2c + * ppc4xx_i2c + * rcar_i2c + * sh_i2c + * soft_i2c + * zynq_i2c + +The deadline for this work is the end of June 2017. If no one steps +forward to convert these, at some point there may come a patch to remove them! + +Here is a suggested approach for converting your I2C driver over to driver +model. Please feel free to update this file with your ideas and suggestions. + +- #ifdef out all your own I2C driver code (#if !CONFIG_IS_ENABLED(DM_I2C)) +- Define CONFIG_DM_I2C for your board, vendor or architecture +- If the board does not already use driver model, you need CONFIG_DM also +- Your board should then build, but will not work fully since there will be + no I2C driver +- Add the U_BOOT_DRIVER piece at the end (e.g. copy tegra_i2c.c for example) +- Add a private struct for the driver data - avoid using static variables +- Implement each of the driver methods, perhaps by calling your old methods +- You may need to adjust the function parameters so that the old and new + implementations can share most of the existing code +- If you convert all existing users of the driver, remove the pre-driver-model + code + +In terms of patches a conversion series typically has these patches: +- clean up / prepare the driver for conversion +- add driver model code +- convert at least one existing board to use driver model serial +- (if no boards remain that don't use driver model) remove the old code + +This may be a good time to move your board to use device tree also. Mostly +this involves these steps: + +- define CONFIG_OF_CONTROL and CONFIG_OF_SEPARATE +- add your device tree files to arch//dts +- update the Makefile there +- Add stdout-path to your /chosen device tree node if it is not already there +- build and get u-boot-dtb.bin so you can test it +- Your drivers can now use device tree +- For device tree in SPL, define CONFIG_SPL_OF_CONTROL diff --git a/doc/develop/driver-model/index.rst b/doc/develop/driver-model/index.rst new file mode 100644 index 00000000000..fd4575db9b7 --- /dev/null +++ b/doc/develop/driver-model/index.rst @@ -0,0 +1,29 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Driver Model +============ + +The following holds information on the U-Boot device driver framework: +driver-model, including the design details of itself and several driver +subsystems + +.. toctree:: + :maxdepth: 2 + + bind + debugging + design + ethernet + fdt-fixup + fs_firmware_loader + i2c-howto + livetree + migration + of-plat + pci-info + pmic-framework + remoteproc-framework + serial-howto + soc-framework + spi-howto + usb-info diff --git a/doc/develop/driver-model/livetree.rst b/doc/develop/driver-model/livetree.rst new file mode 100644 index 00000000000..9f654f3b894 --- /dev/null +++ b/doc/develop/driver-model/livetree.rst @@ -0,0 +1,286 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. sectionauthor:: Simon Glass + +Live Device Tree +================ + + +Introduction +------------ + +Traditionally U-Boot has used a 'flat' device tree. This means that it +reads directly from the device tree binary structure. It is called a flat +device tree because nodes are listed one after the other, with the +hierarchy detected by tags in the format. + +This document describes U-Boot's support for a 'live' device tree, meaning +that the tree is loaded into a hierarchical data structure within U-Boot. + + +Motivation +---------- + +The flat device tree has several advantages: + +- it is the format produced by the device tree compiler, so no translation + is needed + +- it is fairly compact (e.g. there is no need for pointers) + +- it is accessed by the libfdt library, which is well tested and stable + + +However the flat device tree does have some limitations. Adding new +properties can involve copying large amounts of data around to make room. +The overall tree has a fixed maximum size so sometimes the tree must be +rebuilt in a new location to create more space. Even if not adding new +properties or nodes, scanning the tree can be slow. For example, finding +the parent of a node is a slow process. Reading from nodes involves a +small amount parsing which takes a little time. + +Driver model scans the entire device tree sequentially on start-up which +avoids the worst of the flat tree's limitations. But if the tree is to be +modified at run-time, a live tree is much faster. Even if no modification +is necessary, parsing the tree once and using a live tree from then on +seems to save a little time. + + +Implementation +-------------- + +In U-Boot a live device tree ('livetree') is currently supported only +after relocation. Therefore we need a mechanism to specify a device +tree node regardless of whether it is in the flat tree or livetree. + +The 'ofnode' type provides this. An ofnode can point to either a flat tree +node (when the live tree node is not yet set up) or a livetree node. The +caller of an ofnode function does not need to worry about these details. + +The main users of the information in a device tree are drivers. These have +a 'struct udevice \*' which is attached to a device tree node. Therefore it +makes sense to be able to read device tree properties using the +'struct udevice \*', rather than having to obtain the ofnode first. + +The 'dev_read\_...()' interface provides this. It allows properties to be +easily read from the device tree using only a device pointer. Under the +hood it uses ofnode so it works with both flat and live device trees. + + +Enabling livetree +----------------- + +CONFIG_OF_LIVE enables livetree. When this option is enabled, the flat +tree will be used in SPL and before relocation in U-Boot proper. Just +before relocation a livetree is built, and this is used for U-Boot proper +after relocation. + +Most checks for livetree use CONFIG_IS_ENABLED(OF_LIVE). This means that +for SPL, the CONFIG_SPL_OF_LIVE option is checked. At present this does +not exist, since SPL does not support livetree. + + +Porting drivers +--------------- + +Many existing drivers use the fdtdec interface to read device tree +properties. This only works with a flat device tree. The drivers should be +converted to use the dev_read_() interface. + +For example, the old code may be like this: + +.. code-block:: c + + struct udevice *bus; + const void *blob = gd->fdt_blob; + int node = dev_of_offset(bus); + + i2c_bus->regs = (struct i2c_ctlr *)devfdt_get_addr(dev); + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", 500000); + +The new code is: + +.. code-block:: c + + struct udevice *bus; + + i2c_bus->regs = (struct i2c_ctlr *)dev_read_addr(dev); + plat->frequency = dev_read_u32_default(bus, "spi-max-frequency", 500000); + +The dev_read\_...() interface is more convenient and works with both the +flat and live device trees. See include/dm/read.h for a list of functions. + +Where properties must be read from sub-nodes or other nodes, you must fall +back to using ofnode. For example, for old code like this: + +.. code-block:: c + + const void *blob = gd->fdt_blob; + int subnode; + + fdt_for_each_subnode(subnode, blob, dev_of_offset(dev)) { + freq = fdtdec_get_int(blob, node, "spi-max-frequency", 500000); + ... + } + +you should use: + +.. code-block:: c + + ofnode subnode; + + ofnode_for_each_subnode(subnode, dev_ofnode(dev)) { + freq = ofnode_read_u32(node, "spi-max-frequency", 500000); + ... + } + + +Useful ofnode functions +----------------------- + +The internal data structures of the livetree are defined in include/dm/of.h : + + :struct device_node: holds information about a device tree node + :struct property: holds information about a property within a node + +Nodes have pointers to their first property, their parent, their first child +and their sibling. This allows nodes to be linked together in a hierarchical +tree. + +Properties have pointers to the next property. This allows all properties of +a node to be linked together in a chain. + +It should not be necessary to use these data structures in normal code. In +particular, you should refrain from using functions which access the livetree +directly, such as of_read_u32(). Use ofnode functions instead, to allow your +code to work with a flat tree also. + +Some conversion functions are used internally. Generally these are not needed +for driver code. Note that they will not work if called in the wrong context. +For example it is invalid to call ofnode_to_no() when a flat tree is being +used. Similarly it is not possible to call ofnode_to_offset() on a livetree +node. + +ofnode_to_np(): + converts ofnode to struct device_node * +ofnode_to_offset(): + converts ofnode to offset + +no_to_ofnode(): + converts node pointer to ofnode +offset_to_ofnode(): + converts offset to ofnode + + +Other useful functions: + +of_live_active(): + returns true if livetree is in use, false if flat tree +ofnode_valid(): + return true if a given node is valid +ofnode_is_np(): + returns true if a given node is a livetree node +ofnode_equal(): + compares two ofnodes +ofnode_null(): + returns a null ofnode (for which ofnode_valid() returns false) + + +Phandles +-------- + +There is full phandle support for live tree. All functions make use of +struct ofnode_phandle_args, which has an ofnode within it. This supports both +livetree and flat tree transparently. See for example +ofnode_parse_phandle_with_args(). + + +Reading addresses +----------------- + +You should use dev_read_addr() and friends to read addresses from device-tree +nodes. + + +fdtdec +------ + +The existing fdtdec interface will eventually be retired. Please try to avoid +using it in new code. + + +Modifying the livetree +---------------------- + +This is not currently supported. Once implemented it should provide a much +more efficient implementation for modification of the device tree than using +the flat tree. + + +Internal implementation +----------------------- + +The dev_read\_...() functions have two implementations. When +CONFIG_DM_DEV_READ_INLINE is enabled, these functions simply call the ofnode +functions directly. This is useful when livetree is not enabled. The ofnode +functions call ofnode_is_np(node) which will always return false if livetree +is disabled, just falling back to flat tree code. + +This optimisation means that without livetree enabled, the dev_read\_...() and +ofnode interfaces do not noticeably add to code size. + +The CONFIG_DM_DEV_READ_INLINE option defaults to enabled when livetree is +disabled. + +Most livetree code comes directly from Linux and is modified as little as +possible. This is deliberate since this code is fairly stable and does what +we want. Some features (such as get/put) are not supported. Internal macros +take care of removing these features silently. + +Within the of_access.c file there are pointers to the alias node, the chosen +node and the stdout-path alias. + + +Errors +------ + +With a flat device tree, libfdt errors are returned (e.g. -FDT_ERR_NOTFOUND). +For livetree normal 'errno' errors are returned (e.g. -ENOTFOUND). At present +the ofnode and dev_read\_...() functions return either one or other type of +error. This is clearly not desirable. Once tests are added for all the +functions this can be tidied up. + + +Adding new access functions +--------------------------- + +Adding a new function for device-tree access involves the following steps: + + - Add two dev_read() functions: + - inline version in the read.h header file, which calls an ofnode function + - standard version in the read.c file (or perhaps another file), which + also calls an ofnode function + + The implementations of these functions can be the same. The purpose + of the inline version is purely to reduce code size impact. + + - Add an ofnode function. This should call ofnode_is_np() to work out + whether a livetree or flat tree is used. For the livetree it should + call an of\_...() function. For the flat tree it should call an + fdt\_...() function. The livetree version will be optimised out at + compile time if livetree is not enabled. + + - Add an of\_...() function for the livetree implementation. If a similar + function is available in Linux, the implementation should be taken + from there and modified as little as possible (generally not at all). + + +Future work +----------- + +Live tree support was introduced in U-Boot 2017.07. There is still quite a bit +of work to do to flesh this out: + +- tests for all access functions +- support for livetree modification +- addition of more access functions as needed +- support for livetree in SPL and before relocation (if desired) diff --git a/doc/develop/driver-model/migration.rst b/doc/develop/driver-model/migration.rst new file mode 100644 index 00000000000..2284e8a6f70 --- /dev/null +++ b/doc/develop/driver-model/migration.rst @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Migration Schedule +================== + +U-Boot has been migrating to a new driver model since its introduction in +2014. This file describes the schedule for deprecation of pre-driver-model +features. + +CONFIG_DM +--------- + +* Status: In progress +* Deadline: 2020.01 + +Starting with the 2010.01 release CONFIG_DM will be enabled for all boards. +This does not concern CONFIG_DM_SPL and CONFIG_DM_TPL. The conversion date for +these configuration items still needs to be defined. + +CONFIG_DM_MMC +------------- + +* Status: In progress +* Deadline: 2019.04 + +The subsystem itself has been converted and maintainers should submit patches +switching over to using CONFIG_DM_MMC and other base driver model options in +time for inclusion in the 2019.04 rerelease. + +CONFIG_DM_USB +------------- + +* Status: In progress +* Deadline: 2019.07 + +The subsystem itself has been converted along with many of the host controller +and maintainers should submit patches switching over to using CONFIG_DM_USB and +other base driver model options in time for inclusion in the 2019.07 rerelease. + +CONFIG_SATA +----------- + +* Status: In progress +* Deadline: 2019.07 + +The subsystem itself has been converted along with many of the host controller +and maintainers should submit patches switching over to using CONFIG_AHCI and +other base driver model options in time for inclusion in the 2019.07 rerelease. + +CONFIG_BLK +---------- + +* Status: In progress +* Deadline: 2019.07 + +In concert with maintainers migrating their block device usage to the +appropriate DM driver, CONFIG_BLK needs to be set as well. The final deadline +here coincides with the final deadline for migration of the various block +subsystems. At this point we will be able to audit and correct the logic in +Kconfig around using CONFIG_PARTITIONS and CONFIG_HAVE_BLOCK_DEVICE and make +use of CONFIG_BLK / CONFIG_SPL_BLK as needed. + +CONFIG_DM_SPI / CONFIG_DM_SPI_FLASH +----------------------------------- + +Board Maintainers should submit the patches for enabling DM_SPI and DM_SPI_FLASH +to move the migration with in the deadline. + +Partially converted:: + + drivers/spi/fsl_espi.c + drivers/spi/mxc_spi.c + drivers/spi/sh_qspi.c + +* Status: In progress +* Deadline: 2019.07 + +CONFIG_DM_PCI +------------- +Deadline: 2019.07 + +The PCI subsystem has supported driver model since mid 2015. Maintainers should +submit patches switching over to using CONFIG_DM_PCI and other base driver +model options in time for inclusion in the 2019.07 release. + + +CONFIG_DM_VIDEO +--------------- +Deadline: 2019.07 + +The video subsystem has supported driver model since early 2016. Maintainers +should submit patches switching over to using CONFIG_DM_VIDEO and other base +driver model options in time for inclusion in the 2019.07 release. + +CONFIG_DM_ETH +------------- +Deadline: 2020.07 + +The network subsystem has supported the driver model since early 2015. +Maintainers should submit patches switching over to using CONFIG_DM_ETH and +other base driver model options in time for inclusion in the 2020.07 release. diff --git a/doc/develop/driver-model/of-plat.rst b/doc/develop/driver-model/of-plat.rst new file mode 100644 index 00000000000..74f1932473b --- /dev/null +++ b/doc/develop/driver-model/of-plat.rst @@ -0,0 +1,913 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Compiled-in Device Tree / Platform Data +======================================= + + +Introduction +------------ + +Device tree is the standard configuration method in U-Boot. It is used to +define what devices are in the system and provide configuration information +to these devices. + +The overhead of adding devicetree access to U-Boot is fairly modest, +approximately 3KB on Thumb 2 (plus the size of the DT itself). This means +that in most cases it is best to use devicetree for configuration. + +However there are some very constrained environments where U-Boot needs to +work. These include SPL with severe memory limitations. For example, some +SoCs require a 16KB SPL image which must include a full MMC stack. In this +case the overhead of devicetree access may be too great. + +It is possible to create platform data manually by defining C structures +for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This +bypasses the use of devicetree completely, effectively creating a parallel +configuration mechanism. But it is an available option for SPL. + +As an alternative, the 'of-platdata' feature is provided. This converts the +devicetree contents into C code which can be compiled into the SPL binary. +This saves the 3KB of code overhead and perhaps a few hundred more bytes due +to more efficient storage of the data. + + +How it works +------------ + +The feature is enabled by CONFIG OF_PLATDATA. This is only available in +SPL/TPL and should be tested with: + +.. code-block:: c + + #if CONFIG_IS_ENABLED(OF_PLATDATA) + +A tool called 'dtoc' converts a devicetree file either into a set of +struct declarations, one for each compatible node, and a set of +`U_BOOT_DRVINFO()` declarations along with the actual platform data for each +device. As an example, consider this MMC node: + +.. code-block:: none + + sdmmc: dwmmc@ff0c0000 { + compatible = "rockchip,rk3288-dw-mshc"; + clock-freq-min-max = <400000 150000000>; + clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, + <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; + clock-names = "biu", "ciu", "ciu_drv", "ciu_sample"; + fifo-depth = <0x100>; + interrupts = ; + reg = <0xff0c0000 0x4000>; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + card-detect-delay = <200>; + disable-wp; + num-slots = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>; + vmmc-supply = <&vcc_sd>; + status = "okay"; + u-boot,dm-pre-reloc; + }; + + +Some of these properties are dropped by U-Boot under control of the +CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce +the following C struct declaration: + +.. code-block:: c + + struct dtd_rockchip_rk3288_dw_mshc { + fdt32_t bus_width; + bool cap_mmc_highspeed; + bool cap_sd_highspeed; + fdt32_t card_detect_delay; + fdt32_t clock_freq_min_max[2]; + struct phandle_1_arg clocks[4]; + bool disable_wp; + fdt32_t fifo_depth; + fdt32_t interrupts[3]; + fdt32_t num_slots; + fdt32_t reg[2]; + fdt32_t vmmc_supply; + }; + +and the following device declarations: + +.. code-block:: c + + /* Node /clock-controller@ff760000 index 0 */ + ... + + /* Node /dwmmc@ff0c0000 index 2 */ + static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = { + .fifo_depth = 0x100, + .cap_sd_highspeed = true, + .interrupts = {0x0, 0x20, 0x4}, + .clock_freq_min_max = {0x61a80, 0x8f0d180}, + .vmmc_supply = 0xb, + .num_slots = 0x1, + .clocks = {{0, 456}, + {0, 68}, + {0, 114}, + {0, 118}}, + .cap_mmc_highspeed = true, + .disable_wp = true, + .bus_width = 0x4, + .u_boot_dm_pre_reloc = true, + .reg = {0xff0c0000, 0x4000}, + .card_detect_delay = 0xc8, + }; + + U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = { + .name = "rockchip_rk3288_dw_mshc", + .plat = &dtv_dwmmc_at_ff0c0000, + .plat_size = sizeof(dtv_dwmmc_at_ff0c0000), + .parent_idx = -1, + }; + +The device is then instantiated at run-time and the platform data can be +accessed using: + +.. code-block:: c + + struct udevice *dev; + struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev); + +This avoids the code overhead of converting the devicetree data to +platform data in the driver. The `of_to_plat()` method should +therefore do nothing in such a driver. + +Note that for the platform data to be matched with a driver, the 'name' +property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared +via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a +'name' corresponding to the devicetree 'compatible' string (after converting +it to a valid name for C) is needed, so a dedicated driver is required for +each 'compatible' string. + +In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be +used to declare an alias for a driver name, typically a 'compatible' string. +This macro produces no code, but is used by dtoc tool. It must be located in the +same file as its associated driver, ideally just after it. + +The parent_idx is the index of the parent `driver_info` structure within its +linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to +support `dev_get_parent()`. + +During the build process dtoc parses both `U_BOOT_DRIVER()` and +`DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases. +If the 'compatible' string used for a device does not not match a valid driver +name, it will be checked against the list of driver aliases in order to get the +right driver name to use. If in this step there is no match found a warning is +issued to avoid run-time failures. + +Where a node has multiple compatible strings, dtoc generates a `#define` to +make them equivalent, e.g.: + +.. code-block:: c + + #define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc + + +Converting of-platdata to a useful form +--------------------------------------- + +Of course it would be possible to use the of-platdata directly in your driver +whenever configuration information is required. However this means that the +driver will not be able to support devicetree, since the of-platdata +structure is not available when devicetree is used. It would make no sense +to use this structure if devicetree were available, since the structure has +all the limitations metioned in caveats below. + +Therefore it is recommended that the of-platdata structure should be used +only in the `probe()` method of your driver. It cannot be used in the +`of_to_plat()` method since this is not called when platform data is +already present. + + +How to structure your driver +---------------------------- + +Drivers should always support devicetree as an option. The of-platdata +feature is intended as a add-on to existing drivers. + +Your driver should convert the plat struct in its `probe()` method. The +existing devicetree decoding logic should be kept in the +`of_to_plat()` method and wrapped with `#if`. + +For example: + +.. code-block:: c + + #include + + struct mmc_plat { + #if CONFIG_IS_ENABLED(OF_PLATDATA) + /* Put this first since driver model will copy the data here */ + struct dtd_mmc dtplat; + #endif + /* + * Other fields can go here, to be filled in by decoding from + * the devicetree (or the C structures when of-platdata is used). + */ + int fifo_depth; + }; + + static int mmc_of_to_plat(struct udevice *dev) + { + #if !CONFIG_IS_ENABLED(OF_PLATDATA) + /* Decode the devicetree data */ + struct mmc_plat *plat = dev_get_plat(dev); + const void *blob = gd->fdt_blob; + int node = dev_of_offset(dev); + + plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0); + #endif + + return 0; + } + + static int mmc_probe(struct udevice *dev) + { + struct mmc_plat *plat = dev_get_plat(dev); + + #if CONFIG_IS_ENABLED(OF_PLATDATA) + /* Decode the of-platdata from the C structures */ + struct dtd_mmc *dtplat = &plat->dtplat; + + plat->fifo_depth = dtplat->fifo_depth; + #endif + /* Set up the device from the plat data */ + writel(plat->fifo_depth, ...) + } + + static const struct udevice_id mmc_ids[] = { + { .compatible = "vendor,mmc" }, + { } + }; + + U_BOOT_DRIVER(mmc_drv) = { + .name = "mmc_drv", + .id = UCLASS_MMC, + .of_match = mmc_ids, + .of_to_plat = mmc_of_to_plat, + .probe = mmc_probe, + .priv_auto = sizeof(struct mmc_priv), + .plat_auto = sizeof(struct mmc_plat), + }; + + DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */ + +Note that `struct mmc_plat` is defined in the C file, not in a header. This +is to avoid needing to include dt-structs.h in a header file. The idea is to +keep the use of each of-platdata struct to the smallest possible code area. +There is just one driver C file for each struct, that can convert from the +of-platdata struct to the standard one used by the driver. + +In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is +still used to allocate space for the platform data. This is different from +the normal behaviour and is triggered by the use of of-platdata (strictly +speaking it is a non-zero `plat_size` which triggers this). + +The of-platdata struct contents is copied from the C structure data to the +start of the newly allocated area. In the case where devicetree is used, +the platform data is allocated, and starts zeroed. In this case the +`of_to_plat()` method should still set up the platform data (and the +of-platdata struct will not be present). + +SPL must use either of-platdata or devicetree. Drivers cannot use both at +the same time, but they must support devicetree. Supporting of-platdata is +optional. + +The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled, +since the devicetree access code is not compiled in. A corollary is that +a board can only move to using of-platdata if all the drivers it uses support +it. There would be little point in having some drivers require the device +tree data, since then libfdt would still be needed for those drivers and +there would be no code-size benefit. + + +Build-time instantiation +------------------------ + +Even with of-platdata there is a fair amount of code required in driver model. +It is possible to have U-Boot handle the instantiation of devices at build-time, +so avoiding the need for the `device_bind()` code and some parts of +`device_probe()`. + +The feature is enabled by CONFIG_OF_PLATDATA_INST. + +Here is an example device, as generated by dtoc:: + + /* + * Node /serial index 6 + * driver sandbox_serial parent root_driver + */ + + #include + struct sandbox_serial_plat __attribute__ ((section (".priv_data"))) + _sandbox_serial_plat_serial = { + .dtplat = { + .sandbox_text_colour = "cyan", + }, + }; + #include + u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)] + __attribute__ ((section (".priv_data"))); + #include + u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(serial) = { + .driver = DM_DRIVER_REF(sandbox_serial), + .name = "sandbox_serial", + .plat_ = &_sandbox_serial_plat_serial, + .priv_ = _sandbox_serial_priv_serial, + .uclass = DM_UCLASS_REF(serial), + .uclass_priv_ = _sandbox_serial_uc_priv_serial, + .uclass_node = { + .prev = &DM_UCLASS_REF(serial)->dev_head, + .next = &DM_UCLASS_REF(serial)->dev_head, + }, + .child_head = { + .prev = &DM_DEVICE_REF(serial)->child_head, + .next = &DM_DEVICE_REF(serial)->child_head, + }, + .sibling_node = { + .prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node, + .next = &DM_DEVICE_REF(spl_test)->sibling_node, + }, + .seq_ = 0, + }; + +Here is part of the driver, for reference:: + + static const struct udevice_id sandbox_serial_ids[] = { + { .compatible = "sandbox,serial" }, + { } + }; + + U_BOOT_DRIVER(sandbox_serial) = { + .name = "sandbox_serial", + .id = UCLASS_SERIAL, + .of_match = sandbox_serial_ids, + .of_to_plat = sandbox_serial_of_to_plat, + .plat_auto = sizeof(struct sandbox_serial_plat), + .priv_auto = sizeof(struct sandbox_serial_priv), + .probe = sandbox_serial_probe, + .remove = sandbox_serial_remove, + .ops = &sandbox_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + }; + + +The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the +members are from that struct. The private data is declared immediately above, +as `_sandbox_serial_priv_serial`, so there is no need for run-time memory +allocation. The #include lines are generated as well, since dtoc searches the +U-Boot source code for the definition of `struct sandbox_serial_priv` and adds +the relevant header so that the code will compile without errors. + +The `plat_` member is set to the dtv data which is declared immediately above +the device. This is similar to how it would look without of-platdata-inst, but +node that the `dtplat` member inside is part of the wider +`_sandbox_serial_plat_serial` struct. This is because the driver declares its +own platform data, and the part generated by dtoc can only be a portion of it. +The `dtplat` part is always first in the struct. If the device has no +`.plat_auto` field, then a simple dtv struct can be used as with this example:: + + static struct dtd_sandbox_clk dtv_clk_sbox = { + .assigned_clock_rates = 0x141, + .assigned_clocks = {0x7, 0x3}, + }; + + #include + u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(clk_sbox) = { + .driver = DM_DRIVER_REF(sandbox_clk), + .name = "sandbox_clk", + .plat_ = &dtv_clk_sbox, + +Here is part of the driver, for reference:: + + static const struct udevice_id sandbox_clk_ids[] = { + { .compatible = "sandbox,clk" }, + { } + }; + + U_BOOT_DRIVER(sandbox_clk) = { + .name = "sandbox_clk", + .id = UCLASS_CLK, + .of_match = sandbox_clk_ids, + .ops = &sandbox_clk_ops, + .probe = sandbox_clk_probe, + .priv_auto = sizeof(struct sandbox_clk_priv), + }; + + +You can see that `dtv_clk_sbox` just has the devicetree contents and there is +no need for the `dtplat` separation, since the driver has no platform data of +its own, besides that provided by the devicetree (i.e. no `.plat_auto` field). + +The doubly linked lists are handled by explicitly declaring the value of each +node, as you can see with the `.prev` and `.next` values in the example above. +Since dtoc knows the order of devices it can link them into the appropriate +lists correctly. + +One of the features of driver model is the ability for a uclass to have a +small amount of private data for each device in that uclass. This is used to +provide a generic data structure that the uclass can use for all devices, thus +allowing generic features to be implemented in common code. An example is I2C, +which stores the bus speed there. + +Similarly, parent devices can have data associated with each of their children. +This is used to provide information common to all children of a particular bus. +For an I2C bus, this is used to store the I2C address of each child on the bus. + +This is all handled automatically by dtoc:: + + #include + u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)] + __attribute__ ((section (".priv_data"))); + #include + u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)] + __attribute__ ((section (".priv_data"))); + + DM_DEVICE_INST(i2c_at_0) = { + .driver = DM_DRIVER_REF(sandbox_i2c), + .name = "sandbox_i2c", + .plat_ = &dtv_i2c_at_0, + .priv_ = _sandbox_i2c_priv_i2c_at_0, + .uclass = DM_UCLASS_REF(i2c), + .uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0, + ... + +Part of driver, for reference:: + + static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c" }, + { } + }; + + U_BOOT_DRIVER(sandbox_i2c) = { + .name = "sandbox_i2c", + .id = UCLASS_I2C, + .of_match = sandbox_i2c_ids, + .ops = &sandbox_i2c_ops, + .priv_auto = sizeof(struct sandbox_i2c_priv), + }; + +Part of I2C uclass, for reference:: + + UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = i2c_post_bind, + .pre_probe = i2c_pre_probe, + .post_probe = i2c_post_probe, + .per_device_auto = sizeof(struct dm_i2c_bus), + .per_child_plat_auto = sizeof(struct dm_i2c_chip), + .child_post_bind = i2c_child_post_bind, + }; + +Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared +in the device, as required by driver model. The required header file is included +so that the code will compile without errors. A similar mechanism is used for +child devices, but is not shown by this example. + +It would not be that useful to avoid binding devices but still need to allocate +uclasses at runtime. So dtoc generates uclass instances as well:: + + struct list_head uclass_head = { + .prev = &DM_UCLASS_REF(serial)->sibling_node, + .next = &DM_UCLASS_REF(clk)->sibling_node, + }; + + DM_UCLASS_INST(clk) = { + .uc_drv = DM_UCLASS_DRIVER_REF(clk), + .sibling_node = { + .prev = &uclass_head, + .next = &DM_UCLASS_REF(i2c)->sibling_node, + }, + .dev_head = { + .prev = &DM_DEVICE_REF(clk_sbox)->uclass_node, + .next = &DM_DEVICE_REF(clk_fixed)->uclass_node, + }, + }; + +At the top is the list head. Driver model uses this on start-up, instead of +creating its own. + +Below that are a set of `DM_UCLASS_INST()` macros, each declaring a +`struct uclass`. The doubly linked lists work as for devices. + +All private data is placed into a `.priv_data` section so that it is contiguous +in the resulting output binary. + + +Indexes +------- + +U-Boot stores drivers, devices and many other things in linker_list structures. +These are sorted by name, so dtoc knows the order that they will appear when +the linker runs. Each driver_info / udevice is referenced by its index in the +linker_list array, called 'idx' in the code. + +When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it +is the driver_info index. In either case, indexes are used to reference devices +using device_get_by_ofplat_idx(). This allows phandles to work as expected. + + +Phases +------ + +U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. +The latter does not use dtoc. + +In some rare cases different drivers are used for two phases. For example, +in TPL it may not be necessary to use the full PCI subsystem, so a simple +driver can be used instead. + +This works in the build system simply by compiling in one driver or the +other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has +no way of knowing which code is compiled in for which phase, since it does +not inspect Makefiles or dependency graphs. + +So to make this work for dtoc, we need to be able to explicitly mark +drivers with their phase. This is done by adding a macro to the driver:: + + /* code in tpl.c only compiled into TPL */ + U_BOOT_DRIVER(pci_x86) = { + .name = "pci_x86", + .id = UCLASS_SIMPLE_BUS, + .of_match = of_match_ptr(tpl_fake_pci_ids), + DM_PHASE(tpl) + }; + + + /* code in pci_x86.c compiled into SPL and U-Boot proper */ + U_BOOT_DRIVER(pci_x86) = { + .name = "pci_x86", + .id = UCLASS_PCI, + .of_match = pci_x86_ids, + .ops = &pci_x86_ops, + }; + + +Notice that the second driver has the same name but no DM_PHASE(), so it will be +used for SPL and U-Boot. + +Note also that this only affects the code generated by dtoc. You still need to +make sure that only the required driver is build into each phase. + + +Header files +------------ + +With OF_PLATDATA_INST, dtoc must include the correct header file in the +generated code for any structs that are used, so that the code will compile. +For example, if `struct ns16550_plat` is used, the code must include the +`ns16550.h` header file. + +Typically dtoc can detect the header file needed for a driver by looking +for the structs that it uses. For example, if a driver as a `.priv_auto` +that uses `struct ns16550_plat`, then dtoc can search header files for the +definition of that struct and use the file. + +In some cases, enums are used in drivers, typically with the `.data` field +of `struct udevice_id`. Since dtoc does not support searching for these, +you must use the `DM_HDR()` macro to tell dtoc which header to use. This works +as a macro included in the driver definition:: + + static const struct udevice_id apl_syscon_ids[] = { + { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT }, + { } + }; + + U_BOOT_DRIVER(intel_apl_punit) = { + .name = "intel_apl_punit", + .id = UCLASS_SYSCON, + .of_match = apl_syscon_ids, + .probe = apl_punit_probe, + DM_HEADER() /* for X86_SYSCON_PUNIT */ + }; + + + +Caveats +------- + +There are various complications with this feature which mean it should only +be used when strictly necessary, i.e. in SPL with limited memory. Notable +caveats include: + + - Device tree does not describe data types. But the C code must define a + type for each property. These are guessed using heuristics which + are wrong in several fairly common cases. For example an 8-byte value + is considered to be a 2-item integer array, and is byte-swapped. A + boolean value that is not present means 'false', but cannot be + included in the structures since there is generally no mention of it + in the devicetree file. + + - Naming of nodes and properties is automatic. This means that they follow + the naming in the devicetree, which may result in C identifiers that + look a bit strange. + + - It is not possible to find a value given a property name. Code must use + the associated C member variable directly in the code. This makes + the code less robust in the face of devicetree changes. To avoid having + a second struct with similar members and names you need to explicitly + declare it as an alias with `DM_DRIVER_ALIAS()`. + + - The platform data is provided to drivers as a C structure. The driver + must use the same structure to access the data. Since a driver + normally also supports devicetree it must use `#ifdef` to separate + out this code, since the structures are only available in SPL. This could + be fixed fairly easily by making the structs available outside SPL, so + that `IS_ENABLED()` could be used. + + - With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning + that (by default) it is not possible to call `device_bind()` from C code. + This means that all devices must have an associated devicetree node and + compatible string. For example if a GPIO device currently creates child + devices in its `bind()` method, it will not work with + CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the + devicetree binding should be updated to declare compatible strings for + the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this + is not recommended since it increases code size. + + +Internals +--------- + +Generated files +``````````````` + +When enabled, dtoc generates the following five files: + +include/generated/dt-decl.h (OF_PLATDATA_INST only) + Contains declarations for all drivers, devices and uclasses. This allows + any `struct udevice`, `struct driver` or `struct uclass` to be located by its + name + +include/generated/dt-structs-gen.h + Contains the struct definitions for the devicetree nodes that are used. This + is the same as without OF_PLATDATA_INST + +spl/dts/dt-plat.c (only with !OF_PLATDATA_INST) + Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices + at start-up. See above for an example + +spl/dts/dt-device.c (only with OF_PLATDATA_INST) + Contains `DM_DEVICE_INST()` declarations for each device that can be used at + run-time. These are declared in the file along with any private/platform data + that they use. Every device has an idx, as above. Since each device must be + part of a double-linked list, the nodes are declared in the code as well. + +spl/dts/dt-uclass.c (only with OF_PLATDATA_INST) + Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at + run-time. These are declared in the file along with any private data + associated with the uclass itself (the `.priv_auto` member). Since each + uclass must be part of a double-linked list, the nodes are declared in the + code as well. + +The dt-structs.h file includes the generated file +`(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled. +Otherwise (such as in U-Boot proper) these structs are not available. This +prevents them being used inadvertently. All usage must be bracketed with +`#if CONFIG_IS_ENABLED(OF_PLATDATA)`. + +The dt-plat.c file contains the device declarations and is is built in +spl/dt-plat.c. + + +CONFIG options +`````````````` + +Several CONFIG options are used to control the behaviour of of-platdata, all +available for both SPL and TPL: + +OF_PLATDATA + This is the main option which enables the of-platdata feature + +OF_PLATDATA_PARENT + This allows `device_get_parent()` to work. Without this, all devices exist as + direct children of the root node. This option is highly desirable (if not + always absolutely essential) for buses such as I2C. + +OF_PLATDATA_INST + This controls the instantiation of devices at build time. With it disabled, + only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding + in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and + `DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at + runtime. + +OF_PLATDATA_NO_BIND + This controls whether `device_bind()` is supported. It is enabled by default + with OF_PLATDATA_INST since code-size reduction is really the main point of + the feature. It can be disabled if needed but is not likely to be supported + in the long term. + +OF_PLATDATA_DRIVER_RT + This controls whether the `struct driver_rt` records are used by U-Boot. + Normally when a device is bound, U-Boot stores the device pointer in one of + these records. There is one for every `struct driver_info` in the system, + i.e. one for every device that is bound from those records. It provides a + way to locate a device in the code and is used by + `device_get_by_ofplat_idx()`. This option is always enabled with of-platdata, + provided OF_PLATDATA_INST is not. In that case the records are useless since + we don't have any `struct driver_info` records. + +OF_PLATDATA_RT + This controls whether the `struct udevice_rt` records are used by U-Boot. + It moves the updatable fields from `struct udevice` (currently only `flags`) + into a separate structure, allowing the records to be kept in read-only + memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option + also controls whether the private data is used in situ, or first copied into + an allocated region. Again this is to allow the private data declared by + dtoc-generated code to be in read-only memory. Note that access to private + data must be done via accessor functions, such as `dev_get_priv()`, so that + the relocation is handled. + +READ_ONLY + This indicates that the data generated by dtoc should not be modified. Only + a few fields actually do get changed in U-Boot, such as device flags. This + option causes those to move into an allocated space (see OF_PLATDATA_RT). + Also, since updating doubly linked lists is generally impossible when some of + the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled. + +Data structures +``````````````` + +A few extra data structures are used with of-platdata: + +`struct udevice_rt` + Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds + the flags for each device, so that `struct udevice` can remain unchanged by + U-Boot, and potentially reside in read-only memory. Access to flags is then + via functions like `dev_get_flags()` and `dev_or_flags()`. This data + structure is allocated on start-up, where the private data is also copied. + All flags values start at 0 and any changes are handled by `dev_or_flags()` + and `dev_bic_flags()`. It would be more correct for the flags to be set to + `DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since + there is no code to bind/unbind devices and no code to allocate/free + private data / platform data, it doesn't matter. + +`struct driver_rt` + Run-time information for `struct driver_info` records. When + OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device + created by each record. This is needed so that is it possible to locate a + device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to + get a reference to a particular `struct driver_info`, with `name` being the + name of the devicetree node. This is very convenient. It is also fast, since + no searching or string comparison is needed. This data structure is + allocated on start-up, filled out by `device_bind()` and used by + `device_get_by_ofplat_idx()`. + +Other changes +````````````` + +Some other changes are made with of-platdata: + +Accessor functions + Accessing private / platform data via functions such as `dev_get_priv()` has + always been encouraged. With OF_PLATDATA_RT this is essential, since the + `priv_` and `plat_` (etc.) values point to the data generated by dtoc, not + the read-write copy that is sometimes made on start-up. Changing the + private / platform data pointers has always been discouraged (the API is + marked internal) but with OF_PLATDATA_RT this is not currently supported in + general, since it assumes that all such pointers point to the relocated data. + Note also that the renaming of struct members to have a trailing underscore + was partly done to make people aware that they should not be accessed + directly. + +`gd->uclass_root_s` + Normally U-Boot sets up the head of the uclass list here and makes + `gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a + declaration of `uclass_head` in `dt-uclass.c` since it needs to link the + head node into the list. In that case, `gd->uclass_root_s` is not used and + U-Boot just makes `gd->uclass_root` point to `uclass_head`. + +`gd->dm_driver_rt` + This holds a pointer to a list of `struct driver_rt` records, one for each + `struct driver_info`. The list is in alphabetical order by the name used + in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having + an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is + accessed via macros so that it can be used inside IS_ENABLED(), rather than + requiring #ifdefs in the C code when it is not present. + +`gd->dm_udevice_rt` + This holds a pointer to a list of `struct udevice_rt` records, one for each + `struct udevice`. The list is in alphabetical order by the name used + in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by + idx, with the first record having an index of 0. It is only used if + OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be + used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when + it is not present. + +`gd->dm_priv_base` + When OF_PLATDATA_RT is enabled, the private/platform data for each device is + copied into an allocated region by U-Boot on start-up. This points to that + region. All calls to accessor functions (e.g. `dev_get_priv()`) then + translate from the pointer provided by the caller (assumed to lie between + `__priv_data_start` and `__priv_data_end`) to the new allocated region. This + member is accessed via macros so that it can be used inside IS_ENABLED(), + rather than required #ifdefs in the C code when it is not present. + +`struct udevice->flags_` + When OF_PLATDATA_RT is enabled, device flags are no-longer part of + `struct udevice`, but are instead kept in `struct udevice_rt`, as described + above. Flags are accessed via functions, such as `dev_get_flags()` and + `dev_or_flags()`. + +`struct udevice->node_` + When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need + for this field. It is removed, just to save space. + +`DM_PHASE` + This macro is used to indicate which phase of U-Boot a driver is intended + for. See above for details. + +`DM_HDR` + This macro is used to indicate which header file dtoc should use to allow + a driver declaration to compile correctly. See above for details. + +`device_get_by_ofplat_idx()` + There used to be a function called `device_get_by_driver_info()` which + looked up a `struct driver_info` pointer and returned the `struct udevice` + that was created from it. It was only available for use with of-platdata. + This has been removed in favour of `device_get_by_ofplat_idx()` which uses + `idx`, the index of the `struct driver_info` or `struct udevice` in the + linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been + updated to use this index instead of a pointer to `struct driver_info`. + +`DM_DRVINFO_GET` + This has been removed since we now use indexes to obtain a driver from + `struct phandle_0_arg` and the like. + +Two-pass binding + The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated + files so as to have parents declared ahead of children. This was convenient + as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does + not work as the idx value relies on using alphabetical order for everything, + so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are + then bound in order of idx, having no regard to parent/child relationships. + For this reason, device binding now hapens in multiple passes, with parents + being bound before their children. This is important so that children can + find their parents in the bind() method if needed. + +Root device + The root device is generally bound by U-Boot but with OF_PLATDATA_INST it + cannot be, since binding needs to be done at build time. So in this case + dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and + U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot + generally ignores the root node and does not create a `U_BOOT_DRVINFO()` + record for it. This means that the idx numbers used by `struct driver_info` + (when OF_PLATDATA_INST is disabled) and the idx numbers used by + `struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a + root node and the other does not. This does not actually matter, since only + one of them is actually used for any particular build, but it is worth + keeping in mind if comparing index values and switching OF_PLATDATA_INST on + and off. + +`__priv_data_start` and `__priv_data_end` + The private/platform data declared by dtoc is all collected together in + a linker section and these symbols mark the start and end of it. This allows + U-Boot to relocate the area to a new location if needed (with + OF_PLATDATA_RT) + +`dm_priv_to_rw()` + This function converts a private- or platform-data pointer value generated by + dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT + is enabled, in which case it translates the address to the relocated + region. See above for more information. + +The dm_populate_phandle_data() function that was previous needed has now been +removed, since dtoc can address the drivers directly from dt-plat.c and does +not need to fix up things at runtime. + +The pylibfdt Python module is used to access the devicetree. + + +Credits +------- + +This is an implementation of an idea by Tom Rini . + + +Future work +----------- +- Consider programmatically reading binding files instead of devicetree + contents +- Allow IS_ENABLED() to be used in the C code instead of #if + + +.. Simon Glass +.. Google, Inc +.. 6/6/16 +.. Updated Independence Day 2016 +.. Updated 1st October 2020 +.. Updated 5th February 2021 diff --git a/doc/develop/driver-model/pci-info.rst b/doc/develop/driver-model/pci-info.rst new file mode 100644 index 00000000000..251601a51e3 --- /dev/null +++ b/doc/develop/driver-model/pci-info.rst @@ -0,0 +1,172 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +PCI with Driver Model +===================== + +How busses are scanned +---------------------- + +Any config read will end up at pci_read_config(). This uses +uclass_get_device_by_seq() to get the PCI bus for a particular bus number. +Bus number 0 will need to be requested first, and the alias in the device +tree file will point to the correct device:: + + aliases { + pci0 = &pcic; + }; + + pcic: pci@0 { + compatible = "sandbox,pci"; + ... + }; + + +If there is no alias the devices will be numbered sequentially in the device +tree. + +The call to uclass_get_device() will cause the PCI bus to be probed. +This does a scan of the bus to locate available devices. These devices are +bound to their appropriate driver if available. If there is no driver, then +they are bound to a generic PCI driver which does nothing. + +After probing a bus, the available devices will appear in the device tree +under that bus. + +Note that this is all done on a lazy basis, as needed, so until something is +touched on PCI (eg: a call to pci_find_devices()) it will not be probed. + +PCI devices can appear in the flattened device tree. If they do, their node +often contains extra information which cannot be derived from the PCI IDs or +PCI class of the device. Each PCI device node must have a property, as +defined by the IEEE Std 1275-1994 PCI bus binding document v2.1. Compatible +string list is optional and generally not needed, since PCI is discoverable +bus, albeit there are justified exceptions. If the compatible string is +present, matching on it takes precedence over PCI IDs and PCI classes. + +Note we must describe PCI devices with the same bus hierarchy as the +hardware, otherwise driver model cannot detect the correct parent/children +relationship during PCI bus enumeration thus PCI devices won't be bound to +their drivers accordingly. A working example like below:: + + pci { + #address-cells = <3>; + #size-cells = <2>; + compatible = "pci-x86"; + u-boot,dm-pre-reloc; + ranges = <0x02000000 0x0 0x40000000 0x40000000 0 0x80000000 + 0x42000000 0x0 0xc0000000 0xc0000000 0 0x20000000 + 0x01000000 0x0 0x2000 0x2000 0 0xe000>; + + pcie@17,0 { + #address-cells = <3>; + #size-cells = <2>; + compatible = "pci-bridge"; + u-boot,dm-pre-reloc; + reg = <0x0000b800 0x0 0x0 0x0 0x0>; + + topcliff@0,0 { + #address-cells = <3>; + #size-cells = <2>; + compatible = "pci-bridge"; + u-boot,dm-pre-reloc; + reg = <0x00010000 0x0 0x0 0x0 0x0>; + + pciuart0: uart@a,1 { + compatible = "pci8086,8811.00", + "pci8086,8811", + "pciclass,070002", + "pciclass,0700", + "x86-uart"; + u-boot,dm-pre-reloc; + reg = <0x00025100 0x0 0x0 0x0 0x0 + 0x01025110 0x0 0x0 0x0 0x0>; + ...... + }; + + ...... + }; + }; + + ...... + }; + +In this example, the root PCI bus node is the "/pci" which matches "pci-x86" +driver. It has a subnode "pcie@17,0" with driver "pci-bridge". "pcie@17,0" +also has subnode "topcliff@0,0" which is a "pci-bridge" too. Under that bridge, +a PCI UART device "uart@a,1" is described. This exactly reflects the hardware +bus hierarchy: on the root PCI bus, there is a PCIe root port which connects +to a downstream device Topcliff chipset. Inside Topcliff chipset, it has a +PCIe-to-PCI bridge and all the chipset integrated devices like the PCI UART +device are on the PCI bus. Like other devices in the device tree, if we want +to bind PCI devices before relocation, "u-boot,dm-pre-reloc" must be declared +in each of these nodes. + +If PCI devices are not listed in the device tree, U_BOOT_PCI_DEVICE can be used +to specify the driver to use for the device. The device tree takes precedence +over U_BOOT_PCI_DEVICE. Please note with U_BOOT_PCI_DEVICE, only drivers with +DM_FLAG_PRE_RELOC will be bound before relocation. If neither device tree nor +U_BOOT_PCI_DEVICE is provided, the built-in driver (either pci_bridge_drv or +pci_generic_drv) will be used. + + +Sandbox +------- + +With sandbox we need a device emulator for each device on the bus since there +is no real PCI bus. This works by looking in the device tree node for an +emulator driver. For example:: + + pci@1f,0 { + compatible = "pci-generic"; + reg = <0xf800 0 0 0 0>; + sandbox,emul = <&emul_1f>; + }; + pci-emul { + compatible = "sandbox,pci-emul-parent"; + emul_1f: emul@1f,0 { + compatible = "sandbox,swap-case"; + #emul-cells = <0>; + }; + }; + +This means that there is a 'sandbox,swap-case' driver at that bus position. +Note that the first cell in the 'reg' value is the bus/device/function. See +PCI_BDF() for the encoding (it is also specified in the IEEE Std 1275-1994 +PCI bus binding document, v2.1) + +The pci-emul node should go outside the pci bus node, since otherwise it will +be scanned as a PCI device, causing confusion. + +When this bus is scanned we will end up with something like this:: + + `- * pci@0 @ 05c660c8, 0 + `- pci@1f,0 @ 05c661c8, 63488 + `- emul@1f,0 @ 05c662c8 + +When accesses go to the pci@1f,0 device they are forwarded to its emulator. + +The sandbox PCI drivers also support dynamic driver binding, allowing device +driver to declare the driver binding information via U_BOOT_PCI_DEVICE(), +eliminating the need to provide any device tree node under the host controller +node. It is required a "sandbox,dev-info" property must be provided in the +host controller node for this functionality to work. + +.. code-block:: none + + pci1: pci@1 { + compatible = "sandbox,pci"; + ... + sandbox,dev-info = <0x08 0x00 0x1234 0x5678 + 0x0c 0x00 0x1234 0x5678>; + }; + +The "sandbox,dev-info" property specifies all dynamic PCI devices on this bus. +Each dynamic PCI device is encoded as 4 cells a group. The first and second +cells are PCI device number and function number respectively. The third and +fourth cells are PCI vendor ID and device ID respectively. + +When this bus is scanned we will end up with something like this:: + + pci [ + ] pci_sandbo |-- pci1 + pci_emul [ ] sandbox_sw | |-- sandbox_swap_case_emul + pci_emul [ ] sandbox_sw | `-- sandbox_swap_case_emul diff --git a/doc/develop/driver-model/pmic-framework.rst b/doc/develop/driver-model/pmic-framework.rst new file mode 100644 index 00000000000..d24a1badd64 --- /dev/null +++ b/doc/develop/driver-model/pmic-framework.rst @@ -0,0 +1,143 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. (C) Copyright 2014-2015 Samsung Electronics +.. sectionauthor:: Przemyslaw Marczak + +PMIC framework based on Driver Model +==================================== + +Introduction +------------ +This is an introduction to driver-model multi uclass PMIC IC's support. +At present it's based on two uclass types: + +UCLASS_PMIC: + basic uclass type for PMIC I/O, which provides common + read/write interface. +UCLASS_REGULATOR: + additional uclass type for specific PMIC features, which are + Voltage/Current regulators. + +New files: + +UCLASS_PMIC: + - drivers/power/pmic/pmic-uclass.c + - include/power/pmic.h +UCLASS_REGULATOR: + - drivers/power/regulator/regulator-uclass.c + - include/power/regulator.h + +Commands: +- common/cmd_pmic.c +- common/cmd_regulator.c + +How doees it work +----------------- +The Power Management Integrated Circuits (PMIC) are used in embedded systems +to provide stable, precise and specific voltage power source with over-voltage +and thermal protection circuits. + +The single PMIC can provide various functions by single or multiple interfaces, +like in the example below:: + + -- SoC + | + | ______________________________________ + | BUS 0 | Multi interface PMIC IC |--> LDO out 1 + | e.g.I2C0 | |--> LDO out N + |-----------|---- PMIC device 0 (READ/WRITE ops) | + | or SPI0 | |_ REGULATOR device (ldo/... ops) |--> BUCK out 1 + | | |_ CHARGER device (charger ops) |--> BUCK out M + | | |_ MUIC device (microUSB con ops) | + | BUS 1 | |_ ... |---> BATTERY + | e.g.I2C1 | | + |-----------|---- PMIC device 1 (READ/WRITE ops) |---> USB in 1 + . or SPI1 | |_ RTC device (rtc ops) |---> USB in 2 + . |______________________________________|---> USB out + . + +Since U-Boot provides driver model features for I2C and SPI bus drivers, +the PMIC devices should also support this. By the pmic and regulator API's, +PMIC drivers can simply provide a common functions, for multi-interface and +and multi-instance device support. + +Basic design assumptions: + +- Common I/O API: + UCLASS_PMIC. For the multi-function PMIC devices, this can be used as + parent I/O device for each IC's interface. Then, each children uses the + same dev for read/write. + +- Common regulator API: + UCLASS_REGULATOR. For driving the regulator attributes, auto setting + function or command line interface, based on kernel-style regulator device + tree constraints. + +For simple implementations, regulator drivers are not required, so the code can +use pmic read/write directly. + +Pmic uclass +----------- +The basic information: + +* Uclass: 'UCLASS_PMIC' +* Header: 'include/power/pmic.h' +* Core: 'drivers/power/pmic/pmic-uclass.c' (config 'CONFIG_DM_PMIC') +* Command: 'common/cmd_pmic.c' (config 'CONFIG_CMD_PMIC') +* Example: 'drivers/power/pmic/max77686.c' + +For detailed API description, please refer to the header file. + +As an example of the pmic driver, please refer to the MAX77686 driver. + +Please pay attention for the driver's bind() method. Exactly the function call: +'pmic_bind_children()', which is used to bind the regulators by using the array +of regulator's node, compatible prefixes. + +The 'pmic; command also supports the new API. So the pmic command can be enabled +by adding CONFIG_CMD_PMIC. +The new pmic command allows to: +- list pmic devices +- choose the current device (like the mmc command) +- read or write the pmic register +- dump all pmic registers + +This command can use only UCLASS_PMIC devices, since this uclass is designed +for pmic I/O operations only. + +For more information, please refer to the core file. + +Regulator uclass +---------------- +The basic information: + +* Uclass: 'UCLASS_REGULATOR' + +* Header: 'include/power/regulator.h' + +* Core: 'drivers/power/regulator/regulator-uclass.c' + (config 'CONFIG_DM_REGULATOR') + +* Binding: 'doc/device-tree-bindings/regulator/regulator.txt' + +* Command: 'common/cmd_regulator.c' (config 'CONFIG_CMD_REGULATOR') + +* Example: 'drivers/power/regulator/max77686.c' + 'drivers/power/pmic/max77686.c' (required I/O driver for the above) + +* Example: 'drivers/power/regulator/fixed.c' + (config 'CONFIG_DM_REGULATOR_FIXED') + +For detailed API description, please refer to the header file. + +For the example regulator driver, please refer to the MAX77686 regulator driver, +but this driver can't operate without pmic's example driver, which provides an +I/O interface for MAX77686 regulator. + +The second example is a fixed Voltage/Current regulator for a common use. + +The 'regulator' command also supports the new API. The command allow: +- list regulator devices +- choose the current device (like the mmc command) +- do all regulator-specific operations + +For more information, please refer to the command file. diff --git a/doc/develop/driver-model/remoteproc-framework.rst b/doc/develop/driver-model/remoteproc-framework.rst new file mode 100644 index 00000000000..566495a21c4 --- /dev/null +++ b/doc/develop/driver-model/remoteproc-framework.rst @@ -0,0 +1,169 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. (C) Copyright 2015 +.. Texas Instruments Incorporated - http://www.ti.com/ + +Remote Processor Framework +========================== + +Introduction +------------ + +This is an introduction to driver-model for Remote Processors found +on various System on Chip(SoCs). The term remote processor is used to +indicate that this is not the processor on which U-Boot is operating +on, instead is yet another processing entity that may be controlled by +the processor on which we are functional. + +The simplified model depends on a single UCLASS - UCLASS_REMOTEPROC + +UCLASS_REMOTEPROC: + - drivers/remoteproc/rproc-uclass.c + - include/remoteproc.h + +Commands: + - common/cmd_remoteproc.c + +Configuration: + - CONFIG_REMOTEPROC is selected by drivers as needed + - CONFIG_CMD_REMOTEPROC for the commands if required. + +How does it work - The driver +----------------------------- + +Overall, the driver statemachine transitions are typically as follows:: + + (entry) + +-------+ + +---+ init | + | | | <---------------------+ + | +-------+ | + | | + | | + | +--------+ | + Load| | reset | | + | | | <----------+ | + | +--------+ | | + | |Load | | + | | | | + | +----v----+ reset | | + +-> | | (opt) | | + | Loaded +-----------+ | + | | | + +----+----+ | + | Start | + +---v-----+ (opt) | + +->| Running | Stop | + Ping +- | +--------------------+ + (opt) +---------+ + +(is_running does not change state) +opt: Optional state transition implemented by driver. + +NOTE: It depends on the remote processor as to the exact behavior +of the statemachine, remoteproc core does not intent to implement +statemachine logic. Certain processors may allow start/stop without +reloading the image in the middle, certain other processors may only +allow us to start the processor(image from a EEPROM/OTP) etc. + +It is hence the responsibility of the driver to handle the requisite +state transitions of the device as necessary. + +Basic design assumptions: + +Remote processor can operate on a certain firmware that maybe loaded +and released from reset. + +The driver follows a standard UCLASS DM. + +in the bare minimum form: + +.. code-block:: c + + static const struct dm_rproc_ops sandbox_testproc_ops = { + .load = sandbox_testproc_load, + .start = sandbox_testproc_start, + }; + + static const struct udevice_id sandbox_ids[] = { + {.compatible = "sandbox,test-processor"}, + {} + }; + + U_BOOT_DRIVER(sandbox_testproc) = { + .name = "sandbox_test_proc", + .of_match = sandbox_ids, + .id = UCLASS_REMOTEPROC, + .ops = &sandbox_testproc_ops, + .probe = sandbox_testproc_probe, + }; + +This allows for the device to be probed as part of the "init" command +or invocation of 'rproc_init()' function as the system dependencies define. + +The driver is expected to maintain it's own statemachine which is +appropriate for the device it maintains. It must, at the very least +provide a load and start function. We assume here that the device +needs to be loaded and started, else, there is no real purpose of +using the remoteproc framework. + +Describing the device using platform data +----------------------------------------- + +*IMPORTANT* NOTE: THIS SUPPORT IS NOT MEANT FOR USE WITH NEWER PLATFORM +SUPPORT. THIS IS ONLY FOR LEGACY DEVICES. THIS MODE OF INITIALIZATION +*WILL* BE EVENTUALLY REMOVED ONCE ALL NECESSARY PLATFORMS HAVE MOVED +TO DM/FDT. + +Considering that many platforms are yet to move to device-tree model, +a simplified definition of a device is as follows: + +.. code-block:: c + + struct dm_rproc_uclass_pdata proc_3_test = { + .name = "proc_3_legacy", + .mem_type = RPROC_INTERNAL_MEMORY_MAPPED, + .driver_plat_data = &mydriver_data; + }; + + U_BOOT_DRVINFO(proc_3_demo) = { + .name = "sandbox_test_proc", + .plat = &proc_3_test, + }; + +There can be additional data that may be desired depending on the +remoteproc driver specific needs (for example: SoC integration +details such as clock handle or something similar). See appropriate +documentation for specific remoteproc driver for further details. +These are passed via driver_plat_data. + +Describing the device using device tree +--------------------------------------- + +.. code-block: none + + / { + ... + aliases { + ... + remoteproc0 = &rproc_1; + remoteproc1 = &rproc_2; + + }; + ... + + rproc_1: rproc@1 { + compatible = "sandbox,test-processor"; + remoteproc-name = "remoteproc-test-dev1"; + }; + + rproc_2: rproc@2 { + compatible = "sandbox,test-processor"; + internal-memory-mapped; + remoteproc-name = "remoteproc-test-dev2"; + }; + ... + }; + +aliases usage is optional, but it is usually recommended to ensure the +users have a consistent usage model for a platform. +the compatible string used here is specific to the remoteproc driver involved. diff --git a/doc/develop/driver-model/serial-howto.rst b/doc/develop/driver-model/serial-howto.rst new file mode 100644 index 00000000000..1469131124b --- /dev/null +++ b/doc/develop/driver-model/serial-howto.rst @@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +How to port a serial driver to driver model +=========================================== + +Almost all of the serial drivers have been converted as at January 2016. These +ones remain: + + * serial_bfin.c + * serial_pxa.c + +The deadline for this work was the end of January 2016. If no one steps +forward to convert these, at some point there may come a patch to remove them! + +Here is a suggested approach for converting your serial driver over to driver +model. Please feel free to update this file with your ideas and suggestions. + +- #ifdef out all your own serial driver code (#ifndef CONFIG_DM_SERIAL) +- Define CONFIG_DM_SERIAL for your board, vendor or architecture +- If the board does not already use driver model, you need CONFIG_DM also +- Your board should then build, but will not boot since there will be no serial + driver +- Add the U_BOOT_DRIVER piece at the end (e.g. copy serial_s5p.c for example) +- Add a private struct for the driver data - avoid using static variables +- Implement each of the driver methods, perhaps by calling your old methods +- You may need to adjust the function parameters so that the old and new + implementations can share most of the existing code +- If you convert all existing users of the driver, remove the pre-driver-model + code + +In terms of patches a conversion series typically has these patches: +- clean up / prepare the driver for conversion +- add driver model code +- convert at least one existing board to use driver model serial +- (if no boards remain that don't use driver model) remove the old code + +This may be a good time to move your board to use device tree also. Mostly +this involves these steps: + +- define CONFIG_OF_CONTROL and CONFIG_OF_SEPARATE +- add your device tree files to arch//dts +- update the Makefile there +- Add stdout-path to your /chosen device tree node if it is not already there +- build and get u-boot-dtb.bin so you can test it +- Your drivers can now use device tree +- For device tree in SPL, define CONFIG_SPL_OF_CONTROL diff --git a/doc/develop/driver-model/soc-framework.rst b/doc/develop/driver-model/soc-framework.rst new file mode 100644 index 00000000000..2609fda6442 --- /dev/null +++ b/doc/develop/driver-model/soc-framework.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. (C) Copyright 2020 +.. Texas Instruments Incorporated - http://www.ti.com/ + +SOC ID Framework +================ + +Introduction +------------ + +The driver-model SOC ID framework is able to provide identification +information about a specific SoC in use at runtime, and also provide matching +from a set of identification information from an array. This can be useful for +enabling small quirks in drivers that exist between SoC variants that are +impractical to implement using device tree flags. It is based on UCLASS_SOC. + +UCLASS_SOC: + - drivers/soc/soc-uclass.c + - include/soc.h + +Configuration: + - CONFIG_SOC_DEVICE is selected by drivers as needed. + +Implementing a UCLASS_SOC provider +---------------------------------- + +The purpose of this framework is to allow UCLASS_SOC provider drivers to supply +identification information about the SoC in use at runtime. The framework +allows drivers to define soc_ops that return identification strings. All +soc_ops need not be defined and can be left as NULL, in which case the +framework will return -ENOSYS and not consider the value when doing an +soc_device_match. + +It is left to the driver implementor to decide how the information returned is +determined, but in general the same SOC should always return the same set of +identifying information. Information returned must be in the form of a NULL +terminated string. + +See include/soc.h for documentation of the available soc_ops and the intended +meaning of the values that can be returned. See drivers/soc/soc_sandbox.c for +an example UCLASS_SOC provider driver. + +Using a UCLASS_SOC driver +------------------------- + +The framework provides the ability to retrieve and use the identification +strings directly. It also has the ability to return a match from a list of +different sets of SoC data using soc_device_match. + +An array of 'struct soc_attr' can be defined, each containing ID information +for a specific SoC, and when passed to soc_device_match, the identifier values +for each entry in the list will be compared against the values provided by the +UCLASS_SOC driver that is in use. The first entry in the list that matches all +non-null values will be returned by soc_device_match. + +An example of various uses of the framework can be found at test/dm/soc.c. + +Describing the device using device tree +--------------------------------------- + +.. code-block:: none + + chipid: chipid { + compatible = "sandbox,soc"; + }; + +All that is required in a DT node is a compatible for a corresponding +UCLASS_SOC driver. diff --git a/doc/develop/driver-model/spi-howto.rst b/doc/develop/driver-model/spi-howto.rst new file mode 100644 index 00000000000..97fbf750cb6 --- /dev/null +++ b/doc/develop/driver-model/spi-howto.rst @@ -0,0 +1,692 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +How to port a SPI driver to driver model +======================================== + +Here is a rough step-by-step guide. It is based around converting the +exynos SPI driver to driver model (DM) and the example code is based +around U-Boot v2014.10-rc2 (commit be9f643). This has been updated for +v2015.04. + +It is quite long since it includes actual code examples. + +Before driver model, SPI drivers have their own private structure which +contains 'struct spi_slave'. With driver model, 'struct spi_slave' still +exists, but now it is 'per-child data' for the SPI bus. Each child of the +SPI bus is a SPI slave. The information that was stored in the +driver-specific slave structure can now be port in private data for the +SPI bus. + +For example, struct tegra_spi_slave looks like this: + +.. code-block:: c + + struct tegra_spi_slave { + struct spi_slave slave; + struct tegra_spi_ctrl *ctrl; + }; + +In this case 'slave' will be in per-child data, and 'ctrl' will be in the +SPI's buses private data. + + +How long does this take? +------------------------ + +You should be able to complete this within 2 hours, including testing but +excluding preparing the patches. The API is basically the same as before +with only minor changes: + +- methods to set speed and mode are separated out +- cs_info is used to get information on a chip select + + +Enable driver mode for SPI and SPI flash +---------------------------------------- + +Add these to your board config: + +* CONFIG_DM_SPI +* CONFIG_DM_SPI_FLASH + + +Add the skeleton +---------------- + +Put this code at the bottom of your existing driver file: + +.. code-block:: c + + struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) + { + return NULL; + } + + struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, + int spi_node) + { + return NULL; + } + + static int exynos_spi_of_to_plat(struct udevice *dev) + { + return -ENODEV; + } + + static int exynos_spi_probe(struct udevice *dev) + { + return -ENODEV; + } + + static int exynos_spi_remove(struct udevice *dev) + { + return -ENODEV; + } + + static int exynos_spi_claim_bus(struct udevice *dev) + { + + return -ENODEV; + } + + static int exynos_spi_release_bus(struct udevice *dev) + { + + return -ENODEV; + } + + static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) + { + + return -ENODEV; + } + + static int exynos_spi_set_speed(struct udevice *dev, uint speed) + { + return -ENODEV; + } + + static int exynos_spi_set_mode(struct udevice *dev, uint mode) + { + return -ENODEV; + } + + static int exynos_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) + { + return -EINVAL; + } + + static const struct dm_spi_ops exynos_spi_ops = { + .claim_bus = exynos_spi_claim_bus, + .release_bus = exynos_spi_release_bus, + .xfer = exynos_spi_xfer, + .set_speed = exynos_spi_set_speed, + .set_mode = exynos_spi_set_mode, + .cs_info = exynos_cs_info, + }; + + static const struct udevice_id exynos_spi_ids[] = { + { .compatible = "samsung,exynos-spi" }, + { } + }; + + U_BOOT_DRIVER(exynos_spi) = { + .name = "exynos_spi", + .id = UCLASS_SPI, + .of_match = exynos_spi_ids, + .ops = &exynos_spi_ops, + .of_to_plat = exynos_spi_of_to_plat, + .probe = exynos_spi_probe, + .remove = exynos_spi_remove, + }; + + +Replace 'exynos' in the above code with your driver name +-------------------------------------------------------- + + +#ifdef out all of the code in your driver except for the above +-------------------------------------------------------------- + +This will allow you to get it building, which means you can work +incrementally. Since all the methods return an error initially, there is +less chance that you will accidentally leave something in. + +Also, even though your conversion is basically a rewrite, it might help +reviewers if you leave functions in the same place in the file, +particularly for large drivers. + + +Add some includes +----------------- + +Add these includes to your driver: + +.. code-block:: c + + #include + #include + + +Build +----- + +At this point you should be able to build U-Boot for your board with the +empty SPI driver. You still have empty methods in your driver, but we will +write these one by one. + +Set up your platform data structure +----------------------------------- + +This will hold the information your driver to operate, like its hardware +address or maximum frequency. + +You may already have a struct like this, or you may need to create one +from some of the #defines or global variables in the driver. + +Note that this information is not the run-time information. It should not +include state that changes. It should be fixed throughout the live of +U-Boot. Run-time information comes later. + +Here is what was in the exynos spi driver: + +.. code-block:: c + + struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ + int node; + uint deactivate_delay_us; /* Delay to wait after deactivate */ + }; + +Of these, inited is handled by DM and node is the device tree node, which +DM tells you. The name is not quite right. So in this case we would use: + +.. code-block:: c + + struct exynos_spi_plat { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + uint deactivate_delay_us; /* Delay to wait after deactivate */ + }; + + +Write of_to_plat() [for device tree only] +------------------------------------------------- + +This method will convert information in the device tree node into a C +structure in your driver (called platform data). If you are not using +device tree, go to 8b. + +DM will automatically allocate the struct for us when we are using device +tree, but we need to tell it the size: + +.. code-block:: c + + U_BOOT_DRIVER(spi_exynos) = { + ... + .plat_auto = sizeof(struct exynos_spi_plat), + + +Here is a sample function. It gets a pointer to the platform data and +fills in the fields from device tree. + +.. code-block:: c + + static int exynos_spi_of_to_plat(struct udevice *bus) + { + struct exynos_spi_plat *plat = bus->plat; + const void *blob = gd->fdt_blob; + int node = dev_of_offset(bus); + + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); + plat->periph_id = pinmux_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: Invalid peripheral ID %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", + __func__, plat->regs, plat->periph_id, plat->frequency, + plat->deactivate_delay_us); + + return 0; + } + + +Add the platform data [non-device-tree only] +-------------------------------------------- + +Specify this data in a U_BOOT_DRVINFO() declaration in your board file: + +.. code-block:: c + + struct exynos_spi_plat platdata_spi0 = { + .periph_id = ... + .frequency = ... + .regs = ... + .deactivate_delay_us = ... + }; + + U_BOOT_DRVINFO(board_spi0) = { + .name = "exynos_spi", + .plat = &platdata_spi0, + }; + +You will unfortunately need to put the struct definition into a header file +in this case so that your board file can use it. + + +Add the device private data +--------------------------- + +Most devices have some private data which they use to keep track of things +while active. This is the run-time information and needs to be stored in +a structure. There is probably a structure in the driver that includes a +'struct spi_slave', so you can use that. + +.. code-block:: c + + struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + struct spi_bus *bus; /* Pointer to our SPI bus info */ + ulong last_transaction_us; /* Time of last transaction end */ + }; + + +We should rename this to make its purpose more obvious, and get rid of +the slave structure, so we have: + +.. code-block:: c + + struct exynos_spi_priv { + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; + int skip_preamble; + ulong last_transaction_us; /* Time of last transaction end */ + }; + + +DM can auto-allocate this also: + +.. code-block:: c + + U_BOOT_DRIVER(spi_exynos) = { + ... + .priv_auto = sizeof(struct exynos_spi_priv), + + +Note that this is created before the probe method is called, and destroyed +after the remove method is called. It will be zeroed when the probe +method is called. + + +Add the probe() and remove() methods +------------------------------------ + +Note: It's a good idea to build repeatedly as you are working, to avoid a +huge amount of work getting things compiling at the end. + +The probe method is supposed to set up the hardware. U-Boot used to use +spi_setup_slave() to do this. So take a look at this function and see +what you can copy out to set things up. + +.. code-block:: c + + static int exynos_spi_probe(struct udevice *bus) + { + struct exynos_spi_plat *plat = dev_get_plat(bus); + struct exynos_spi_priv *priv = dev_get_priv(bus); + + priv->regs = plat->regs; + if (plat->periph_id == PERIPH_ID_SPI1 || + plat->periph_id == PERIPH_ID_SPI2) + priv->fifo_size = 64; + else + priv->fifo_size = 256; + + priv->skip_preamble = 0; + priv->last_transaction_us = timer_get_us(); + priv->freq = plat->frequency; + priv->periph_id = plat->periph_id; + + return 0; + } + +This implementation doesn't actually touch the hardware, which is somewhat +unusual for a driver. In this case we will do that when the device is +claimed by something that wants to use the SPI bus. + +For remove we could shut down the clocks, but in this case there is +nothing to do. DM frees any memory that it allocated, so we can just +remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. + + +Implement set_speed() +--------------------- + +This should set up clocks so that the SPI bus is running at the right +speed. With the old API spi_claim_bus() would normally do this and several +of the following functions, so let's look at that function: + +.. code-block:: c + + int spi_claim_bus(struct spi_slave *slave) + { + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; + } + + +It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. +With DM these will happen in separate methods. + + +Here is an example for the speed part: + +.. code-block:: c + + static int exynos_spi_set_speed(struct udevice *bus, uint speed) + { + struct exynos_spi_plat *plat = bus->plat; + struct exynos_spi_priv *priv = dev_get_priv(bus); + int ret; + + if (speed > plat->frequency) + speed = plat->frequency; + ret = set_spi_clk(priv->periph_id, speed); + if (ret) + return ret; + priv->freq = speed; + debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); + + return 0; + } + + +Implement set_mode() +-------------------- + +This should adjust the SPI mode (polarity, etc.). Again this code probably +comes from the old spi_claim_bus(). Here is an example: + +.. code-block:: c + + static int exynos_spi_set_mode(struct udevice *bus, uint mode) + { + struct exynos_spi_priv *priv = dev_get_priv(bus); + uint32_t reg; + + reg = readl(&priv->regs->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, &priv->regs->ch_cfg); + priv->mode = mode; + debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); + + return 0; + } + + +Implement claim_bus() +--------------------- + +This is where a client wants to make use of the bus, so claims it first. +At this point we need to make sure everything is set up ready for data +transfer. Note that this function is wholly internal to the driver - at +present the SPI uclass never calls it. + +Here again we look at the old claim function and see some code that is +needed. It is anything unrelated to speed and mode: + +.. code-block:: c + + static int exynos_spi_claim_bus(struct udevice *bus) + { + struct exynos_spi_priv *priv = dev_get_priv(bus); + + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); + spi_flush_fifo(priv->regs); + + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); + + return 0; + } + +The spi_flush_fifo() function is in the removed part of the code, so we +need to expose it again (perhaps with an #endif before it and '#if 0' +after it). It only needs access to priv->regs which is why we have +passed that in: + +.. code-block:: c + + /** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param regs Pointer to SPI registers + */ + static void spi_flush_fifo(struct exynos_spi *regs) + { + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); + } + + +Implement release_bus() +----------------------- + +This releases the bus - in our example the old code in spi_release_bus() +is a call to spi_flush_fifo, so we add: + +.. code-block:: c + + static int exynos_spi_release_bus(struct udevice *bus) + { + struct exynos_spi_priv *priv = dev_get_priv(bus); + + spi_flush_fifo(priv->regs); + + return 0; + } + + +Implement xfer() +---------------- + +This is the final method that we need to create, and it is where all the +work happens. The method parameters are the same as the old spi_xfer() with +the addition of a 'struct udevice' so conversion is pretty easy. Start +by copying the contents of spi_xfer() to your new xfer() method and proceed +from there. + +If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an +activate function, something like this: + +.. code-block:: c + + void spi_cs_activate(struct spi_slave *slave) + { + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + /* If it's too soon to do another transaction, wait */ + if (spi_slave->bus->deactivate_delay_us && + spi_slave->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - spi_slave->last_transaction_us; + if (delay_us < spi_slave->bus->deactivate_delay_us) + udelay(spi_slave->bus->deactivate_delay_us - delay_us); + } + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); + spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; + } + +The new version looks like this: + +.. code-block:: c + + static void spi_cs_activate(struct udevice *dev) + { + struct udevice *bus = dev->parent; + struct exynos_spi_plat *pdata = dev_get_plat(bus); + struct exynos_spi_priv *priv = dev_get_priv(bus); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus '%s'\n", bus->name); + priv->skip_preamble = priv->mode & SPI_PREAMBLE; + } + +All we have really done here is change the pointers and print the device name +instead of the bus number. Other local static functions can be treated in +the same way. + + +Set up the per-child data and child pre-probe function +------------------------------------------------------ + +To minimise the pain and complexity of the SPI subsystem while the driver +model change-over is in place, struct spi_slave is used to reference a +SPI bus slave, even though that slave is actually a struct udevice. In fact +struct spi_slave is the device's child data. We need to make sure this space +is available. It is possible to allocate more space that struct spi_slave +needs, but this is the minimum. + +.. code-block:: c + + U_BOOT_DRIVER(exynos_spi) = { + ... + .per_child_auto = sizeof(struct spi_slave), + } + + +Optional: Set up cs_info() if you want it +----------------------------------------- + +Sometimes it is useful to know whether a SPI chip select is valid, but this +is not obvious from outside the driver. In this case you can provide a +method for cs_info() to deal with this. If you don't provide it, then the +device tree will be used to determine what chip selects are valid. + +Return -EINVAL if the supplied chip select is invalid, or 0 if it is valid. +If you don't provide the cs_info() method, 0 is assumed for all chip selects +that do not appear in the device tree. + + +Test it +------- + +Now that you have the code written and it compiles, try testing it using +the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your +board. + + +Prepare patches and send them to the mailing lists +-------------------------------------------------- + +You can use 'tools/patman/patman' to prepare, check and send patches for +your work. See tools/patman/README for details. + +A little note about SPI uclass features +--------------------------------------- + +The SPI uclass keeps some information about each device 'dev' on the bus: + + struct dm_spi_slave_plat: + This is device_get_parent_plat(dev). + This is where the chip select number is stored, along with + the default bus speed and mode. It is automatically read + from the device tree in spi_child_post_bind(). It must not + be changed at run-time after being set up because platform + data is supposed to be immutable at run-time. + struct spi_slave: + This is device_get_parentdata(dev). + Already mentioned above. It holds run-time information about + the device. + +There are also some SPI uclass methods that get called behind the scenes: + + spi_post_bind(): + Called when a new bus is bound. + This scans the device tree for devices on the bus, and binds + each one. This in turn causes spi_child_post_bind() to be + called for each, which reads the device tree information + into the parent (per-child) platform data. + spi_child_post_bind(): + Called when a new child is bound. + As mentioned above this reads the device tree information + into the per-child platform data + spi_child_pre_probe(): + Called before a new child is probed. + This sets up the mode and speed in struct spi_slave by + copying it from the parent's platform data for this child. + It also sets the 'dev' pointer, needed to permit passing + 'struct spi_slave' around the place without needing a + separate 'struct udevice' pointer. + +The above housekeeping makes it easier to write your SPI driver. diff --git a/doc/develop/driver-model/usb-info.rst b/doc/develop/driver-model/usb-info.rst new file mode 100644 index 00000000000..24d1e81a6c6 --- /dev/null +++ b/doc/develop/driver-model/usb-info.rst @@ -0,0 +1,423 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +How USB works with driver model +=============================== + +Introduction +------------ + +Driver model USB support makes use of existing features but changes how +drivers are found. This document provides some information intended to help +understand how things work with USB in U-Boot when driver model is enabled. + + +Enabling driver model for USB +----------------------------- + +A new CONFIG_DM_USB option is provided to enable driver model for USB. This +causes the USB uclass to be included, and drops the equivalent code in +usb.c. In particular the usb_init() function is then implemented by the +uclass. + + +Support for EHCI and XHCI +------------------------- + +So far OHCI is not supported. Both EHCI and XHCI drivers should be declared +as drivers in the USB uclass. For example: + +.. code-block:: c + + static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 }, + { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 }, + { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 }, + { } + }; + + U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_tegra", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .of_to_plat = ehci_usb_of_to_plat, + .probe = tegra_ehci_usb_probe, + .remove = tegra_ehci_usb_remove, + .ops = &ehci_usb_ops, + .plat_auto = sizeof(struct usb_plat), + .priv_auto = sizeof(struct fdt_usb), + .flags = DM_FLAG_ALLOC_PRIV_DMA, + }; + +Here ehci_usb_ids is used to list the controllers that the driver supports. +Each has its own data value. Controllers must be in the UCLASS_USB uclass. + +The of_to_plat() method allows the controller driver to grab any +necessary settings from the device tree. + +The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in +most cases, since they are all EHCI-compatible. For EHCI there are also some +special operations that can be overridden when calling ehci_register(). + +The driver can use priv_auto to set the size of its private data. +This can hold run-time information needed by the driver for operation. It +exists when the device is probed (not when it is bound) and is removed when +the driver is removed. + +Note that usb_plat is currently only used to deal with setting up a bus +in USB device mode (OTG operation). It can be omitted if that is not +supported. + +The driver's probe() method should do the basic controller init and then +call ehci_register() to register itself as an EHCI device. It should call +ehci_deregister() in the remove() method. Registering a new EHCI device +does not by itself cause the bus to be scanned. + +The old ehci_hcd_init() function is no-longer used. Nor is it necessary to +set up the USB controllers from board init code. When 'usb start' is used, +each controller will be probed and its bus scanned. + +XHCI works in a similar way. + + +Data structures +--------------- + +The following primary data structures are in use: + +- struct usb_device: + This holds information about a device on the bus. All devices have + this structure, even the root hub. The controller itself does not + have this structure. You can access it for a device 'dev' with + dev_get_parent_priv(dev). It matches the old structure except that the + parent and child information is not present (since driver model + handles that). Once the device is set up, you can find the device + descriptor and current configuration descriptor in this structure. + +- struct usb_plat: + This holds platform data for a controller. So far this is only used + as a work-around for controllers which can act as USB devices in OTG + mode, since the gadget framework does not use driver model. + +- struct usb_dev_plat: + This holds platform data for a device. You can access it for a + device 'dev' with dev_get_parent_plat(dev). It holds the device + address and speed - anything that can be determined before the device + driver is actually set up. When probing the bus this structure is + used to provide essential information to the device driver. + +- struct usb_bus_priv: + This is private information for each controller, maintained by the + controller uclass. It is mostly used to keep track of the next + device address to use. + +Of these, only struct usb_device was used prior to driver model. + + +USB buses +--------- + +Given a controller, you know the bus - it is the one attached to the +controller. Each controller handles exactly one bus. Every controller has a +root hub attached to it. This hub, which is itself a USB device, can provide +one or more 'ports' to which additional devices can be attached. It is +possible to power up a hub and find out which of its ports have devices +attached. + +Devices are given addresses starting at 1. The root hub is always address 1, +and from there the devices are numbered in sequence. The USB uclass takes +care of this numbering automatically during enumeration. + +USB devices are enumerated by finding a device on a particular hub, and +setting its address to the next available address. The USB bus stretches out +in a tree structure, potentially with multiple hubs each with several ports +and perhaps other hubs. Some hubs will have their own power since otherwise +the 5V 500mA power supplied by the controller will not be sufficient to run +very many devices. + +Enumeration in U-Boot takes a long time since devices are probed one at a +time, and each is given sufficient time to wake up and announce itself. The +timeouts are set for the slowest device. + +Up to 127 devices can be on each bus. USB has four bus speeds: low +(1.5Mbps), full (12Mbps), high (480Mbps) which is only available with USB2 +and newer (EHCI), and super (5Gbps) which is only available with USB3 and +newer (XHCI). If you connect a super-speed device to a high-speed hub, you +will only get high-speed. + + +USB operations +-------------- + +As before driver model, messages can be sent using submit_bulk_msg() and the +like. These are now implemented by the USB uclass and route through the +controller drivers. Note that messages are not sent to the driver of the +device itself - i.e. they don't pass down the stack to the controller. +U-Boot simply finds the controller to which the device is attached, and sends +the message there with an appropriate 'pipe' value so it can be addressed +properly. Having said that, the USB device which should receive the message +is passed in to the driver methods, for use by sandbox. This design decision +is open for review and the code impact of changing it is small since the +methods are typically implemented by the EHCI and XHCI stacks. + +Controller drivers (in UCLASS_USB) themselves provide methods for sending +each message type. For XHCI an additional alloc_device() method is provided +since XHCI needs to allocate a device context before it can even read the +device's descriptor. + +These methods use a 'pipe' which is a collection of bit fields used to +describe the type of message, direction of transfer and the intended +recipient (device number). + + +USB Devices +----------- + +USB devices are found using a simple algorithm which works through the +available hubs in a depth-first search. Devices can be in any uclass, but +are attached to a parent hub (or controller in the case of the root hub) and +so have parent data attached to them (this is struct usb_device). + +By the time the device's probe() method is called, it is enumerated and is +ready to talk to the host. + +The enumeration process needs to work out which driver to attach to each USB +device. It does this by examining the device class, interface class, vendor +ID, product ID, etc. See struct usb_driver_entry for how drivers are matched +with USB devices - you can use the USB_DEVICE() macro to declare a USB +driver. For example, usb_storage.c defines a USB_DEVICE() to handle storage +devices, and it will be used for all USB devices which match. + + + +Technical details on enumeration flow +------------------------------------- + +It is useful to understand precisely how a USB bus is enumerating to avoid +confusion when dealing with USB devices. + +Device initialisation happens roughly like this: + +- At some point the 'usb start' command is run +- This calls usb_init() which works through each controller in turn +- The controller is probed(). This does no enumeration. +- Then usb_scan_bus() is called. This calls usb_scan_device() to scan the + (only) device that is attached to the controller - a root hub +- usb_scan_device() sets up a fake struct usb_device and calls + usb_setup_device(), passing the port number to be scanned, in this case + port 0 +- usb_setup_device() first calls usb_prepare_device() to set the device + address, then usb_select_config() to select the first configuration +- at this point the device is enumerated but we do not have a real struct + udevice for it. But we do have the descriptor in struct usb_device so we can + use this to figure out what driver to use +- back in usb_scan_device(), we call usb_find_child() to try to find an + existing device which matches the one we just found on the bus. This can + happen if the device is mentioned in the device tree, or if we previously + scanned the bus and so the device was created before +- if usb_find_child() does not find an existing device, we call + usb_find_and_bind_driver() which tries to bind one +- usb_find_and_bind_driver() searches all available USB drivers (declared + with USB_DEVICE()). If it finds a match it binds that driver to create a + new device. +- If it does not, it binds a generic driver. A generic driver is good enough + to allow access to the device (sending it packets, etc.) but all + functionality will need to be implemented outside the driver model. +- in any case, when usb_find_child() and/or usb_find_and_bind_driver() are + done, we have a device with the correct uclass. At this point we want to + probe the device +- first we store basic information about the new device (address, port, + speed) in its parent platform data. We cannot store it its private data + since that will not exist until the device is probed. +- then we call device_probe() which probes the device +- the first probe step is actually the USB controller's (or USB hubs's) + child_pre_probe() method. This gets called before anything else and is + intended to set up a child device ready to be used with its parent bus. For + USB this calls usb_child_pre_probe() which grabs the information that was + stored in the parent platform data and stores it in the parent private data + (which is struct usb_device, a real one this time). It then calls + usb_select_config() again to make sure that everything about the device is + set up +- note that we have called usb_select_config() twice. This is inefficient + but the alternative is to store additional information in the platform data. + The time taken is minimal and this way is simpler +- at this point the device is set up and ready for use so far as the USB + subsystem is concerned +- the device's probe() method is then called. It can send messages and do + whatever else it wants to make the device work. + +Note that the first device is always a root hub, and this must be scanned to +find any devices. The above steps will have created a hub (UCLASS_USB_HUB), +given it address 1 and set the configuration. + +For hubs, the hub uclass has a post_probe() method. This means that after +any hub is probed, the uclass gets to do some processing. In this case +usb_hub_post_probe() is called, and the following steps take place: + +- usb_hub_post_probe() calls usb_hub_scan() to scan the hub, which in turn + calls usb_hub_configure() +- hub power is enabled +- we loop through each port on the hub, performing the same steps for each +- first, check if there is a device present. This happens in + usb_hub_port_connect_change(). If so, then usb_scan_device() is called to + scan the device, passing the appropriate port number. +- you will recognise usb_scan_device() from the steps above. It sets up the + device ready for use. If it is a hub, it will scan that hub before it + continues here (recursively, depth-first) +- once all hub ports are scanned in this way, the hub is ready for use and + all of its downstream devices also +- additional controllers are scanned in the same way + +The above method has some nice properties: + +- the bus enumeration happens by virtue of driver model's natural device flow +- most logic is in the USB controller and hub uclasses; the actual device + drivers do not need to know they are on a USB bus, at least so far as + enumeration goes +- hub scanning happens automatically after a hub is probed + + +Hubs +---- + +USB hubs are scanned as in the section above. While hubs have their own +uclass, they share some common elements with controllers: + +- they both attach private data to their children (struct usb_device, + accessible for a child with dev_get_parent_priv(child)) +- they both use usb_child_pre_probe() to set up their children as proper USB + devices + + +Example - Mass Storage +---------------------- + +As an example of a USB device driver, see usb_storage.c. It uses its own +uclass and declares itself as follows: + +.. code-block:: c + + U_BOOT_DRIVER(usb_mass_storage) = { + .name = "usb_mass_storage", + .id = UCLASS_MASS_STORAGE, + .of_match = usb_mass_storage_ids, + .probe = usb_mass_storage_probe, + }; + + static const struct usb_device_id mass_storage_id_table[] = { + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_MASS_STORAGE}, + { } /* Terminating entry */ + }; + + USB_DEVICE(usb_mass_storage, mass_storage_id_table); + +The USB_DEVICE() macro attaches the given table of matching information to +the given driver. Note that the driver is declared in U_BOOT_DRIVER() as +'usb_mass_storage' and this must match the first parameter of USB_DEVICE. + +When usb_find_and_bind_driver() is called on a USB device with the +bInterfaceClass value of USB_CLASS_MASS_STORAGE, it will automatically find +this driver and use it. + + +Counter-example: USB Ethernet +----------------------------- + +As an example of the old way of doing things, see usb_ether.c. When the bus +is scanned, all Ethernet devices will be created as generic USB devices (in +uclass UCLASS_USB_DEV_GENERIC). Then, when the scan is completed, +usb_host_eth_scan() will be called. This looks through all the devices on +each bus and manually figures out which are Ethernet devices in the ways of +yore. + +In fact, usb_ether should be moved to driver model. Each USB Ethernet driver +(e.g drivers/usb/eth/asix.c) should include a USB_DEVICE() declaration, so +that it will be found as part of normal USB enumeration. Then, instead of a +generic USB driver, a real (driver-model-aware) driver will be used. Since +Ethernet now supports driver model, this should be fairly easy to achieve, +and then usb_ether.c and the usb_host_eth_scan() will melt away. + + +Sandbox +------- + +All driver model uclasses must have tests and USB is no exception. To +achieve this, a sandbox USB controller is provided. This can make use of +emulation drivers which pretend to be USB devices. Emulations are provided +for a hub and a flash stick. These are enough to create a pretend USB bus +(defined by the sandbox device tree sandbox.dts) which can be scanned and +used. + +Tests in test/dm/usb.c make use of this feature. It allows much of the USB +stack to be tested without real hardware being needed. + +Here is an example device tree fragment: + +.. code-block:: none + + usb@1 { + compatible = "sandbox,usb"; + hub { + compatible = "usb-hub"; + usb,device-class = ; + hub-emul { + compatible = "sandbox,usb-hub"; + #address-cells = <1>; + #size-cells = <0>; + flash-stick { + reg = <0>; + compatible = "sandbox,usb-flash"; + sandbox,filepath = "flash.bin"; + }; + }; + }; + }; + +This defines a single controller, containing a root hub (which is required). +The hub is emulated by a hub emulator, and the emulated hub has a single +flash stick to emulate on one of its ports. + +When 'usb start' is used, the following 'dm tree' output will be available:: + + usb [ + ] `-- usb@1 + usb_hub [ + ] `-- hub + usb_emul [ + ] |-- hub-emul + usb_emul [ + ] | `-- flash-stick + usb_mass_st [ + ] `-- usb_mass_storage + + +This may look confusing. Most of it mirrors the device tree, but the +'usb_mass_storage' device is not in the device tree. This is created by +usb_find_and_bind_driver() based on the USB_DRIVER in usb_storage.c. While +'flash-stick' is the emulation device, 'usb_mass_storage' is the real U-Boot +USB device driver that talks to it. + + +Future work +----------- + +It is pretty uncommon to have a large USB bus with lots of hubs on an +embedded system. In fact anything other than a root hub is uncommon. Still +it would be possible to speed up enumeration in two ways: + +- breadth-first search would allow devices to be reset and probed in + parallel to some extent +- enumeration could be lazy, in the sense that we could enumerate just the + root hub at first, then only progress to the next 'level' when a device is + used that we cannot find. This could be made easier if the devices were + statically declared in the device tree (which is acceptable for production + boards where the same, known, things are on each bus). + +But in common cases the current algorithm is sufficient. + +Other things that need doing: +- Convert usb_ether to use driver model as described above +- Test that keyboards work (and convert to driver model) +- Move the USB gadget framework to driver model +- Implement OHCI in driver model +- Implement USB PHYs in driver model +- Work out a clever way to provide lazy init for USB devices + + +.. Simon Glass +.. 23-Mar-15 diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 84914bb47bf..444df679578 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -10,6 +10,7 @@ Implementation :maxdepth: 1 commands + driver-model/index global_data logging menus diff --git a/doc/driver-model/bind.rst b/doc/driver-model/bind.rst deleted file mode 100644 index b19661b5fe2..00000000000 --- a/doc/driver-model/bind.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. sectionauthor:: Patrice Chotard - -Binding/unbinding a driver -========================== - -This document aims to describe the bind and unbind commands. - -For debugging purpose, it should be useful to bind or unbind a driver from -the U-boot command line. - -The unbind command calls the remove device driver callback and unbind the -device from its driver. - -The bind command binds a device to its driver. - -In some cases it can be useful to be able to bind a device to a driver from -the command line. -The obvious example is for versatile devices such as USB gadget. -Another use case is when the devices are not yet ready at startup and -require some setup before the drivers are bound (ex: FPGA which bitsream is -fetched from a mass storage or ethernet) - -usage: - -bind -bind - -unbind -unbind -unbind - -Where: - - is the node's device tree path - - is one of the class available in the list given by the "dm uclass" - command or first column of "dm tree" command. - - is the index of the parent's node (second column of "dm tree" output). - - is the driver name to bind given by the "dm drivers" command or the by - the fourth column of "dm tree" output. - -example: - -bind usb_dev_generic 0 usb_ether -unbind usb_dev_generic 0 usb_ether -or -unbind eth 1 - -bind /ocp/omap_dwc3@48380000/usb@48390000 usb_ether -unbind /ocp/omap_dwc3@48380000/usb@48390000 diff --git a/doc/driver-model/debugging.rst b/doc/driver-model/debugging.rst deleted file mode 100644 index bbb2794340f..00000000000 --- a/doc/driver-model/debugging.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. sectionauthor:: Simon Glass - -Debugging driver model -====================== - -This document aims to provide help when you cannot work out why driver model is -not doing what you expect. - - -Useful techniques in general ----------------------------- - -Here are some useful debugging features generally. - - - If you are writing a new feature, consider doing it in sandbox instead of - on your board. Sandbox has no limits, allows easy debugging (e.g. gdb) and - you can write emulators for most common devices. - - Put '#define DEBUG' at the top of a file, to activate all the debug() and - log_debug() statements in that file. - - Where logging is used, change the logging level, e.g. in SPL with - CONFIG_SPL_LOG_MAX_LEVEL=7 (which is LOGL_DEBUG) and - CONFIG_LOG_DEFAULT_LEVEL=7 - - Where logging of return values is implemented with log_msg_ret(), set - CONFIG_LOG_ERROR_RETURN=y to see exactly where the error is happening - - Make sure you have a debug UART enabled - see CONFIG_DEBUG_UART. With this - you can get serial output (printf(), etc.) before the serial driver is - running. - - Use a JTAG emulator to set breakpoints and single-step through code - -Not that most of these increase code/data size somewhat when enabled. - - -Failure to locate a device --------------------------- - -Let's say you have uclass_first_device_err() and it is not finding anything. - -If it is returning an error, then that gives you a clue. Look up linux/errno.h -to see errors. Common ones are: - - - -ENOMEM which indicates that memory is short. If it happens in SPL or - before relocation in U-Boot, check CONFIG_SPL_SYS_MALLOC_F_LEN and - CONFIG_SYS_MALLOC_F_LEN as they may need to be larger. Add '#define DEBUG' - at the very top of malloc_simple.c to get an idea of where your memory is - going. - - -EINVAL which typically indicates that something was missing or wrong in - the device tree node. Check that everything is correct and look at the - of_to_plat() method in the driver. - -If there is no error, you should check if the device is actually bound. Call -dm_dump_all() just before you locate the device to make sure it exists. - -If it does not exist, check your device tree compatible strings match up with -what the driver expects (in the struct udevice_id array). - -If you are using of-platdata (e.g. CONFIG_SPL_OF_PLATDATA), check that the -driver name is the same as the first compatible string in the device tree (with -invalid-variable characters converted to underscore). - -If you are really stuck, putting '#define LOG_DEBUG' at the top of -drivers/core/lists.c should show you what is going on. diff --git a/doc/driver-model/design.rst b/doc/driver-model/design.rst deleted file mode 100644 index 4e5cecbab6a..00000000000 --- a/doc/driver-model/design.rst +++ /dev/null @@ -1,1016 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. sectionauthor:: Simon Glass - -Design Details -============== - -This README contains high-level information about driver model, a unified -way of declaring and accessing drivers in U-Boot. The original work was done -by: - - * Marek Vasut - * Pavel Herrmann - * Viktor Křivák - * Tomas Hlavacek - -This has been both simplified and extended into the current implementation -by: - - * Simon Glass - - -Terminology ------------ - -Uclass - a group of devices which operate in the same way. A uclass provides - a way of accessing individual devices within the group, but always - using the same interface. For example a GPIO uclass provides - operations for get/set value. An I2C uclass may have 10 I2C ports, - 4 with one driver, and 6 with another. - -Driver - some code which talks to a peripheral and presents a higher-level - interface to it. - -Device - an instance of a driver, tied to a particular port or peripheral. - - -How to try it -------------- - -Build U-Boot sandbox and run it:: - - make sandbox_defconfig - make - ./u-boot -d u-boot.dtb - - (type 'reset' to exit U-Boot) - - -There is a uclass called 'demo'. This uclass handles -saying hello, and reporting its status. There are two drivers in this -uclass: - - - simple: Just prints a message for hello, doesn't implement status - - shape: Prints shapes and reports number of characters printed as status - -The demo class is pretty simple, but not trivial. The intention is that it -can be used for testing, so it will implement all driver model features and -provide good code coverage of them. It does have multiple drivers, it -handles parameter data and plat (data which tells the driver how -to operate on a particular platform) and it uses private driver data. - -To try it, see the example session below:: - - =>demo hello 1 - Hello '@' from 07981110: red 4 - =>demo status 2 - Status: 0 - =>demo hello 2 - g - r@ - e@@ - e@@@ - n@@@@ - g@@@@@ - =>demo status 2 - Status: 21 - =>demo hello 4 ^ - y^^^ - e^^^^^ - l^^^^^^^ - l^^^^^^^ - o^^^^^ - w^^^ - =>demo status 4 - Status: 36 - => - - -Running the tests ------------------ - -The intent with driver model is that the core portion has 100% test coverage -in sandbox, and every uclass has its own test. As a move towards this, tests -are provided in test/dm. To run them, try:: - - ./test/py/test.py --bd sandbox --build -k ut_dm -v - -You should see something like this:: - - (venv)$ ./test/py/test.py --bd sandbox --build -k ut_dm -v - +make O=/root/u-boot/build-sandbox -s sandbox_defconfig - +make O=/root/u-boot/build-sandbox -s -j8 - ============================= test session starts ============================== - platform linux2 -- Python 2.7.5, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- /root/u-boot/venv/bin/python - cachedir: .cache - rootdir: /root/u-boot, inifile: - collected 199 items - - test/py/tests/test_ut.py::test_ut_dm_init PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_bind] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_conversion] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_shot] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_conversion] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_shot] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_supply] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_adc_wrong_channel_selection] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_autobind] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_alloc] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_valid] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_autoprobe] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind_uclass] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_pre_probe_uclass] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_children] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_funcs] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_iterators] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data_uclass] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_ops] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata_uclass] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_children] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_clk_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_clk_periph] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_device_get_uclass_id] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_eth] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_eth_act] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_eth_alias] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_eth_prime] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_eth_rotate] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_fdt] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_fdt_offset] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_fdt_pre_reloc] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_fdt_uclass_seq] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio_anon] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio_copy] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio_leak] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio_phandles] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_gpio_requestf] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_bytewise] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_find] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset_len] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_probe_empty] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_read_write] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_i2c_speed] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_leak] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_led_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_led_gpio] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_led_label] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_lifecycle] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_mmc_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_net_retry] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_operations] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_ordering] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_pci_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_pci_busnum] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_pci_swapcase] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_platdata] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_get] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_io] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset_list] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_get] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_current] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_enable] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_mode] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_voltage] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_pre_reloc] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_ram_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_regmap_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_regmap_syscon] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_remoteproc_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_remove] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_reset_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_reset_walk] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_rtc_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_rtc_dual] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_rtc_reset] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_rtc_set_get] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_spi_find] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_spi_flash] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_spi_xfer] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_syscon_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_syscon_by_driver_data] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_timer_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass_before_ready] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find_by_name] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get_by_name] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_flash] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_keyb] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_multi] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_remove] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_remove] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_reorder] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_base] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp_comp] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_chars] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_context] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation1] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation2] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation3] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_text] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_bs] PASSED - test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_scroll] PASSED - - ======================= 84 tests deselected by '-kut_dm' ======================= - ================== 115 passed, 84 deselected in 3.77 seconds =================== - -What is going on? ------------------ - -Let's start at the top. The demo command is in cmd/demo.c. It does -the usual command processing and then: - -.. code-block:: c - - struct udevice *demo_dev; - - ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); - -UCLASS_DEMO means the class of devices which implement 'demo'. Other -classes might be MMC, or GPIO, hashing or serial. The idea is that the -devices in the class all share a particular way of working. The class -presents a unified view of all these devices to U-Boot. - -This function looks up a device for the demo uclass. Given a device -number we can find the device because all devices have registered with -the UCLASS_DEMO uclass. - -The device is automatically activated ready for use by uclass_get_device(). - -Now that we have the device we can do things like: - -.. code-block:: c - - return demo_hello(demo_dev, ch); - -This function is in the demo uclass. It takes care of calling the 'hello' -method of the relevant driver. Bearing in mind that there are two drivers, -this particular device may use one or other of them. - -The code for demo_hello() is in drivers/demo/demo-uclass.c: - -.. code-block:: c - - int demo_hello(struct udevice *dev, int ch) - { - const struct demo_ops *ops = device_get_ops(dev); - - if (!ops->hello) - return -ENOSYS; - - return ops->hello(dev, ch); - } - -As you can see it just calls the relevant driver method. One of these is -in drivers/demo/demo-simple.c: - -.. code-block:: c - - static int simple_hello(struct udevice *dev, int ch) - { - const struct dm_demo_pdata *pdata = dev_get_plat(dev); - - printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), - pdata->colour, pdata->sides); - - return 0; - } - - -So that is a trip from top (command execution) to bottom (driver action) -but it leaves a lot of topics to address. - - -Declaring Drivers ------------------ - -A driver declaration looks something like this (see -drivers/demo/demo-shape.c): - -.. code-block:: c - - static const struct demo_ops shape_ops = { - .hello = shape_hello, - .status = shape_status, - }; - - U_BOOT_DRIVER(demo_shape_drv) = { - .name = "demo_shape_drv", - .id = UCLASS_DEMO, - .ops = &shape_ops, - .priv_data_size = sizeof(struct shape_data), - }; - - -This driver has two methods (hello and status) and requires a bit of -private data (accessible through dev_get_priv(dev) once the driver has -been probed). It is a member of UCLASS_DEMO so will register itself -there. - -In U_BOOT_DRIVER it is also possible to specify special methods for bind -and unbind, and these are called at appropriate times. For many drivers -it is hoped that only 'probe' and 'remove' will be needed. - -The U_BOOT_DRIVER macro creates a data structure accessible from C, -so driver model can find the drivers that are available. - -The methods a device can provide are documented in the device.h header. -Briefly, they are: - - * bind - make the driver model aware of a device (bind it to its driver) - * unbind - make the driver model forget the device - * of_to_plat - convert device tree data to plat - see later - * probe - make a device ready for use - * remove - remove a device so it cannot be used until probed again - -The sequence to get a device to work is bind, of_to_plat (if using -device tree) and probe. - - -Platform Data -------------- - -Note: platform data is the old way of doing things. It is -basically a C structure which is passed to drivers to tell them about -platform-specific settings like the address of its registers, bus -speed, etc. Device tree is now the preferred way of handling this. -Unless you have a good reason not to use device tree (the main one -being you need serial support in SPL and don't have enough SRAM for -the cut-down device tree and libfdt libraries) you should stay away -from platform data. - -Platform data is like Linux platform data, if you are familiar with that. -It provides the board-specific information to start up a device. - -Why is this information not just stored in the device driver itself? The -idea is that the device driver is generic, and can in principle operate on -any board that has that type of device. For example, with modern -highly-complex SoCs it is common for the IP to come from an IP vendor, and -therefore (for example) the MMC controller may be the same on chips from -different vendors. It makes no sense to write independent drivers for the -MMC controller on each vendor's SoC, when they are all almost the same. -Similarly, we may have 6 UARTs in an SoC, all of which are mostly the same, -but lie at different addresses in the address space. - -Using the UART example, we have a single driver and it is instantiated 6 -times by supplying 6 lots of platform data. Each lot of platform data -gives the driver name and a pointer to a structure containing information -about this instance - e.g. the address of the register space. It may be that -one of the UARTS supports RS-485 operation - this can be added as a flag in -the platform data, which is set for this one port and clear for the rest. - -Think of your driver as a generic piece of code which knows how to talk to -a device, but needs to know where it is, any variant/option information and -so on. Platform data provides this link between the generic piece of code -and the specific way it is bound on a particular board. - -Examples of platform data include: - - - The base address of the IP block's register space - - Configuration options, like: - - the SPI polarity and maximum speed for a SPI controller - - the I2C speed to use for an I2C device - - the number of GPIOs available in a GPIO device - -Where does the platform data come from? It is either held in a structure -which is compiled into U-Boot, or it can be parsed from the Device Tree -(see 'Device Tree' below). - -For an example of how it can be compiled in, see demo-pdata.c which -sets up a table of driver names and their associated platform data. -The data can be interpreted by the drivers however they like - it is -basically a communication scheme between the board-specific code and -the generic drivers, which are intended to work on any board. - -Drivers can access their data via dev->info->plat. Here is -the declaration for the platform data, which would normally appear -in the board file. - -.. code-block:: c - - static const struct dm_demo_pdata red_square = { - .colour = "red", - .sides = 4. - }; - - static const struct driver_info info[] = { - { - .name = "demo_shape_drv", - .plat = &red_square, - }, - }; - - demo1 = driver_bind(root, &info[0]); - - -Device Tree ------------ - -While plat is useful, a more flexible way of providing device data is -by using device tree. In U-Boot you should use this where possible. Avoid -sending patches which make use of the U_BOOT_DRVINFO() macro unless strictly -necessary. - -With device tree we replace the above code with the following device tree -fragment: - -.. code-block:: c - - red-square { - compatible = "demo-shape"; - colour = "red"; - sides = <4>; - }; - -This means that instead of having lots of U_BOOT_DRVINFO() declarations in -the board file, we put these in the device tree. This approach allows a lot -more generality, since the same board file can support many types of boards -(e,g. with the same SoC) just by using different device trees. An added -benefit is that the Linux device tree can be used, thus further simplifying -the task of board-bring up either for U-Boot or Linux devs (whoever gets to -the board first!). - -The easiest way to make this work it to add a few members to the driver: - -.. code-block:: c - - .plat_auto = sizeof(struct dm_test_pdata), - .of_to_plat = testfdt_of_to_plat, - -The 'auto' feature allowed space for the plat to be allocated -and zeroed before the driver's of_to_plat() method is called. The -of_to_plat() method, which the driver write supplies, should parse -the device tree node for this device and place it in dev->plat. Thus -when the probe method is called later (to set up the device ready for use) -the platform data will be present. - -Note that both methods are optional. If you provide an of_to_plat -method then it will be called first (during activation). If you provide a -probe method it will be called next. See Driver Lifecycle below for more -details. - -If you don't want to have the plat automatically allocated then you -can leave out plat_auto. In this case you can use malloc -in your of_to_plat (or probe) method to allocate the required memory, -and you should free it in the remove method. - -The driver model tree is intended to mirror that of the device tree. The -root driver is at device tree offset 0 (the root node, '/'), and its -children are the children of the root node. - -In order for a device tree to be valid, the content must be correct with -respect to either device tree specification -(https://www.devicetree.org/specifications/) or the device tree bindings that -are found in the doc/device-tree-bindings directory. When not U-Boot specific -the bindings in this directory tend to come from the Linux Kernel. As such -certain design decisions may have been made already for us in terms of how -specific devices are described and bound. In most circumstances we wish to -retain compatibility without additional changes being made to the device tree -source files. - -Declaring Uclasses ------------------- - -The demo uclass is declared like this: - -.. code-block:: c - - UCLASS_DRIVER(demo) = { - .id = UCLASS_DEMO, - }; - -It is also possible to specify special methods for probe, etc. The uclass -numbering comes from include/dm/uclass-id.h. To add a new uclass, add to the -end of the enum there, then declare your uclass as above. - - -Device Sequence Numbers ------------------------ - -U-Boot numbers devices from 0 in many situations, such as in the command -line for I2C and SPI buses, and the device names for serial ports (serial0, -serial1, ...). Driver model supports this numbering and permits devices -to be locating by their 'sequence'. This numbering uniquely identifies a -device in its uclass, so no two devices within a particular uclass can have -the same sequence number. - -Sequence numbers start from 0 but gaps are permitted. For example, a board -may have I2C buses 1, 4, 5 but no 0, 2 or 3. The choice of how devices are -numbered is up to a particular board, and may be set by the SoC in some -cases. While it might be tempting to automatically renumber the devices -where there are gaps in the sequence, this can lead to confusion and is -not the way that U-Boot works. - -Where a device gets its sequence number is controlled by the DM_SEQ_ALIAS -Kconfig option, which can have a different value in U-Boot proper and SPL. -If this option is not set, aliases are ignored. - -Even if CONFIG_DM_SEQ_ALIAS is enabled, the uclass must still have the -DM_UC_FLAG_SEQ_ALIAS flag set, for its devices to be sequenced by aliases. - -With those options set, devices with an alias (e.g. "serial2") will get that -sequence number (e.g. 2). Other devices get the next available number after all -aliases and all existing numbers. This means that if there is just a single -alias "serial2", unaliased serial devices will be assigned 3 or more, with 0 and -1 being unused. - -If CONFIG_DM_SEQ_ALIAS or DM_UC_FLAG_SEQ_ALIAS are not set, all devices will get -sequence numbers in a simple ordering starting from 0. To find the next number -to allocate, driver model scans through to find the maximum existing number, -then uses the next one. It does not attempt to fill in gaps. - -.. code-block:: none - - aliases { - serial2 = "/serial@22230000"; - }; - -This indicates that in the uclass called "serial", the named node -("/serial@22230000") will be given sequence number 2. Any command or driver -which requests serial device 2 will obtain this device. - -More commonly you can use node references, which expand to the full path: - -.. code-block:: none - - aliases { - serial2 = &serial_2; - }; - ... - serial_2: serial@22230000 { - ... - }; - -The alias resolves to the same string in this case, but this version is -easier to read. - -Device sequence numbers are resolved when a device is bound and the number does -not change for the life of the device. - -There are some situations where the uclass must allocate sequence numbers, -since a strictly increase sequence (with devicetree nodes bound first) is not -suitable. An example of this is the PCI bus. In this case, you can set the -uclass DM_UC_FLAG_NO_AUTO_SEQ flag. With this flag set, only devices with an -alias will be assigned a number by driver model. The rest is left to the uclass -to sort out, e.g. when enumerating the bus. - -Note that changing the sequence number for a device (e.g. in a driver) is not -permitted. If it is felt to be necessary, ask on the mailing list. - -Bus Drivers ------------ - -A common use of driver model is to implement a bus, a device which provides -access to other devices. Example of buses include SPI and I2C. Typically -the bus provides some sort of transport or translation that makes it -possible to talk to the devices on the bus. - -Driver model provides some useful features to help with implementing buses. -Firstly, a bus can request that its children store some 'parent data' which -can be used to keep track of child state. Secondly, the bus can define -methods which are called when a child is probed or removed. This is similar -to the methods the uclass driver provides. Thirdly, per-child platform data -can be provided to specify things like the child's address on the bus. This -persists across child probe()/remove() cycles. - -For consistency and ease of implementation, the bus uclass can specify the -per-child platform data, so that it can be the same for all children of buses -in that uclass. There are also uclass methods which can be called when -children are bound and probed. - -Here an explanation of how a bus fits with a uclass may be useful. Consider -a USB bus with several devices attached to it, each from a different (made -up) uclass:: - - xhci_usb (UCLASS_USB) - eth (UCLASS_ETH) - camera (UCLASS_CAMERA) - flash (UCLASS_FLASH_STORAGE) - -Each of the devices is connected to a different address on the USB bus. -The bus device wants to store this address and some other information such -as the bus speed for each device. - -To achieve this, the bus device can use dev->parent_plat in each of its -three children. This can be auto-allocated if the bus driver (or bus uclass) -has a non-zero value for per_child_plat_auto. If not, then -the bus device or uclass can allocate the space itself before the child -device is probed. - -Also the bus driver can define the child_pre_probe() and child_post_remove() -methods to allow it to do some processing before the child is activated or -after it is deactivated. - -Similarly the bus uclass can define the child_post_bind() method to obtain -the per-child platform data from the device tree and set it up for the child. -The bus uclass can also provide a child_pre_probe() method. Very often it is -the bus uclass that controls these features, since it avoids each driver -having to do the same processing. Of course the driver can still tweak and -override these activities. - -Note that the information that controls this behaviour is in the bus's -driver, not the child's. In fact it is possible that child has no knowledge -that it is connected to a bus. The same child device may even be used on two -different bus types. As an example. the 'flash' device shown above may also -be connected on a SATA bus or standalone with no bus:: - - xhci_usb (UCLASS_USB) - flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by USB bus - - sata (UCLASS_AHCI) - flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by SATA bus - - flash (UCLASS_FLASH_STORAGE) - no parent data/methods (not on a bus) - -Above you can see that the driver for xhci_usb/sata controls the child's -bus methods. In the third example the device is not on a bus, and therefore -will not have these methods at all. Consider the case where the flash -device defines child methods. These would be used for *its* children, and -would be quite separate from the methods defined by the driver for the bus -that the flash device is connetced to. The act of attaching a device to a -parent device which is a bus, causes the device to start behaving like a -bus device, regardless of its own views on the matter. - -The uclass for the device can also contain data private to that uclass. -But note that each device on the bus may be a member of a different -uclass, and this data has nothing to do with the child data for each child -on the bus. It is the bus' uclass that controls the child with respect to -the bus. - - -Driver Lifecycle ----------------- - -Here are the stages that a device goes through in driver model. Note that all -methods mentioned here are optional - e.g. if there is no probe() method for -a device then it will not be called. A simple device may have very few -methods actually defined. - -Bind stage -^^^^^^^^^^ - -U-Boot discovers devices using one of these two methods: - -- Scan the U_BOOT_DRVINFO() definitions. U-Boot looks up the name specified - by each, to find the appropriate U_BOOT_DRIVER() definition. In this case, - there is no path by which driver_data may be provided, but the U_BOOT_DRVINFO() - may provide plat. - -- Scan through the device tree definitions. U-Boot looks at top-level - nodes in the the device tree. It looks at the compatible string in each node - and uses the of_match table of the U_BOOT_DRIVER() structure to find the - right driver for each node. In this case, the of_match table may provide a - driver_data value, but plat cannot be provided until later. - -For each device that is discovered, U-Boot then calls device_bind() to create a -new device, initializes various core fields of the device object such as name, -uclass & driver, initializes any optional fields of the device object that are -applicable such as of_offset, driver_data & plat, and finally calls the -driver's bind() method if one is defined. - -At this point all the devices are known, and bound to their drivers. There -is a 'struct udevice' allocated for all devices. However, nothing has been -activated (except for the root device). Each bound device that was created -from a U_BOOT_DRVINFO() declaration will hold the plat pointer specified -in that declaration. For a bound device created from the device tree, -plat will be NULL, but of_offset will be the offset of the device tree -node that caused the device to be created. The uclass is set correctly for -the device. - -The device's sequence number is assigned, either the requested one or the next -available one (after all aliases are processed) if nothing particular is -requested. - -The device's bind() method is permitted to perform simple actions, but -should not scan the device tree node, not initialise hardware, nor set up -structures or allocate memory. All of these tasks should be left for -the probe() method. - -Note that compared to Linux, U-Boot's driver model has a separate step of -probe/remove which is independent of bind/unbind. This is partly because in -U-Boot it may be expensive to probe devices and we don't want to do it until -they are needed, or perhaps until after relocation. - -Reading ofdata -^^^^^^^^^^^^^^ - -Most devices have data in the device tree which they can read to find out the -base address of hardware registers and parameters relating to driver -operation. This is called 'ofdata' (Open-Firmware data). - -The device's of_to_plat() implemnents allocation and reading of -plat. A parent's ofdata is always read before a child. - -The steps are: - - 1. If priv_auto is non-zero, then the device-private space - is allocated for the device and zeroed. It will be accessible as - dev->priv. The driver can put anything it likes in there, but should use - it for run-time information, not platform data (which should be static - and known before the device is probed). - - 2. If plat_auto is non-zero, then the platform data space - is allocated. This is only useful for device tree operation, since - otherwise you would have to specify the platform data in the - U_BOOT_DRVINFO() declaration. The space is allocated for the device and - zeroed. It will be accessible as dev->plat. - - 3. If the device's uclass specifies a non-zero per_device_auto, - then this space is allocated and zeroed also. It is allocated for and - stored in the device, but it is uclass data. owned by the uclass driver. - It is possible for the device to access it. - - 4. If the device's immediate parent specifies a per_child_auto - then this space is allocated. This is intended for use by the parent - device to keep track of things related to the child. For example a USB - flash stick attached to a USB host controller would likely use this - space. The controller can hold information about the USB state of each - of its children. - - 5. If the driver provides an of_to_plat() method, then this is - called to convert the device tree data into platform data. This should - do various calls like dev_read_u32(dev, ...) to access the node and store - the resulting information into dev->plat. After this point, the device - works the same way whether it was bound using a device tree node or - U_BOOT_DRVINFO() structure. In either case, the platform data is now stored - in the plat structure. Typically you will use the - plat_auto feature to specify the size of the platform data - structure, and U-Boot will automatically allocate and zero it for you before - entry to of_to_plat(). But if not, you can allocate it yourself in - of_to_plat(). Note that it is preferable to do all the device tree - decoding in of_to_plat() rather than in probe(). (Apart from the - ugliness of mixing configuration and run-time data, one day it is possible - that U-Boot will cache platform data for devices which are regularly - de/activated). - - 6. The device is marked 'plat valid'. - -Note that ofdata reading is always done (for a child and all its parents) -before probing starts. Thus devices go through two distinct states when -probing: reading platform data and actually touching the hardware to bring -the device up. - -Having probing separate from ofdata-reading helps deal with of-platdata, where -the probe() method is common to both DT/of-platdata operation, but the -of_to_plat() method is implemented differently. - -Another case has come up where this separate is useful. Generation of ACPI -tables uses the of-platdata but does not want to probe the device. Probing -would cause U-Boot to violate one of its design principles, viz that it -should only probe devices that are used. For ACPI we want to generate a -table for each device, even if U-Boot does not use it. In fact it may not -even be possible to probe the device - e.g. an SD card which is not -present will cause an error on probe, yet we still must tell Linux about -the SD card connector in case it is used while Linux is running. - -It is important that the of_to_plat() method does not actually probe -the device itself. However there are cases where other devices must be probed -in the of_to_plat() method. An example is where a device requires a -GPIO for it to operate. To select a GPIO obviously requires that the GPIO -device is probed. This is OK when used by common, core devices such as GPIO, -clock, interrupts, reset and the like. - -If your device relies on its parent setting up a suitable address space, so -that dev_read_addr() works correctly, then make sure that the parent device -has its setup code in of_to_plat(). If it has it in the probe method, -then you cannot call dev_read_addr() from the child device's -of_to_plat() method. Move it to probe() instead. Buses like PCI can -fall afoul of this rule. - -Activation/probe -^^^^^^^^^^^^^^^^ - -When a device needs to be used, U-Boot activates it, by first reading ofdata -as above and then following these steps (see device_probe()): - - 1. All parent devices are probed. It is not possible to activate a device - unless its predecessors (all the way up to the root device) are activated. - This means (for example) that an I2C driver will require that its bus - be activated. - - 2. The device's probe() method is called. This should do anything that - is required by the device to get it going. This could include checking - that the hardware is actually present, setting up clocks for the - hardware and setting up hardware registers to initial values. The code - in probe() can access: - - - platform data in dev->plat (for configuration) - - private data in dev->priv (for run-time state) - - uclass data in dev->uclass_priv (for things the uclass stores - about this device) - - Note: If you don't use priv_auto then you will need to - allocate the priv space here yourself. The same applies also to - plat_auto. Remember to free them in the remove() method. - - 3. The device is marked 'activated' - - 4. The uclass's post_probe() method is called, if one exists. This may - cause the uclass to do some housekeeping to record the device as - activated and 'known' by the uclass. - -Running stage -^^^^^^^^^^^^^ - -The device is now activated and can be used. From now until it is removed -all of the above structures are accessible. The device appears in the -uclass's list of devices (so if the device is in UCLASS_GPIO it will appear -as a device in the GPIO uclass). This is the 'running' state of the device. - -Removal stage -^^^^^^^^^^^^^ - -When the device is no-longer required, you can call device_remove() to -remove it. This performs the probe steps in reverse: - - 1. The uclass's pre_remove() method is called, if one exists. This may - cause the uclass to do some housekeeping to record the device as - deactivated and no-longer 'known' by the uclass. - - 2. All the device's children are removed. It is not permitted to have - an active child device with a non-active parent. This means that - device_remove() is called for all the children recursively at this point. - - 3. The device's remove() method is called. At this stage nothing has been - deallocated so platform data, private data and the uclass data will all - still be present. This is where the hardware can be shut down. It is - intended that the device be completely inactive at this point, For U-Boot - to be sure that no hardware is running, it should be enough to remove - all devices. - - 4. The device memory is freed (platform data, private data, uclass data, - parent data). - - Note: Because the platform data for a U_BOOT_DRVINFO() is defined with a - static pointer, it is not de-allocated during the remove() method. For - a device instantiated using the device tree data, the platform data will - be dynamically allocated, and thus needs to be deallocated during the - remove() method, either: - - - if the plat_auto is non-zero, the deallocation happens automatically - within the driver model core in the unbind stage; or - - - when plat_auto is 0, both the allocation (in probe() - or preferably of_to_plat()) and the deallocation in remove() - are the responsibility of the driver author. - - 5. The device is marked inactive. Note that it is still bound, so the - device structure itself is not freed at this point. Should the device be - activated again, then the cycle starts again at step 2 above. - -Unbind stage -^^^^^^^^^^^^ - -The device is unbound. This is the step that actually destroys the device. -If a parent has children these will be destroyed first. After this point -the device does not exist and its memory has be deallocated. - - -Special cases for removal -------------------------- - -Some devices need to do clean-up before the OS is called. For example, a USB -driver may want to stop the bus. This can be done in the remove() method. -Some special flags are used to determine whether to remove the device: - - DM_FLAG_OS_PREPARE - indicates that the device needs to get ready for OS - boot. The device will be removed just before the OS is booted - DM_REMOVE_ACTIVE_DMA - indicates that the device uses DMA. This is - effectively the same as DM_FLAG_OS_PREPARE, so the device is removed - before the OS is booted - DM_FLAG_VITAL - indicates that the device is 'vital' to the operation of - other devices. It is possible to remove this device after all regular - devices are removed. This is useful e.g. for a clock, which need to - be active during the device-removal phase. - -The dm_remove_devices_flags() function can be used to remove devices based on -their driver flags. - -Data Structures ---------------- - -Driver model uses a doubly-linked list as the basic data structure. Some -nodes have several lists running through them. Creating a more efficient -data structure might be worthwhile in some rare cases, once we understand -what the bottlenecks are. - - -Changes since v1 ----------------- - -For the record, this implementation uses a very similar approach to the -original patches, but makes at least the following changes: - -- Tried to aggressively remove boilerplate, so that for most drivers there - is little or no 'driver model' code to write. -- Moved some data from code into data structure - e.g. store a pointer to - the driver operations structure in the driver, rather than passing it - to the driver bind function. -- Rename some structures to make them more similar to Linux (struct udevice - instead of struct instance, struct plat, etc.) -- Change the name 'core' to 'uclass', meaning U-Boot class. It seems that - this concept relates to a class of drivers (or a subsystem). We shouldn't - use 'class' since it is a C++ reserved word, so U-Boot class (uclass) seems - better than 'core'. -- Remove 'struct driver_instance' and just use a single 'struct udevice'. - This removes a level of indirection that doesn't seem necessary. -- Built in device tree support, to avoid the need for plat -- Removed the concept of driver relocation, and just make it possible for - the new driver (created after relocation) to access the old driver data. - I feel that relocation is a very special case and will only apply to a few - drivers, many of which can/will just re-init anyway. So the overhead of - dealing with this might not be worth it. -- Implemented a GPIO system, trying to keep it simple - - -Pre-Relocation Support ----------------------- - -For pre-relocation we simply call the driver model init function. Only -drivers marked with DM_FLAG_PRE_RELOC or the device tree 'u-boot,dm-pre-reloc' -property are initialised prior to relocation. This helps to reduce the driver -model overhead. This flag applies to SPL and TPL as well, if device tree is -enabled (CONFIG_OF_CONTROL) there. - -Note when device tree is enabled, the device tree 'u-boot,dm-pre-reloc' -property can provide better control granularity on which device is bound -before relocation. While with DM_FLAG_PRE_RELOC flag of the driver all -devices with the same driver are bound, which requires allocation a large -amount of memory. When device tree is not used, DM_FLAG_PRE_RELOC is the -only way for statically declared devices via U_BOOT_DRVINFO() to be bound -prior to relocation. - -It is possible to limit this to specific relocation steps, by using -the more specialized 'u-boot,dm-spl' and 'u-boot,dm-tpl' flags -in the device tree node. For U-Boot proper you can use 'u-boot,dm-pre-proper' -which means that it will be processed (and a driver bound) in U-Boot proper -prior to relocation, but will not be available in SPL or TPL. - -To reduce the size of SPL and TPL, only the nodes with pre-relocation properties -('u-boot,dm-pre-reloc', 'u-boot,dm-spl' or 'u-boot,dm-tpl') are keept in their -device trees (see README.SPL for details); the remaining nodes are always bound. - -Then post relocation we throw that away and re-init driver model again. -For drivers which require some sort of continuity between pre- and -post-relocation devices, we can provide access to the pre-relocation -device pointers, but this is not currently implemented (the root device -pointer is saved but not made available through the driver model API). - - -SPL Support ------------ - -Driver model can operate in SPL. Its efficient implementation and small code -size provide for a small overhead which is acceptable for all but the most -constrained systems. - -To enable driver model in SPL, define CONFIG_SPL_DM. You might want to -consider the following option also. See the main README for more details. - - - CONFIG_SYS_MALLOC_SIMPLE - - CONFIG_DM_WARN - - CONFIG_DM_DEVICE_REMOVE - - CONFIG_DM_STDIO - - -Enabling Driver Model ---------------------- - -Driver model is being brought into U-Boot gradually. As each subsystems gets -support, a uclass is created and a CONFIG to enable use of driver model for -that subsystem. - -For example CONFIG_DM_SERIAL enables driver model for serial. With that -defined, the old serial support is not enabled, and your serial driver must -conform to driver model. With that undefined, the old serial support is -enabled and driver model is not available for serial. This means that when -you convert a driver, you must either convert all its boards, or provide for -the driver to be compiled both with and without driver model (generally this -is not very hard). - -See the main README for full details of the available driver model CONFIG -options. - - -Things to punt for later ------------------------- - -Uclasses are statically numbered at compile time. It would be possible to -change this to dynamic numbering, but then we would require some sort of -lookup service, perhaps searching by name. This is slightly less efficient -so has been left out for now. One small advantage of dynamic numbering might -be fewer merge conflicts in uclass-id.h. diff --git a/doc/driver-model/ethernet.rst b/doc/driver-model/ethernet.rst deleted file mode 100644 index cdbccca34d6..00000000000 --- a/doc/driver-model/ethernet.rst +++ /dev/null @@ -1,321 +0,0 @@ -Ethernet Driver Guide -======================= - -The networking stack in Das U-Boot is designed for multiple network devices -to be easily added and controlled at runtime. This guide is meant for people -who wish to review the net driver stack with an eye towards implementing your -own ethernet device driver. Here we will describe a new pseudo 'APE' driver. - -Most existing drivers do already - and new network driver MUST - use the -U-Boot core driver model. Generic information about this can be found in -doc/driver-model/design.rst, this document will thus focus on the network -specific code parts. -Some drivers are still using the old Ethernet interface, differences between -the two and hints about porting will be handled at the end. - -Driver framework ------------------- - -A network driver following the driver model must declare itself using -the UCLASS_ETH .id field in the U-Boot driver struct: - -.. code-block:: c - - U_BOOT_DRIVER(eth_ape) = { - .name = "eth_ape", - .id = UCLASS_ETH, - .of_match = eth_ape_ids, - .of_to_plat = eth_ape_of_to_plat, - .probe = eth_ape_probe, - .ops = ð_ape_ops, - .priv_auto = sizeof(struct eth_ape_priv), - .plat_auto = sizeof(struct eth_ape_pdata), - .flags = DM_FLAG_ALLOC_PRIV_DMA, - }; - -struct eth_ape_priv contains runtime per-instance data, like buffers, pointers -to current descriptors, current speed settings, pointers to PHY related data -(like struct mii_dev) and so on. Declaring its size in .priv_auto -will let the driver framework allocate it at the right time. -It can be retrieved using a dev_get_priv(dev) call. - -struct eth_ape_pdata contains static platform data, like the MMIO base address, -a hardware variant, the MAC address. ``struct eth_pdata eth_pdata`` -as the first member of this struct helps to avoid duplicated code. -If you don't need any more platform data beside the standard member, -just use sizeof(struct eth_pdata) for the plat_auto. - -PCI devices add a line pointing to supported vendor/device ID pairs: - -.. code-block:: c - - static struct pci_device_id supported[] = { - { PCI_DEVICE(PCI_VENDOR_ID_APE, 0x4223) }, - {} - }; - - U_BOOT_PCI_DEVICE(eth_ape, supported); - -It is also possible to declare support for a whole class of PCI devices:: - - { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI << 8, 0xffff00) }, - -Device probing and instantiation will be handled by the driver model framework, -so follow the guidelines there. The probe() function would initialise the -platform specific parts of the hardware, like clocks, resets, GPIOs, the MDIO -bus. Also it would take care of any special PHY setup (power rails, enable -bits for internal PHYs, etc.). - -Driver methods ----------------- - -The real work will be done in the driver method functions the driver provides -by defining the members of struct eth_ops: - -.. code-block:: c - - struct eth_ops { - int (*start)(struct udevice *dev); - int (*send)(struct udevice *dev, void *packet, int length); - int (*recv)(struct udevice *dev, int flags, uchar **packetp); - int (*free_pkt)(struct udevice *dev, uchar *packet, int length); - void (*stop)(struct udevice *dev); - int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); - int (*write_hwaddr)(struct udevice *dev); - int (*read_rom_hwaddr)(struct udevice *dev); - }; - -An up-to-date version of this struct together with more information can be -found in include/net.h. - -Only start, stop, send and recv are required, the rest are optional and are -handled by generic code or ignored if not provided. - -The **start** function initialises the hardware and gets it ready for send/recv -operations. You often do things here such as resetting the MAC -and/or PHY, and waiting for the link to autonegotiate. You should also take -the opportunity to program the device's MAC address with the enetaddr member -of the generic struct eth_pdata (which would be the first member of your -own plat struct). This allows the rest of U-Boot to dynamically change -the MAC address and have the new settings be respected. - -The **send** function does what you think -- transmit the specified packet -whose size is specified by length (in bytes). The packet buffer can (and -will!) be reused for subsequent calls to send(), so it must be no longer -used when the send() function returns. The easiest way to achieve this is -to wait until the transmission is complete. Alternatively, if supported by -the hardware, just waiting for the buffer to be consumed (by some DMA engine) -might be an option as well. -Another way of consuming the buffer could be to copy the data to be send, -then just queue the copied packet (for instance handing it over to a DMA -engine), and return immediately afterwards. -In any case you should leave the state such that the send function can be -called multiple times in a row. - -The **recv** function polls for availability of a new packet. If none is -available, it must return with -EAGAIN. -If a packet has been received, make sure it is accessible to the CPU -(invalidate caches if needed), then write its address to the packetp pointer, -and return the length. If there is an error (receive error, too short or too -long packet), return 0 if you require the packet to be cleaned up normally, -or a negative error code otherwise (cleanup not necessary or already done). -The U-Boot network stack will then process the packet. - -If **free_pkt** is defined, U-Boot will call it after a received packet has -been processed, so the packet buffer can be freed or recycled. Typically you -would hand it back to the hardware to acquire another packet. free_pkt() will -be called after recv(), for the same packet, so you don't necessarily need -to infer the buffer to free from the ``packet`` pointer, but can rely on that -being the last packet that recv() handled. -The common code sets up packet buffers for you already in the .bss -(net_rx_packets), so there should be no need to allocate your own. This doesn't -mean you must use the net_rx_packets array however; you're free to use any -buffer you wish. - -The **stop** function should turn off / disable the hardware and place it back -in its reset state. It can be called at any time (before any call to the -related start() function), so make sure it can handle this sort of thing. - -The (optional) **write_hwaddr** function should program the MAC address stored -in pdata->enetaddr into the Ethernet controller. - -So the call graph at this stage would look something like: - -.. code-block:: c - - (some net operation (ping / tftp / whatever...)) - eth_init() - ops->start() - eth_send() - ops->send() - eth_rx() - ops->recv() - (process packet) - if (ops->free_pkt) - ops->free_pkt() - eth_halt() - ops->stop() - - -CONFIG_PHYLIB / CONFIG_CMD_MII --------------------------------- - -If your device supports banging arbitrary values on the MII bus (pretty much -every device does), you should add support for the mii command. Doing so is -fairly trivial and makes debugging mii issues a lot easier at runtime. - -In your driver's ``probe()`` function, add a call to mdio_alloc() and -mdio_register() like so: - -.. code-block:: c - - bus = mdio_alloc(); - if (!bus) { - ... - return -ENOMEM; - } - - bus->read = ape_mii_read; - bus->write = ape_mii_write; - mdio_register(bus); - -And then define the mii_read and mii_write functions if you haven't already. -Their syntax is straightforward:: - - int mii_read(struct mii_dev *bus, int addr, int devad, int reg); - int mii_write(struct mii_dev *bus, int addr, int devad, int reg, - u16 val); - -The read function should read the register 'reg' from the phy at address 'addr' -and return the result to its caller. The implementation for the write function -should logically follow. - -................................................................ - -Legacy network drivers ------------------------- - -!!! WARNING !!! - -This section below describes the old way of doing things. No new Ethernet -drivers should be implemented this way. All new drivers should be written -against the U-Boot core driver model, as described above. - -The actual callback functions are fairly similar, the differences are: - -- ``start()`` is called ``init()`` -- ``stop()`` is called ``halt()`` -- The ``recv()`` function must loop until all packets have been received, for - each packet it must call the net_process_received_packet() function, - handing it over the pointer and the length. Afterwards it should free - the packet, before checking for new data. - -For porting an old driver to the new driver model, split the existing recv() -function into the actual new recv() function, just fetching **one** packet, -remove the call to net_process_received_packet(), then move the packet -cleanup into the ``free_pkt()`` function. - -Registering the driver and probing a device is handled very differently, -follow the recommendations in the driver model design documentation for -instructions on how to port this over. For the records, the old way of -initialising a network driver is as follows: - -Old network driver registration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When U-Boot initializes, it will call the common function eth_initialize(). -This will in turn call the board-specific board_eth_init() (or if that fails, -the cpu-specific cpu_eth_init()). These board-specific functions can do random -system handling, but ultimately they will call the driver-specific register -function which in turn takes care of initializing that particular instance. - -Keep in mind that you should code the driver to avoid storing state in global -data as someone might want to hook up two of the same devices to one board. -Any such information that is specific to an interface should be stored in a -private, driver-defined data structure and pointed to by eth->priv (see below). - -So the call graph at this stage would look something like: - -.. code-block:: c - - board_init() - eth_initialize() - board_eth_init() / cpu_eth_init() - driver_register() - initialize eth_device - eth_register() - -At this point in time, the only thing you need to worry about is the driver's -register function. The pseudo code would look something like: - -.. code-block:: c - - int ape_register(struct bd_info *bis, int iobase) - { - struct ape_priv *priv; - struct eth_device *dev; - struct mii_dev *bus; - - priv = malloc(sizeof(*priv)); - if (priv == NULL) - return -ENOMEM; - - dev = malloc(sizeof(*dev)); - if (dev == NULL) { - free(priv); - return -ENOMEM; - } - - /* setup whatever private state you need */ - - memset(dev, 0, sizeof(*dev)); - sprintf(dev->name, "APE"); - - /* - * if your device has dedicated hardware storage for the - * MAC, read it and initialize dev->enetaddr with it - */ - ape_mac_read(dev->enetaddr); - - dev->iobase = iobase; - dev->priv = priv; - dev->init = ape_init; - dev->halt = ape_halt; - dev->send = ape_send; - dev->recv = ape_recv; - dev->write_hwaddr = ape_write_hwaddr; - - eth_register(dev); - - #ifdef CONFIG_PHYLIB - bus = mdio_alloc(); - if (!bus) { - free(priv); - free(dev); - return -ENOMEM; - } - - bus->read = ape_mii_read; - bus->write = ape_mii_write; - mdio_register(bus); - #endif - - return 1; - } - -The exact arguments needed to initialize your device are up to you. If you -need to pass more/less arguments, that's fine. You should also add the -prototype for your new register function to include/netdev.h. - -The return value for this function should be as follows: -< 0 - failure (hardware failure, not probe failure) ->=0 - number of interfaces detected - -You might notice that many drivers seem to use xxx_initialize() rather than -xxx_register(). This is the old naming convention and should be avoided as it -causes confusion with the driver-specific init function. - -Other than locating the MAC address in dedicated hardware storage, you should -not touch the hardware in anyway. That step is handled in the driver-specific -init function. Remember that we are only registering the device here, we are -not checking its state or doing random probing. diff --git a/doc/driver-model/fdt-fixup.rst b/doc/driver-model/fdt-fixup.rst deleted file mode 100644 index 974c09031ed..00000000000 --- a/doc/driver-model/fdt-fixup.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. 2017-01-06, Mario Six - -Pre-relocation device tree manipulation -======================================= - -Purpose -------- - -In certain markets, it is beneficial for manufacturers of embedded devices to -offer certain ranges of products, where the functionality of the devices within -one series either don't differ greatly from another, or can be thought of as -"extensions" of each other, where one device only differs from another in the -addition of a small number of features (e.g. an additional output connector). - -To realize this in hardware, one method is to have a motherboard, and several -possible daughter boards that can be attached to this mother board. Different -daughter boards then either offer the slightly different functionality, or the -addition of the daughter board to the device realizes the "extension" of -functionality to the device described previously. - -For the software, we obviously want to reuse components for all these -variations of the device. This means that the software somehow needs to cope -with the situation that certain ICs may or may not be present on any given -system, depending on which daughter boards are connected to the motherboard. - -In the Linux kernel, one possible solution to this problem is to employ the -device tree overlay mechanism: There exists one "base" device tree, which -features only the components guaranteed to exist in all varieties of the -device. At the start of the kernel, the presence and type of the daughter -boards is then detected, and the corresponding device tree overlays are applied -to support the components on the daughter boards. - -Note that the components present on every variety of the board must, of course, -provide a way to find out if and which daughter boards are installed for this -mechanism to work. - -In the U-Boot boot loader, support for device tree overlays has recently been -integrated, and is used on some boards to alter the device tree that is later -passed to Linux. But since U-Boot's driver model, which is device tree-based as -well, is being used in more and more drivers, the same problem of altering the -device tree starts cropping up in U-Boot itself as well. - -An additional problem with the device tree in U-Boot is that it is read-only, -and the current mechanisms don't allow easy manipulation of the device tree -after the driver model has been initialized. While migrating to a live device -tree (at least after the relocation) would greatly simplify the solution of -this problem, it is a non-negligible task to implement it, an a interim -solution is needed to address the problem at least in the medium-term. - -Hence, we propose a solution to this problem by offering a board-specific -call-back function, which is passed a writeable pointer to the device tree. -This function is called before the device tree is relocated, and specifically -before the main U-Boot's driver model is instantiated, hence the main U-Boot -"sees" all modifications to the device tree made in this function. Furthermore, -we have the pre-relocation driver model at our disposal at this stage, which -means that we can query the hardware for the existence and variety of the -components easily. - -Implementation --------------- - -To take advantage of the pre-relocation device tree manipulation mechanism, -boards have to implement the function board_fix_fdt, which has the following -signature: - -.. code-block:: c - - int board_fix_fdt (void *rw_fdt_blob) - -The passed-in void pointer is a writeable pointer to the device tree, which can -be used to manipulate the device tree using e.g. functions from -include/fdt_support.h. The return value should either be 0 in case of -successful execution of the device tree manipulation or something else for a -failure. Note that returning a non-null value from the function will -unrecoverably halt the boot process, as with any function from init_sequence_f -(in common/board_f.c). - -Furthermore, the Kconfig option OF_BOARD_FIXUP has to be set for the function -to be called:: - - Device Tree Control - -> [*] Board-specific manipulation of Device Tree - -+----------------------------------------------------------+ -| WARNING: The actual manipulation of the device tree has | -| to be the _last_ set of operations in board_fix_fdt! | -| Since the pre-relocation driver model does not adapt to | -| changes made to the device tree either, its references | -| into the device tree will be invalid after manipulating | -| it, and unpredictable behavior might occur when | -| functions that rely on them are executed! | -+----------------------------------------------------------+ - -Hence, the recommended layout of the board_fixup_fdt call-back function is the -following: - -.. code-block:: c - - int board_fix_fdt(void *rw_fdt_blob) - { - /* - * Collect information about device's hardware and store - * them in e.g. local variables - */ - - /* Do device tree manipulation using the values previously collected */ - - /* Return 0 on successful manipulation and non-zero otherwise */ - } - -If this convention is kept, both an "additive" approach, meaning that nodes for -detected components are added to the device tree, as well as a "subtractive" -approach, meaning that nodes for absent components are removed from the tree, -as well as a combination of both approaches should work. - -Example -------- - -The controlcenterdc board (board/gdsys/a38x/controlcenterdc.c) features a -board_fix_fdt function, in which six GPIO expanders (which might be present or -not, since they are on daughter boards) on a I2C bus are queried for, and -subsequently deactivated in the device tree if they are not present. - -Note that the dm_i2c_simple_probe function does not use the device tree, hence -it is safe to call it after the tree has already been manipulated. - -Work to be done ---------------- - -* The application of device tree overlay should be possible in board_fixup_fdt, - but has not been tested at this stage. diff --git a/doc/driver-model/fs_firmware_loader.rst b/doc/driver-model/fs_firmware_loader.rst deleted file mode 100644 index a44708cb4c5..00000000000 --- a/doc/driver-model/fs_firmware_loader.rst +++ /dev/null @@ -1,154 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. Copyright (C) 2018-2019 Intel Corporation - -File System Firmware Loader -=========================== - -This is file system firmware loader for U-Boot framework, which has very close -to some Linux Firmware API. For the details of Linux Firmware API, you can refer -to https://01.org/linuxgraphics/gfx-docs/drm/driver-api/firmware/index.html. - -File system firmware loader can be used to load whatever(firmware, image, -and binary) from the storage device in file system format into target location -such as memory, then consumer driver such as FPGA driver can program FPGA image -from the target location into FPGA. - -To enable firmware loader, CONFIG_FS_LOADER need to be set at -_defconfig such as "CONFIG_FS_LOADER=y". - -Firmware Loader API core features ---------------------------------- - -Firmware storage device described in device tree source -------------------------------------------------------- -For passing data like storage device phandle and partition where the -firmware loading from to the firmware loader driver, those data could be -defined in fs-loader node as shown in below: - -Example for block device:: - - fs_loader0: fs-loader { - u-boot,dm-pre-reloc; - compatible = "u-boot,fs-loader"; - phandlepart = <&mmc 1>; - }; - -<&mmc 1> means block storage device pointer and its partition. - -Above example is a description for block storage, but for UBI storage -device, it can be described in FDT as shown in below: - -Example for ubi:: - - fs_loader1: fs-loader { - u-boot,dm-pre-reloc; - compatible = "u-boot,fs-loader"; - mtdpart = "UBI", - ubivol = "ubi0"; - }; - -Then, firmware-loader property can be added with any device node, which -driver would use the firmware loader for loading. - -The value of the firmware-loader property should be set with phandle -of the fs-loader node. For example:: - - firmware-loader = <&fs_loader0>; - -If there are majority of devices using the same fs-loader node, then -firmware-loader property can be added under /chosen node instead of -adding to each of device node. - -For example:: - - /{ - chosen { - firmware-loader = <&fs_loader0>; - }; - }; - -In each respective driver of devices using firmware loader, the firmware -loaded instance should be created by DT phandle. - -For example of getting DT phandle from /chosen and creating instance: - -.. code-block:: c - - chosen_node = ofnode_path("/chosen"); - if (!ofnode_valid(chosen_node)) { - debug("/chosen node was not found.\n"); - return -ENOENT; - } - - phandle_p = ofnode_get_property(chosen_node, "firmware-loader", &size); - if (!phandle_p) { - debug("firmware-loader property was not found.\n"); - return -ENOENT; - } - - phandle = fdt32_to_cpu(*phandle_p); - ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, - phandle, &dev); - if (ret) - return ret; - -Firmware loader driver is also designed to support U-boot environment -variables, so all these data from FDT can be overwritten -through the U-boot environment variable during run time. - -For examples: - -storage_interface: - Storage interface, it can be "mmc", "usb", "sata" or "ubi". -fw_dev_part: - Block device number and its partition, it can be "0:1". -fw_ubi_mtdpart: - UBI device mtd partition, it can be "UBI". -fw_ubi_volume: - UBI volume, it can be "ubi0". - -When above environment variables are set, environment values would be -used instead of data from FDT. -The benefit of this design allows user to change storage attribute data -at run time through U-boot console and saving the setting as default -environment values in the storage for the next power cycle, so no -compilation is required for both driver and FDT. - -File system firmware Loader API -------------------------------- - -.. code-block:: c - - int request_firmware_into_buf(struct udevice *dev, - const char *name, - void *buf, size_t size, u32 offset) - -Load firmware into a previously allocated buffer - -Parameters: - -* struct udevice \*dev: An instance of a driver -* const char \*name: name of firmware file -* void \*buf: address of buffer to load firmware into -* size_t size: size of buffer -* u32 offset: offset of a file for start reading into buffer - -Returns: - size of total read - -ve when error - -Description: - The firmware is loaded directly into the buffer pointed to by buf - -Example of calling request_firmware_into_buf API after creating firmware loader -instance: - -.. code-block:: c - - ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER, - phandle, &dev); - if (ret) - return ret; - - request_firmware_into_buf(dev, filename, buffer_location, buffer_size, - offset_ofreading); diff --git a/doc/driver-model/i2c-howto.rst b/doc/driver-model/i2c-howto.rst deleted file mode 100644 index 27e7440cd46..00000000000 --- a/doc/driver-model/i2c-howto.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -How to port an I2C driver to driver model -========================================= - -Over half of the I2C drivers have been converted as at November 2016. These -ones remain: - - * adi_i2c - * davinci_i2c - * fti2c010 - * ihs_i2c - * kona_i2c - * lpc32xx_i2c - * pca9564_i2c - * ppc4xx_i2c - * rcar_i2c - * sh_i2c - * soft_i2c - * zynq_i2c - -The deadline for this work is the end of June 2017. If no one steps -forward to convert these, at some point there may come a patch to remove them! - -Here is a suggested approach for converting your I2C driver over to driver -model. Please feel free to update this file with your ideas and suggestions. - -- #ifdef out all your own I2C driver code (#if !CONFIG_IS_ENABLED(DM_I2C)) -- Define CONFIG_DM_I2C for your board, vendor or architecture -- If the board does not already use driver model, you need CONFIG_DM also -- Your board should then build, but will not work fully since there will be - no I2C driver -- Add the U_BOOT_DRIVER piece at the end (e.g. copy tegra_i2c.c for example) -- Add a private struct for the driver data - avoid using static variables -- Implement each of the driver methods, perhaps by calling your old methods -- You may need to adjust the function parameters so that the old and new - implementations can share most of the existing code -- If you convert all existing users of the driver, remove the pre-driver-model - code - -In terms of patches a conversion series typically has these patches: -- clean up / prepare the driver for conversion -- add driver model code -- convert at least one existing board to use driver model serial -- (if no boards remain that don't use driver model) remove the old code - -This may be a good time to move your board to use device tree also. Mostly -this involves these steps: - -- define CONFIG_OF_CONTROL and CONFIG_OF_SEPARATE -- add your device tree files to arch//dts -- update the Makefile there -- Add stdout-path to your /chosen device tree node if it is not already there -- build and get u-boot-dtb.bin so you can test it -- Your drivers can now use device tree -- For device tree in SPL, define CONFIG_SPL_OF_CONTROL diff --git a/doc/driver-model/index.rst b/doc/driver-model/index.rst deleted file mode 100644 index c9faf0a5910..00000000000 --- a/doc/driver-model/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -Driver Model -============ - -.. toctree:: - :maxdepth: 2 - - bind - debugging - design - ethernet - fdt-fixup - fs_firmware_loader - i2c-howto - livetree - migration - of-plat - pci-info - pmic-framework - remoteproc-framework - serial-howto - soc-framework - spi-howto - usb-info diff --git a/doc/driver-model/livetree.rst b/doc/driver-model/livetree.rst deleted file mode 100644 index 9f654f3b894..00000000000 --- a/doc/driver-model/livetree.rst +++ /dev/null @@ -1,286 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. sectionauthor:: Simon Glass - -Live Device Tree -================ - - -Introduction ------------- - -Traditionally U-Boot has used a 'flat' device tree. This means that it -reads directly from the device tree binary structure. It is called a flat -device tree because nodes are listed one after the other, with the -hierarchy detected by tags in the format. - -This document describes U-Boot's support for a 'live' device tree, meaning -that the tree is loaded into a hierarchical data structure within U-Boot. - - -Motivation ----------- - -The flat device tree has several advantages: - -- it is the format produced by the device tree compiler, so no translation - is needed - -- it is fairly compact (e.g. there is no need for pointers) - -- it is accessed by the libfdt library, which is well tested and stable - - -However the flat device tree does have some limitations. Adding new -properties can involve copying large amounts of data around to make room. -The overall tree has a fixed maximum size so sometimes the tree must be -rebuilt in a new location to create more space. Even if not adding new -properties or nodes, scanning the tree can be slow. For example, finding -the parent of a node is a slow process. Reading from nodes involves a -small amount parsing which takes a little time. - -Driver model scans the entire device tree sequentially on start-up which -avoids the worst of the flat tree's limitations. But if the tree is to be -modified at run-time, a live tree is much faster. Even if no modification -is necessary, parsing the tree once and using a live tree from then on -seems to save a little time. - - -Implementation --------------- - -In U-Boot a live device tree ('livetree') is currently supported only -after relocation. Therefore we need a mechanism to specify a device -tree node regardless of whether it is in the flat tree or livetree. - -The 'ofnode' type provides this. An ofnode can point to either a flat tree -node (when the live tree node is not yet set up) or a livetree node. The -caller of an ofnode function does not need to worry about these details. - -The main users of the information in a device tree are drivers. These have -a 'struct udevice \*' which is attached to a device tree node. Therefore it -makes sense to be able to read device tree properties using the -'struct udevice \*', rather than having to obtain the ofnode first. - -The 'dev_read\_...()' interface provides this. It allows properties to be -easily read from the device tree using only a device pointer. Under the -hood it uses ofnode so it works with both flat and live device trees. - - -Enabling livetree ------------------ - -CONFIG_OF_LIVE enables livetree. When this option is enabled, the flat -tree will be used in SPL and before relocation in U-Boot proper. Just -before relocation a livetree is built, and this is used for U-Boot proper -after relocation. - -Most checks for livetree use CONFIG_IS_ENABLED(OF_LIVE). This means that -for SPL, the CONFIG_SPL_OF_LIVE option is checked. At present this does -not exist, since SPL does not support livetree. - - -Porting drivers ---------------- - -Many existing drivers use the fdtdec interface to read device tree -properties. This only works with a flat device tree. The drivers should be -converted to use the dev_read_() interface. - -For example, the old code may be like this: - -.. code-block:: c - - struct udevice *bus; - const void *blob = gd->fdt_blob; - int node = dev_of_offset(bus); - - i2c_bus->regs = (struct i2c_ctlr *)devfdt_get_addr(dev); - plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", 500000); - -The new code is: - -.. code-block:: c - - struct udevice *bus; - - i2c_bus->regs = (struct i2c_ctlr *)dev_read_addr(dev); - plat->frequency = dev_read_u32_default(bus, "spi-max-frequency", 500000); - -The dev_read\_...() interface is more convenient and works with both the -flat and live device trees. See include/dm/read.h for a list of functions. - -Where properties must be read from sub-nodes or other nodes, you must fall -back to using ofnode. For example, for old code like this: - -.. code-block:: c - - const void *blob = gd->fdt_blob; - int subnode; - - fdt_for_each_subnode(subnode, blob, dev_of_offset(dev)) { - freq = fdtdec_get_int(blob, node, "spi-max-frequency", 500000); - ... - } - -you should use: - -.. code-block:: c - - ofnode subnode; - - ofnode_for_each_subnode(subnode, dev_ofnode(dev)) { - freq = ofnode_read_u32(node, "spi-max-frequency", 500000); - ... - } - - -Useful ofnode functions ------------------------ - -The internal data structures of the livetree are defined in include/dm/of.h : - - :struct device_node: holds information about a device tree node - :struct property: holds information about a property within a node - -Nodes have pointers to their first property, their parent, their first child -and their sibling. This allows nodes to be linked together in a hierarchical -tree. - -Properties have pointers to the next property. This allows all properties of -a node to be linked together in a chain. - -It should not be necessary to use these data structures in normal code. In -particular, you should refrain from using functions which access the livetree -directly, such as of_read_u32(). Use ofnode functions instead, to allow your -code to work with a flat tree also. - -Some conversion functions are used internally. Generally these are not needed -for driver code. Note that they will not work if called in the wrong context. -For example it is invalid to call ofnode_to_no() when a flat tree is being -used. Similarly it is not possible to call ofnode_to_offset() on a livetree -node. - -ofnode_to_np(): - converts ofnode to struct device_node * -ofnode_to_offset(): - converts ofnode to offset - -no_to_ofnode(): - converts node pointer to ofnode -offset_to_ofnode(): - converts offset to ofnode - - -Other useful functions: - -of_live_active(): - returns true if livetree is in use, false if flat tree -ofnode_valid(): - return true if a given node is valid -ofnode_is_np(): - returns true if a given node is a livetree node -ofnode_equal(): - compares two ofnodes -ofnode_null(): - returns a null ofnode (for which ofnode_valid() returns false) - - -Phandles --------- - -There is full phandle support for live tree. All functions make use of -struct ofnode_phandle_args, which has an ofnode within it. This supports both -livetree and flat tree transparently. See for example -ofnode_parse_phandle_with_args(). - - -Reading addresses ------------------ - -You should use dev_read_addr() and friends to read addresses from device-tree -nodes. - - -fdtdec ------- - -The existing fdtdec interface will eventually be retired. Please try to avoid -using it in new code. - - -Modifying the livetree ----------------------- - -This is not currently supported. Once implemented it should provide a much -more efficient implementation for modification of the device tree than using -the flat tree. - - -Internal implementation ------------------------ - -The dev_read\_...() functions have two implementations. When -CONFIG_DM_DEV_READ_INLINE is enabled, these functions simply call the ofnode -functions directly. This is useful when livetree is not enabled. The ofnode -functions call ofnode_is_np(node) which will always return false if livetree -is disabled, just falling back to flat tree code. - -This optimisation means that without livetree enabled, the dev_read\_...() and -ofnode interfaces do not noticeably add to code size. - -The CONFIG_DM_DEV_READ_INLINE option defaults to enabled when livetree is -disabled. - -Most livetree code comes directly from Linux and is modified as little as -possible. This is deliberate since this code is fairly stable and does what -we want. Some features (such as get/put) are not supported. Internal macros -take care of removing these features silently. - -Within the of_access.c file there are pointers to the alias node, the chosen -node and the stdout-path alias. - - -Errors ------- - -With a flat device tree, libfdt errors are returned (e.g. -FDT_ERR_NOTFOUND). -For livetree normal 'errno' errors are returned (e.g. -ENOTFOUND). At present -the ofnode and dev_read\_...() functions return either one or other type of -error. This is clearly not desirable. Once tests are added for all the -functions this can be tidied up. - - -Adding new access functions ---------------------------- - -Adding a new function for device-tree access involves the following steps: - - - Add two dev_read() functions: - - inline version in the read.h header file, which calls an ofnode function - - standard version in the read.c file (or perhaps another file), which - also calls an ofnode function - - The implementations of these functions can be the same. The purpose - of the inline version is purely to reduce code size impact. - - - Add an ofnode function. This should call ofnode_is_np() to work out - whether a livetree or flat tree is used. For the livetree it should - call an of\_...() function. For the flat tree it should call an - fdt\_...() function. The livetree version will be optimised out at - compile time if livetree is not enabled. - - - Add an of\_...() function for the livetree implementation. If a similar - function is available in Linux, the implementation should be taken - from there and modified as little as possible (generally not at all). - - -Future work ------------ - -Live tree support was introduced in U-Boot 2017.07. There is still quite a bit -of work to do to flesh this out: - -- tests for all access functions -- support for livetree modification -- addition of more access functions as needed -- support for livetree in SPL and before relocation (if desired) diff --git a/doc/driver-model/migration.rst b/doc/driver-model/migration.rst deleted file mode 100644 index 2284e8a6f70..00000000000 --- a/doc/driver-model/migration.rst +++ /dev/null @@ -1,101 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -Migration Schedule -================== - -U-Boot has been migrating to a new driver model since its introduction in -2014. This file describes the schedule for deprecation of pre-driver-model -features. - -CONFIG_DM ---------- - -* Status: In progress -* Deadline: 2020.01 - -Starting with the 2010.01 release CONFIG_DM will be enabled for all boards. -This does not concern CONFIG_DM_SPL and CONFIG_DM_TPL. The conversion date for -these configuration items still needs to be defined. - -CONFIG_DM_MMC -------------- - -* Status: In progress -* Deadline: 2019.04 - -The subsystem itself has been converted and maintainers should submit patches -switching over to using CONFIG_DM_MMC and other base driver model options in -time for inclusion in the 2019.04 rerelease. - -CONFIG_DM_USB -------------- - -* Status: In progress -* Deadline: 2019.07 - -The subsystem itself has been converted along with many of the host controller -and maintainers should submit patches switching over to using CONFIG_DM_USB and -other base driver model options in time for inclusion in the 2019.07 rerelease. - -CONFIG_SATA ------------ - -* Status: In progress -* Deadline: 2019.07 - -The subsystem itself has been converted along with many of the host controller -and maintainers should submit patches switching over to using CONFIG_AHCI and -other base driver model options in time for inclusion in the 2019.07 rerelease. - -CONFIG_BLK ----------- - -* Status: In progress -* Deadline: 2019.07 - -In concert with maintainers migrating their block device usage to the -appropriate DM driver, CONFIG_BLK needs to be set as well. The final deadline -here coincides with the final deadline for migration of the various block -subsystems. At this point we will be able to audit and correct the logic in -Kconfig around using CONFIG_PARTITIONS and CONFIG_HAVE_BLOCK_DEVICE and make -use of CONFIG_BLK / CONFIG_SPL_BLK as needed. - -CONFIG_DM_SPI / CONFIG_DM_SPI_FLASH ------------------------------------ - -Board Maintainers should submit the patches for enabling DM_SPI and DM_SPI_FLASH -to move the migration with in the deadline. - -Partially converted:: - - drivers/spi/fsl_espi.c - drivers/spi/mxc_spi.c - drivers/spi/sh_qspi.c - -* Status: In progress -* Deadline: 2019.07 - -CONFIG_DM_PCI -------------- -Deadline: 2019.07 - -The PCI subsystem has supported driver model since mid 2015. Maintainers should -submit patches switching over to using CONFIG_DM_PCI and other base driver -model options in time for inclusion in the 2019.07 release. - - -CONFIG_DM_VIDEO ---------------- -Deadline: 2019.07 - -The video subsystem has supported driver model since early 2016. Maintainers -should submit patches switching over to using CONFIG_DM_VIDEO and other base -driver model options in time for inclusion in the 2019.07 release. - -CONFIG_DM_ETH -------------- -Deadline: 2020.07 - -The network subsystem has supported the driver model since early 2015. -Maintainers should submit patches switching over to using CONFIG_DM_ETH and -other base driver model options in time for inclusion in the 2020.07 release. diff --git a/doc/driver-model/of-plat.rst b/doc/driver-model/of-plat.rst deleted file mode 100644 index 74f1932473b..00000000000 --- a/doc/driver-model/of-plat.rst +++ /dev/null @@ -1,913 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -Compiled-in Device Tree / Platform Data -======================================= - - -Introduction ------------- - -Device tree is the standard configuration method in U-Boot. It is used to -define what devices are in the system and provide configuration information -to these devices. - -The overhead of adding devicetree access to U-Boot is fairly modest, -approximately 3KB on Thumb 2 (plus the size of the DT itself). This means -that in most cases it is best to use devicetree for configuration. - -However there are some very constrained environments where U-Boot needs to -work. These include SPL with severe memory limitations. For example, some -SoCs require a 16KB SPL image which must include a full MMC stack. In this -case the overhead of devicetree access may be too great. - -It is possible to create platform data manually by defining C structures -for it, and reference that data in a `U_BOOT_DRVINFO()` declaration. This -bypasses the use of devicetree completely, effectively creating a parallel -configuration mechanism. But it is an available option for SPL. - -As an alternative, the 'of-platdata' feature is provided. This converts the -devicetree contents into C code which can be compiled into the SPL binary. -This saves the 3KB of code overhead and perhaps a few hundred more bytes due -to more efficient storage of the data. - - -How it works ------------- - -The feature is enabled by CONFIG OF_PLATDATA. This is only available in -SPL/TPL and should be tested with: - -.. code-block:: c - - #if CONFIG_IS_ENABLED(OF_PLATDATA) - -A tool called 'dtoc' converts a devicetree file either into a set of -struct declarations, one for each compatible node, and a set of -`U_BOOT_DRVINFO()` declarations along with the actual platform data for each -device. As an example, consider this MMC node: - -.. code-block:: none - - sdmmc: dwmmc@ff0c0000 { - compatible = "rockchip,rk3288-dw-mshc"; - clock-freq-min-max = <400000 150000000>; - clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>, - <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>; - clock-names = "biu", "ciu", "ciu_drv", "ciu_sample"; - fifo-depth = <0x100>; - interrupts = ; - reg = <0xff0c0000 0x4000>; - bus-width = <4>; - cap-mmc-highspeed; - cap-sd-highspeed; - card-detect-delay = <200>; - disable-wp; - num-slots = <1>; - pinctrl-names = "default"; - pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>; - vmmc-supply = <&vcc_sd>; - status = "okay"; - u-boot,dm-pre-reloc; - }; - - -Some of these properties are dropped by U-Boot under control of the -CONFIG_OF_SPL_REMOVE_PROPS option. The rest are processed. This will produce -the following C struct declaration: - -.. code-block:: c - - struct dtd_rockchip_rk3288_dw_mshc { - fdt32_t bus_width; - bool cap_mmc_highspeed; - bool cap_sd_highspeed; - fdt32_t card_detect_delay; - fdt32_t clock_freq_min_max[2]; - struct phandle_1_arg clocks[4]; - bool disable_wp; - fdt32_t fifo_depth; - fdt32_t interrupts[3]; - fdt32_t num_slots; - fdt32_t reg[2]; - fdt32_t vmmc_supply; - }; - -and the following device declarations: - -.. code-block:: c - - /* Node /clock-controller@ff760000 index 0 */ - ... - - /* Node /dwmmc@ff0c0000 index 2 */ - static struct dtd_rockchip_rk3288_dw_mshc dtv_dwmmc_at_ff0c0000 = { - .fifo_depth = 0x100, - .cap_sd_highspeed = true, - .interrupts = {0x0, 0x20, 0x4}, - .clock_freq_min_max = {0x61a80, 0x8f0d180}, - .vmmc_supply = 0xb, - .num_slots = 0x1, - .clocks = {{0, 456}, - {0, 68}, - {0, 114}, - {0, 118}}, - .cap_mmc_highspeed = true, - .disable_wp = true, - .bus_width = 0x4, - .u_boot_dm_pre_reloc = true, - .reg = {0xff0c0000, 0x4000}, - .card_detect_delay = 0xc8, - }; - - U_BOOT_DRVINFO(dwmmc_at_ff0c0000) = { - .name = "rockchip_rk3288_dw_mshc", - .plat = &dtv_dwmmc_at_ff0c0000, - .plat_size = sizeof(dtv_dwmmc_at_ff0c0000), - .parent_idx = -1, - }; - -The device is then instantiated at run-time and the platform data can be -accessed using: - -.. code-block:: c - - struct udevice *dev; - struct dtd_rockchip_rk3288_dw_mshc *plat = dev_get_plat(dev); - -This avoids the code overhead of converting the devicetree data to -platform data in the driver. The `of_to_plat()` method should -therefore do nothing in such a driver. - -Note that for the platform data to be matched with a driver, the 'name' -property of the `U_BOOT_DRVINFO()` declaration has to match a driver declared -via `U_BOOT_DRIVER()`. This effectively means that a `U_BOOT_DRIVER()` with a -'name' corresponding to the devicetree 'compatible' string (after converting -it to a valid name for C) is needed, so a dedicated driver is required for -each 'compatible' string. - -In order to make this a bit more flexible, the `DM_DRIVER_ALIAS()` macro can be -used to declare an alias for a driver name, typically a 'compatible' string. -This macro produces no code, but is used by dtoc tool. It must be located in the -same file as its associated driver, ideally just after it. - -The parent_idx is the index of the parent `driver_info` structure within its -linker list (instantiated by the `U_BOOT_DRVINFO()` macro). This is used to -support `dev_get_parent()`. - -During the build process dtoc parses both `U_BOOT_DRIVER()` and -`DM_DRIVER_ALIAS()` to build a list of valid driver names and driver aliases. -If the 'compatible' string used for a device does not not match a valid driver -name, it will be checked against the list of driver aliases in order to get the -right driver name to use. If in this step there is no match found a warning is -issued to avoid run-time failures. - -Where a node has multiple compatible strings, dtoc generates a `#define` to -make them equivalent, e.g.: - -.. code-block:: c - - #define dtd_rockchip_rk3299_dw_mshc dtd_rockchip_rk3288_dw_mshc - - -Converting of-platdata to a useful form ---------------------------------------- - -Of course it would be possible to use the of-platdata directly in your driver -whenever configuration information is required. However this means that the -driver will not be able to support devicetree, since the of-platdata -structure is not available when devicetree is used. It would make no sense -to use this structure if devicetree were available, since the structure has -all the limitations metioned in caveats below. - -Therefore it is recommended that the of-platdata structure should be used -only in the `probe()` method of your driver. It cannot be used in the -`of_to_plat()` method since this is not called when platform data is -already present. - - -How to structure your driver ----------------------------- - -Drivers should always support devicetree as an option. The of-platdata -feature is intended as a add-on to existing drivers. - -Your driver should convert the plat struct in its `probe()` method. The -existing devicetree decoding logic should be kept in the -`of_to_plat()` method and wrapped with `#if`. - -For example: - -.. code-block:: c - - #include - - struct mmc_plat { - #if CONFIG_IS_ENABLED(OF_PLATDATA) - /* Put this first since driver model will copy the data here */ - struct dtd_mmc dtplat; - #endif - /* - * Other fields can go here, to be filled in by decoding from - * the devicetree (or the C structures when of-platdata is used). - */ - int fifo_depth; - }; - - static int mmc_of_to_plat(struct udevice *dev) - { - #if !CONFIG_IS_ENABLED(OF_PLATDATA) - /* Decode the devicetree data */ - struct mmc_plat *plat = dev_get_plat(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); - - plat->fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0); - #endif - - return 0; - } - - static int mmc_probe(struct udevice *dev) - { - struct mmc_plat *plat = dev_get_plat(dev); - - #if CONFIG_IS_ENABLED(OF_PLATDATA) - /* Decode the of-platdata from the C structures */ - struct dtd_mmc *dtplat = &plat->dtplat; - - plat->fifo_depth = dtplat->fifo_depth; - #endif - /* Set up the device from the plat data */ - writel(plat->fifo_depth, ...) - } - - static const struct udevice_id mmc_ids[] = { - { .compatible = "vendor,mmc" }, - { } - }; - - U_BOOT_DRIVER(mmc_drv) = { - .name = "mmc_drv", - .id = UCLASS_MMC, - .of_match = mmc_ids, - .of_to_plat = mmc_of_to_plat, - .probe = mmc_probe, - .priv_auto = sizeof(struct mmc_priv), - .plat_auto = sizeof(struct mmc_plat), - }; - - DM_DRIVER_ALIAS(mmc_drv, vendor_mmc) /* matches compatible string */ - -Note that `struct mmc_plat` is defined in the C file, not in a header. This -is to avoid needing to include dt-structs.h in a header file. The idea is to -keep the use of each of-platdata struct to the smallest possible code area. -There is just one driver C file for each struct, that can convert from the -of-platdata struct to the standard one used by the driver. - -In the case where SPL_OF_PLATDATA is enabled, `plat_auto` is -still used to allocate space for the platform data. This is different from -the normal behaviour and is triggered by the use of of-platdata (strictly -speaking it is a non-zero `plat_size` which triggers this). - -The of-platdata struct contents is copied from the C structure data to the -start of the newly allocated area. In the case where devicetree is used, -the platform data is allocated, and starts zeroed. In this case the -`of_to_plat()` method should still set up the platform data (and the -of-platdata struct will not be present). - -SPL must use either of-platdata or devicetree. Drivers cannot use both at -the same time, but they must support devicetree. Supporting of-platdata is -optional. - -The devicetree becomes inaccessible when CONFIG_SPL_OF_PLATDATA is enabled, -since the devicetree access code is not compiled in. A corollary is that -a board can only move to using of-platdata if all the drivers it uses support -it. There would be little point in having some drivers require the device -tree data, since then libfdt would still be needed for those drivers and -there would be no code-size benefit. - - -Build-time instantiation ------------------------- - -Even with of-platdata there is a fair amount of code required in driver model. -It is possible to have U-Boot handle the instantiation of devices at build-time, -so avoiding the need for the `device_bind()` code and some parts of -`device_probe()`. - -The feature is enabled by CONFIG_OF_PLATDATA_INST. - -Here is an example device, as generated by dtoc:: - - /* - * Node /serial index 6 - * driver sandbox_serial parent root_driver - */ - - #include - struct sandbox_serial_plat __attribute__ ((section (".priv_data"))) - _sandbox_serial_plat_serial = { - .dtplat = { - .sandbox_text_colour = "cyan", - }, - }; - #include - u8 _sandbox_serial_priv_serial[sizeof(struct sandbox_serial_priv)] - __attribute__ ((section (".priv_data"))); - #include - u8 _sandbox_serial_uc_priv_serial[sizeof(struct serial_dev_priv)] - __attribute__ ((section (".priv_data"))); - - DM_DEVICE_INST(serial) = { - .driver = DM_DRIVER_REF(sandbox_serial), - .name = "sandbox_serial", - .plat_ = &_sandbox_serial_plat_serial, - .priv_ = _sandbox_serial_priv_serial, - .uclass = DM_UCLASS_REF(serial), - .uclass_priv_ = _sandbox_serial_uc_priv_serial, - .uclass_node = { - .prev = &DM_UCLASS_REF(serial)->dev_head, - .next = &DM_UCLASS_REF(serial)->dev_head, - }, - .child_head = { - .prev = &DM_DEVICE_REF(serial)->child_head, - .next = &DM_DEVICE_REF(serial)->child_head, - }, - .sibling_node = { - .prev = &DM_DEVICE_REF(i2c_at_0)->sibling_node, - .next = &DM_DEVICE_REF(spl_test)->sibling_node, - }, - .seq_ = 0, - }; - -Here is part of the driver, for reference:: - - static const struct udevice_id sandbox_serial_ids[] = { - { .compatible = "sandbox,serial" }, - { } - }; - - U_BOOT_DRIVER(sandbox_serial) = { - .name = "sandbox_serial", - .id = UCLASS_SERIAL, - .of_match = sandbox_serial_ids, - .of_to_plat = sandbox_serial_of_to_plat, - .plat_auto = sizeof(struct sandbox_serial_plat), - .priv_auto = sizeof(struct sandbox_serial_priv), - .probe = sandbox_serial_probe, - .remove = sandbox_serial_remove, - .ops = &sandbox_serial_ops, - .flags = DM_FLAG_PRE_RELOC, - }; - - -The `DM_DEVICE_INST()` macro declares a struct udevice so you can see that the -members are from that struct. The private data is declared immediately above, -as `_sandbox_serial_priv_serial`, so there is no need for run-time memory -allocation. The #include lines are generated as well, since dtoc searches the -U-Boot source code for the definition of `struct sandbox_serial_priv` and adds -the relevant header so that the code will compile without errors. - -The `plat_` member is set to the dtv data which is declared immediately above -the device. This is similar to how it would look without of-platdata-inst, but -node that the `dtplat` member inside is part of the wider -`_sandbox_serial_plat_serial` struct. This is because the driver declares its -own platform data, and the part generated by dtoc can only be a portion of it. -The `dtplat` part is always first in the struct. If the device has no -`.plat_auto` field, then a simple dtv struct can be used as with this example:: - - static struct dtd_sandbox_clk dtv_clk_sbox = { - .assigned_clock_rates = 0x141, - .assigned_clocks = {0x7, 0x3}, - }; - - #include - u8 _sandbox_clk_priv_clk_sbox[sizeof(struct sandbox_clk_priv)] - __attribute__ ((section (".priv_data"))); - - DM_DEVICE_INST(clk_sbox) = { - .driver = DM_DRIVER_REF(sandbox_clk), - .name = "sandbox_clk", - .plat_ = &dtv_clk_sbox, - -Here is part of the driver, for reference:: - - static const struct udevice_id sandbox_clk_ids[] = { - { .compatible = "sandbox,clk" }, - { } - }; - - U_BOOT_DRIVER(sandbox_clk) = { - .name = "sandbox_clk", - .id = UCLASS_CLK, - .of_match = sandbox_clk_ids, - .ops = &sandbox_clk_ops, - .probe = sandbox_clk_probe, - .priv_auto = sizeof(struct sandbox_clk_priv), - }; - - -You can see that `dtv_clk_sbox` just has the devicetree contents and there is -no need for the `dtplat` separation, since the driver has no platform data of -its own, besides that provided by the devicetree (i.e. no `.plat_auto` field). - -The doubly linked lists are handled by explicitly declaring the value of each -node, as you can see with the `.prev` and `.next` values in the example above. -Since dtoc knows the order of devices it can link them into the appropriate -lists correctly. - -One of the features of driver model is the ability for a uclass to have a -small amount of private data for each device in that uclass. This is used to -provide a generic data structure that the uclass can use for all devices, thus -allowing generic features to be implemented in common code. An example is I2C, -which stores the bus speed there. - -Similarly, parent devices can have data associated with each of their children. -This is used to provide information common to all children of a particular bus. -For an I2C bus, this is used to store the I2C address of each child on the bus. - -This is all handled automatically by dtoc:: - - #include - u8 _sandbox_i2c_priv_i2c_at_0[sizeof(struct sandbox_i2c_priv)] - __attribute__ ((section (".priv_data"))); - #include - u8 _sandbox_i2c_uc_priv_i2c_at_0[sizeof(struct dm_i2c_bus)] - __attribute__ ((section (".priv_data"))); - - DM_DEVICE_INST(i2c_at_0) = { - .driver = DM_DRIVER_REF(sandbox_i2c), - .name = "sandbox_i2c", - .plat_ = &dtv_i2c_at_0, - .priv_ = _sandbox_i2c_priv_i2c_at_0, - .uclass = DM_UCLASS_REF(i2c), - .uclass_priv_ = _sandbox_i2c_uc_priv_i2c_at_0, - ... - -Part of driver, for reference:: - - static const struct udevice_id sandbox_i2c_ids[] = { - { .compatible = "sandbox,i2c" }, - { } - }; - - U_BOOT_DRIVER(sandbox_i2c) = { - .name = "sandbox_i2c", - .id = UCLASS_I2C, - .of_match = sandbox_i2c_ids, - .ops = &sandbox_i2c_ops, - .priv_auto = sizeof(struct sandbox_i2c_priv), - }; - -Part of I2C uclass, for reference:: - - UCLASS_DRIVER(i2c) = { - .id = UCLASS_I2C, - .name = "i2c", - .flags = DM_UC_FLAG_SEQ_ALIAS, - .post_bind = i2c_post_bind, - .pre_probe = i2c_pre_probe, - .post_probe = i2c_post_probe, - .per_device_auto = sizeof(struct dm_i2c_bus), - .per_child_plat_auto = sizeof(struct dm_i2c_chip), - .child_post_bind = i2c_child_post_bind, - }; - -Here, `_sandbox_i2c_uc_priv_i2c_at_0` is required by the uclass but is declared -in the device, as required by driver model. The required header file is included -so that the code will compile without errors. A similar mechanism is used for -child devices, but is not shown by this example. - -It would not be that useful to avoid binding devices but still need to allocate -uclasses at runtime. So dtoc generates uclass instances as well:: - - struct list_head uclass_head = { - .prev = &DM_UCLASS_REF(serial)->sibling_node, - .next = &DM_UCLASS_REF(clk)->sibling_node, - }; - - DM_UCLASS_INST(clk) = { - .uc_drv = DM_UCLASS_DRIVER_REF(clk), - .sibling_node = { - .prev = &uclass_head, - .next = &DM_UCLASS_REF(i2c)->sibling_node, - }, - .dev_head = { - .prev = &DM_DEVICE_REF(clk_sbox)->uclass_node, - .next = &DM_DEVICE_REF(clk_fixed)->uclass_node, - }, - }; - -At the top is the list head. Driver model uses this on start-up, instead of -creating its own. - -Below that are a set of `DM_UCLASS_INST()` macros, each declaring a -`struct uclass`. The doubly linked lists work as for devices. - -All private data is placed into a `.priv_data` section so that it is contiguous -in the resulting output binary. - - -Indexes -------- - -U-Boot stores drivers, devices and many other things in linker_list structures. -These are sorted by name, so dtoc knows the order that they will appear when -the linker runs. Each driver_info / udevice is referenced by its index in the -linker_list array, called 'idx' in the code. - -When CONFIG_OF_PLATDATA_INST is enabled, idx is the udevice index, otherwise it -is the driver_info index. In either case, indexes are used to reference devices -using device_get_by_ofplat_idx(). This allows phandles to work as expected. - - -Phases ------- - -U-Boot operates in several phases, typically TPL, SPL and U-Boot proper. -The latter does not use dtoc. - -In some rare cases different drivers are used for two phases. For example, -in TPL it may not be necessary to use the full PCI subsystem, so a simple -driver can be used instead. - -This works in the build system simply by compiling in one driver or the -other (e.g. PCI driver + uclass for SPL; simple_bus for TPL). But dtoc has -no way of knowing which code is compiled in for which phase, since it does -not inspect Makefiles or dependency graphs. - -So to make this work for dtoc, we need to be able to explicitly mark -drivers with their phase. This is done by adding a macro to the driver:: - - /* code in tpl.c only compiled into TPL */ - U_BOOT_DRIVER(pci_x86) = { - .name = "pci_x86", - .id = UCLASS_SIMPLE_BUS, - .of_match = of_match_ptr(tpl_fake_pci_ids), - DM_PHASE(tpl) - }; - - - /* code in pci_x86.c compiled into SPL and U-Boot proper */ - U_BOOT_DRIVER(pci_x86) = { - .name = "pci_x86", - .id = UCLASS_PCI, - .of_match = pci_x86_ids, - .ops = &pci_x86_ops, - }; - - -Notice that the second driver has the same name but no DM_PHASE(), so it will be -used for SPL and U-Boot. - -Note also that this only affects the code generated by dtoc. You still need to -make sure that only the required driver is build into each phase. - - -Header files ------------- - -With OF_PLATDATA_INST, dtoc must include the correct header file in the -generated code for any structs that are used, so that the code will compile. -For example, if `struct ns16550_plat` is used, the code must include the -`ns16550.h` header file. - -Typically dtoc can detect the header file needed for a driver by looking -for the structs that it uses. For example, if a driver as a `.priv_auto` -that uses `struct ns16550_plat`, then dtoc can search header files for the -definition of that struct and use the file. - -In some cases, enums are used in drivers, typically with the `.data` field -of `struct udevice_id`. Since dtoc does not support searching for these, -you must use the `DM_HDR()` macro to tell dtoc which header to use. This works -as a macro included in the driver definition:: - - static const struct udevice_id apl_syscon_ids[] = { - { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT }, - { } - }; - - U_BOOT_DRIVER(intel_apl_punit) = { - .name = "intel_apl_punit", - .id = UCLASS_SYSCON, - .of_match = apl_syscon_ids, - .probe = apl_punit_probe, - DM_HEADER() /* for X86_SYSCON_PUNIT */ - }; - - - -Caveats -------- - -There are various complications with this feature which mean it should only -be used when strictly necessary, i.e. in SPL with limited memory. Notable -caveats include: - - - Device tree does not describe data types. But the C code must define a - type for each property. These are guessed using heuristics which - are wrong in several fairly common cases. For example an 8-byte value - is considered to be a 2-item integer array, and is byte-swapped. A - boolean value that is not present means 'false', but cannot be - included in the structures since there is generally no mention of it - in the devicetree file. - - - Naming of nodes and properties is automatic. This means that they follow - the naming in the devicetree, which may result in C identifiers that - look a bit strange. - - - It is not possible to find a value given a property name. Code must use - the associated C member variable directly in the code. This makes - the code less robust in the face of devicetree changes. To avoid having - a second struct with similar members and names you need to explicitly - declare it as an alias with `DM_DRIVER_ALIAS()`. - - - The platform data is provided to drivers as a C structure. The driver - must use the same structure to access the data. Since a driver - normally also supports devicetree it must use `#ifdef` to separate - out this code, since the structures are only available in SPL. This could - be fixed fairly easily by making the structs available outside SPL, so - that `IS_ENABLED()` could be used. - - - With CONFIG_OF_PLATDATA_INST all binding happens at build-time, meaning - that (by default) it is not possible to call `device_bind()` from C code. - This means that all devices must have an associated devicetree node and - compatible string. For example if a GPIO device currently creates child - devices in its `bind()` method, it will not work with - CONFIG_OF_PLATDATA_INST. Arguably this is bad practice anyway and the - devicetree binding should be updated to declare compatible strings for - the child devices. It is possible to disable OF_PLATDATA_NO_BIND but this - is not recommended since it increases code size. - - -Internals ---------- - -Generated files -``````````````` - -When enabled, dtoc generates the following five files: - -include/generated/dt-decl.h (OF_PLATDATA_INST only) - Contains declarations for all drivers, devices and uclasses. This allows - any `struct udevice`, `struct driver` or `struct uclass` to be located by its - name - -include/generated/dt-structs-gen.h - Contains the struct definitions for the devicetree nodes that are used. This - is the same as without OF_PLATDATA_INST - -spl/dts/dt-plat.c (only with !OF_PLATDATA_INST) - Contains the `U_BOOT_DRVINFO()` declarations that U-Boot uses to bind devices - at start-up. See above for an example - -spl/dts/dt-device.c (only with OF_PLATDATA_INST) - Contains `DM_DEVICE_INST()` declarations for each device that can be used at - run-time. These are declared in the file along with any private/platform data - that they use. Every device has an idx, as above. Since each device must be - part of a double-linked list, the nodes are declared in the code as well. - -spl/dts/dt-uclass.c (only with OF_PLATDATA_INST) - Contains `DM_UCLASS_INST()` declarations for each uclass that can be used at - run-time. These are declared in the file along with any private data - associated with the uclass itself (the `.priv_auto` member). Since each - uclass must be part of a double-linked list, the nodes are declared in the - code as well. - -The dt-structs.h file includes the generated file -`(include/generated/dt-structs.h`) if CONFIG_SPL_OF_PLATDATA is enabled. -Otherwise (such as in U-Boot proper) these structs are not available. This -prevents them being used inadvertently. All usage must be bracketed with -`#if CONFIG_IS_ENABLED(OF_PLATDATA)`. - -The dt-plat.c file contains the device declarations and is is built in -spl/dt-plat.c. - - -CONFIG options -`````````````` - -Several CONFIG options are used to control the behaviour of of-platdata, all -available for both SPL and TPL: - -OF_PLATDATA - This is the main option which enables the of-platdata feature - -OF_PLATDATA_PARENT - This allows `device_get_parent()` to work. Without this, all devices exist as - direct children of the root node. This option is highly desirable (if not - always absolutely essential) for buses such as I2C. - -OF_PLATDATA_INST - This controls the instantiation of devices at build time. With it disabled, - only `U_BOOT_DRVINFO()` records are created, with U-Boot handling the binding - in `device_bind()` on start-up. With it enabled, only `DM_DEVICE_INST()` and - `DM_UCLASS_INST()` records are created, and `device_bind()` is not needed at - runtime. - -OF_PLATDATA_NO_BIND - This controls whether `device_bind()` is supported. It is enabled by default - with OF_PLATDATA_INST since code-size reduction is really the main point of - the feature. It can be disabled if needed but is not likely to be supported - in the long term. - -OF_PLATDATA_DRIVER_RT - This controls whether the `struct driver_rt` records are used by U-Boot. - Normally when a device is bound, U-Boot stores the device pointer in one of - these records. There is one for every `struct driver_info` in the system, - i.e. one for every device that is bound from those records. It provides a - way to locate a device in the code and is used by - `device_get_by_ofplat_idx()`. This option is always enabled with of-platdata, - provided OF_PLATDATA_INST is not. In that case the records are useless since - we don't have any `struct driver_info` records. - -OF_PLATDATA_RT - This controls whether the `struct udevice_rt` records are used by U-Boot. - It moves the updatable fields from `struct udevice` (currently only `flags`) - into a separate structure, allowing the records to be kept in read-only - memory. It is generally enabled if OF_PLATDATA_INST is enabled. This option - also controls whether the private data is used in situ, or first copied into - an allocated region. Again this is to allow the private data declared by - dtoc-generated code to be in read-only memory. Note that access to private - data must be done via accessor functions, such as `dev_get_priv()`, so that - the relocation is handled. - -READ_ONLY - This indicates that the data generated by dtoc should not be modified. Only - a few fields actually do get changed in U-Boot, such as device flags. This - option causes those to move into an allocated space (see OF_PLATDATA_RT). - Also, since updating doubly linked lists is generally impossible when some of - the nodes cannot be updated, OF_PLATDATA_NO_BIND is enabled. - -Data structures -``````````````` - -A few extra data structures are used with of-platdata: - -`struct udevice_rt` - Run-time information for devices. When OF_PLATDATA_RT is enabled, this holds - the flags for each device, so that `struct udevice` can remain unchanged by - U-Boot, and potentially reside in read-only memory. Access to flags is then - via functions like `dev_get_flags()` and `dev_or_flags()`. This data - structure is allocated on start-up, where the private data is also copied. - All flags values start at 0 and any changes are handled by `dev_or_flags()` - and `dev_bic_flags()`. It would be more correct for the flags to be set to - `DM_FLAG_BOUND`, or perhaps `DM_FLAG_BOUND | DM_FLAG_ALLOC_PDATA`, but since - there is no code to bind/unbind devices and no code to allocate/free - private data / platform data, it doesn't matter. - -`struct driver_rt` - Run-time information for `struct driver_info` records. When - OF_PLATDATA_DRIVER_RT is enabled, this holds a pointer to the device - created by each record. This is needed so that is it possible to locate a - device from C code. Specifically, the code can use `DM_DRVINFO_GET(name)` to - get a reference to a particular `struct driver_info`, with `name` being the - name of the devicetree node. This is very convenient. It is also fast, since - no searching or string comparison is needed. This data structure is - allocated on start-up, filled out by `device_bind()` and used by - `device_get_by_ofplat_idx()`. - -Other changes -````````````` - -Some other changes are made with of-platdata: - -Accessor functions - Accessing private / platform data via functions such as `dev_get_priv()` has - always been encouraged. With OF_PLATDATA_RT this is essential, since the - `priv_` and `plat_` (etc.) values point to the data generated by dtoc, not - the read-write copy that is sometimes made on start-up. Changing the - private / platform data pointers has always been discouraged (the API is - marked internal) but with OF_PLATDATA_RT this is not currently supported in - general, since it assumes that all such pointers point to the relocated data. - Note also that the renaming of struct members to have a trailing underscore - was partly done to make people aware that they should not be accessed - directly. - -`gd->uclass_root_s` - Normally U-Boot sets up the head of the uclass list here and makes - `gd->uclass_root` point to it. With OF_PLATDATA_INST, dtoc generates a - declaration of `uclass_head` in `dt-uclass.c` since it needs to link the - head node into the list. In that case, `gd->uclass_root_s` is not used and - U-Boot just makes `gd->uclass_root` point to `uclass_head`. - -`gd->dm_driver_rt` - This holds a pointer to a list of `struct driver_rt` records, one for each - `struct driver_info`. The list is in alphabetical order by the name used - in `U_BOOT_DRVINFO(name)` and indexed by idx, with the first record having - an index of 0. It is only used if OF_PLATDATA_INST is not enabled. This is - accessed via macros so that it can be used inside IS_ENABLED(), rather than - requiring #ifdefs in the C code when it is not present. - -`gd->dm_udevice_rt` - This holds a pointer to a list of `struct udevice_rt` records, one for each - `struct udevice`. The list is in alphabetical order by the name used - in `DM_DEVICE_INST(name)` (a C version of the devicetree node) and indexed by - idx, with the first record having an index of 0. It is only used if - OF_PLATDATA_INST is enabled. This is accessed via macros so that it can be - used inside `IS_ENABLED()`, rather than requiring #ifdefs in the C code when - it is not present. - -`gd->dm_priv_base` - When OF_PLATDATA_RT is enabled, the private/platform data for each device is - copied into an allocated region by U-Boot on start-up. This points to that - region. All calls to accessor functions (e.g. `dev_get_priv()`) then - translate from the pointer provided by the caller (assumed to lie between - `__priv_data_start` and `__priv_data_end`) to the new allocated region. This - member is accessed via macros so that it can be used inside IS_ENABLED(), - rather than required #ifdefs in the C code when it is not present. - -`struct udevice->flags_` - When OF_PLATDATA_RT is enabled, device flags are no-longer part of - `struct udevice`, but are instead kept in `struct udevice_rt`, as described - above. Flags are accessed via functions, such as `dev_get_flags()` and - `dev_or_flags()`. - -`struct udevice->node_` - When OF_PLATDATA is enabled, there is no devicetree at runtime, so no need - for this field. It is removed, just to save space. - -`DM_PHASE` - This macro is used to indicate which phase of U-Boot a driver is intended - for. See above for details. - -`DM_HDR` - This macro is used to indicate which header file dtoc should use to allow - a driver declaration to compile correctly. See above for details. - -`device_get_by_ofplat_idx()` - There used to be a function called `device_get_by_driver_info()` which - looked up a `struct driver_info` pointer and returned the `struct udevice` - that was created from it. It was only available for use with of-platdata. - This has been removed in favour of `device_get_by_ofplat_idx()` which uses - `idx`, the index of the `struct driver_info` or `struct udevice` in the - linker_list. Similarly, the `struct phandle_0_arg` (etc.) structs have been - updated to use this index instead of a pointer to `struct driver_info`. - -`DM_DRVINFO_GET` - This has been removed since we now use indexes to obtain a driver from - `struct phandle_0_arg` and the like. - -Two-pass binding - The original of-platdata tried to order `U_BOOT_DRVINFO()` in the generated - files so as to have parents declared ahead of children. This was convenient - as it avoided any special code in U-Boot. With OF_PLATDATA_INST this does - not work as the idx value relies on using alphabetical order for everything, - so that dtoc and U-Boot's linker_lists agree on the idx value. Devices are - then bound in order of idx, having no regard to parent/child relationships. - For this reason, device binding now hapens in multiple passes, with parents - being bound before their children. This is important so that children can - find their parents in the bind() method if needed. - -Root device - The root device is generally bound by U-Boot but with OF_PLATDATA_INST it - cannot be, since binding needs to be done at build time. So in this case - dtoc sets up a root device using `DM_DEVICE_INST()` in `dt-device.c` and - U-Boot makes use of that. When OF_PLATDATA_INST is not enabled, U-Boot - generally ignores the root node and does not create a `U_BOOT_DRVINFO()` - record for it. This means that the idx numbers used by `struct driver_info` - (when OF_PLATDATA_INST is disabled) and the idx numbers used by - `struct udevice` (when OF_PLATDATA_INST is enabled) differ, since one has a - root node and the other does not. This does not actually matter, since only - one of them is actually used for any particular build, but it is worth - keeping in mind if comparing index values and switching OF_PLATDATA_INST on - and off. - -`__priv_data_start` and `__priv_data_end` - The private/platform data declared by dtoc is all collected together in - a linker section and these symbols mark the start and end of it. This allows - U-Boot to relocate the area to a new location if needed (with - OF_PLATDATA_RT) - -`dm_priv_to_rw()` - This function converts a private- or platform-data pointer value generated by - dtoc into one that can be used by U-Boot. It is a NOP unless OF_PLATDATA_RT - is enabled, in which case it translates the address to the relocated - region. See above for more information. - -The dm_populate_phandle_data() function that was previous needed has now been -removed, since dtoc can address the drivers directly from dt-plat.c and does -not need to fix up things at runtime. - -The pylibfdt Python module is used to access the devicetree. - - -Credits -------- - -This is an implementation of an idea by Tom Rini . - - -Future work ------------ -- Consider programmatically reading binding files instead of devicetree - contents -- Allow IS_ENABLED() to be used in the C code instead of #if - - -.. Simon Glass -.. Google, Inc -.. 6/6/16 -.. Updated Independence Day 2016 -.. Updated 1st October 2020 -.. Updated 5th February 2021 diff --git a/doc/driver-model/pci-info.rst b/doc/driver-model/pci-info.rst deleted file mode 100644 index 251601a51e3..00000000000 --- a/doc/driver-model/pci-info.rst +++ /dev/null @@ -1,172 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -PCI with Driver Model -===================== - -How busses are scanned ----------------------- - -Any config read will end up at pci_read_config(). This uses -uclass_get_device_by_seq() to get the PCI bus for a particular bus number. -Bus number 0 will need to be requested first, and the alias in the device -tree file will point to the correct device:: - - aliases { - pci0 = &pcic; - }; - - pcic: pci@0 { - compatible = "sandbox,pci"; - ... - }; - - -If there is no alias the devices will be numbered sequentially in the device -tree. - -The call to uclass_get_device() will cause the PCI bus to be probed. -This does a scan of the bus to locate available devices. These devices are -bound to their appropriate driver if available. If there is no driver, then -they are bound to a generic PCI driver which does nothing. - -After probing a bus, the available devices will appear in the device tree -under that bus. - -Note that this is all done on a lazy basis, as needed, so until something is -touched on PCI (eg: a call to pci_find_devices()) it will not be probed. - -PCI devices can appear in the flattened device tree. If they do, their node -often contains extra information which cannot be derived from the PCI IDs or -PCI class of the device. Each PCI device node must have a property, as -defined by the IEEE Std 1275-1994 PCI bus binding document v2.1. Compatible -string list is optional and generally not needed, since PCI is discoverable -bus, albeit there are justified exceptions. If the compatible string is -present, matching on it takes precedence over PCI IDs and PCI classes. - -Note we must describe PCI devices with the same bus hierarchy as the -hardware, otherwise driver model cannot detect the correct parent/children -relationship during PCI bus enumeration thus PCI devices won't be bound to -their drivers accordingly. A working example like below:: - - pci { - #address-cells = <3>; - #size-cells = <2>; - compatible = "pci-x86"; - u-boot,dm-pre-reloc; - ranges = <0x02000000 0x0 0x40000000 0x40000000 0 0x80000000 - 0x42000000 0x0 0xc0000000 0xc0000000 0 0x20000000 - 0x01000000 0x0 0x2000 0x2000 0 0xe000>; - - pcie@17,0 { - #address-cells = <3>; - #size-cells = <2>; - compatible = "pci-bridge"; - u-boot,dm-pre-reloc; - reg = <0x0000b800 0x0 0x0 0x0 0x0>; - - topcliff@0,0 { - #address-cells = <3>; - #size-cells = <2>; - compatible = "pci-bridge"; - u-boot,dm-pre-reloc; - reg = <0x00010000 0x0 0x0 0x0 0x0>; - - pciuart0: uart@a,1 { - compatible = "pci8086,8811.00", - "pci8086,8811", - "pciclass,070002", - "pciclass,0700", - "x86-uart"; - u-boot,dm-pre-reloc; - reg = <0x00025100 0x0 0x0 0x0 0x0 - 0x01025110 0x0 0x0 0x0 0x0>; - ...... - }; - - ...... - }; - }; - - ...... - }; - -In this example, the root PCI bus node is the "/pci" which matches "pci-x86" -driver. It has a subnode "pcie@17,0" with driver "pci-bridge". "pcie@17,0" -also has subnode "topcliff@0,0" which is a "pci-bridge" too. Under that bridge, -a PCI UART device "uart@a,1" is described. This exactly reflects the hardware -bus hierarchy: on the root PCI bus, there is a PCIe root port which connects -to a downstream device Topcliff chipset. Inside Topcliff chipset, it has a -PCIe-to-PCI bridge and all the chipset integrated devices like the PCI UART -device are on the PCI bus. Like other devices in the device tree, if we want -to bind PCI devices before relocation, "u-boot,dm-pre-reloc" must be declared -in each of these nodes. - -If PCI devices are not listed in the device tree, U_BOOT_PCI_DEVICE can be used -to specify the driver to use for the device. The device tree takes precedence -over U_BOOT_PCI_DEVICE. Please note with U_BOOT_PCI_DEVICE, only drivers with -DM_FLAG_PRE_RELOC will be bound before relocation. If neither device tree nor -U_BOOT_PCI_DEVICE is provided, the built-in driver (either pci_bridge_drv or -pci_generic_drv) will be used. - - -Sandbox -------- - -With sandbox we need a device emulator for each device on the bus since there -is no real PCI bus. This works by looking in the device tree node for an -emulator driver. For example:: - - pci@1f,0 { - compatible = "pci-generic"; - reg = <0xf800 0 0 0 0>; - sandbox,emul = <&emul_1f>; - }; - pci-emul { - compatible = "sandbox,pci-emul-parent"; - emul_1f: emul@1f,0 { - compatible = "sandbox,swap-case"; - #emul-cells = <0>; - }; - }; - -This means that there is a 'sandbox,swap-case' driver at that bus position. -Note that the first cell in the 'reg' value is the bus/device/function. See -PCI_BDF() for the encoding (it is also specified in the IEEE Std 1275-1994 -PCI bus binding document, v2.1) - -The pci-emul node should go outside the pci bus node, since otherwise it will -be scanned as a PCI device, causing confusion. - -When this bus is scanned we will end up with something like this:: - - `- * pci@0 @ 05c660c8, 0 - `- pci@1f,0 @ 05c661c8, 63488 - `- emul@1f,0 @ 05c662c8 - -When accesses go to the pci@1f,0 device they are forwarded to its emulator. - -The sandbox PCI drivers also support dynamic driver binding, allowing device -driver to declare the driver binding information via U_BOOT_PCI_DEVICE(), -eliminating the need to provide any device tree node under the host controller -node. It is required a "sandbox,dev-info" property must be provided in the -host controller node for this functionality to work. - -.. code-block:: none - - pci1: pci@1 { - compatible = "sandbox,pci"; - ... - sandbox,dev-info = <0x08 0x00 0x1234 0x5678 - 0x0c 0x00 0x1234 0x5678>; - }; - -The "sandbox,dev-info" property specifies all dynamic PCI devices on this bus. -Each dynamic PCI device is encoded as 4 cells a group. The first and second -cells are PCI device number and function number respectively. The third and -fourth cells are PCI vendor ID and device ID respectively. - -When this bus is scanned we will end up with something like this:: - - pci [ + ] pci_sandbo |-- pci1 - pci_emul [ ] sandbox_sw | |-- sandbox_swap_case_emul - pci_emul [ ] sandbox_sw | `-- sandbox_swap_case_emul diff --git a/doc/driver-model/pmic-framework.rst b/doc/driver-model/pmic-framework.rst deleted file mode 100644 index d24a1badd64..00000000000 --- a/doc/driver-model/pmic-framework.rst +++ /dev/null @@ -1,143 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. (C) Copyright 2014-2015 Samsung Electronics -.. sectionauthor:: Przemyslaw Marczak - -PMIC framework based on Driver Model -==================================== - -Introduction ------------- -This is an introduction to driver-model multi uclass PMIC IC's support. -At present it's based on two uclass types: - -UCLASS_PMIC: - basic uclass type for PMIC I/O, which provides common - read/write interface. -UCLASS_REGULATOR: - additional uclass type for specific PMIC features, which are - Voltage/Current regulators. - -New files: - -UCLASS_PMIC: - - drivers/power/pmic/pmic-uclass.c - - include/power/pmic.h -UCLASS_REGULATOR: - - drivers/power/regulator/regulator-uclass.c - - include/power/regulator.h - -Commands: -- common/cmd_pmic.c -- common/cmd_regulator.c - -How doees it work ------------------ -The Power Management Integrated Circuits (PMIC) are used in embedded systems -to provide stable, precise and specific voltage power source with over-voltage -and thermal protection circuits. - -The single PMIC can provide various functions by single or multiple interfaces, -like in the example below:: - - -- SoC - | - | ______________________________________ - | BUS 0 | Multi interface PMIC IC |--> LDO out 1 - | e.g.I2C0 | |--> LDO out N - |-----------|---- PMIC device 0 (READ/WRITE ops) | - | or SPI0 | |_ REGULATOR device (ldo/... ops) |--> BUCK out 1 - | | |_ CHARGER device (charger ops) |--> BUCK out M - | | |_ MUIC device (microUSB con ops) | - | BUS 1 | |_ ... |---> BATTERY - | e.g.I2C1 | | - |-----------|---- PMIC device 1 (READ/WRITE ops) |---> USB in 1 - . or SPI1 | |_ RTC device (rtc ops) |---> USB in 2 - . |______________________________________|---> USB out - . - -Since U-Boot provides driver model features for I2C and SPI bus drivers, -the PMIC devices should also support this. By the pmic and regulator API's, -PMIC drivers can simply provide a common functions, for multi-interface and -and multi-instance device support. - -Basic design assumptions: - -- Common I/O API: - UCLASS_PMIC. For the multi-function PMIC devices, this can be used as - parent I/O device for each IC's interface. Then, each children uses the - same dev for read/write. - -- Common regulator API: - UCLASS_REGULATOR. For driving the regulator attributes, auto setting - function or command line interface, based on kernel-style regulator device - tree constraints. - -For simple implementations, regulator drivers are not required, so the code can -use pmic read/write directly. - -Pmic uclass ------------ -The basic information: - -* Uclass: 'UCLASS_PMIC' -* Header: 'include/power/pmic.h' -* Core: 'drivers/power/pmic/pmic-uclass.c' (config 'CONFIG_DM_PMIC') -* Command: 'common/cmd_pmic.c' (config 'CONFIG_CMD_PMIC') -* Example: 'drivers/power/pmic/max77686.c' - -For detailed API description, please refer to the header file. - -As an example of the pmic driver, please refer to the MAX77686 driver. - -Please pay attention for the driver's bind() method. Exactly the function call: -'pmic_bind_children()', which is used to bind the regulators by using the array -of regulator's node, compatible prefixes. - -The 'pmic; command also supports the new API. So the pmic command can be enabled -by adding CONFIG_CMD_PMIC. -The new pmic command allows to: -- list pmic devices -- choose the current device (like the mmc command) -- read or write the pmic register -- dump all pmic registers - -This command can use only UCLASS_PMIC devices, since this uclass is designed -for pmic I/O operations only. - -For more information, please refer to the core file. - -Regulator uclass ----------------- -The basic information: - -* Uclass: 'UCLASS_REGULATOR' - -* Header: 'include/power/regulator.h' - -* Core: 'drivers/power/regulator/regulator-uclass.c' - (config 'CONFIG_DM_REGULATOR') - -* Binding: 'doc/device-tree-bindings/regulator/regulator.txt' - -* Command: 'common/cmd_regulator.c' (config 'CONFIG_CMD_REGULATOR') - -* Example: 'drivers/power/regulator/max77686.c' - 'drivers/power/pmic/max77686.c' (required I/O driver for the above) - -* Example: 'drivers/power/regulator/fixed.c' - (config 'CONFIG_DM_REGULATOR_FIXED') - -For detailed API description, please refer to the header file. - -For the example regulator driver, please refer to the MAX77686 regulator driver, -but this driver can't operate without pmic's example driver, which provides an -I/O interface for MAX77686 regulator. - -The second example is a fixed Voltage/Current regulator for a common use. - -The 'regulator' command also supports the new API. The command allow: -- list regulator devices -- choose the current device (like the mmc command) -- do all regulator-specific operations - -For more information, please refer to the command file. diff --git a/doc/driver-model/remoteproc-framework.rst b/doc/driver-model/remoteproc-framework.rst deleted file mode 100644 index 566495a21c4..00000000000 --- a/doc/driver-model/remoteproc-framework.rst +++ /dev/null @@ -1,169 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. (C) Copyright 2015 -.. Texas Instruments Incorporated - http://www.ti.com/ - -Remote Processor Framework -========================== - -Introduction ------------- - -This is an introduction to driver-model for Remote Processors found -on various System on Chip(SoCs). The term remote processor is used to -indicate that this is not the processor on which U-Boot is operating -on, instead is yet another processing entity that may be controlled by -the processor on which we are functional. - -The simplified model depends on a single UCLASS - UCLASS_REMOTEPROC - -UCLASS_REMOTEPROC: - - drivers/remoteproc/rproc-uclass.c - - include/remoteproc.h - -Commands: - - common/cmd_remoteproc.c - -Configuration: - - CONFIG_REMOTEPROC is selected by drivers as needed - - CONFIG_CMD_REMOTEPROC for the commands if required. - -How does it work - The driver ------------------------------ - -Overall, the driver statemachine transitions are typically as follows:: - - (entry) - +-------+ - +---+ init | - | | | <---------------------+ - | +-------+ | - | | - | | - | +--------+ | - Load| | reset | | - | | | <----------+ | - | +--------+ | | - | |Load | | - | | | | - | +----v----+ reset | | - +-> | | (opt) | | - | Loaded +-----------+ | - | | | - +----+----+ | - | Start | - +---v-----+ (opt) | - +->| Running | Stop | - Ping +- | +--------------------+ - (opt) +---------+ - -(is_running does not change state) -opt: Optional state transition implemented by driver. - -NOTE: It depends on the remote processor as to the exact behavior -of the statemachine, remoteproc core does not intent to implement -statemachine logic. Certain processors may allow start/stop without -reloading the image in the middle, certain other processors may only -allow us to start the processor(image from a EEPROM/OTP) etc. - -It is hence the responsibility of the driver to handle the requisite -state transitions of the device as necessary. - -Basic design assumptions: - -Remote processor can operate on a certain firmware that maybe loaded -and released from reset. - -The driver follows a standard UCLASS DM. - -in the bare minimum form: - -.. code-block:: c - - static const struct dm_rproc_ops sandbox_testproc_ops = { - .load = sandbox_testproc_load, - .start = sandbox_testproc_start, - }; - - static const struct udevice_id sandbox_ids[] = { - {.compatible = "sandbox,test-processor"}, - {} - }; - - U_BOOT_DRIVER(sandbox_testproc) = { - .name = "sandbox_test_proc", - .of_match = sandbox_ids, - .id = UCLASS_REMOTEPROC, - .ops = &sandbox_testproc_ops, - .probe = sandbox_testproc_probe, - }; - -This allows for the device to be probed as part of the "init" command -or invocation of 'rproc_init()' function as the system dependencies define. - -The driver is expected to maintain it's own statemachine which is -appropriate for the device it maintains. It must, at the very least -provide a load and start function. We assume here that the device -needs to be loaded and started, else, there is no real purpose of -using the remoteproc framework. - -Describing the device using platform data ------------------------------------------ - -*IMPORTANT* NOTE: THIS SUPPORT IS NOT MEANT FOR USE WITH NEWER PLATFORM -SUPPORT. THIS IS ONLY FOR LEGACY DEVICES. THIS MODE OF INITIALIZATION -*WILL* BE EVENTUALLY REMOVED ONCE ALL NECESSARY PLATFORMS HAVE MOVED -TO DM/FDT. - -Considering that many platforms are yet to move to device-tree model, -a simplified definition of a device is as follows: - -.. code-block:: c - - struct dm_rproc_uclass_pdata proc_3_test = { - .name = "proc_3_legacy", - .mem_type = RPROC_INTERNAL_MEMORY_MAPPED, - .driver_plat_data = &mydriver_data; - }; - - U_BOOT_DRVINFO(proc_3_demo) = { - .name = "sandbox_test_proc", - .plat = &proc_3_test, - }; - -There can be additional data that may be desired depending on the -remoteproc driver specific needs (for example: SoC integration -details such as clock handle or something similar). See appropriate -documentation for specific remoteproc driver for further details. -These are passed via driver_plat_data. - -Describing the device using device tree ---------------------------------------- - -.. code-block: none - - / { - ... - aliases { - ... - remoteproc0 = &rproc_1; - remoteproc1 = &rproc_2; - - }; - ... - - rproc_1: rproc@1 { - compatible = "sandbox,test-processor"; - remoteproc-name = "remoteproc-test-dev1"; - }; - - rproc_2: rproc@2 { - compatible = "sandbox,test-processor"; - internal-memory-mapped; - remoteproc-name = "remoteproc-test-dev2"; - }; - ... - }; - -aliases usage is optional, but it is usually recommended to ensure the -users have a consistent usage model for a platform. -the compatible string used here is specific to the remoteproc driver involved. diff --git a/doc/driver-model/serial-howto.rst b/doc/driver-model/serial-howto.rst deleted file mode 100644 index 1469131124b..00000000000 --- a/doc/driver-model/serial-howto.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -How to port a serial driver to driver model -=========================================== - -Almost all of the serial drivers have been converted as at January 2016. These -ones remain: - - * serial_bfin.c - * serial_pxa.c - -The deadline for this work was the end of January 2016. If no one steps -forward to convert these, at some point there may come a patch to remove them! - -Here is a suggested approach for converting your serial driver over to driver -model. Please feel free to update this file with your ideas and suggestions. - -- #ifdef out all your own serial driver code (#ifndef CONFIG_DM_SERIAL) -- Define CONFIG_DM_SERIAL for your board, vendor or architecture -- If the board does not already use driver model, you need CONFIG_DM also -- Your board should then build, but will not boot since there will be no serial - driver -- Add the U_BOOT_DRIVER piece at the end (e.g. copy serial_s5p.c for example) -- Add a private struct for the driver data - avoid using static variables -- Implement each of the driver methods, perhaps by calling your old methods -- You may need to adjust the function parameters so that the old and new - implementations can share most of the existing code -- If you convert all existing users of the driver, remove the pre-driver-model - code - -In terms of patches a conversion series typically has these patches: -- clean up / prepare the driver for conversion -- add driver model code -- convert at least one existing board to use driver model serial -- (if no boards remain that don't use driver model) remove the old code - -This may be a good time to move your board to use device tree also. Mostly -this involves these steps: - -- define CONFIG_OF_CONTROL and CONFIG_OF_SEPARATE -- add your device tree files to arch//dts -- update the Makefile there -- Add stdout-path to your /chosen device tree node if it is not already there -- build and get u-boot-dtb.bin so you can test it -- Your drivers can now use device tree -- For device tree in SPL, define CONFIG_SPL_OF_CONTROL diff --git a/doc/driver-model/soc-framework.rst b/doc/driver-model/soc-framework.rst deleted file mode 100644 index 2609fda6442..00000000000 --- a/doc/driver-model/soc-framework.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ -.. (C) Copyright 2020 -.. Texas Instruments Incorporated - http://www.ti.com/ - -SOC ID Framework -================ - -Introduction ------------- - -The driver-model SOC ID framework is able to provide identification -information about a specific SoC in use at runtime, and also provide matching -from a set of identification information from an array. This can be useful for -enabling small quirks in drivers that exist between SoC variants that are -impractical to implement using device tree flags. It is based on UCLASS_SOC. - -UCLASS_SOC: - - drivers/soc/soc-uclass.c - - include/soc.h - -Configuration: - - CONFIG_SOC_DEVICE is selected by drivers as needed. - -Implementing a UCLASS_SOC provider ----------------------------------- - -The purpose of this framework is to allow UCLASS_SOC provider drivers to supply -identification information about the SoC in use at runtime. The framework -allows drivers to define soc_ops that return identification strings. All -soc_ops need not be defined and can be left as NULL, in which case the -framework will return -ENOSYS and not consider the value when doing an -soc_device_match. - -It is left to the driver implementor to decide how the information returned is -determined, but in general the same SOC should always return the same set of -identifying information. Information returned must be in the form of a NULL -terminated string. - -See include/soc.h for documentation of the available soc_ops and the intended -meaning of the values that can be returned. See drivers/soc/soc_sandbox.c for -an example UCLASS_SOC provider driver. - -Using a UCLASS_SOC driver -------------------------- - -The framework provides the ability to retrieve and use the identification -strings directly. It also has the ability to return a match from a list of -different sets of SoC data using soc_device_match. - -An array of 'struct soc_attr' can be defined, each containing ID information -for a specific SoC, and when passed to soc_device_match, the identifier values -for each entry in the list will be compared against the values provided by the -UCLASS_SOC driver that is in use. The first entry in the list that matches all -non-null values will be returned by soc_device_match. - -An example of various uses of the framework can be found at test/dm/soc.c. - -Describing the device using device tree ---------------------------------------- - -.. code-block:: none - - chipid: chipid { - compatible = "sandbox,soc"; - }; - -All that is required in a DT node is a compatible for a corresponding -UCLASS_SOC driver. diff --git a/doc/driver-model/spi-howto.rst b/doc/driver-model/spi-howto.rst deleted file mode 100644 index 97fbf750cb6..00000000000 --- a/doc/driver-model/spi-howto.rst +++ /dev/null @@ -1,692 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -How to port a SPI driver to driver model -======================================== - -Here is a rough step-by-step guide. It is based around converting the -exynos SPI driver to driver model (DM) and the example code is based -around U-Boot v2014.10-rc2 (commit be9f643). This has been updated for -v2015.04. - -It is quite long since it includes actual code examples. - -Before driver model, SPI drivers have their own private structure which -contains 'struct spi_slave'. With driver model, 'struct spi_slave' still -exists, but now it is 'per-child data' for the SPI bus. Each child of the -SPI bus is a SPI slave. The information that was stored in the -driver-specific slave structure can now be port in private data for the -SPI bus. - -For example, struct tegra_spi_slave looks like this: - -.. code-block:: c - - struct tegra_spi_slave { - struct spi_slave slave; - struct tegra_spi_ctrl *ctrl; - }; - -In this case 'slave' will be in per-child data, and 'ctrl' will be in the -SPI's buses private data. - - -How long does this take? ------------------------- - -You should be able to complete this within 2 hours, including testing but -excluding preparing the patches. The API is basically the same as before -with only minor changes: - -- methods to set speed and mode are separated out -- cs_info is used to get information on a chip select - - -Enable driver mode for SPI and SPI flash ----------------------------------------- - -Add these to your board config: - -* CONFIG_DM_SPI -* CONFIG_DM_SPI_FLASH - - -Add the skeleton ----------------- - -Put this code at the bottom of your existing driver file: - -.. code-block:: c - - struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, - unsigned int max_hz, unsigned int mode) - { - return NULL; - } - - struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, - int spi_node) - { - return NULL; - } - - static int exynos_spi_of_to_plat(struct udevice *dev) - { - return -ENODEV; - } - - static int exynos_spi_probe(struct udevice *dev) - { - return -ENODEV; - } - - static int exynos_spi_remove(struct udevice *dev) - { - return -ENODEV; - } - - static int exynos_spi_claim_bus(struct udevice *dev) - { - - return -ENODEV; - } - - static int exynos_spi_release_bus(struct udevice *dev) - { - - return -ENODEV; - } - - static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) - { - - return -ENODEV; - } - - static int exynos_spi_set_speed(struct udevice *dev, uint speed) - { - return -ENODEV; - } - - static int exynos_spi_set_mode(struct udevice *dev, uint mode) - { - return -ENODEV; - } - - static int exynos_cs_info(struct udevice *bus, uint cs, - struct spi_cs_info *info) - { - return -EINVAL; - } - - static const struct dm_spi_ops exynos_spi_ops = { - .claim_bus = exynos_spi_claim_bus, - .release_bus = exynos_spi_release_bus, - .xfer = exynos_spi_xfer, - .set_speed = exynos_spi_set_speed, - .set_mode = exynos_spi_set_mode, - .cs_info = exynos_cs_info, - }; - - static const struct udevice_id exynos_spi_ids[] = { - { .compatible = "samsung,exynos-spi" }, - { } - }; - - U_BOOT_DRIVER(exynos_spi) = { - .name = "exynos_spi", - .id = UCLASS_SPI, - .of_match = exynos_spi_ids, - .ops = &exynos_spi_ops, - .of_to_plat = exynos_spi_of_to_plat, - .probe = exynos_spi_probe, - .remove = exynos_spi_remove, - }; - - -Replace 'exynos' in the above code with your driver name --------------------------------------------------------- - - -#ifdef out all of the code in your driver except for the above --------------------------------------------------------------- - -This will allow you to get it building, which means you can work -incrementally. Since all the methods return an error initially, there is -less chance that you will accidentally leave something in. - -Also, even though your conversion is basically a rewrite, it might help -reviewers if you leave functions in the same place in the file, -particularly for large drivers. - - -Add some includes ------------------ - -Add these includes to your driver: - -.. code-block:: c - - #include - #include - - -Build ------ - -At this point you should be able to build U-Boot for your board with the -empty SPI driver. You still have empty methods in your driver, but we will -write these one by one. - -Set up your platform data structure ------------------------------------ - -This will hold the information your driver to operate, like its hardware -address or maximum frequency. - -You may already have a struct like this, or you may need to create one -from some of the #defines or global variables in the driver. - -Note that this information is not the run-time information. It should not -include state that changes. It should be fixed throughout the live of -U-Boot. Run-time information comes later. - -Here is what was in the exynos spi driver: - -.. code-block:: c - - struct spi_bus { - enum periph_id periph_id; - s32 frequency; /* Default clock frequency, -1 for none */ - struct exynos_spi *regs; - int inited; /* 1 if this bus is ready for use */ - int node; - uint deactivate_delay_us; /* Delay to wait after deactivate */ - }; - -Of these, inited is handled by DM and node is the device tree node, which -DM tells you. The name is not quite right. So in this case we would use: - -.. code-block:: c - - struct exynos_spi_plat { - enum periph_id periph_id; - s32 frequency; /* Default clock frequency, -1 for none */ - struct exynos_spi *regs; - uint deactivate_delay_us; /* Delay to wait after deactivate */ - }; - - -Write of_to_plat() [for device tree only] -------------------------------------------------- - -This method will convert information in the device tree node into a C -structure in your driver (called platform data). If you are not using -device tree, go to 8b. - -DM will automatically allocate the struct for us when we are using device -tree, but we need to tell it the size: - -.. code-block:: c - - U_BOOT_DRIVER(spi_exynos) = { - ... - .plat_auto = sizeof(struct exynos_spi_plat), - - -Here is a sample function. It gets a pointer to the platform data and -fills in the fields from device tree. - -.. code-block:: c - - static int exynos_spi_of_to_plat(struct udevice *bus) - { - struct exynos_spi_plat *plat = bus->plat; - const void *blob = gd->fdt_blob; - int node = dev_of_offset(bus); - - plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); - plat->periph_id = pinmux_decode_periph_id(blob, node); - - if (plat->periph_id == PERIPH_ID_NONE) { - debug("%s: Invalid peripheral ID %d\n", __func__, - plat->periph_id); - return -FDT_ERR_NOTFOUND; - } - - /* Use 500KHz as a suitable default */ - plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", - 500000); - plat->deactivate_delay_us = fdtdec_get_int(blob, node, - "spi-deactivate-delay", 0); - debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", - __func__, plat->regs, plat->periph_id, plat->frequency, - plat->deactivate_delay_us); - - return 0; - } - - -Add the platform data [non-device-tree only] --------------------------------------------- - -Specify this data in a U_BOOT_DRVINFO() declaration in your board file: - -.. code-block:: c - - struct exynos_spi_plat platdata_spi0 = { - .periph_id = ... - .frequency = ... - .regs = ... - .deactivate_delay_us = ... - }; - - U_BOOT_DRVINFO(board_spi0) = { - .name = "exynos_spi", - .plat = &platdata_spi0, - }; - -You will unfortunately need to put the struct definition into a header file -in this case so that your board file can use it. - - -Add the device private data ---------------------------- - -Most devices have some private data which they use to keep track of things -while active. This is the run-time information and needs to be stored in -a structure. There is probably a structure in the driver that includes a -'struct spi_slave', so you can use that. - -.. code-block:: c - - struct exynos_spi_slave { - struct spi_slave slave; - struct exynos_spi *regs; - unsigned int freq; /* Default frequency */ - unsigned int mode; - enum periph_id periph_id; /* Peripheral ID for this device */ - unsigned int fifo_size; - int skip_preamble; - struct spi_bus *bus; /* Pointer to our SPI bus info */ - ulong last_transaction_us; /* Time of last transaction end */ - }; - - -We should rename this to make its purpose more obvious, and get rid of -the slave structure, so we have: - -.. code-block:: c - - struct exynos_spi_priv { - struct exynos_spi *regs; - unsigned int freq; /* Default frequency */ - unsigned int mode; - enum periph_id periph_id; /* Peripheral ID for this device */ - unsigned int fifo_size; - int skip_preamble; - ulong last_transaction_us; /* Time of last transaction end */ - }; - - -DM can auto-allocate this also: - -.. code-block:: c - - U_BOOT_DRIVER(spi_exynos) = { - ... - .priv_auto = sizeof(struct exynos_spi_priv), - - -Note that this is created before the probe method is called, and destroyed -after the remove method is called. It will be zeroed when the probe -method is called. - - -Add the probe() and remove() methods ------------------------------------- - -Note: It's a good idea to build repeatedly as you are working, to avoid a -huge amount of work getting things compiling at the end. - -The probe method is supposed to set up the hardware. U-Boot used to use -spi_setup_slave() to do this. So take a look at this function and see -what you can copy out to set things up. - -.. code-block:: c - - static int exynos_spi_probe(struct udevice *bus) - { - struct exynos_spi_plat *plat = dev_get_plat(bus); - struct exynos_spi_priv *priv = dev_get_priv(bus); - - priv->regs = plat->regs; - if (plat->periph_id == PERIPH_ID_SPI1 || - plat->periph_id == PERIPH_ID_SPI2) - priv->fifo_size = 64; - else - priv->fifo_size = 256; - - priv->skip_preamble = 0; - priv->last_transaction_us = timer_get_us(); - priv->freq = plat->frequency; - priv->periph_id = plat->periph_id; - - return 0; - } - -This implementation doesn't actually touch the hardware, which is somewhat -unusual for a driver. In this case we will do that when the device is -claimed by something that wants to use the SPI bus. - -For remove we could shut down the clocks, but in this case there is -nothing to do. DM frees any memory that it allocated, so we can just -remove exynos_spi_remove() and its reference in U_BOOT_DRIVER. - - -Implement set_speed() ---------------------- - -This should set up clocks so that the SPI bus is running at the right -speed. With the old API spi_claim_bus() would normally do this and several -of the following functions, so let's look at that function: - -.. code-block:: c - - int spi_claim_bus(struct spi_slave *slave) - { - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); - struct exynos_spi *regs = spi_slave->regs; - u32 reg = 0; - int ret; - - ret = set_spi_clk(spi_slave->periph_id, - spi_slave->freq); - if (ret < 0) { - debug("%s: Failed to setup spi clock\n", __func__); - return ret; - } - - exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); - - spi_flush_fifo(slave); - - reg = readl(®s->ch_cfg); - reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); - - if (spi_slave->mode & SPI_CPHA) - reg |= SPI_CH_CPHA_B; - - if (spi_slave->mode & SPI_CPOL) - reg |= SPI_CH_CPOL_L; - - writel(reg, ®s->ch_cfg); - writel(SPI_FB_DELAY_180, ®s->fb_clk); - - return 0; - } - - -It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs. -With DM these will happen in separate methods. - - -Here is an example for the speed part: - -.. code-block:: c - - static int exynos_spi_set_speed(struct udevice *bus, uint speed) - { - struct exynos_spi_plat *plat = bus->plat; - struct exynos_spi_priv *priv = dev_get_priv(bus); - int ret; - - if (speed > plat->frequency) - speed = plat->frequency; - ret = set_spi_clk(priv->periph_id, speed); - if (ret) - return ret; - priv->freq = speed; - debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); - - return 0; - } - - -Implement set_mode() --------------------- - -This should adjust the SPI mode (polarity, etc.). Again this code probably -comes from the old spi_claim_bus(). Here is an example: - -.. code-block:: c - - static int exynos_spi_set_mode(struct udevice *bus, uint mode) - { - struct exynos_spi_priv *priv = dev_get_priv(bus); - uint32_t reg; - - reg = readl(&priv->regs->ch_cfg); - reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); - - if (mode & SPI_CPHA) - reg |= SPI_CH_CPHA_B; - - if (mode & SPI_CPOL) - reg |= SPI_CH_CPOL_L; - - writel(reg, &priv->regs->ch_cfg); - priv->mode = mode; - debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); - - return 0; - } - - -Implement claim_bus() ---------------------- - -This is where a client wants to make use of the bus, so claims it first. -At this point we need to make sure everything is set up ready for data -transfer. Note that this function is wholly internal to the driver - at -present the SPI uclass never calls it. - -Here again we look at the old claim function and see some code that is -needed. It is anything unrelated to speed and mode: - -.. code-block:: c - - static int exynos_spi_claim_bus(struct udevice *bus) - { - struct exynos_spi_priv *priv = dev_get_priv(bus); - - exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); - spi_flush_fifo(priv->regs); - - writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); - - return 0; - } - -The spi_flush_fifo() function is in the removed part of the code, so we -need to expose it again (perhaps with an #endif before it and '#if 0' -after it). It only needs access to priv->regs which is why we have -passed that in: - -.. code-block:: c - - /** - * Flush spi tx, rx fifos and reset the SPI controller - * - * @param regs Pointer to SPI registers - */ - static void spi_flush_fifo(struct exynos_spi *regs) - { - clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); - clrbits_le32(®s->ch_cfg, SPI_CH_RST); - setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); - } - - -Implement release_bus() ------------------------ - -This releases the bus - in our example the old code in spi_release_bus() -is a call to spi_flush_fifo, so we add: - -.. code-block:: c - - static int exynos_spi_release_bus(struct udevice *bus) - { - struct exynos_spi_priv *priv = dev_get_priv(bus); - - spi_flush_fifo(priv->regs); - - return 0; - } - - -Implement xfer() ----------------- - -This is the final method that we need to create, and it is where all the -work happens. The method parameters are the same as the old spi_xfer() with -the addition of a 'struct udevice' so conversion is pretty easy. Start -by copying the contents of spi_xfer() to your new xfer() method and proceed -from there. - -If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an -activate function, something like this: - -.. code-block:: c - - void spi_cs_activate(struct spi_slave *slave) - { - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); - - /* If it's too soon to do another transaction, wait */ - if (spi_slave->bus->deactivate_delay_us && - spi_slave->last_transaction_us) { - ulong delay_us; /* The delay completed so far */ - delay_us = timer_get_us() - spi_slave->last_transaction_us; - if (delay_us < spi_slave->bus->deactivate_delay_us) - udelay(spi_slave->bus->deactivate_delay_us - delay_us); - } - - clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); - debug("Activate CS, bus %d\n", spi_slave->slave.bus); - spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; - } - -The new version looks like this: - -.. code-block:: c - - static void spi_cs_activate(struct udevice *dev) - { - struct udevice *bus = dev->parent; - struct exynos_spi_plat *pdata = dev_get_plat(bus); - struct exynos_spi_priv *priv = dev_get_priv(bus); - - /* If it's too soon to do another transaction, wait */ - if (pdata->deactivate_delay_us && - priv->last_transaction_us) { - ulong delay_us; /* The delay completed so far */ - delay_us = timer_get_us() - priv->last_transaction_us; - if (delay_us < pdata->deactivate_delay_us) - udelay(pdata->deactivate_delay_us - delay_us); - } - - clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); - debug("Activate CS, bus '%s'\n", bus->name); - priv->skip_preamble = priv->mode & SPI_PREAMBLE; - } - -All we have really done here is change the pointers and print the device name -instead of the bus number. Other local static functions can be treated in -the same way. - - -Set up the per-child data and child pre-probe function ------------------------------------------------------- - -To minimise the pain and complexity of the SPI subsystem while the driver -model change-over is in place, struct spi_slave is used to reference a -SPI bus slave, even though that slave is actually a struct udevice. In fact -struct spi_slave is the device's child data. We need to make sure this space -is available. It is possible to allocate more space that struct spi_slave -needs, but this is the minimum. - -.. code-block:: c - - U_BOOT_DRIVER(exynos_spi) = { - ... - .per_child_auto = sizeof(struct spi_slave), - } - - -Optional: Set up cs_info() if you want it ------------------------------------------ - -Sometimes it is useful to know whether a SPI chip select is valid, but this -is not obvious from outside the driver. In this case you can provide a -method for cs_info() to deal with this. If you don't provide it, then the -device tree will be used to determine what chip selects are valid. - -Return -EINVAL if the supplied chip select is invalid, or 0 if it is valid. -If you don't provide the cs_info() method, 0 is assumed for all chip selects -that do not appear in the device tree. - - -Test it -------- - -Now that you have the code written and it compiles, try testing it using -the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your -board. - - -Prepare patches and send them to the mailing lists --------------------------------------------------- - -You can use 'tools/patman/patman' to prepare, check and send patches for -your work. See tools/patman/README for details. - -A little note about SPI uclass features ---------------------------------------- - -The SPI uclass keeps some information about each device 'dev' on the bus: - - struct dm_spi_slave_plat: - This is device_get_parent_plat(dev). - This is where the chip select number is stored, along with - the default bus speed and mode. It is automatically read - from the device tree in spi_child_post_bind(). It must not - be changed at run-time after being set up because platform - data is supposed to be immutable at run-time. - struct spi_slave: - This is device_get_parentdata(dev). - Already mentioned above. It holds run-time information about - the device. - -There are also some SPI uclass methods that get called behind the scenes: - - spi_post_bind(): - Called when a new bus is bound. - This scans the device tree for devices on the bus, and binds - each one. This in turn causes spi_child_post_bind() to be - called for each, which reads the device tree information - into the parent (per-child) platform data. - spi_child_post_bind(): - Called when a new child is bound. - As mentioned above this reads the device tree information - into the per-child platform data - spi_child_pre_probe(): - Called before a new child is probed. - This sets up the mode and speed in struct spi_slave by - copying it from the parent's platform data for this child. - It also sets the 'dev' pointer, needed to permit passing - 'struct spi_slave' around the place without needing a - separate 'struct udevice' pointer. - -The above housekeeping makes it easier to write your SPI driver. diff --git a/doc/driver-model/usb-info.rst b/doc/driver-model/usb-info.rst deleted file mode 100644 index 24d1e81a6c6..00000000000 --- a/doc/driver-model/usb-info.rst +++ /dev/null @@ -1,423 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0+ - -How USB works with driver model -=============================== - -Introduction ------------- - -Driver model USB support makes use of existing features but changes how -drivers are found. This document provides some information intended to help -understand how things work with USB in U-Boot when driver model is enabled. - - -Enabling driver model for USB ------------------------------ - -A new CONFIG_DM_USB option is provided to enable driver model for USB. This -causes the USB uclass to be included, and drops the equivalent code in -usb.c. In particular the usb_init() function is then implemented by the -uclass. - - -Support for EHCI and XHCI -------------------------- - -So far OHCI is not supported. Both EHCI and XHCI drivers should be declared -as drivers in the USB uclass. For example: - -.. code-block:: c - - static const struct udevice_id ehci_usb_ids[] = { - { .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 }, - { .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 }, - { .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 }, - { } - }; - - U_BOOT_DRIVER(usb_ehci) = { - .name = "ehci_tegra", - .id = UCLASS_USB, - .of_match = ehci_usb_ids, - .of_to_plat = ehci_usb_of_to_plat, - .probe = tegra_ehci_usb_probe, - .remove = tegra_ehci_usb_remove, - .ops = &ehci_usb_ops, - .plat_auto = sizeof(struct usb_plat), - .priv_auto = sizeof(struct fdt_usb), - .flags = DM_FLAG_ALLOC_PRIV_DMA, - }; - -Here ehci_usb_ids is used to list the controllers that the driver supports. -Each has its own data value. Controllers must be in the UCLASS_USB uclass. - -The of_to_plat() method allows the controller driver to grab any -necessary settings from the device tree. - -The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in -most cases, since they are all EHCI-compatible. For EHCI there are also some -special operations that can be overridden when calling ehci_register(). - -The driver can use priv_auto to set the size of its private data. -This can hold run-time information needed by the driver for operation. It -exists when the device is probed (not when it is bound) and is removed when -the driver is removed. - -Note that usb_plat is currently only used to deal with setting up a bus -in USB device mode (OTG operation). It can be omitted if that is not -supported. - -The driver's probe() method should do the basic controller init and then -call ehci_register() to register itself as an EHCI device. It should call -ehci_deregister() in the remove() method. Registering a new EHCI device -does not by itself cause the bus to be scanned. - -The old ehci_hcd_init() function is no-longer used. Nor is it necessary to -set up the USB controllers from board init code. When 'usb start' is used, -each controller will be probed and its bus scanned. - -XHCI works in a similar way. - - -Data structures ---------------- - -The following primary data structures are in use: - -- struct usb_device: - This holds information about a device on the bus. All devices have - this structure, even the root hub. The controller itself does not - have this structure. You can access it for a device 'dev' with - dev_get_parent_priv(dev). It matches the old structure except that the - parent and child information is not present (since driver model - handles that). Once the device is set up, you can find the device - descriptor and current configuration descriptor in this structure. - -- struct usb_plat: - This holds platform data for a controller. So far this is only used - as a work-around for controllers which can act as USB devices in OTG - mode, since the gadget framework does not use driver model. - -- struct usb_dev_plat: - This holds platform data for a device. You can access it for a - device 'dev' with dev_get_parent_plat(dev). It holds the device - address and speed - anything that can be determined before the device - driver is actually set up. When probing the bus this structure is - used to provide essential information to the device driver. - -- struct usb_bus_priv: - This is private information for each controller, maintained by the - controller uclass. It is mostly used to keep track of the next - device address to use. - -Of these, only struct usb_device was used prior to driver model. - - -USB buses ---------- - -Given a controller, you know the bus - it is the one attached to the -controller. Each controller handles exactly one bus. Every controller has a -root hub attached to it. This hub, which is itself a USB device, can provide -one or more 'ports' to which additional devices can be attached. It is -possible to power up a hub and find out which of its ports have devices -attached. - -Devices are given addresses starting at 1. The root hub is always address 1, -and from there the devices are numbered in sequence. The USB uclass takes -care of this numbering automatically during enumeration. - -USB devices are enumerated by finding a device on a particular hub, and -setting its address to the next available address. The USB bus stretches out -in a tree structure, potentially with multiple hubs each with several ports -and perhaps other hubs. Some hubs will have their own power since otherwise -the 5V 500mA power supplied by the controller will not be sufficient to run -very many devices. - -Enumeration in U-Boot takes a long time since devices are probed one at a -time, and each is given sufficient time to wake up and announce itself. The -timeouts are set for the slowest device. - -Up to 127 devices can be on each bus. USB has four bus speeds: low -(1.5Mbps), full (12Mbps), high (480Mbps) which is only available with USB2 -and newer (EHCI), and super (5Gbps) which is only available with USB3 and -newer (XHCI). If you connect a super-speed device to a high-speed hub, you -will only get high-speed. - - -USB operations --------------- - -As before driver model, messages can be sent using submit_bulk_msg() and the -like. These are now implemented by the USB uclass and route through the -controller drivers. Note that messages are not sent to the driver of the -device itself - i.e. they don't pass down the stack to the controller. -U-Boot simply finds the controller to which the device is attached, and sends -the message there with an appropriate 'pipe' value so it can be addressed -properly. Having said that, the USB device which should receive the message -is passed in to the driver methods, for use by sandbox. This design decision -is open for review and the code impact of changing it is small since the -methods are typically implemented by the EHCI and XHCI stacks. - -Controller drivers (in UCLASS_USB) themselves provide methods for sending -each message type. For XHCI an additional alloc_device() method is provided -since XHCI needs to allocate a device context before it can even read the -device's descriptor. - -These methods use a 'pipe' which is a collection of bit fields used to -describe the type of message, direction of transfer and the intended -recipient (device number). - - -USB Devices ------------ - -USB devices are found using a simple algorithm which works through the -available hubs in a depth-first search. Devices can be in any uclass, but -are attached to a parent hub (or controller in the case of the root hub) and -so have parent data attached to them (this is struct usb_device). - -By the time the device's probe() method is called, it is enumerated and is -ready to talk to the host. - -The enumeration process needs to work out which driver to attach to each USB -device. It does this by examining the device class, interface class, vendor -ID, product ID, etc. See struct usb_driver_entry for how drivers are matched -with USB devices - you can use the USB_DEVICE() macro to declare a USB -driver. For example, usb_storage.c defines a USB_DEVICE() to handle storage -devices, and it will be used for all USB devices which match. - - - -Technical details on enumeration flow -------------------------------------- - -It is useful to understand precisely how a USB bus is enumerating to avoid -confusion when dealing with USB devices. - -Device initialisation happens roughly like this: - -- At some point the 'usb start' command is run -- This calls usb_init() which works through each controller in turn -- The controller is probed(). This does no enumeration. -- Then usb_scan_bus() is called. This calls usb_scan_device() to scan the - (only) device that is attached to the controller - a root hub -- usb_scan_device() sets up a fake struct usb_device and calls - usb_setup_device(), passing the port number to be scanned, in this case - port 0 -- usb_setup_device() first calls usb_prepare_device() to set the device - address, then usb_select_config() to select the first configuration -- at this point the device is enumerated but we do not have a real struct - udevice for it. But we do have the descriptor in struct usb_device so we can - use this to figure out what driver to use -- back in usb_scan_device(), we call usb_find_child() to try to find an - existing device which matches the one we just found on the bus. This can - happen if the device is mentioned in the device tree, or if we previously - scanned the bus and so the device was created before -- if usb_find_child() does not find an existing device, we call - usb_find_and_bind_driver() which tries to bind one -- usb_find_and_bind_driver() searches all available USB drivers (declared - with USB_DEVICE()). If it finds a match it binds that driver to create a - new device. -- If it does not, it binds a generic driver. A generic driver is good enough - to allow access to the device (sending it packets, etc.) but all - functionality will need to be implemented outside the driver model. -- in any case, when usb_find_child() and/or usb_find_and_bind_driver() are - done, we have a device with the correct uclass. At this point we want to - probe the device -- first we store basic information about the new device (address, port, - speed) in its parent platform data. We cannot store it its private data - since that will not exist until the device is probed. -- then we call device_probe() which probes the device -- the first probe step is actually the USB controller's (or USB hubs's) - child_pre_probe() method. This gets called before anything else and is - intended to set up a child device ready to be used with its parent bus. For - USB this calls usb_child_pre_probe() which grabs the information that was - stored in the parent platform data and stores it in the parent private data - (which is struct usb_device, a real one this time). It then calls - usb_select_config() again to make sure that everything about the device is - set up -- note that we have called usb_select_config() twice. This is inefficient - but the alternative is to store additional information in the platform data. - The time taken is minimal and this way is simpler -- at this point the device is set up and ready for use so far as the USB - subsystem is concerned -- the device's probe() method is then called. It can send messages and do - whatever else it wants to make the device work. - -Note that the first device is always a root hub, and this must be scanned to -find any devices. The above steps will have created a hub (UCLASS_USB_HUB), -given it address 1 and set the configuration. - -For hubs, the hub uclass has a post_probe() method. This means that after -any hub is probed, the uclass gets to do some processing. In this case -usb_hub_post_probe() is called, and the following steps take place: - -- usb_hub_post_probe() calls usb_hub_scan() to scan the hub, which in turn - calls usb_hub_configure() -- hub power is enabled -- we loop through each port on the hub, performing the same steps for each -- first, check if there is a device present. This happens in - usb_hub_port_connect_change(). If so, then usb_scan_device() is called to - scan the device, passing the appropriate port number. -- you will recognise usb_scan_device() from the steps above. It sets up the - device ready for use. If it is a hub, it will scan that hub before it - continues here (recursively, depth-first) -- once all hub ports are scanned in this way, the hub is ready for use and - all of its downstream devices also -- additional controllers are scanned in the same way - -The above method has some nice properties: - -- the bus enumeration happens by virtue of driver model's natural device flow -- most logic is in the USB controller and hub uclasses; the actual device - drivers do not need to know they are on a USB bus, at least so far as - enumeration goes -- hub scanning happens automatically after a hub is probed - - -Hubs ----- - -USB hubs are scanned as in the section above. While hubs have their own -uclass, they share some common elements with controllers: - -- they both attach private data to their children (struct usb_device, - accessible for a child with dev_get_parent_priv(child)) -- they both use usb_child_pre_probe() to set up their children as proper USB - devices - - -Example - Mass Storage ----------------------- - -As an example of a USB device driver, see usb_storage.c. It uses its own -uclass and declares itself as follows: - -.. code-block:: c - - U_BOOT_DRIVER(usb_mass_storage) = { - .name = "usb_mass_storage", - .id = UCLASS_MASS_STORAGE, - .of_match = usb_mass_storage_ids, - .probe = usb_mass_storage_probe, - }; - - static const struct usb_device_id mass_storage_id_table[] = { - { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, - .bInterfaceClass = USB_CLASS_MASS_STORAGE}, - { } /* Terminating entry */ - }; - - USB_DEVICE(usb_mass_storage, mass_storage_id_table); - -The USB_DEVICE() macro attaches the given table of matching information to -the given driver. Note that the driver is declared in U_BOOT_DRIVER() as -'usb_mass_storage' and this must match the first parameter of USB_DEVICE. - -When usb_find_and_bind_driver() is called on a USB device with the -bInterfaceClass value of USB_CLASS_MASS_STORAGE, it will automatically find -this driver and use it. - - -Counter-example: USB Ethernet ------------------------------ - -As an example of the old way of doing things, see usb_ether.c. When the bus -is scanned, all Ethernet devices will be created as generic USB devices (in -uclass UCLASS_USB_DEV_GENERIC). Then, when the scan is completed, -usb_host_eth_scan() will be called. This looks through all the devices on -each bus and manually figures out which are Ethernet devices in the ways of -yore. - -In fact, usb_ether should be moved to driver model. Each USB Ethernet driver -(e.g drivers/usb/eth/asix.c) should include a USB_DEVICE() declaration, so -that it will be found as part of normal USB enumeration. Then, instead of a -generic USB driver, a real (driver-model-aware) driver will be used. Since -Ethernet now supports driver model, this should be fairly easy to achieve, -and then usb_ether.c and the usb_host_eth_scan() will melt away. - - -Sandbox -------- - -All driver model uclasses must have tests and USB is no exception. To -achieve this, a sandbox USB controller is provided. This can make use of -emulation drivers which pretend to be USB devices. Emulations are provided -for a hub and a flash stick. These are enough to create a pretend USB bus -(defined by the sandbox device tree sandbox.dts) which can be scanned and -used. - -Tests in test/dm/usb.c make use of this feature. It allows much of the USB -stack to be tested without real hardware being needed. - -Here is an example device tree fragment: - -.. code-block:: none - - usb@1 { - compatible = "sandbox,usb"; - hub { - compatible = "usb-hub"; - usb,device-class = ; - hub-emul { - compatible = "sandbox,usb-hub"; - #address-cells = <1>; - #size-cells = <0>; - flash-stick { - reg = <0>; - compatible = "sandbox,usb-flash"; - sandbox,filepath = "flash.bin"; - }; - }; - }; - }; - -This defines a single controller, containing a root hub (which is required). -The hub is emulated by a hub emulator, and the emulated hub has a single -flash stick to emulate on one of its ports. - -When 'usb start' is used, the following 'dm tree' output will be available:: - - usb [ + ] `-- usb@1 - usb_hub [ + ] `-- hub - usb_emul [ + ] |-- hub-emul - usb_emul [ + ] | `-- flash-stick - usb_mass_st [ + ] `-- usb_mass_storage - - -This may look confusing. Most of it mirrors the device tree, but the -'usb_mass_storage' device is not in the device tree. This is created by -usb_find_and_bind_driver() based on the USB_DRIVER in usb_storage.c. While -'flash-stick' is the emulation device, 'usb_mass_storage' is the real U-Boot -USB device driver that talks to it. - - -Future work ------------ - -It is pretty uncommon to have a large USB bus with lots of hubs on an -embedded system. In fact anything other than a root hub is uncommon. Still -it would be possible to speed up enumeration in two ways: - -- breadth-first search would allow devices to be reset and probed in - parallel to some extent -- enumeration could be lazy, in the sense that we could enumerate just the - root hub at first, then only progress to the next 'level' when a device is - used that we cannot find. This could be made easier if the devices were - statically declared in the device tree (which is acceptable for production - boards where the same, known, things are on each bus). - -But in common cases the current algorithm is sufficient. - -Other things that need doing: -- Convert usb_ether to use driver model as described above -- Test that keyboards work (and convert to driver model) -- Move the USB gadget framework to driver model -- Implement OHCI in driver model -- Implement USB PHYs in driver model -- Work out a clever way to provide lazy init for USB devices - - -.. Simon Glass -.. 23-Mar-15 diff --git a/doc/index.rst b/doc/index.rst index 366963813ac..f7aada966b0 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -38,17 +38,6 @@ want to contribute to U-Boot. develop/index -Driver-Model documentation --------------------------- - -The following holds information on the U-Boot device driver framework: -driver-model, including the design details of itself and several driver -subsystems. - -.. toctree:: - :maxdepth: 2 - - driver-model/index U-Boot API documentation ------------------------ -- cgit v1.3.1 From 61adb2d2474eb72ea05365ef81e5c6d7e5f61441 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:13 +1300 Subject: binman: doc: Add documentation to htmldocs Add a link to binman's documentation and adjust the files so that it is accessible. Use the name README.rst so it is easy to discover when binman is installed without U-Boot. Signed-off-by: Simon Glass --- doc/develop/index.rst | 8 + doc/develop/package/binman.rst | 1 + doc/develop/package/index.rst | 19 + doc/usage/fit.rst | 8 + doc/usage/index.rst | 1 + tools/binman/README | 1138 --------------------------------------- tools/binman/README.rst | 1 + tools/binman/binman.rst | 1140 ++++++++++++++++++++++++++++++++++++++++ tools/binman/control.py | 2 +- tools/binman/ftest.py | 4 +- tools/binman/index.rst | 9 + tools/binman/setup.py | 2 +- 12 files changed, 1191 insertions(+), 1142 deletions(-) create mode 120000 doc/develop/package/binman.rst create mode 100644 doc/develop/package/index.rst create mode 100644 doc/usage/fit.rst delete mode 100644 tools/binman/README create mode 120000 tools/binman/README.rst create mode 100644 tools/binman/binman.rst create mode 100644 tools/binman/index.rst (limited to 'doc/develop') diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 444df679578..3edffbc6373 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -26,6 +26,14 @@ Debugging crash_dumps trace +Packaging +--------- + +.. toctree:: + :maxdepth: 1 + + package/index + Testing ------- diff --git a/doc/develop/package/binman.rst b/doc/develop/package/binman.rst new file mode 120000 index 00000000000..2e26e84b7d2 --- /dev/null +++ b/doc/develop/package/binman.rst @@ -0,0 +1 @@ +../../../tools/binman/binman.rst \ No newline at end of file diff --git a/doc/develop/package/index.rst b/doc/develop/package/index.rst new file mode 100644 index 00000000000..9374be2e62c --- /dev/null +++ b/doc/develop/package/index.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Package U-Boot +============== + +U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging +images that it it reads and boots. Documentation about FIT is available at +doc/uImage.FIT + +U-Boot also provides binman for cases not covered by FIT. Examples include +initial execution (since FIT itself does not have an executable header) and +dealing with device boundaries, such as the read-only/read-write separation in +SPI flash. + + +.. toctree:: + :maxdepth: 2 + + binman diff --git a/doc/usage/fit.rst b/doc/usage/fit.rst new file mode 100644 index 00000000000..70374340577 --- /dev/null +++ b/doc/usage/fit.rst @@ -0,0 +1,8 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Flat Image Tree (FIT) +===================== + +U-Boot uses Flat Image Tree (FIT) as a standard file format for packaging +images that it it reads and boots. Documentation about FIT is available at +doc/uImage.FIT diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 637b73ccab6..35c515f8b59 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -5,6 +5,7 @@ Use U-Boot :maxdepth: 1 fdt_overlays + fit netconsole partitions diff --git a/tools/binman/README b/tools/binman/README deleted file mode 100644 index 1de703cc653..00000000000 --- a/tools/binman/README +++ /dev/null @@ -1,1138 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -# Copyright (c) 2016 Google, Inc - -Introduction ------------- - -Firmware often consists of several components which must be packaged together. -For example, we may have SPL, U-Boot, a device tree and an environment area -grouped together and placed in MMC flash. When the system starts, it must be -able to find these pieces. - -So far U-Boot has not provided a way to handle creating such images in a -general way. Each SoC does what it needs to build an image, often packing or -concatenating images in the U-Boot build system. - -Binman aims to provide a mechanism for building images, from simple -SPL + U-Boot combinations, to more complex arrangements with many parts. - - -What it does ------------- - -Binman reads your board's device tree and finds a node which describes the -required image layout. It uses this to work out what to place where. The -output file normally contains the device tree, so it is in principle possible -to read an image and extract its constituent parts. - - -Features --------- - -So far binman is pretty simple. It supports binary blobs, such as 'u-boot', -'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can -place entries at a fixed location in the image, or fit them together with -suitable padding and alignment. It provides a way to process binaries before -they are included, by adding a Python plug-in. The device tree is available -to U-Boot at run-time so that the images can be interpreted. - -Binman can update the device tree with the final location of everything when it -is done. Entry positions can be provided to U-Boot SPL as run-time symbols, -avoiding device-tree code overhead. - -Binman can also support incorporating filesystems in the image if required. -For example x86 platforms may use CBFS in some cases. - -Binman is intended for use with U-Boot but is designed to be general enough -to be useful in other image-packaging situations. - - -Motivation ----------- - -Packaging of firmware is quite a different task from building the various -parts. In many cases the various binaries which go into the image come from -separate build systems. For example, ARM Trusted Firmware is used on ARMv8 -devices but is not built in the U-Boot tree. If a Linux kernel is included -in the firmware image, it is built elsewhere. - -It is of course possible to add more and more build rules to the U-Boot -build system to cover these cases. It can shell out to other Makefiles and -build scripts. But it seems better to create a clear divide between building -software and packaging it. - -At present this is handled by manual instructions, different for each board, -on how to create images that will boot. By turning these instructions into a -standard format, we can support making valid images for any board without -manual effort, lots of READMEs, etc. - -Benefits: -- Each binary can have its own build system and tool chain without creating -any dependencies between them -- Avoids the need for a single-shot build: individual parts can be updated -and brought in as needed -- Provides for a standard image description available in the build and at -run-time -- SoC-specific image-signing tools can be accommodated -- Avoids cluttering the U-Boot build system with image-building code -- The image description is automatically available at run-time in U-Boot, -SPL. It can be made available to other software also -- The image description is easily readable (it's a text file in device-tree -format) and permits flexible packing of binaries - - -Terminology ------------ - -Binman uses the following terms: - -- image - an output file containing a firmware image -- binary - an input binary that goes into the image - - -Relationship to FIT -------------------- - -FIT is U-Boot's official image format. It supports multiple binaries with -load / execution addresses, compression. It also supports verification -through hashing and RSA signatures. - -FIT was originally designed to support booting a Linux kernel (with an -optional ramdisk) and device tree chosen from various options in the FIT. -Now that U-Boot supports configuration via device tree, it is possible to -load U-Boot from a FIT, with the device tree chosen by SPL. - -Binman considers FIT to be one of the binaries it can place in the image. - -Where possible it is best to put as much as possible in the FIT, with binman -used to deal with cases not covered by FIT. Examples include initial -execution (since FIT itself does not have an executable header) and dealing -with device boundaries, such as the read-only/read-write separation in SPI -flash. - -For U-Boot, binman should not be used to create ad-hoc images in place of -FIT. - - -Relationship to mkimage ------------------------ - -The mkimage tool provides a means to create a FIT. Traditionally it has -needed an image description file: a device tree, like binman, but in a -different format. More recently it has started to support a '-f auto' mode -which can generate that automatically. - -More relevant to binman, mkimage also permits creation of many SoC-specific -image types. These can be listed by running 'mkimage -T list'. Examples -include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often -called from the U-Boot build system for this reason. - -Binman considers the output files created by mkimage to be binary blobs -which it can place in an image. Binman does not replace the mkimage tool or -this purpose. It would be possible in some situations to create a new entry -type for the images in mkimage, but this would not add functionality. It -seems better to use the mkimage tool to generate binaries and avoid blurring -the boundaries between building input files (mkimage) and packaging then -into a final image (binman). - - -Example use of binman in U-Boot -------------------------------- - -Binman aims to replace some of the ad-hoc image creation in the U-Boot -build system. - -Consider sunxi. It has the following steps: - -1. It uses a custom mksunxiboot tool to build an SPL image called -sunxi-spl.bin. This should probably move into mkimage. - -2. It uses mkimage to package U-Boot into a legacy image file (so that it can -hold the load and execution address) called u-boot.img. - -3. It builds a final output image called u-boot-sunxi-with-spl.bin which -consists of sunxi-spl.bin, some padding and u-boot.img. - -Binman is intended to replace the last step. The U-Boot build system builds -u-boot.bin and sunxi-spl.bin. Binman can then take over creation of -sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any -case, it would then create the image from the component parts. - -This simplifies the U-Boot Makefile somewhat, since various pieces of logic -can be replaced by a call to binman. - - -Example use of binman for x86 ------------------------------ - -In most cases x86 images have a lot of binary blobs, 'black-box' code -provided by Intel which must be run for the platform to work. Typically -these blobs are not relocatable and must be placed at fixed areas in the -firmware image. - -Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA -BIOS, reference code and Intel ME binaries into a u-boot.rom file. - -Binman is intended to replace all of this, with ifdtool left to handle only -the configuration of the Intel-format descriptor. - - -Running binman --------------- - -First install prerequisites, e.g. - - sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ - liblz4-tool - -Type: - - binman build -b - -to build an image for a board. The board name is the same name used when -configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). -Binman assumes that the input files for the build are in ../b/. - -Or you can specify this explicitly: - - binman build -I - -where is the build directory containing the output of the U-Boot -build. - -(Future work will make this more configurable) - -In either case, binman picks up the device tree file (u-boot.dtb) and looks -for its instructions in the 'binman' node. - -Binman has a few other options which you can see by running 'binman -h'. - - -Enabling binman for a board ---------------------------- - -At present binman is invoked from a rule in the main Makefile. Typically you -will have a rule like: - -ifneq ($(CONFIG_ARCH_),) -u-boot-.bin: checkbinman FORCE - $(call if_changed,binman) -endif - -This assumes that u-boot-.bin is a target, and is the final file -that you need to produce. You can make it a target by adding it to INPUTS-y -either in the main Makefile or in a config.mk file in your arch subdirectory. - -Once binman is executed it will pick up its instructions from a device-tree -file, typically -u-boot.dtsi, where is your CONFIG_SYS_SOC value. -You can use other, more specific CONFIG options - see 'Automatic .dtsi -inclusion' below. - - -Image description format ------------------------- - -The binman node is called 'binman'. An example image description is shown -below: - - binman { - filename = "u-boot-sunxi-with-spl.bin"; - pad-byte = <0xff>; - blob { - filename = "spl/sunxi-spl.bin"; - }; - u-boot { - offset = ; - }; - }; - - -This requests binman to create an image file called u-boot-sunxi-with-spl.bin -consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the -normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The -padding comes from the fact that the second binary is placed at -CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would -immediately follow the SPL binary. - -The binman node describes an image. The sub-nodes describe entries in the -image. Each entry represents a region within the overall image. The name of -the entry (blob, u-boot) tells binman what to put there. For 'blob' we must -provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'. - -Entries are normally placed into the image sequentially, one after the other. -The image size is the total size of all entries. As you can see, you can -specify the start offset of an entry using the 'offset' property. - -Note that due to a device tree requirement, all entries must have a unique -name. If you want to put the same binary in the image multiple times, you can -use any unique name, with the 'type' property providing the type. - -The attributes supported for entries are described below. - -offset: - This sets the offset of an entry within the image or section containing - it. The first byte of the image is normally at offset 0. If 'offset' is - not provided, binman sets it to the end of the previous region, or the - start of the image's entry area (normally 0) if there is no previous - region. - -align: - This sets the alignment of the entry. The entry offset is adjusted - so that the entry starts on an aligned boundary within the containing - section or image. For example 'align = <16>' means that the entry will - start on a 16-byte boundary. This may mean that padding is added before - the entry. The padding is part of the containing section but is not - included in the entry, meaning that an empty space may be created before - the entry starts. Alignment should be a power of 2. If 'align' is not - provided, no alignment is performed. - -size: - This sets the size of the entry. The contents will be padded out to - this size. If this is not provided, it will be set to the size of the - contents. - -pad-before: - Padding before the contents of the entry. Normally this is 0, meaning - that the contents start at the beginning of the entry. This can be used - to offset the entry contents a little. While this does not affect the - contents of the entry within binman itself (the padding is performed - only when its parent section is assembled), the end result will be that - the entry starts with the padding bytes, so may grow. Defaults to 0. - -pad-after: - Padding after the contents of the entry. Normally this is 0, meaning - that the entry ends at the last byte of content (unless adjusted by - other properties). This allows room to be created in the image for - this entry to expand later. While this does not affect the contents of - the entry within binman itself (the padding is performed only when its - parent section is assembled), the end result will be that the entry ends - with the padding bytes, so may grow. Defaults to 0. - -align-size: - This sets the alignment of the entry size. For example, to ensure - that the size of an entry is a multiple of 64 bytes, set this to 64. - While this does not affect the contents of the entry within binman - itself (the padding is performed only when its parent section is - assembled), the end result is that the entry ends with the padding - bytes, so may grow. If 'align-size' is not provided, no alignment is - performed. - -align-end: - This sets the alignment of the end of an entry with respect to the - containing section. Some entries require that they end on an alignment - boundary, regardless of where they start. This does not move the start - of the entry, so the contents of the entry will still start at the - beginning. But there may be padding at the end. While this does not - affect the contents of the entry within binman itself (the padding is - performed only when its parent section is assembled), the end result - is that the entry ends with the padding bytes, so may grow. - If 'align-end' is not provided, no alignment is performed. - -filename: - For 'blob' types this provides the filename containing the binary to - put into the entry. If binman knows about the entry type (like - u-boot-bin), then there is no need to specify this. - -type: - Sets the type of an entry. This defaults to the entry name, but it is - possible to use any name, and then add (for example) 'type = "u-boot"' - to specify the type. - -offset-unset: - Indicates that the offset of this entry should not be set by placing - it immediately after the entry before. Instead, is set by another - entry which knows where this entry should go. When this boolean - property is present, binman will give an error if another entry does - not set the offset (with the GetOffsets() method). - -image-pos: - This cannot be set on entry (or at least it is ignored if it is), but - with the -u option, binman will set it to the absolute image position - for each entry. This makes it easy to find out exactly where the entry - ended up in the image, regardless of parent sections, etc. - -expand-size: - Expand the size of this entry to fit available space. This space is only - limited by the size of the image/section and the position of the next - entry. - -compress: - Sets the compression algortihm to use (for blobs only). See the entry - documentation for details. - -missing-msg: - Sets the tag of the message to show if this entry is missing. This is - used for external blobs. When they are missing it is helpful to show - information about what needs to be fixed. See missing-blob-help for the - message for each tag. - -The attributes supported for images and sections are described below. Several -are similar to those for entries. - -size: - Sets the image size in bytes, for example 'size = <0x100000>' for a - 1MB image. - -offset: - This is similar to 'offset' in entries, setting the offset of a section - within the image or section containing it. The first byte of the section - is normally at offset 0. If 'offset' is not provided, binman sets it to - the end of the previous region, or the start of the image's entry area - (normally 0) if there is no previous region. - -align-size: - This sets the alignment of the image size. For example, to ensure - that the image ends on a 512-byte boundary, use 'align-size = <512>'. - If 'align-size' is not provided, no alignment is performed. - -pad-before: - This sets the padding before the image entries. The first entry will - be positioned after the padding. This defaults to 0. - -pad-after: - This sets the padding after the image entries. The padding will be - placed after the last entry. This defaults to 0. - -pad-byte: - This specifies the pad byte to use when padding in the image. It - defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. - -filename: - This specifies the image filename. It defaults to 'image.bin'. - -sort-by-offset: - This causes binman to reorder the entries as needed to make sure they - are in increasing positional order. This can be used when your entry - order may not match the positional order. A common situation is where - the 'offset' properties are set by CONFIG options, so their ordering is - not known a priori. - - This is a boolean property so needs no value. To enable it, add a - line 'sort-by-offset;' to your description. - -multiple-images: - Normally only a single image is generated. To create more than one - image, put this property in the binman node. For example, this will - create image1.bin containing u-boot.bin, and image2.bin containing - both spl/u-boot-spl.bin and u-boot.bin: - - binman { - multiple-images; - image1 { - u-boot { - }; - }; - - image2 { - spl { - }; - u-boot { - }; - }; - }; - -end-at-4gb: - For x86 machines the ROM offsets start just before 4GB and extend - up so that the image finished at the 4GB boundary. This boolean - option can be enabled to support this. The image size must be - provided so that binman knows when the image should start. For an - 8MB ROM, the offset of the first entry would be 0xfff80000 with - this option, instead of 0 without this option. - -skip-at-start: - This property specifies the entry offset of the first entry. - - For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry - offset of the first entry. It can be 0xeff40000 or 0xfff40000 for - nor flash boot, 0x201000 for sd boot etc. - - 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + - Image size != 4gb. - -Examples of the above options can be found in the tests. See the -tools/binman/test directory. - -It is possible to have the same binary appear multiple times in the image, -either by using a unit number suffix (u-boot@0, u-boot@1) or by using a -different name for each and specifying the type with the 'type' attribute. - - -Sections and hierachical images -------------------------------- - -Sometimes it is convenient to split an image into several pieces, each of which -contains its own set of binaries. An example is a flash device where part of -the image is read-only and part is read-write. We can set up sections for each -of these, and place binaries in them independently. The image is still produced -as a single output file. - -This feature provides a way of creating hierarchical images. For example here -is an example image with two copies of U-Boot. One is read-only (ro), intended -to be written only in the factory. Another is read-write (rw), so that it can be -upgraded in the field. The sizes are fixed so that the ro/rw boundary is known -and can be programmed: - - binman { - section@0 { - read-only; - name-prefix = "ro-"; - size = <0x100000>; - u-boot { - }; - }; - section@1 { - name-prefix = "rw-"; - size = <0x100000>; - u-boot { - }; - }; - }; - -This image could be placed into a SPI flash chip, with the protection boundary -set at 1MB. - -A few special properties are provided for sections: - -read-only: - Indicates that this section is read-only. This has no impact on binman's - operation, but his property can be read at run time. - -name-prefix: - This string is prepended to all the names of the binaries in the - section. In the example above, the 'u-boot' binaries which actually be - renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to - distinguish binaries with otherwise identical names. - - -Image Properties ----------------- - -Image nodes act like sections but also have a few extra properties: - -filename: - Output filename for the image. This defaults to image.bin (or in the - case of multiple images .bin where is the name of - the image node. - -allow-repack: - Create an image that can be repacked. With this option it is possible - to change anything in the image after it is created, including updating - the position and size of image components. By default this is not - permitted since it is not possibly to know whether this might violate a - constraint in the image description. For example, if a section has to - increase in size to hold a larger binary, that might cause the section - to fall out of its allow region (e.g. read-only portion of flash). - - Adding this property causes the original offset and size values in the - image description to be stored in the FDT and fdtmap. - - -Entry Documentation -------------------- - -For details on the various entry types supported by binman and how to use them, -see README.entries. This is generated from the source code using: - - binman entry-docs >tools/binman/README.entries - - -Listing images --------------- - -It is possible to list the entries in an existing firmware image created by -binman, provided that there is an 'fdtmap' entry in the image. For example: - - $ binman ls -i image.bin - Name Image-pos Size Entry-type Offset Uncomp-size - ---------------------------------------------------------------------- - main-section c00 section 0 - u-boot 0 4 u-boot 0 - section 5fc section 4 - cbfs 100 400 cbfs 0 - u-boot 138 4 u-boot 38 - u-boot-dtb 180 108 u-boot-dtb 80 3b5 - u-boot-dtb 500 1ff u-boot-dtb 400 3b5 - fdtmap 6fc 381 fdtmap 6fc - image-header bf8 8 image-header bf8 - -This shows the hierarchy of the image, the position, size and type of each -entry, the offset of each entry within its parent and the uncompressed size if -the entry is compressed. - -It is also possible to list just some files in an image, e.g. - - $ binman ls -i image.bin section/cbfs - Name Image-pos Size Entry-type Offset Uncomp-size - -------------------------------------------------------------------- - cbfs 100 400 cbfs 0 - u-boot 138 4 u-boot 38 - u-boot-dtb 180 108 u-boot-dtb 80 3b5 - -or with wildcards: - - $ binman ls -i image.bin "*cb*" "*head*" - Name Image-pos Size Entry-type Offset Uncomp-size - ---------------------------------------------------------------------- - cbfs 100 400 cbfs 0 - u-boot 138 4 u-boot 38 - u-boot-dtb 180 108 u-boot-dtb 80 3b5 - image-header bf8 8 image-header bf8 - - -Extracting files from images ----------------------------- - -You can extract files from an existing firmware image created by binman, -provided that there is an 'fdtmap' entry in the image. For example: - - $ binman extract -i image.bin section/cbfs/u-boot - -which will write the uncompressed contents of that entry to the file 'u-boot' in -the current directory. You can also extract to a particular file, in this case -u-boot.bin: - - $ binman extract -i image.bin section/cbfs/u-boot -f u-boot.bin - -It is possible to extract all files into a destination directory, which will -put files in subdirectories matching the entry hierarchy: - - $ binman extract -i image.bin -O outdir - -or just a selection: - - $ binman extract -i image.bin "*u-boot*" -O outdir - - -Replacing files in an image ---------------------------- - -You can replace files in an existing firmware image created by binman, provided -that there is an 'fdtmap' entry in the image. For example: - - $ binman replace -i image.bin section/cbfs/u-boot - -which will write the contents of the file 'u-boot' from the current directory -to the that entry, compressing if necessary. If the entry size changes, you must -add the 'allow-repack' property to the original image before generating it (see -above), otherwise you will get an error. - -You can also use a particular file, in this case u-boot.bin: - - $ binman replace -i image.bin section/cbfs/u-boot -f u-boot.bin - -It is possible to replace all files from a source directory which uses the same -hierarchy as the entries: - - $ binman replace -i image.bin -I indir - -Files that are missing will generate a warning. - -You can also replace just a selection of entries: - - $ binman replace -i image.bin "*u-boot*" -I indir - - -Logging -------- - -Binman normally operates silently unless there is an error, in which case it -just displays the error. The -D/--debug option can be used to create a full -backtrace when errors occur. You can use BINMAN_DEBUG=1 when building to select -this. - -Internally binman logs some output while it is running. This can be displayed -by increasing the -v/--verbosity from the default of 1: - - 0: silent - 1: warnings only - 2: notices (important messages) - 3: info about major operations - 4: detailed information about each operation - 5: debug (all output) - -You can use BINMAN_VERBOSE=5 (for example) when building to select this. - -Hashing Entries ---------------- - -It is possible to ask binman to hash the contents of an entry and write that -value back to the device-tree node. For example: - - binman { - u-boot { - hash { - algo = "sha256"; - }; - }; - }; - -Here, a new 'value' property will be written to the 'hash' node containing -the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole -sections can be hased if desired, by adding the 'hash' node to the section. - -The has value can be chcked at runtime by hashing the data actually read and -comparing this has to the value in the device tree. - - -Order of image creation ------------------------ - -Image creation proceeds in the following order, for each entry in the image. - -1. AddMissingProperties() - binman can add calculated values to the device -tree as part of its processing, for example the offset and size of each -entry. This method adds any properties associated with this, expanding the -device tree as needed. These properties can have placeholder values which are -set later by SetCalculatedProperties(). By that stage the size of sections -cannot be changed (since it would cause the images to need to be repacked), -but the correct values can be inserted. - -2. ProcessFdt() - process the device tree information as required by the -particular entry. This may involve adding or deleting properties. If the -processing is complete, this method should return True. If the processing -cannot complete because it needs the ProcessFdt() method of another entry to -run first, this method should return False, in which case it will be called -again later. - -3. GetEntryContents() - the contents of each entry are obtained, normally by -reading from a file. This calls the Entry.ObtainContents() to read the -contents. The default version of Entry.ObtainContents() calls -Entry.GetDefaultFilename() and then reads that file. So a common mechanism -to select a file to read is to override that function in the subclass. The -functions must return True when they have read the contents. Binman will -retry calling the functions a few times if False is returned, allowing -dependencies between the contents of different entries. - -4. GetEntryOffsets() - calls Entry.GetOffsets() for each entry. This can -return a dict containing entries that need updating. The key should be the -entry name and the value is a tuple (offset, size). This allows an entry to -provide the offset and size for other entries. The default implementation -of GetEntryOffsets() returns {}. - -5. PackEntries() - calls Entry.Pack() which figures out the offset and -size of an entry. The 'current' image offset is passed in, and the function -returns the offset immediately after the entry being packed. The default -implementation of Pack() is usually sufficient. - -Note: for sections, this also checks that the entries do not overlap, nor extend -outside the section. If the section does not have a defined size, the size is -set large enough to hold all the entries. - -6. SetImagePos() - sets the image position of every entry. This is the absolute -position 'image-pos', as opposed to 'offset' which is relative to the containing -section. This must be done after all offsets are known, which is why it is quite -late in the ordering. - -7. SetCalculatedProperties() - update any calculated properties in the device -tree. This sets the correct 'offset' and 'size' vaues, for example. - -8. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. -The default implementatoin does nothing. This can be overriden to adjust the -contents of an entry in some way. For example, it would be possible to create -an entry containing a hash of the contents of some other entries. At this -stage the offset and size of entries should not be adjusted unless absolutely -necessary, since it requires a repack (going back to PackEntries()). - -9. ResetForPack() - if the ProcessEntryContents() step failed, in that an entry -has changed its size, then there is no alternative but to go back to step 5 and -try again, repacking the entries with the updated size. ResetForPack() removes -the fixed offset/size values added by binman, so that the packing can start from -scratch. - -10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary. -See 'Access to binman entry offsets at run time' below for a description of -what happens in this stage. - -11. BuildImage() - builds the image and writes it to a file - -12. WriteMap() - writes a text file containing a map of the image. This is the -final step. - - -Automatic .dtsi inclusion -------------------------- - -It is sometimes inconvenient to add a 'binman' node to the .dts file for each -board. This can be done by using #include to bring in a common file. Another -approach supported by the U-Boot build system is to automatically include -a common header. You can then put the binman node (and anything else that is -specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header -file. - -Binman will search for the following files in arch//dts: - - -u-boot.dtsi where is the base name of the .dts file - -u-boot.dtsi - -u-boot.dtsi - -u-boot.dtsi - u-boot.dtsi - -U-Boot will only use the first one that it finds. If you need to include a -more general file you can do that from the more specific file using #include. -If you are having trouble figuring out what is going on, you can uncomment -the 'warning' line in scripts/Makefile.lib to see what it has found: - - # Uncomment for debugging - # This shows all the files that were considered and the one that we chose. - # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) - - -Access to binman entry offsets at run time (symbols) ----------------------------------------------------- - -Binman assembles images and determines where each entry is placed in the image. -This information may be useful to U-Boot at run time. For example, in SPL it -is useful to be able to find the location of U-Boot so that it can be executed -when SPL is finished. - -Binman allows you to declare symbols in the SPL image which are filled in -with their correct values during the build. For example: - - binman_sym_declare(ulong, u_boot_any, image_pos); - -declares a ulong value which will be assigned to the image-pos of any U-Boot -image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. -You can access this value with something like: - - ulong u_boot_offset = binman_sym(ulong, u_boot_any, image_pos); - -Thus u_boot_offset will be set to the image-pos of U-Boot in memory, assuming -that the whole image has been loaded, or is available in flash. You can then -jump to that address to start U-Boot. - -At present this feature is only supported in SPL and TPL. In principle it is -possible to fill in such symbols in U-Boot proper, as well, but a future C -library is planned for this instead, to read from the device tree. - -As well as image-pos, it is possible to read the size of an entry and its -offset (which is the start position of the entry within its parent). - -A small technical note: Binman automatically adds the base address of the image -(i.e. __image_copy_start) to the value of the image-pos symbol, so that when the -image is loaded to its linked address, the value will be correct and actually -point into the image. - -For example, say SPL is at the start of the image and linked to start at address -80108000. If U-Boot's image-pos is 0x8000 then binman will write an image-pos -for U-Boot of 80110000 into the SPL binary, since it assumes the image is loaded -to 80108000, with SPL at 80108000 and U-Boot at 80110000. - -For x86 devices (with the end-at-4gb property) this base address is not added -since it is assumed that images are XIP and the offsets already include the -address. - - -Access to binman entry offsets at run time (fdt) ------------------------------------------------- - -Binman can update the U-Boot FDT to include the final position and size of -each entry in the images it processes. The option to enable this is -u and it -causes binman to make sure that the 'offset', 'image-pos' and 'size' properties -are set correctly for every entry. Since it is not necessary to specify these in -the image definition, binman calculates the final values and writes these to -the device tree. These can be used by U-Boot at run-time to find the location -of each entry. - -Alternatively, an FDT map entry can be used to add a special FDT containing -just the information about the image. This is preceded by a magic string so can -be located anywhere in the image. An image header (typically at the start or end -of the image) can be used to point to the FDT map. See fdtmap and image-header -entries for more information. - - -Expanded entries ----------------- - -Binman automatically replaces 'u-boot' with an expanded version of that, i.e. -'u-boot-expanded'. This means that when you write: - - u-boot { - }; - -you actually get: - - u-boot { - type = "u-boot-expanded'; - }; - -which in turn expands to: - - u-boot { - type = "section"; - - u-boot-nodtb { - }; - - u-boot-dtb { - }; - }; - -U-Boot's various phase binaries actually comprise two or three pieces. -For example, u-boot.bin has the executable followed by a devicetree. - -With binman we want to be able to update that devicetree with full image -information so that it is accessible to the executable. This is tricky -if it is not clear where the devicetree starts. - -The above feature ensures that the devicetree is clearly separated from the -U-Boot executable and can be updated separately by binman as needed. It can be -disabled with the --no-expanded flag if required. - -The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion -includes the BSS padding, so for example: - - spl { - type = "u-boot-spl" - }; - -you actually get: - - spl { - type = "u-boot-expanded'; - }; - -which in turn expands to: - - spl { - type = "section"; - - u-boot-spl-nodtb { - }; - - u-boot-spl-bss-pad { - }; - - u-boot-spl-dtb { - }; - }; - - -Of course we should not expand SPL if it has no devicetree. Also if the BSS -padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), -the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned -entry type is controlled by the UseExpanded() method. In the SPL case it checks -the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. - -For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All -entry args are provided by the U-Boot Makefile. - - -Compression ------------ - -Binman support compression for 'blob' entries (those of type 'blob' and -derivatives). To enable this for an entry, add a 'compress' property: - - blob { - filename = "datafile"; - compress = "lz4"; - }; - -The entry will then contain the compressed data, using the 'lz4' compression -algorithm. Currently this is the only one that is supported. The uncompressed -size is written to the node in an 'uncomp-size' property, if -u is used. - -Compression is also supported for sections. In that case the entire section is -compressed in one block, including all its contents. This means that accessing -an entry from the section required decompressing the entire section. Also, the -size of a section indicates the space that it consumes in its parent section -(and typically the image). With compression, the section may contain more data, -and the uncomp-size property indicates that, as above. The contents of the -section is compressed first, before any padding is added. This ensures that the -padding itself is not compressed, which would be a waste of time. - - -Map files ---------- - -The -m option causes binman to output a .map file for each image that it -generates. This shows the offset and size of each entry. For example: - - Offset Size Name - 00000000 00000028 main-section - 00000000 00000010 section@0 - 00000000 00000004 u-boot - 00000010 00000010 section@1 - 00000000 00000004 u-boot - -This shows a hierarchical image with two sections, each with a single entry. The -offsets of the sections are absolute hex byte offsets within the image. The -offsets of the entries are relative to their respective sections. The size of -each entry is also shown, in bytes (hex). The indentation shows the entries -nested inside their sections. - - -Passing command-line arguments to entries ------------------------------------------ - -Sometimes it is useful to pass binman the value of an entry property from the -command line. For example some entries need access to files and it is not -always convenient to put these filenames in the image definition (device tree). - -The-a option supports this: - - -a= - -where - - is the property to set - is the value to set it to - -Not all properties can be provided this way. Only some entries support it, -typically for filenames. - - -External tools --------------- - -Binman can make use of external command-line tools to handle processing of -entry contents or to generate entry contents. These tools are executed using -the 'tools' module's Run() method. The tools generally must exist on the PATH, -but the --toolpath option can be used to specify additional search paths to -use. This option can be specified multiple times to add more than one path. - -For some compile tools binman will use the versions specified by commonly-used -environment variables like CC and HOSTCC for the C compiler, based on whether -the tool's output will be used for the target or for the host machine. If those -aren't given, it will also try to derive target-specific versions from the -CROSS_COMPILE environment variable during a cross-compilation. - - -Code coverage -------------- - -Binman is a critical tool and is designed to be very testable. Entry -implementations target 100% test coverage. Run 'binman test -T' to check this. - -To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): - - $ sudo apt-get install python-coverage python3-coverage python-pytest - - -Concurrent tests ----------------- - -Binman tries to run tests concurrently. This means that the tests make use of -all available CPUs to run. - - To enable this: - - $ sudo apt-get install python-subunit python3-subunit - -Use '-P 1' to disable this. It is automatically disabled when code coverage is -being used (-T) since they are incompatible. - - -Debugging tests ---------------- - -Sometimes when debugging tests it is useful to keep the input and output -directories so they can be examined later. Use -X or --test-preserve-dirs for -this. - - -Running tests on non-x86 architectures --------------------------------------- - -Binman's tests have been written under the assumption that they'll be run on a -x86-like host and there hasn't been an attempt to make them portable yet. -However, it's possible to run the tests by cross-compiling to x86. - -To install an x86 cross-compiler on Debian-type distributions (e.g. Ubuntu): - - $ sudo apt-get install gcc-x86-64-linux-gnu - -Then, you can run the tests under cross-compilation: - - $ CROSS_COMPILE=x86_64-linux-gnu- binman test -T - -You can also use gcc-i686-linux-gnu similar to the above. - - -Advanced Features / Technical docs ----------------------------------- - -The behaviour of entries is defined by the Entry class. All other entries are -a subclass of this. An important subclass is Entry_blob which takes binary -data from a file and places it in the entry. In fact most entry types are -subclasses of Entry_blob. - -Each entry type is a separate file in the tools/binman/etype directory. Each -file contains a class called Entry_ where is the entry type. -New entry types can be supported by adding new files in that directory. -These will automatically be detected by binman when needed. - -Entry properties are documented in entry.py. The entry subclasses are free -to change the values of properties to support special behaviour. For example, -when Entry_blob loads a file, it sets content_size to the size of the file. -Entry classes can adjust other entries. For example, an entry that knows -where other entries should be positioned can set up those entries' offsets -so they don't need to be set in the binman decription. It can also adjust -entry contents. - -Most of the time such essoteric behaviour is not needed, but it can be -essential for complex images. - -If you need to specify a particular device-tree compiler to use, you can define -the DTC environment variable. This can be useful when the system dtc is too -old. - -To enable a full backtrace and other debugging features in binman, pass -BINMAN_DEBUG=1 to your build: - - make qemu-x86_defconfig - make BINMAN_DEBUG=1 - -To enable verbose logging from binman, base BINMAN_VERBOSE to your build, which -adds a -v option to the call to binman: - - make qemu-x86_defconfig - make BINMAN_VERBOSE=5 - - -History / Credits ------------------ - -Binman takes a lot of inspiration from a Chrome OS tool called -'cros_bundle_firmware', which I wrote some years ago. That tool was based on -a reasonably simple and sound design but has expanded greatly over the -years. In particular its handling of x86 images is convoluted. - -Quite a few lessons have been learned which are hopefully applied here. - - -Design notes ------------- - -On the face of it, a tool to create firmware images should be fairly simple: -just find all the input binaries and place them at the right place in the -image. The difficulty comes from the wide variety of input types (simple -flat binaries containing code, packaged data with various headers), packing -requirments (alignment, spacing, device boundaries) and other required -features such as hierarchical images. - -The design challenge is to make it easy to create simple images, while -allowing the more complex cases to be supported. For example, for most -images we don't much care exactly where each binary ends up, so we should -not have to specify that unnecessarily. - -New entry types should aim to provide simple usage where possible. If new -core features are needed, they can be added in the Entry base class. - - -To do ------ - -Some ideas: -- Use of-platdata to make the information available to code that is unable - to use device tree (such as a very small SPL image) -- Allow easy building of images by specifying just the board name -- Support building an image for a board (-b) more completely, with a - configurable build directory -- Detect invalid properties in nodes -- Sort the fdtmap by offset -- Output temporary files to a different directory - --- -Simon Glass -7/7/2016 diff --git a/tools/binman/README.rst b/tools/binman/README.rst new file mode 120000 index 00000000000..b734f544b73 --- /dev/null +++ b/tools/binman/README.rst @@ -0,0 +1 @@ +binman.rst \ No newline at end of file diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst new file mode 100644 index 00000000000..fd6308b6e42 --- /dev/null +++ b/tools/binman/binman.rst @@ -0,0 +1,1140 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright (c) 2016 Google, Inc + +Introduction +------------ + +Firmware often consists of several components which must be packaged together. +For example, we may have SPL, U-Boot, a device tree and an environment area +grouped together and placed in MMC flash. When the system starts, it must be +able to find these pieces. + +So far U-Boot has not provided a way to handle creating such images in a +general way. Each SoC does what it needs to build an image, often packing or +concatenating images in the U-Boot build system. + +Binman aims to provide a mechanism for building images, from simple +SPL + U-Boot combinations, to more complex arrangements with many parts. + + +What it does +------------ + +Binman reads your board's device tree and finds a node which describes the +required image layout. It uses this to work out what to place where. The +output file normally contains the device tree, so it is in principle possible +to read an image and extract its constituent parts. + + +Features +-------- + +So far binman is pretty simple. It supports binary blobs, such as 'u-boot', +'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can +place entries at a fixed location in the image, or fit them together with +suitable padding and alignment. It provides a way to process binaries before +they are included, by adding a Python plug-in. The device tree is available +to U-Boot at run-time so that the images can be interpreted. + +Binman can update the device tree with the final location of everything when it +is done. Entry positions can be provided to U-Boot SPL as run-time symbols, +avoiding device-tree code overhead. + +Binman can also support incorporating filesystems in the image if required. +For example x86 platforms may use CBFS in some cases. + +Binman is intended for use with U-Boot but is designed to be general enough +to be useful in other image-packaging situations. + + +Motivation +---------- + +Packaging of firmware is quite a different task from building the various +parts. In many cases the various binaries which go into the image come from +separate build systems. For example, ARM Trusted Firmware is used on ARMv8 +devices but is not built in the U-Boot tree. If a Linux kernel is included +in the firmware image, it is built elsewhere. + +It is of course possible to add more and more build rules to the U-Boot +build system to cover these cases. It can shell out to other Makefiles and +build scripts. But it seems better to create a clear divide between building +software and packaging it. + +At present this is handled by manual instructions, different for each board, +on how to create images that will boot. By turning these instructions into a +standard format, we can support making valid images for any board without +manual effort, lots of READMEs, etc. + +Benefits: + + - Each binary can have its own build system and tool chain without creating + any dependencies between them + - Avoids the need for a single-shot build: individual parts can be updated + and brought in as needed + - Provides for a standard image description available in the build and at + run-time + - SoC-specific image-signing tools can be accommodated + - Avoids cluttering the U-Boot build system with image-building code + - The image description is automatically available at run-time in U-Boot, + SPL. It can be made available to other software also + - The image description is easily readable (it's a text file in device-tree + format) and permits flexible packing of binaries + + +Terminology +----------- + +Binman uses the following terms: + +- image - an output file containing a firmware image +- binary - an input binary that goes into the image + + +Relationship to FIT +------------------- + +FIT is U-Boot's official image format. It supports multiple binaries with +load / execution addresses, compression. It also supports verification +through hashing and RSA signatures. + +FIT was originally designed to support booting a Linux kernel (with an +optional ramdisk) and device tree chosen from various options in the FIT. +Now that U-Boot supports configuration via device tree, it is possible to +load U-Boot from a FIT, with the device tree chosen by SPL. + +Binman considers FIT to be one of the binaries it can place in the image. + +Where possible it is best to put as much as possible in the FIT, with binman +used to deal with cases not covered by FIT. Examples include initial +execution (since FIT itself does not have an executable header) and dealing +with device boundaries, such as the read-only/read-write separation in SPI +flash. + +For U-Boot, binman should not be used to create ad-hoc images in place of +FIT. + + +Relationship to mkimage +----------------------- + +The mkimage tool provides a means to create a FIT. Traditionally it has +needed an image description file: a device tree, like binman, but in a +different format. More recently it has started to support a '-f auto' mode +which can generate that automatically. + +More relevant to binman, mkimage also permits creation of many SoC-specific +image types. These can be listed by running 'mkimage -T list'. Examples +include 'rksd', the Rockchip SD/MMC boot format. The mkimage tool is often +called from the U-Boot build system for this reason. + +Binman considers the output files created by mkimage to be binary blobs +which it can place in an image. Binman does not replace the mkimage tool or +this purpose. It would be possible in some situations to create a new entry +type for the images in mkimage, but this would not add functionality. It +seems better to use the mkimage tool to generate binaries and avoid blurring +the boundaries between building input files (mkimage) and packaging then +into a final image (binman). + + +Example use of binman in U-Boot +------------------------------- + +Binman aims to replace some of the ad-hoc image creation in the U-Boot +build system. + +Consider sunxi. It has the following steps: + + #. It uses a custom mksunxiboot tool to build an SPL image called + sunxi-spl.bin. This should probably move into mkimage. + + #. It uses mkimage to package U-Boot into a legacy image file (so that it can + hold the load and execution address) called u-boot.img. + + #. It builds a final output image called u-boot-sunxi-with-spl.bin which + consists of sunxi-spl.bin, some padding and u-boot.img. + +Binman is intended to replace the last step. The U-Boot build system builds +u-boot.bin and sunxi-spl.bin. Binman can then take over creation of +sunxi-spl.bin (by calling mksunxiboot, or hopefully one day mkimage). In any +case, it would then create the image from the component parts. + +This simplifies the U-Boot Makefile somewhat, since various pieces of logic +can be replaced by a call to binman. + + +Example use of binman for x86 +----------------------------- + +In most cases x86 images have a lot of binary blobs, 'black-box' code +provided by Intel which must be run for the platform to work. Typically +these blobs are not relocatable and must be placed at fixed areas in the +firmware image. + +Currently this is handled by ifdtool, which places microcode, FSP, MRC, VGA +BIOS, reference code and Intel ME binaries into a u-boot.rom file. + +Binman is intended to replace all of this, with ifdtool left to handle only +the configuration of the Intel-format descriptor. + + +Running binman +-------------- + +First install prerequisites, e.g:: + + sudo apt-get install python-pyelftools python3-pyelftools lzma-alone \ + liblz4-tool + +Type:: + + binman build -b + +to build an image for a board. The board name is the same name used when +configuring U-Boot (e.g. for sandbox_defconfig the board name is 'sandbox'). +Binman assumes that the input files for the build are in ../b/. + +Or you can specify this explicitly:: + + binman build -I + +where is the build directory containing the output of the U-Boot +build. + +(Future work will make this more configurable) + +In either case, binman picks up the device tree file (u-boot.dtb) and looks +for its instructions in the 'binman' node. + +Binman has a few other options which you can see by running 'binman -h'. + + +Enabling binman for a board +--------------------------- + +At present binman is invoked from a rule in the main Makefile. Typically you +will have a rule like:: + + ifneq ($(CONFIG_ARCH_),) + u-boot-.bin: checkbinman FORCE + $(call if_changed,binman) + endif + +This assumes that u-boot-.bin is a target, and is the final file +that you need to produce. You can make it a target by adding it to INPUTS-y +either in the main Makefile or in a config.mk file in your arch subdirectory. + +Once binman is executed it will pick up its instructions from a device-tree +file, typically -u-boot.dtsi, where is your CONFIG_SYS_SOC value. +You can use other, more specific CONFIG options - see 'Automatic .dtsi +inclusion' below. + + +Image description format +------------------------ + +The binman node is called 'binman'. An example image description is shown +below:: + + binman { + filename = "u-boot-sunxi-with-spl.bin"; + pad-byte = <0xff>; + blob { + filename = "spl/sunxi-spl.bin"; + }; + u-boot { + offset = ; + }; + }; + + +This requests binman to create an image file called u-boot-sunxi-with-spl.bin +consisting of a specially formatted SPL (spl/sunxi-spl.bin, built by the +normal U-Boot Makefile), some 0xff padding, and a U-Boot legacy image. The +padding comes from the fact that the second binary is placed at +CONFIG_SPL_PAD_TO. If that line were omitted then the U-Boot binary would +immediately follow the SPL binary. + +The binman node describes an image. The sub-nodes describe entries in the +image. Each entry represents a region within the overall image. The name of +the entry (blob, u-boot) tells binman what to put there. For 'blob' we must +provide a filename. For 'u-boot', binman knows that this means 'u-boot.bin'. + +Entries are normally placed into the image sequentially, one after the other. +The image size is the total size of all entries. As you can see, you can +specify the start offset of an entry using the 'offset' property. + +Note that due to a device tree requirement, all entries must have a unique +name. If you want to put the same binary in the image multiple times, you can +use any unique name, with the 'type' property providing the type. + +The attributes supported for entries are described below. + +offset: + This sets the offset of an entry within the image or section containing + it. The first byte of the image is normally at offset 0. If 'offset' is + not provided, binman sets it to the end of the previous region, or the + start of the image's entry area (normally 0) if there is no previous + region. + +align: + This sets the alignment of the entry. The entry offset is adjusted + so that the entry starts on an aligned boundary within the containing + section or image. For example 'align = <16>' means that the entry will + start on a 16-byte boundary. This may mean that padding is added before + the entry. The padding is part of the containing section but is not + included in the entry, meaning that an empty space may be created before + the entry starts. Alignment should be a power of 2. If 'align' is not + provided, no alignment is performed. + +size: + This sets the size of the entry. The contents will be padded out to + this size. If this is not provided, it will be set to the size of the + contents. + +pad-before: + Padding before the contents of the entry. Normally this is 0, meaning + that the contents start at the beginning of the entry. This can be used + to offset the entry contents a little. While this does not affect the + contents of the entry within binman itself (the padding is performed + only when its parent section is assembled), the end result will be that + the entry starts with the padding bytes, so may grow. Defaults to 0. + +pad-after: + Padding after the contents of the entry. Normally this is 0, meaning + that the entry ends at the last byte of content (unless adjusted by + other properties). This allows room to be created in the image for + this entry to expand later. While this does not affect the contents of + the entry within binman itself (the padding is performed only when its + parent section is assembled), the end result will be that the entry ends + with the padding bytes, so may grow. Defaults to 0. + +align-size: + This sets the alignment of the entry size. For example, to ensure + that the size of an entry is a multiple of 64 bytes, set this to 64. + While this does not affect the contents of the entry within binman + itself (the padding is performed only when its parent section is + assembled), the end result is that the entry ends with the padding + bytes, so may grow. If 'align-size' is not provided, no alignment is + performed. + +align-end: + This sets the alignment of the end of an entry with respect to the + containing section. Some entries require that they end on an alignment + boundary, regardless of where they start. This does not move the start + of the entry, so the contents of the entry will still start at the + beginning. But there may be padding at the end. While this does not + affect the contents of the entry within binman itself (the padding is + performed only when its parent section is assembled), the end result + is that the entry ends with the padding bytes, so may grow. + If 'align-end' is not provided, no alignment is performed. + +filename: + For 'blob' types this provides the filename containing the binary to + put into the entry. If binman knows about the entry type (like + u-boot-bin), then there is no need to specify this. + +type: + Sets the type of an entry. This defaults to the entry name, but it is + possible to use any name, and then add (for example) 'type = "u-boot"' + to specify the type. + +offset-unset: + Indicates that the offset of this entry should not be set by placing + it immediately after the entry before. Instead, is set by another + entry which knows where this entry should go. When this boolean + property is present, binman will give an error if another entry does + not set the offset (with the GetOffsets() method). + +image-pos: + This cannot be set on entry (or at least it is ignored if it is), but + with the -u option, binman will set it to the absolute image position + for each entry. This makes it easy to find out exactly where the entry + ended up in the image, regardless of parent sections, etc. + +expand-size: + Expand the size of this entry to fit available space. This space is only + limited by the size of the image/section and the position of the next + entry. + +compress: + Sets the compression algortihm to use (for blobs only). See the entry + documentation for details. + +missing-msg: + Sets the tag of the message to show if this entry is missing. This is + used for external blobs. When they are missing it is helpful to show + information about what needs to be fixed. See missing-blob-help for the + message for each tag. + +The attributes supported for images and sections are described below. Several +are similar to those for entries. + +size: + Sets the image size in bytes, for example 'size = <0x100000>' for a + 1MB image. + +offset: + This is similar to 'offset' in entries, setting the offset of a section + within the image or section containing it. The first byte of the section + is normally at offset 0. If 'offset' is not provided, binman sets it to + the end of the previous region, or the start of the image's entry area + (normally 0) if there is no previous region. + +align-size: + This sets the alignment of the image size. For example, to ensure + that the image ends on a 512-byte boundary, use 'align-size = <512>'. + If 'align-size' is not provided, no alignment is performed. + +pad-before: + This sets the padding before the image entries. The first entry will + be positioned after the padding. This defaults to 0. + +pad-after: + This sets the padding after the image entries. The padding will be + placed after the last entry. This defaults to 0. + +pad-byte: + This specifies the pad byte to use when padding in the image. It + defaults to 0. To use 0xff, you would add 'pad-byte = <0xff>'. + +filename: + This specifies the image filename. It defaults to 'image.bin'. + +sort-by-offset: + This causes binman to reorder the entries as needed to make sure they + are in increasing positional order. This can be used when your entry + order may not match the positional order. A common situation is where + the 'offset' properties are set by CONFIG options, so their ordering is + not known a priori. + + This is a boolean property so needs no value. To enable it, add a + line 'sort-by-offset;' to your description. + +multiple-images: + Normally only a single image is generated. To create more than one + image, put this property in the binman node. For example, this will + create image1.bin containing u-boot.bin, and image2.bin containing + both spl/u-boot-spl.bin and u-boot.bin:: + + binman { + multiple-images; + image1 { + u-boot { + }; + }; + + image2 { + spl { + }; + u-boot { + }; + }; + }; + +end-at-4gb: + For x86 machines the ROM offsets start just before 4GB and extend + up so that the image finished at the 4GB boundary. This boolean + option can be enabled to support this. The image size must be + provided so that binman knows when the image should start. For an + 8MB ROM, the offset of the first entry would be 0xfff80000 with + this option, instead of 0 without this option. + +skip-at-start: + This property specifies the entry offset of the first entry. + + For PowerPC mpc85xx based CPU, CONFIG_SYS_TEXT_BASE is the entry + offset of the first entry. It can be 0xeff40000 or 0xfff40000 for + nor flash boot, 0x201000 for sd boot etc. + + 'end-at-4gb' property is not applicable where CONFIG_SYS_TEXT_BASE + + Image size != 4gb. + +Examples of the above options can be found in the tests. See the +tools/binman/test directory. + +It is possible to have the same binary appear multiple times in the image, +either by using a unit number suffix (u-boot@0, u-boot@1) or by using a +different name for each and specifying the type with the 'type' attribute. + + +Sections and hierachical images +------------------------------- + +Sometimes it is convenient to split an image into several pieces, each of which +contains its own set of binaries. An example is a flash device where part of +the image is read-only and part is read-write. We can set up sections for each +of these, and place binaries in them independently. The image is still produced +as a single output file. + +This feature provides a way of creating hierarchical images. For example here +is an example image with two copies of U-Boot. One is read-only (ro), intended +to be written only in the factory. Another is read-write (rw), so that it can be +upgraded in the field. The sizes are fixed so that the ro/rw boundary is known +and can be programmed:: + + binman { + section@0 { + read-only; + name-prefix = "ro-"; + size = <0x100000>; + u-boot { + }; + }; + section@1 { + name-prefix = "rw-"; + size = <0x100000>; + u-boot { + }; + }; + }; + +This image could be placed into a SPI flash chip, with the protection boundary +set at 1MB. + +A few special properties are provided for sections: + +read-only: + Indicates that this section is read-only. This has no impact on binman's + operation, but his property can be read at run time. + +name-prefix: + This string is prepended to all the names of the binaries in the + section. In the example above, the 'u-boot' binaries which actually be + renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to + distinguish binaries with otherwise identical names. + + +Image Properties +---------------- + +Image nodes act like sections but also have a few extra properties: + +filename: + Output filename for the image. This defaults to image.bin (or in the + case of multiple images .bin where is the name of + the image node. + +allow-repack: + Create an image that can be repacked. With this option it is possible + to change anything in the image after it is created, including updating + the position and size of image components. By default this is not + permitted since it is not possibly to know whether this might violate a + constraint in the image description. For example, if a section has to + increase in size to hold a larger binary, that might cause the section + to fall out of its allow region (e.g. read-only portion of flash). + + Adding this property causes the original offset and size values in the + image description to be stored in the FDT and fdtmap. + + +Entry Documentation +------------------- + +For details on the various entry types supported by binman and how to use them, +see README.entries. This is generated from the source code using: + + binman entry-docs >tools/binman/README.entries + + +Listing images +-------------- + +It is possible to list the entries in an existing firmware image created by +binman, provided that there is an 'fdtmap' entry in the image. For example:: + + $ binman ls -i image.bin + Name Image-pos Size Entry-type Offset Uncomp-size + ---------------------------------------------------------------------- + main-section c00 section 0 + u-boot 0 4 u-boot 0 + section 5fc section 4 + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + u-boot-dtb 500 1ff u-boot-dtb 400 3b5 + fdtmap 6fc 381 fdtmap 6fc + image-header bf8 8 image-header bf8 + +This shows the hierarchy of the image, the position, size and type of each +entry, the offset of each entry within its parent and the uncompressed size if +the entry is compressed. + +It is also possible to list just some files in an image, e.g.:: + + $ binman ls -i image.bin section/cbfs + Name Image-pos Size Entry-type Offset Uncomp-size + -------------------------------------------------------------------- + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + +or with wildcards:: + + $ binman ls -i image.bin "*cb*" "*head*" + Name Image-pos Size Entry-type Offset Uncomp-size + ---------------------------------------------------------------------- + cbfs 100 400 cbfs 0 + u-boot 138 4 u-boot 38 + u-boot-dtb 180 108 u-boot-dtb 80 3b5 + image-header bf8 8 image-header bf8 + + +Extracting files from images +---------------------------- + +You can extract files from an existing firmware image created by binman, +provided that there is an 'fdtmap' entry in the image. For example:: + + $ binman extract -i image.bin section/cbfs/u-boot + +which will write the uncompressed contents of that entry to the file 'u-boot' in +the current directory. You can also extract to a particular file, in this case +u-boot.bin:: + + $ binman extract -i image.bin section/cbfs/u-boot -f u-boot.bin + +It is possible to extract all files into a destination directory, which will +put files in subdirectories matching the entry hierarchy:: + + $ binman extract -i image.bin -O outdir + +or just a selection:: + + $ binman extract -i image.bin "*u-boot*" -O outdir + + +Replacing files in an image +--------------------------- + +You can replace files in an existing firmware image created by binman, provided +that there is an 'fdtmap' entry in the image. For example: + + $ binman replace -i image.bin section/cbfs/u-boot + +which will write the contents of the file 'u-boot' from the current directory +to the that entry, compressing if necessary. If the entry size changes, you must +add the 'allow-repack' property to the original image before generating it (see +above), otherwise you will get an error. + +You can also use a particular file, in this case u-boot.bin:: + + $ binman replace -i image.bin section/cbfs/u-boot -f u-boot.bin + +It is possible to replace all files from a source directory which uses the same +hierarchy as the entries:: + + $ binman replace -i image.bin -I indir + +Files that are missing will generate a warning. + +You can also replace just a selection of entries:: + + $ binman replace -i image.bin "*u-boot*" -I indir + + +Logging +------- + +Binman normally operates silently unless there is an error, in which case it +just displays the error. The -D/--debug option can be used to create a full +backtrace when errors occur. You can use BINMAN_DEBUG=1 when building to select +this. + +Internally binman logs some output while it is running. This can be displayed +by increasing the -v/--verbosity from the default of 1: + + 0: silent + 1: warnings only + 2: notices (important messages) + 3: info about major operations + 4: detailed information about each operation + 5: debug (all output) + +You can use BINMAN_VERBOSE=5 (for example) when building to select this. + +Hashing Entries +--------------- + +It is possible to ask binman to hash the contents of an entry and write that +value back to the device-tree node. For example:: + + binman { + u-boot { + hash { + algo = "sha256"; + }; + }; + }; + +Here, a new 'value' property will be written to the 'hash' node containing +the hash of the 'u-boot' entry. Only SHA256 is supported at present. Whole +sections can be hased if desired, by adding the 'hash' node to the section. + +The has value can be chcked at runtime by hashing the data actually read and +comparing this has to the value in the device tree. + + +Order of image creation +----------------------- + +Image creation proceeds in the following order, for each entry in the image. + +1. AddMissingProperties() - binman can add calculated values to the device +tree as part of its processing, for example the offset and size of each +entry. This method adds any properties associated with this, expanding the +device tree as needed. These properties can have placeholder values which are +set later by SetCalculatedProperties(). By that stage the size of sections +cannot be changed (since it would cause the images to need to be repacked), +but the correct values can be inserted. + +2. ProcessFdt() - process the device tree information as required by the +particular entry. This may involve adding or deleting properties. If the +processing is complete, this method should return True. If the processing +cannot complete because it needs the ProcessFdt() method of another entry to +run first, this method should return False, in which case it will be called +again later. + +3. GetEntryContents() - the contents of each entry are obtained, normally by +reading from a file. This calls the Entry.ObtainContents() to read the +contents. The default version of Entry.ObtainContents() calls +Entry.GetDefaultFilename() and then reads that file. So a common mechanism +to select a file to read is to override that function in the subclass. The +functions must return True when they have read the contents. Binman will +retry calling the functions a few times if False is returned, allowing +dependencies between the contents of different entries. + +4. GetEntryOffsets() - calls Entry.GetOffsets() for each entry. This can +return a dict containing entries that need updating. The key should be the +entry name and the value is a tuple (offset, size). This allows an entry to +provide the offset and size for other entries. The default implementation +of GetEntryOffsets() returns {}. + +5. PackEntries() - calls Entry.Pack() which figures out the offset and +size of an entry. The 'current' image offset is passed in, and the function +returns the offset immediately after the entry being packed. The default +implementation of Pack() is usually sufficient. + +Note: for sections, this also checks that the entries do not overlap, nor extend +outside the section. If the section does not have a defined size, the size is +set large enough to hold all the entries. + +6. SetImagePos() - sets the image position of every entry. This is the absolute +position 'image-pos', as opposed to 'offset' which is relative to the containing +section. This must be done after all offsets are known, which is why it is quite +late in the ordering. + +7. SetCalculatedProperties() - update any calculated properties in the device +tree. This sets the correct 'offset' and 'size' vaues, for example. + +8. ProcessEntryContents() - this calls Entry.ProcessContents() on each entry. +The default implementatoin does nothing. This can be overriden to adjust the +contents of an entry in some way. For example, it would be possible to create +an entry containing a hash of the contents of some other entries. At this +stage the offset and size of entries should not be adjusted unless absolutely +necessary, since it requires a repack (going back to PackEntries()). + +9. ResetForPack() - if the ProcessEntryContents() step failed, in that an entry +has changed its size, then there is no alternative but to go back to step 5 and +try again, repacking the entries with the updated size. ResetForPack() removes +the fixed offset/size values added by binman, so that the packing can start from +scratch. + +10. WriteSymbols() - write the value of symbols into the U-Boot SPL binary. +See 'Access to binman entry offsets at run time' below for a description of +what happens in this stage. + +11. BuildImage() - builds the image and writes it to a file + +12. WriteMap() - writes a text file containing a map of the image. This is the +final step. + + +Automatic .dtsi inclusion +------------------------- + +It is sometimes inconvenient to add a 'binman' node to the .dts file for each +board. This can be done by using #include to bring in a common file. Another +approach supported by the U-Boot build system is to automatically include +a common header. You can then put the binman node (and anything else that is +specific to U-Boot, such as u-boot,dm-pre-reloc properies) in that header +file. + +Binman will search for the following files in arch//dts:: + + -u-boot.dtsi where is the base name of the .dts file + -u-boot.dtsi + -u-boot.dtsi + -u-boot.dtsi + u-boot.dtsi + +U-Boot will only use the first one that it finds. If you need to include a +more general file you can do that from the more specific file using #include. +If you are having trouble figuring out what is going on, you can uncomment +the 'warning' line in scripts/Makefile.lib to see what it has found:: + + # Uncomment for debugging + # This shows all the files that were considered and the one that we chose. + # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) + + +Access to binman entry offsets at run time (symbols) +---------------------------------------------------- + +Binman assembles images and determines where each entry is placed in the image. +This information may be useful to U-Boot at run time. For example, in SPL it +is useful to be able to find the location of U-Boot so that it can be executed +when SPL is finished. + +Binman allows you to declare symbols in the SPL image which are filled in +with their correct values during the build. For example:: + + binman_sym_declare(ulong, u_boot_any, image_pos); + +declares a ulong value which will be assigned to the image-pos of any U-Boot +image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. +You can access this value with something like:: + + ulong u_boot_offset = binman_sym(ulong, u_boot_any, image_pos); + +Thus u_boot_offset will be set to the image-pos of U-Boot in memory, assuming +that the whole image has been loaded, or is available in flash. You can then +jump to that address to start U-Boot. + +At present this feature is only supported in SPL and TPL. In principle it is +possible to fill in such symbols in U-Boot proper, as well, but a future C +library is planned for this instead, to read from the device tree. + +As well as image-pos, it is possible to read the size of an entry and its +offset (which is the start position of the entry within its parent). + +A small technical note: Binman automatically adds the base address of the image +(i.e. __image_copy_start) to the value of the image-pos symbol, so that when the +image is loaded to its linked address, the value will be correct and actually +point into the image. + +For example, say SPL is at the start of the image and linked to start at address +80108000. If U-Boot's image-pos is 0x8000 then binman will write an image-pos +for U-Boot of 80110000 into the SPL binary, since it assumes the image is loaded +to 80108000, with SPL at 80108000 and U-Boot at 80110000. + +For x86 devices (with the end-at-4gb property) this base address is not added +since it is assumed that images are XIP and the offsets already include the +address. + + +Access to binman entry offsets at run time (fdt) +------------------------------------------------ + +Binman can update the U-Boot FDT to include the final position and size of +each entry in the images it processes. The option to enable this is -u and it +causes binman to make sure that the 'offset', 'image-pos' and 'size' properties +are set correctly for every entry. Since it is not necessary to specify these in +the image definition, binman calculates the final values and writes these to +the device tree. These can be used by U-Boot at run-time to find the location +of each entry. + +Alternatively, an FDT map entry can be used to add a special FDT containing +just the information about the image. This is preceded by a magic string so can +be located anywhere in the image. An image header (typically at the start or end +of the image) can be used to point to the FDT map. See fdtmap and image-header +entries for more information. + + +Expanded entries +---------------- + +Binman automatically replaces 'u-boot' with an expanded version of that, i.e. +'u-boot-expanded'. This means that when you write:: + + u-boot { + }; + +you actually get:: + + u-boot { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + u-boot { + type = "section"; + + u-boot-nodtb { + }; + + u-boot-dtb { + }; + }; + +U-Boot's various phase binaries actually comprise two or three pieces. +For example, u-boot.bin has the executable followed by a devicetree. + +With binman we want to be able to update that devicetree with full image +information so that it is accessible to the executable. This is tricky +if it is not clear where the devicetree starts. + +The above feature ensures that the devicetree is clearly separated from the +U-Boot executable and can be updated separately by binman as needed. It can be +disabled with the --no-expanded flag if required. + +The same applies for u-boot-spl and u-boot-spl. In those cases, the expansion +includes the BSS padding, so for example:: + + spl { + type = "u-boot-spl" + }; + +you actually get:: + + spl { + type = "u-boot-expanded'; + }; + +which in turn expands to:: + + spl { + type = "section"; + + u-boot-spl-nodtb { + }; + + u-boot-spl-bss-pad { + }; + + u-boot-spl-dtb { + }; + }; + + +Of course we should not expand SPL if it has no devicetree. Also if the BSS +padding is not needed (because BSS is in RAM as with CONFIG_SPL_SEPARATE_BSS), +the 'u-boot-spl-bss-pad' subnode should not be created. The use of the expaned +entry type is controlled by the UseExpanded() method. In the SPL case it checks +the 'spl-dtb' entry arg, which is 'y' or '1' if SPL has a devicetree. + +For the BSS case, a 'spl-bss-pad' entry arg controls whether it is present. All +entry args are provided by the U-Boot Makefile. + + +Compression +----------- + +Binman support compression for 'blob' entries (those of type 'blob' and +derivatives). To enable this for an entry, add a 'compress' property:: + + blob { + filename = "datafile"; + compress = "lz4"; + }; + +The entry will then contain the compressed data, using the 'lz4' compression +algorithm. Currently this is the only one that is supported. The uncompressed +size is written to the node in an 'uncomp-size' property, if -u is used. + +Compression is also supported for sections. In that case the entire section is +compressed in one block, including all its contents. This means that accessing +an entry from the section required decompressing the entire section. Also, the +size of a section indicates the space that it consumes in its parent section +(and typically the image). With compression, the section may contain more data, +and the uncomp-size property indicates that, as above. The contents of the +section is compressed first, before any padding is added. This ensures that the +padding itself is not compressed, which would be a waste of time. + + +Map files +--------- + +The -m option causes binman to output a .map file for each image that it +generates. This shows the offset and size of each entry. For example:: + + Offset Size Name + 00000000 00000028 main-section + 00000000 00000010 section@0 + 00000000 00000004 u-boot + 00000010 00000010 section@1 + 00000000 00000004 u-boot + +This shows a hierarchical image with two sections, each with a single entry. The +offsets of the sections are absolute hex byte offsets within the image. The +offsets of the entries are relative to their respective sections. The size of +each entry is also shown, in bytes (hex). The indentation shows the entries +nested inside their sections. + + +Passing command-line arguments to entries +----------------------------------------- + +Sometimes it is useful to pass binman the value of an entry property from the +command line. For example some entries need access to files and it is not +always convenient to put these filenames in the image definition (device tree). + +The-a option supports this:: + + -a= + +where:: + + is the property to set + is the value to set it to + +Not all properties can be provided this way. Only some entries support it, +typically for filenames. + + +External tools +-------------- + +Binman can make use of external command-line tools to handle processing of +entry contents or to generate entry contents. These tools are executed using +the 'tools' module's Run() method. The tools generally must exist on the PATH, +but the --toolpath option can be used to specify additional search paths to +use. This option can be specified multiple times to add more than one path. + +For some compile tools binman will use the versions specified by commonly-used +environment variables like CC and HOSTCC for the C compiler, based on whether +the tool's output will be used for the target or for the host machine. If those +aren't given, it will also try to derive target-specific versions from the +CROSS_COMPILE environment variable during a cross-compilation. + + +Code coverage +------------- + +Binman is a critical tool and is designed to be very testable. Entry +implementations target 100% test coverage. Run 'binman test -T' to check this. + +To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):: + + $ sudo apt-get install python-coverage python3-coverage python-pytest + + +Concurrent tests +---------------- + +Binman tries to run tests concurrently. This means that the tests make use of +all available CPUs to run. + + To enable this:: + + $ sudo apt-get install python-subunit python3-subunit + +Use '-P 1' to disable this. It is automatically disabled when code coverage is +being used (-T) since they are incompatible. + + +Debugging tests +--------------- + +Sometimes when debugging tests it is useful to keep the input and output +directories so they can be examined later. Use -X or --test-preserve-dirs for +this. + + +Running tests on non-x86 architectures +-------------------------------------- + +Binman's tests have been written under the assumption that they'll be run on a +x86-like host and there hasn't been an attempt to make them portable yet. +However, it's possible to run the tests by cross-compiling to x86. + +To install an x86 cross-compiler on Debian-type distributions (e.g. Ubuntu):: + + $ sudo apt-get install gcc-x86-64-linux-gnu + +Then, you can run the tests under cross-compilation:: + + $ CROSS_COMPILE=x86_64-linux-gnu- binman test -T + +You can also use gcc-i686-linux-gnu similar to the above. + + +Advanced Features / Technical docs +---------------------------------- + +The behaviour of entries is defined by the Entry class. All other entries are +a subclass of this. An important subclass is Entry_blob which takes binary +data from a file and places it in the entry. In fact most entry types are +subclasses of Entry_blob. + +Each entry type is a separate file in the tools/binman/etype directory. Each +file contains a class called Entry_ where is the entry type. +New entry types can be supported by adding new files in that directory. +These will automatically be detected by binman when needed. + +Entry properties are documented in entry.py. The entry subclasses are free +to change the values of properties to support special behaviour. For example, +when Entry_blob loads a file, it sets content_size to the size of the file. +Entry classes can adjust other entries. For example, an entry that knows +where other entries should be positioned can set up those entries' offsets +so they don't need to be set in the binman decription. It can also adjust +entry contents. + +Most of the time such essoteric behaviour is not needed, but it can be +essential for complex images. + +If you need to specify a particular device-tree compiler to use, you can define +the DTC environment variable. This can be useful when the system dtc is too +old. + +To enable a full backtrace and other debugging features in binman, pass +BINMAN_DEBUG=1 to your build:: + + make qemu-x86_defconfig + make BINMAN_DEBUG=1 + +To enable verbose logging from binman, base BINMAN_VERBOSE to your build, which +adds a -v option to the call to binman:: + + make qemu-x86_defconfig + make BINMAN_VERBOSE=5 + + +History / Credits +----------------- + +Binman takes a lot of inspiration from a Chrome OS tool called +'cros_bundle_firmware', which I wrote some years ago. That tool was based on +a reasonably simple and sound design but has expanded greatly over the +years. In particular its handling of x86 images is convoluted. + +Quite a few lessons have been learned which are hopefully applied here. + + +Design notes +------------ + +On the face of it, a tool to create firmware images should be fairly simple: +just find all the input binaries and place them at the right place in the +image. The difficulty comes from the wide variety of input types (simple +flat binaries containing code, packaged data with various headers), packing +requirments (alignment, spacing, device boundaries) and other required +features such as hierarchical images. + +The design challenge is to make it easy to create simple images, while +allowing the more complex cases to be supported. For example, for most +images we don't much care exactly where each binary ends up, so we should +not have to specify that unnecessarily. + +New entry types should aim to provide simple usage where possible. If new +core features are needed, they can be added in the Entry base class. + + +To do +----- + +Some ideas: + +- Use of-platdata to make the information available to code that is unable + to use device tree (such as a very small SPL image) +- Allow easy building of images by specifying just the board name +- Support building an image for a board (-b) more completely, with a + configurable build directory +- Detect invalid properties in nodes +- Sort the fdtmap by offset +- Output temporary files to a different directory + +-- +Simon Glass +7/7/2016 diff --git a/tools/binman/control.py b/tools/binman/control.py index 9709aa9a2b2..f57e34daaaa 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -569,7 +569,7 @@ def Binman(args): if not pager: pager = 'more' fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), - 'README') + 'README.rst') command.Run(pager, fname) return 0 diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 432c463b58b..81c213a908a 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -631,7 +631,7 @@ class TestFunctional(unittest.TestCase): def testFullHelp(self): """Test that the full help is displayed with -H""" result = self._RunBinman('-H') - help_file = os.path.join(self._binman_dir, 'README') + help_file = os.path.join(self._binman_dir, 'README.rst') # Remove possible extraneous strings extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n' gothelp = result.stdout.replace(extra, '') @@ -644,7 +644,7 @@ class TestFunctional(unittest.TestCase): try: command.test_result = command.CommandResult() result = self._DoBinman('-H') - help_file = os.path.join(self._binman_dir, 'README') + help_file = os.path.join(self._binman_dir, 'README.rst') finally: command.test_result = None diff --git a/tools/binman/index.rst b/tools/binman/index.rst new file mode 100644 index 00000000000..6eef7b5d050 --- /dev/null +++ b/tools/binman/index.rst @@ -0,0 +1,9 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Binman +====== + +.. toctree:: + :maxdepth: 2 + + README diff --git a/tools/binman/setup.py b/tools/binman/setup.py index fe408ed6911..2dad43d4937 100644 --- a/tools/binman/setup.py +++ b/tools/binman/setup.py @@ -7,6 +7,6 @@ setup(name='binman', scripts=['binman'], packages=['binman', 'binman.etype'], package_dir={'binman': ''}, - package_data={'binman': ['README', 'README.entries']}, + package_data={'binman': ['README.rst', 'README.entries']}, classifiers=['Environment :: Console', 'Topic :: Software Development :: Embedded Systems']) -- cgit v1.3.1 From fcae6682a7929abf88d9f0b756f9d5f9725e4d14 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 18 Mar 2021 20:25:17 +1300 Subject: binman: Update various pieces of the documentation A few sections are a little out of date now. Update them. Signed-off-by: Simon Glass --- doc/develop/package/entries.rst | 1 + tools/binman/binman.rst | 83 ++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 39 deletions(-) create mode 120000 doc/develop/package/entries.rst (limited to 'doc/develop') diff --git a/doc/develop/package/entries.rst b/doc/develop/package/entries.rst new file mode 120000 index 00000000000..ecedcebaad4 --- /dev/null +++ b/doc/develop/package/entries.rst @@ -0,0 +1 @@ +../../../tools/binman/entries.rst \ No newline at end of file diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index 1cdc9de752a..15314d19586 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -9,39 +9,43 @@ For example, we may have SPL, U-Boot, a device tree and an environment area grouped together and placed in MMC flash. When the system starts, it must be able to find these pieces. -So far U-Boot has not provided a way to handle creating such images in a -general way. Each SoC does what it needs to build an image, often packing or -concatenating images in the U-Boot build system. - -Binman aims to provide a mechanism for building images, from simple -SPL + U-Boot combinations, to more complex arrangements with many parts. +Building firmware should be separate from packaging it. Many of the complexities +of modern firmware build systems come from trying to do both at once. With +binman, you build all the pieces that are needed, using whatever assortment of +projects and build systems are needed, then use binman to stitch everything +together. What it does ------------ Binman reads your board's device tree and finds a node which describes the -required image layout. It uses this to work out what to place where. The -output file normally contains the device tree, so it is in principle possible -to read an image and extract its constituent parts. +required image layout. It uses this to work out what to place where. + +Binman provides a mechanism for building images, from simple SPL + U-Boot +combinations, to more complex arrangements with many parts. It also allows +users to inspect images, extract and replace binaries within them, repacking if +needed. Features -------- -So far binman is pretty simple. It supports binary blobs, such as 'u-boot', -'spl' and 'fdt'. It supports empty entries (such as setting to 0xff). It can -place entries at a fixed location in the image, or fit them together with -suitable padding and alignment. It provides a way to process binaries before -they are included, by adding a Python plug-in. The device tree is available -to U-Boot at run-time so that the images can be interpreted. +Apart from basic padding, alignment and positioning features, Binman supports +hierarchical images, compression, hashing and dealing with the binary blobs +which are a sad trend in open-source firmware at present. -Binman can update the device tree with the final location of everything when it -is done. Entry positions can be provided to U-Boot SPL as run-time symbols, -avoiding device-tree code overhead. +Executable binaries can access the location of other binaries in an image by +using special linker symbols (zero-overhead but somewhat limited) or by reading +the devicetree description of the image. -Binman can also support incorporating filesystems in the image if required. -For example x86 platforms may use CBFS in some cases. +Binman is designed primarily for use with U-Boot and associated binaries such +as ARM Trusted Firmware, but it is suitable for use with other projects, such +as Zephyr. Binman also provides facilities useful in Chromium OS, such as CBFS, +vblocks and and the like. + +Binman provides a way to process binaries before they are included, by adding a +Python plug-in. Binman is intended for use with U-Boot but is designed to be general enough to be useful in other image-packaging situations. @@ -50,11 +54,11 @@ to be useful in other image-packaging situations. Motivation ---------- -Packaging of firmware is quite a different task from building the various -parts. In many cases the various binaries which go into the image come from -separate build systems. For example, ARM Trusted Firmware is used on ARMv8 -devices but is not built in the U-Boot tree. If a Linux kernel is included -in the firmware image, it is built elsewhere. +As mentioned above, packaging of firmware is quite a different task from +building the various parts. In many cases the various binaries which go into +the image come from separate build systems. For example, ARM Trusted Firmware +is used on ARMv8 devices but is not built in the U-Boot tree. If a Linux kernel +is included in the firmware image, it is built elsewhere. It is of course possible to add more and more build rules to the U-Boot build system to cover these cases. It can shell out to other Makefiles and @@ -215,17 +219,12 @@ Binman has a few other options which you can see by running 'binman -h'. Enabling binman for a board --------------------------- -At present binman is invoked from a rule in the main Makefile. Typically you -will have a rule like:: - - ifneq ($(CONFIG_ARCH_),) - u-boot-.bin: checkbinman FORCE - $(call if_changed,binman) - endif +At present binman is invoked from a rule in the main Makefile. You should be +able to enable CONFIG_BINMAN to enable this rule. -This assumes that u-boot-.bin is a target, and is the final file -that you need to produce. You can make it a target by adding it to INPUTS-y -either in the main Makefile or in a config.mk file in your arch subdirectory. +The output file is typically named image.bin and is located in the output +directory. If input files are needed to you add these to INPUTS-y either in the +main Makefile or in a config.mk file in your arch subdirectory. Once binman is executed it will pick up its instructions from a device-tree file, typically -u-boot.dtsi, where is your CONFIG_SYS_SOC value. @@ -786,12 +785,17 @@ the 'warning' line in scripts/Makefile.lib to see what it has found:: Entry Documentation -------------------- +=================== For details on the various entry types supported by binman and how to use them, -see README.entries. This is generated from the source code using: +see entries.rst which is generated from the source code using: + + binman entry-docs >tools/binman/entries.rst + +.. toctree:: + :maxdepth: 2 - binman entry-docs >tools/binman/README.entries + entries Managing images @@ -1136,7 +1140,8 @@ To do Some ideas: - Use of-platdata to make the information available to code that is unable - to use device tree (such as a very small SPL image) + to use device tree (such as a very small SPL image). For now, limited info is + available via linker symbols - Allow easy building of images by specifying just the board name - Support building an image for a board (-b) more completely, with a configurable build directory -- cgit v1.3.1