Merge tag 'u-boot-imx-20191009' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
authorTom Rini <trini@konsulko.com>
Wed, 9 Oct 2019 13:35:43 +0000 (09:35 -0400)
committerTom Rini <trini@konsulko.com>
Wed, 9 Oct 2019 15:44:45 +0000 (11:44 -0400)
u-boot-imx-20191009
-------------------

Travis : https://travis-ci.org/sbabic/u-boot-imx/builds/595148532

- MX6UL / ULZ
- Toradex board
- Allow to set OCRAM for MX6Q/D
- MX7ULP
- MX8: (container image, imx8mq_mek), SCU API
- fix several board booting from SD/EMMC (cubox-i for example)
- pico boards

[trini: display5 merged manually]
Signed-off-by: Tom Rini <trini@konsulko.com>
239 files changed:
.gitlab-ci.yml
.travis.yml
Makefile
arch/arm/Kconfig
arch/arm/cpu/arm926ejs/Makefile
arch/arm/cpu/armv8/fsl-layerscape/doc/README.lsch2
arch/arm/dts/Makefile
arch/arm/dts/at91-sama5d27_wlsom1_ek-u-boot.dtsi [new file with mode: 0644]
arch/arm/dts/at91-sama5d27_wlsom1_ek.dts [new file with mode: 0644]
arch/arm/dts/sam9x60.dtsi [new file with mode: 0644]
arch/arm/dts/sam9x60ek-u-boot.dtsi [new file with mode: 0644]
arch/arm/dts/sam9x60ek.dts [new file with mode: 0644]
arch/arm/dts/sama5d2.dtsi
arch/arm/dts/sama5d27_wlsom1.dtsi [new file with mode: 0644]
arch/arm/mach-at91/Kconfig
arch/arm/mach-at91/Makefile
arch/arm/mach-at91/arm926ejs/Makefile
arch/arm/mach-at91/arm926ejs/sam9x60_devices.c [new file with mode: 0644]
arch/arm/mach-at91/armv7/sama5d2_devices.c
arch/arm/mach-at91/armv7/sama5d4_devices.c
arch/arm/mach-at91/atmel_sfr.c
arch/arm/mach-at91/include/mach/at91_common.h
arch/arm/mach-at91/include/mach/at91_sfr.h [new file with mode: 0644]
arch/arm/mach-at91/include/mach/atmel_mpddrc.h
arch/arm/mach-at91/include/mach/hardware.h
arch/arm/mach-at91/include/mach/sam9x60.h [new file with mode: 0644]
arch/arm/mach-at91/include/mach/sama5_sfr.h [deleted file]
arch/arm/mach-at91/include/mach/sama5d2.h
arch/arm/mach-at91/mpddrc.c
arch/arm/mach-omap2/Kconfig
arch/arm/mach-rmobile/Kconfig
arch/arm/mach-rmobile/Kconfig.32
arch/arm/mach-socfpga/Kconfig
arch/arm/mach-tegra/board2.c
arch/arm/mach-uniphier/pinctrl-glue.c
arch/sandbox/cpu/cpu.c
arch/sandbox/cpu/spl.c
arch/sandbox/cpu/start.c
arch/sandbox/dts/sandbox.dtsi
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/io.h
arch/sandbox/include/asm/state.h
arch/sandbox/include/asm/test.h
arch/sandbox/lib/pci_io.c
arch/x86/Kconfig
arch/x86/cpu/Makefile
arch/x86/cpu/baytrail/acpi.c
arch/x86/cpu/baytrail/cpu.c
arch/x86/cpu/baytrail/fsp_configs.c
arch/x86/cpu/braswell/fsp_configs.c
arch/x86/cpu/broadwell/cpu.c
arch/x86/cpu/broadwell/cpu_full.c
arch/x86/cpu/cpu.c
arch/x86/cpu/intel_common/cpu.c
arch/x86/cpu/intel_common/cpu_from_spl.c
arch/x86/cpu/ivybridge/cpu.c
arch/x86/cpu/ivybridge/fsp_configs.c
arch/x86/cpu/ivybridge/model_206ax.c
arch/x86/cpu/ivybridge/northbridge.c
arch/x86/cpu/mtrr.c
arch/x86/cpu/pci.c
arch/x86/cpu/queensbay/fsp_configs.c
arch/x86/cpu/queensbay/tnc.c
arch/x86/cpu/start.S
arch/x86/cpu/start_from_spl.S
arch/x86/cpu/start_from_tpl.S
arch/x86/cpu/turbo.c
arch/x86/cpu/u-boot-spl.lds
arch/x86/cpu/wakeup.S
arch/x86/include/asm/acpi_s3.h [deleted file]
arch/x86/include/asm/arch-broadwell/cpu.h
arch/x86/include/asm/arch-broadwell/pch.h
arch/x86/include/asm/arch-ivybridge/model_206ax.h
arch/x86/include/asm/cpu_common.h
arch/x86/include/asm/fsp/fsp_api.h [deleted file]
arch/x86/include/asm/fsp/fsp_ffs.h [deleted file]
arch/x86/include/asm/fsp/fsp_hob.h
arch/x86/include/asm/fsp/fsp_infoheader.h
arch/x86/include/asm/fsp/fsp_support.h
arch/x86/include/asm/fsp1/fsp_api.h [new file with mode: 0644]
arch/x86/include/asm/fsp1/fsp_ffs.h [new file with mode: 0644]
arch/x86/include/asm/fsp1/fsp_support.h [new file with mode: 0644]
arch/x86/include/asm/fsp_arch.h [new file with mode: 0644]
arch/x86/include/asm/handoff.h
arch/x86/include/asm/hob.h
arch/x86/include/asm/msr-index.h
arch/x86/include/asm/mtrr.h
arch/x86/include/asm/pci.h
arch/x86/include/asm/spl.h
arch/x86/include/asm/u-boot-x86.h
arch/x86/lib/Makefile
arch/x86/lib/acpi_s3.c
arch/x86/lib/coreboot_table.c
arch/x86/lib/fsp/Makefile
arch/x86/lib/fsp/fsp_car.S [deleted file]
arch/x86/lib/fsp/fsp_common.c
arch/x86/lib/fsp/fsp_dram.c
arch/x86/lib/fsp/fsp_graphics.c [deleted file]
arch/x86/lib/fsp/fsp_support.c
arch/x86/lib/fsp1/Makefile [new file with mode: 0644]
arch/x86/lib/fsp1/fsp_car.S [new file with mode: 0644]
arch/x86/lib/fsp1/fsp_common.c [new file with mode: 0644]
arch/x86/lib/fsp1/fsp_dram.c [new file with mode: 0644]
arch/x86/lib/fsp1/fsp_graphics.c [new file with mode: 0644]
arch/x86/lib/fsp1/fsp_support.c [new file with mode: 0644]
arch/x86/lib/hob.c
arch/x86/lib/init_helpers.c
arch/x86/lib/lpc-uclass.c
arch/x86/lib/spl.c
arch/x86/lib/tpl.c
board/advantech/som-db5800-som-6867/som-db5800-som-6867.c
board/atmel/common/video_display.c
board/atmel/sam9x60ek/Kconfig [new file with mode: 0644]
board/atmel/sam9x60ek/MAINTAINERS [new file with mode: 0644]
board/atmel/sam9x60ek/Makefile [new file with mode: 0644]
board/atmel/sam9x60ek/sam9x60ek.c [new file with mode: 0644]
board/atmel/sama5d27_wlsom1_ek/Kconfig [new file with mode: 0644]
board/atmel/sama5d27_wlsom1_ek/MAINTAINERS [new file with mode: 0644]
board/atmel/sama5d27_wlsom1_ek/Makefile [new file with mode: 0644]
board/atmel/sama5d27_wlsom1_ek/sama5d27_wlsom1_ek.c [new file with mode: 0644]
board/intel/cherryhill/cherryhill.c
board/laird/wb50n/wb50n.c
cmd/Kconfig
cmd/io.c
cmd/nvedit_efi.c
cmd/x86/fsp.c
common/spl/Kconfig
common/spl/spl.c
common/xyzModem.c
configs/am335x_pdu001_defconfig
configs/dh_imx6_defconfig
configs/display5_defconfig
configs/display5_factory_defconfig
configs/evb-rk3288_defconfig
configs/ge_bx50v3_defconfig
configs/kp_imx6q_tpc_defconfig
configs/ls1043ardb_nand_SECURE_BOOT_defconfig
configs/ls1043ardb_nand_defconfig
configs/ls1043ardb_sdcard_SECURE_BOOT_defconfig
configs/ls1043ardb_sdcard_defconfig
configs/m53menlo_defconfig
configs/mx53ppd_defconfig
configs/sam9x60ek_mmc_defconfig [new file with mode: 0644]
configs/sam9x60ek_nandflash_defconfig [new file with mode: 0644]
configs/sam9x60ek_qspiflash_defconfig [new file with mode: 0644]
configs/sama5d27_wlsom1_ek_mmc_defconfig [new file with mode: 0644]
configs/sama5d27_wlsom1_ek_qspiflash_defconfig [new file with mode: 0644]
configs/sandbox_spl_defconfig
configs/slimbootloader_defconfig
configs/tinker-rk3288_defconfig
configs/tqma6s_wru4_mmc_defconfig
configs/warp_defconfig
doc/arch/sandbox.rst
doc/driver-model/debugging.rst [new file with mode: 0644]
doc/driver-model/index.rst
doc/driver-model/pci-info.rst
drivers/block/blk-uclass.c
drivers/clk/at91/clk-utmi.c
drivers/core/device.c
drivers/core/fdtaddr.c
drivers/core/lists.c
drivers/core/read.c
drivers/core/uclass.c
drivers/gpio/gpio-uclass.c
drivers/gpio/pm8916_gpio.c
drivers/misc/Makefile
drivers/misc/swap_case.c
drivers/mmc/atmel_sdhci.c
drivers/mmc/mmc.c
drivers/mmc/sandbox_mmc.c
drivers/mtd/spi/sf-uclass.c
drivers/net/macb.c
drivers/nvme/nvme.c
drivers/nvme/nvme.h
drivers/pch/pch-uclass.c
drivers/pci/pci-emul-uclass.c
drivers/pci/pci-uclass.c
drivers/pci/pci_auto.c
drivers/pci/pci_auto_common.c
drivers/pci/pci_rom.c
drivers/pci/pci_x86.c
drivers/serial/ns16550.c
drivers/serial/sandbox.c
drivers/sysreset/Kconfig
drivers/sysreset/Makefile
drivers/sysreset/sysreset_x86.c
drivers/watchdog/Kconfig
include/acpi_s3.h [new file with mode: 0644]
include/configs/dh_imx6.h
include/configs/display5.h
include/configs/ge_bx50v3.h
include/configs/kp_imx6q_tpc.h
include/configs/m53menlo.h
include/configs/mx53ppd.h
include/configs/mxs.h
include/configs/sam9x60ek.h [new file with mode: 0644]
include/configs/sama5d27_wlsom1_ek.h [new file with mode: 0644]
include/configs/socfpga_common.h
include/configs/socfpga_stratix10_socdk.h
include/configs/tqma6_wru4.h
include/configs/warp.h
include/dm/device.h
include/dm/fdtaddr.h
include/dm/read.h
include/dm/uclass-id.h
include/dm/uclass-internal.h
include/ec_commands.h
include/efi_api.h
include/efi_loader.h
include/fdtdec.h
include/handoff.h
include/linux/mtd/spi-nor.h
include/log.h
include/ns16550.h
include/nvme.h
include/pci.h
include/spl.h
lib/Kconfig
lib/Makefile
lib/display_options.c
lib/efi_loader/efi_device_path.c
lib/efi_loader/efi_device_path_to_text.c
lib/efi_loader/efi_variable.c
lib/efi_selftest/efi_selftest_variables.c
lib/fdtdec.c
scripts/config_whitelist.txt
test/dm/core.c
test/dm/pci.c
test/dm/test-main.c
tools/binman/README
tools/binman/control.py
tools/binman/entry.py
tools/binman/etype/image_header.py
tools/binman/etype/section.py
tools/binman/image.py
tools/buildman/func_test.py
tools/buildman/kconfiglib.py
tools/genboardscfg.py
tools/moveconfig.py

index 0c4343468d1fbd2281b5d0ac4f0e5b0da8f07d1e..967abed9f250006045e847463fcf74199869354d 100644 (file)
@@ -2,7 +2,7 @@
 
 # Grab our configured image.  The source for this is found at:
 # https://gitlab.denx.de/u-boot/gitlab-ci-runner
-image: trini/u-boot-gitlab-ci-runner:xenial-20190720-02Aug2019
+image: trini/u-boot-gitlab-ci-runner:bionic-20190912.1-03Oct2019
 
 # We run some tests in different order, to catch some failures quicker.
 stages:
@@ -22,8 +22,9 @@ stages:
     - . /tmp/venv/bin/activate
     - pip install pytest==2.8.7
     - pip install python-subunit
-    - grub-mkimage -o ~/grub_x86.efi -O i386-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
-    - grub-mkimage -o ~/grub_x64.efi -O x86_64-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
+    - pip install coverage
+    - grub-mkimage --prefix="" -o ~/grub_x86.efi -O i386-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
+    - grub-mkimage --prefix="" -o ~/grub_x64.efi -O x86_64-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
     - mkdir ~/grub2-arm
     - ( cd ~/grub2-arm; wget -O - http://download.opensuse.org/ports/armv7hl/distribution/leap/42.2/repo/oss/suse/armv7hl/grub2-arm-efi-2.02~beta2-87.1.armv7hl.rpm | rpm2cpio | cpio -di )
     - mkdir ~/grub2-arm64
@@ -36,9 +37,9 @@ stages:
     # use clang only do one configuration.
     - if [[ "${BUILDMAN}" != "" ]]; then
         ret=0;
-        tools/buildman/buildman -P -E ${BUILDMAN} ${OVERRIDE}|| ret=$?;
+        tools/buildman/buildman -o /tmp -P -E ${BUILDMAN} ${OVERRIDE}|| ret=$?;
         if [[ $ret -ne 0 && $ret -ne 129 ]]; then
-          tools/buildman/buildman -sdeP ${BUILDMAN};
+          tools/buildman/buildman -o /tmp -sdeP ${BUILDMAN};
           exit $ret;
         fi;
       fi
@@ -46,7 +47,7 @@ stages:
     # never prevent any test from running. That way, we can always pass
     # "-k something" even when $TEST_PY_TEST_SPEC doesnt need a custom
     # value.
-    - export UBOOT_TRAVIS_BUILD_DIR=`cd .. && pwd`/.bm-work/${TEST_PY_BD};
+    - export UBOOT_TRAVIS_BUILD_DIR=/tmp/.bm-work/${TEST_PY_BD};
       export PATH=/opt/qemu/bin:/tmp/uboot-test-hooks/bin:/usr/bin:/bin;
       export PYTHONPATH=/tmp/uboot-test-hooks/py/travis-ci;
       if [[ "${TEST_PY_BD}" != "" ]]; then
@@ -64,9 +65,9 @@ build all 32bit ARM platforms:
   stage: world build
   script:
     - ret=0;
-     ./tools/buildman/buildman -P -E arm -x aarch64 || ret=$?;
+     ./tools/buildman/buildman -o /tmp -P -E arm -x aarch64 || ret=$?;
      if [[ $ret -ne 0 && $ret -ne 129 ]]; then
-       ./tools/buildman/buildman -sdeP;
+       ./tools/buildman/buildman -o /tmp -sdeP;
        exit $ret;
      fi;
 
@@ -78,9 +79,9 @@ build all 64bit ARM platforms:
     - . /tmp/venv/bin/activate
     - pip install pyelftools
     - ret=0;
-     ./tools/buildman/buildman -P -E aarch64 || ret=$?;
+     ./tools/buildman/buildman -o /tmp -P -E aarch64 || ret=$?;
      if [[ $ret -ne 0 && $ret -ne 129 ]]; then
-       ./tools/buildman/buildman -sdeP;
+       ./tools/buildman/buildman -o /tmp -sdeP;
        exit $ret;
      fi;
 
@@ -89,9 +90,9 @@ build all PowerPC platforms:
   stage: world build
   script:
     - ret=0;
-     ./tools/buildman/buildman -P -E powerpc || ret=$?;
+     ./tools/buildman/buildman -o /tmp -P -E powerpc || ret=$?;
      if [[ $ret -ne 0 && $ret -ne 129 ]]; then
-       ./tools/buildman/buildman -sdeP;
+       ./tools/buildman/buildman -o /tmp -sdeP;
        exit $ret;
      fi;
 
@@ -100,9 +101,9 @@ build all other platforms:
   stage: world build
   script:
     - ret=0;
-     ./tools/buildman/buildman -P -E -x arm,powerpc || ret=$?;
+     ./tools/buildman/buildman -o /tmp -P -E -x arm,powerpc || ret=$?;
      if [[ $ret -ne 0 && $ret -ne 129 ]]; then
-       ./tools/buildman/buildman -sdeP;
+       ./tools/buildman/buildman -o /tmp -sdeP;
        exit $ret;
      fi;
 
@@ -162,10 +163,10 @@ Run binman, buildman, dtoc and patman testsuites:
       virtualenv /tmp/venv;
       . /tmp/venv/bin/activate;
       pip install pyelftools;
-      export UBOOT_TRAVIS_BUILD_DIR=`cd .. && pwd`/.bm-work/sandbox_spl;
+      export UBOOT_TRAVIS_BUILD_DIR=/tmp/.bm-work/sandbox_spl;
       export PYTHONPATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc/pylibfdt";
       export PATH="${UBOOT_TRAVIS_BUILD_DIR}/scripts/dtc:${PATH}";
-      ./tools/buildman/buildman -P sandbox_spl;
+      ./tools/buildman/buildman -o /tmp -P sandbox_spl;
       ./tools/binman/binman --toolpath ${UBOOT_TRAVIS_BUILD_DIR}/tools test;
       ./tools/buildman/buildman -t;
       ./tools/dtoc/dtoc -t;
index 0ce09e35b7c6a14aaa36e5acb31ef540e687c8ea..c48b711659e9c5512677394cbf490294d3435854 100644 (file)
@@ -4,7 +4,7 @@
 # build U-Boot on Travis CI - https://travis-ci.org/
 
 sudo: required
-dist: xenial
+dist: bionic
 
 language: c
 
@@ -12,7 +12,7 @@ addons:
   apt:
     sources:
     - ubuntu-toolchain-r-test
-    - llvm-toolchain-xenial-7
+    - llvm-toolchain-bionic-7
     packages:
     - cppcheck
     - sloccount
@@ -52,12 +52,13 @@ install:
  - pip install pytest==2.8.7
  - pip install python-subunit
  - pip install pyelftools
- - grub-mkimage -o ~/grub_x86.efi -O i386-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
- - grub-mkimage -o ~/grub_x64.efi -O x86_64-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
+ - grub-mkimage --prefix="" -o ~/grub_x86.efi -O i386-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
+ - grub-mkimage --prefix="" -o ~/grub_x64.efi -O x86_64-efi normal  echo lsefimmap lsefi lsefisystab efinet tftp minicmd
  - mkdir ~/grub2-arm
  - ( cd ~/grub2-arm; wget -O - http://download.opensuse.org/ports/armv7hl/distribution/leap/42.2/repo/oss/suse/armv7hl/grub2-arm-efi-2.02~beta2-87.1.armv7hl.rpm | rpm2cpio | cpio -di )
  - mkdir ~/grub2-arm64
  - ( cd ~/grub2-arm64; wget -O - http://download.opensuse.org/ports/aarch64/distribution/leap/42.2/repo/oss/suse/aarch64/grub2-arm64-efi-2.02~beta2-87.1.aarch64.rpm | rpm2cpio | cpio -di )
+ - wget http://mirrors.kernel.org/ubuntu/pool/main/m/mpfr4/libmpfr4_3.1.4-1_amd64.deb && sudo dpkg -i libmpfr4_3.1.4-1_amd64.deb && rm libmpfr4_3.1.4-1_amd64.deb
 
 env:
   global:
index 80b7811f7be8caa2b7fa68bb54be0fc906166313..842bc6c11b6b7191452a3031ef4c23200bc1afc2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -337,14 +337,18 @@ endif
 #  KBUILD_MODULES := 1
 #endif
 
+# Check ths size of a binary:
+# Args:
+#   $1: File to check
+#   #2: Size limit in bytes (decimal or 0xhex)
 define size_check
        actual=$$( wc -c $1 | awk '{print $$1}'); \
        limit=$$( printf "%d" $2 ); \
        if test $$actual -gt $$limit; then \
                echo "$1 exceeds file size limit:" >&2; \
-               echo "  limit:  $$limit bytes" >&2; \
-               echo "  actual: $$actual bytes" >&2; \
-               echo "  excess: $$((actual - limit)) bytes" >&2; \
+               echo "  limit:  $$(printf %#x bytes $$limit) bytes" >&2; \
+               echo "  actual: $$(printf %#x $$actual) bytes" >&2; \
+               echo "  excess: $$(printf %#x $$((actual - limit))) bytes" >&2;\
                exit 1; \
        fi
 endef
@@ -1213,7 +1217,9 @@ u-boot.ldr:       u-boot
 # Use 'make BINMAN_DEBUG=1' to enable debugging
 quiet_cmd_binman = BINMAN  $@
 cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-                build -u -d u-boot.dtb -O . -m \
+                --toolpath $(objtree)/tools \
+               $(if $(BINMAN_VERBOSE),-v$(BINMAN_VERBOSE)) \
+               build -u -d u-boot.dtb -O . -m \
                -I . -I $(srctree) -I $(srctree)/board/$(BOARDDIR) \
                $(BINMAN_$(@F))
 
index c219a53be82346f5909a6988e09dafecc87c6b09..384e382e246c51510919422bfb51bcc77821f6d7 100644 (file)
@@ -954,7 +954,7 @@ config ARCH_SUNXI
        select USB if DISTRO_DEFAULTS
        select USB_KEYBOARD if DISTRO_DEFAULTS
        select USB_STORAGE if DISTRO_DEFAULTS
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
        imply CMD_GPT
        imply CMD_UBI if NAND
index fdb0c926fb2ced72fbc7511707573ed336de5741..b051025bb0a568d9c36883942b5cbf89802d6c5d 100644 (file)
@@ -7,7 +7,7 @@ extra-y = start.o
 obj-y  = cpu.o cache.o
 
 ifdef  CONFIG_SPL_BUILD
-ifdef  CONFIG_SPL_NO_CPU_SUPPORT_CODE
+ifdef  CONFIG_SPL_NO_CPU_SUPPORT
 extra-y        :=
 endif
 endif
index 9583bf743ee8a6aeaae52fc4e8bd2523ef8defa3..d7f7b9f1110150026cd6cfeb9f74a9797ce9a9d9 100644 (file)
@@ -16,6 +16,5 @@ You can enable it by setting CONFIG_IMX_WATCHDOG.
 Use following config to set watchdog timeout, if this config is not defined,
 the default timeout value is 128s which is the maximum. Set 10 seconds for
 example:
-    #define CONFIG_WATCHDOG_TIMEOUT_MSECS 10000
 Set CONFIG_WATCHDOG_RESET_DISABLE to disable reset watchdog, so that the
 watchdog will not be fed in u-boot.
index 2c86b293a8008165d3c3e70f7cfb3375c5b03556..73d47f5ac46467f504ee1234d787c3c4e98e6704 100644 (file)
@@ -704,6 +704,8 @@ dtb-$(CONFIG_TARGET_AT91SAM9X5EK) += \
        at91sam9x25ek.dtb       \
        at91sam9x35ek.dtb
 
+dtb-$(CONFIG_TARGET_SAM9X60EK) += sam9x60ek.dtb
+
 dtb-$(CONFIG_TARGET_AT91SAM9N12EK) += at91sam9n12ek.dtb
 
 dtb-$(CONFIG_TARGET_GARDENA_SMART_GATEWAY_AT91SAM) += \
@@ -740,6 +742,9 @@ dtb-$(CONFIG_TARGET_SAMA5D2_XPLAINED) += \
 dtb-$(CONFIG_TARGET_SAMA5D27_SOM1_EK) += \
        at91-sama5d27_som1_ek.dtb
 
+dtb-$(CONFIG_TARGET_SAMA5D27_WLSOM1_EK) += \
+       at91-sama5d27_wlsom1_ek.dtb
+
 dtb-$(CONFIG_TARGET_SAMA5D2_ICP) += \
        at91-sama5d2_icp.dtb
 
diff --git a/arch/arm/dts/at91-sama5d27_wlsom1_ek-u-boot.dtsi b/arch/arm/dts/at91-sama5d27_wlsom1_ek-u-boot.dtsi
new file mode 100644 (file)
index 0000000..8c84dd0
--- /dev/null
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * at91-sama5d27_wlsom1_ek-u-boot.dts - Device Tree file for SAMA5D27 WLSOM1 EK
+ *
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ */
+
+/ {
+       chosen {
+               u-boot,dm-pre-reloc;
+       };
+};
+
+&hlcdc {
+       u-boot,dm-pre-reloc;
+};
+
+&qspi1 {
+       u-boot,dm-pre-reloc;
+};
+
+&qspi1_flash {
+       u-boot,dm-pre-reloc;
+};
+
+&sdmmc0 {
+       u-boot,dm-pre-reloc;
+};
+
+&uart0 {
+       u-boot,dm-pre-reloc;
+};
+
+&sfr {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_sdmmc0_cmd_dat_default {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_sdmmc0_ck_cd_default {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_uart0_default {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_qspi1_default {
+       u-boot,dm-pre-reloc;
+};
diff --git a/arch/arm/dts/at91-sama5d27_wlsom1_ek.dts b/arch/arm/dts/at91-sama5d27_wlsom1_ek.dts
new file mode 100644 (file)
index 0000000..ab23f5c
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * at91-sama5d27_wlsom1_ek.dts - Device Tree file for SAMA5D27 WLSOM1 EK
+ *
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Nicolas Ferre <nicolas.ferre@microcihp.com>
+ */
+/dts-v1/;
+#include "sama5d27_wlsom1.dtsi"
+
+/ {
+       model = "Microchip SAMA5D27 WLSOM1 EK";
+       compatible = "microchip,sama5d27-wlsom1-ek", "microchip,sama5d27-wlsom1", "atmel,sama5d2", "atmel,sama5";
+
+       chosen {
+               stdout-path = &uart0;
+       };
+
+       onewire_tm: onewire {
+               gpios = <&pioA PIN_PC9 0>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_onewire_tm_default>;
+               status = "okay";
+
+               w1_eeprom: w1_eeprom@0 {
+                       compatible = "maxim,ds24b33";
+                       status = "okay";
+               };
+       };
+
+       ahb {
+               sdmmc0: sdio-host@a0000000 {
+                       bus-width = <4>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_sdmmc0_cmd_dat_default &pinctrl_sdmmc0_ck_cd_default>;
+                       status = "okay";
+               };
+
+               apb {
+                       hlcdc: hlcdc@f0000000 {
+                               atmel,vl-bpix = <4>;
+                               atmel,output-mode = <24>;
+                               atmel,guard-time = <1>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_pwm &pinctrl_lcd_rgb666>;
+                               status = "okay";
+
+                               display-timings {
+                                       800x480 {
+                                               clock-frequency = <33300000>;
+                                               xres = <800>;
+                                               yres = <480>;
+                                               hactive = <800>;
+                                               vactive = <480>;
+                                               hsync-len = <64>;
+                                               hfront-porch = <1>;
+                                               hback-porch = <64>;
+                                               vfront-porch = <1>;
+                                               vback-porch = <22>;
+                                               vsync-len = <23>;
+                                       };
+                               };
+                       };
+
+                       qspi1: spi@f0024000 {
+                               status = "okay";
+                       };
+
+                       macb0: ethernet@f8008000 {
+                               status = "okay";
+                       };
+
+                       uart0: serial@f801c000 {
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_uart0_default>;
+                               status = "okay";
+                       };
+
+                       pioA: gpio@fc038000 {
+                               pinctrl {
+                                       pinctrl_lcd_base: pinctrl_lcd_base {
+                                               pinmux = <PIN_PC30__LCDVSYNC>,
+                                                        <PIN_PC31__LCDHSYNC>,
+                                                        <PIN_PD1__LCDDEN>,
+                                                        <PIN_PD0__LCDPCK>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_lcd_pwm: pinctrl_lcd_pwm {
+                                               pinmux = <PIN_PC28__LCDPWM>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_lcd_rgb666: pinctrl_lcd_rgb666 {
+                                               pinmux = <PIN_PC10__LCDDAT2>,
+                                                        <PIN_PC11__LCDDAT3>,
+                                                        <PIN_PC12__LCDDAT4>,
+                                                        <PIN_PC13__LCDDAT5>,
+                                                        <PIN_PC14__LCDDAT6>,
+                                                        <PIN_PC15__LCDDAT7>,
+                                                        <PIN_PC16__LCDDAT10>,
+                                                        <PIN_PC17__LCDDAT11>,
+                                                        <PIN_PC18__LCDDAT12>,
+                                                        <PIN_PC19__LCDDAT13>,
+                                                        <PIN_PC20__LCDDAT14>,
+                                                        <PIN_PC21__LCDDAT15>,
+                                                        <PIN_PC22__LCDDAT18>,
+                                                        <PIN_PC23__LCDDAT19>,
+                                                        <PIN_PC24__LCDDAT20>,
+                                                        <PIN_PC25__LCDDAT21>,
+                                                        <PIN_PC26__LCDDAT22>,
+                                                        <PIN_PC27__LCDDAT23>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_sdmmc0_cmd_dat_default: sdmmc0_cmd_dat_default {
+                                               pinmux = <PIN_PA1__SDMMC0_CMD>,
+                                                        <PIN_PA2__SDMMC0_DAT0>,
+                                                        <PIN_PA3__SDMMC0_DAT1>,
+                                                        <PIN_PA4__SDMMC0_DAT2>,
+                                                        <PIN_PA5__SDMMC0_DAT3>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_sdmmc0_ck_cd_default: sdmmc0_ck_cd_default {
+                                               pinmux = <PIN_PA0__SDMMC0_CK>,
+                                                        <PIN_PA11__SDMMC0_VDDSEL>,
+                                                        <PIN_PA12__SDMMC0_WP>,
+                                                        <PIN_PA13__SDMMC0_CD>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_uart0_default: uart0_default {
+                                               pinmux = <PIN_PB26__URXD0>,
+                                                        <PIN_PB27__UTXD0>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_onewire_tm_default: onewire_tm_default {
+                                               pinmux = <PIN_PC9__GPIO>;
+                                               bias-pull-up;
+                                       };
+                               };
+                       };
+               };
+       };
+};
diff --git a/arch/arm/dts/sam9x60.dtsi b/arch/arm/dts/sam9x60.dtsi
new file mode 100644 (file)
index 0000000..e01539e
--- /dev/null
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * sam9x60.dtsi - Device Tree Include file for SAM9X60 SoC.
+ *
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Sandeep Sheriker M <sandeepsheriker.mallikarjun@microchip.com>
+ */
+
+#include "skeleton.dtsi"
+#include <dt-bindings/dma/at91.h>
+#include <dt-bindings/pinctrl/at91.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/clock/at91.h>
+
+/{
+       model = "Microchip SAM9X60 SoC";
+       compatible = "microchip,sam9x60";
+
+       aliases {
+               serial0 = &dbgu;
+               gpio0 = &pioA;
+               gpio1 = &pioB;
+               gpio3 = &pioD;
+               spi0 = &qspi;
+       };
+
+       clocks {
+               slow_xtal: slow_xtal {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <0>;
+               };
+
+               main_xtal: main_xtal {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <0>;
+               };
+       };
+
+       ahb {
+               compatible = "simple-bus";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               sdhci0: sdhci-host@80000000 {
+                       compatible = "microchip,sam9x60-sdhci";
+                       reg = <0x80000000 0x300>;
+                       clocks = <&sdhci0_clk>, <&sdhci0_gclk>, <&main>;
+                       clock-names = "hclock", "multclk", "baseclk";
+                       bus-width = <4>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_sdhci0>;
+               };
+
+               apb {
+                       compatible = "simple-bus";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       qspi: spi@f0014000 {
+                               compatible = "microchip,sam9x60-qspi";
+                               reg = <0xf0014000 0x100>, <0x70000000 0x10000000>;
+                               reg-names = "qspi_base", "qspi_mmap";
+                               clocks =  <&qspi_clk>, <&qspick>;
+                               clock-names = "pclk", "qspick";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               status = "disabled";
+                       };
+
+                       macb0: ethernet@f802c000 {
+                               compatible = "cdns,sam9x60-macb", "cdns,macb";
+                               reg = <0xf802c000 0x100>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_macb0_rmii>;
+                               clock-names = "hclk", "pclk";
+                               clocks = <&macb0_clk>, <&macb0_clk>;
+                               status = "disabled";
+                       };
+
+                       dbgu: serial@fffff200 {
+                               compatible = "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart";
+                               reg = <0xfffff200 0x200>;
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_dbgu>;
+                               clocks = <&dbgu_clk>;
+                               clock-names = "usart";
+                       };
+
+                       pinctrl {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "microchip,sam9x60-pinctrl", "simple-bus";
+                               ranges = <0xfffff400 0xfffff400 0x800>;
+                               reg = <0xfffff400 0x200         /* pioA */
+                                      0xfffff600 0x200         /* pioB */
+                                      0xfffff800 0x200         /* pioC */
+                                      0xfffffa00 0x200>;       /* pioD */
+
+                               /* shared pinctrl settings */
+                               dbgu {
+                                       pinctrl_dbgu: dbgu-0 {
+                                               atmel,pins =
+                                                       <AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_PULL_UP
+                                                       AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE>;
+                                       };
+                               };
+
+                               macb0 {
+                                       pinctrl_macb0_rmii: macb0_rmii-0 {
+                                               atmel,pins =
+                                                       <AT91_PIOB 0 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB0 periph A */
+                                                        AT91_PIOB 1 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB1 periph A */
+                                                        AT91_PIOB 2 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB2 periph A */
+                                                        AT91_PIOB 3 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB3 periph A */
+                                                        AT91_PIOB 4 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB4 periph A */
+                                                        AT91_PIOB 5 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB5 periph A */
+                                                        AT91_PIOB 6 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB6 periph A */
+                                                        AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB7 periph A */
+                                                        AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE    /* PB9 periph A */
+                                                        AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PB10 periph A */
+                                       };
+                               };
+
+                               sdhci0 {
+                                       pinctrl_sdhci0: sdhci0 {
+                                               atmel,pins =
+                                                       <AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_DRIVE_STRENGTH_DEFAULT /* PA17 CK  periph A with pullup */
+                                                        AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_PULL_UP                /* PA16 CMD periph A with pullup */
+                                                        AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_PULL_UP                /* PA15 DAT0 periph A */
+                                                        AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_PULL_UP                /* PA18 DAT1 periph A with pullup */
+                                                        AT91_PIOA 19 AT91_PERIPH_A AT91_PINCTRL_PULL_UP                /* PA19 DAT2 periph A with pullup */
+                                                        AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;              /* PA20 DAT3 periph A with pullup */
+                                       };
+                               };
+                       };
+
+                       pioA: gpio@fffff400 {
+                               compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
+                               reg = <0xfffff400 0x200>;
+                               #gpio-cells = <2>;
+                               gpio-controller;
+                               clocks = <&pioA_clk>;
+                       };
+
+                       pioB: gpio@fffff600 {
+                               compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
+                               reg = <0xfffff600 0x200>;
+                               #gpio-cells = <2>;
+                               gpio-controller;
+                               clocks = <&pioB_clk>;
+                       };
+
+                       pioD: gpio@fffffa00 {
+                               compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
+                               reg = <0xfffffa00 0x200>;
+                               #gpio-cells = <2>;
+                               gpio-controller;
+                               clocks = <&pioD_clk>;
+                       };
+
+                       pmc: pmc@fffffc00 {
+                               compatible = "atmel,at91sam9x5-pmc";
+                               reg = <0xfffffc00 0x200>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               main: mainck {
+                                       compatible = "atmel,at91sam9x5-clk-main";
+                                       #clock-cells = <0>;
+                               };
+
+                               plla: pllack {
+                                       compatible = "microchip,sam9x60-clk-pll";
+                                       #clock-cells = <0>;
+                                       clocks = <&main>;
+                                       reg = <0>;
+                                       atmel,clk-input-range = <8000000 24000000>;
+                                       #atmel,pll-clk-output-range-cells = <4>;
+                                       atmel,pll-clk-output-ranges = <140000000 1200000000 0 0>;
+                               };
+
+                               mck: masterck {
+                                       compatible = "atmel,at91sam9x5-clk-master";
+                                       #clock-cells = <0>;
+                                       clocks = <&md_slck>, <&main>, <&plla>;
+                                       atmel,clk-output-range = <140000000 200000000>;
+                                       atmel,clk-divisors = <1 2 4 6>;
+                               };
+
+                               system: systemck {
+                                       compatible = "atmel,at91rm9200-clk-system";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+
+                                       qspick: qspick {
+                                               #clock-cells = <0>;
+                                               reg = <19>;
+                                               clocks = <&mck>;
+                                       };
+                               };
+
+                               periph: periphck {
+                                       compatible = "microchip,sam9x60-clk-peripheral";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       clocks = <&mck>;
+
+                                       pioA_clk: pioA_clk {
+                                               #clock-cells = <0>;
+                                               reg = <2>;
+                                       };
+
+                                       pioB_clk: pioB_clk {
+                                               #clock-cells = <0>;
+                                               reg = <3>;
+                                       };
+
+                                       pioD_clk: pioD_clk {
+                                               #clock-cells = <0>;
+                                               reg = <44>;
+                                       };
+
+                                       sdhci0_clk: sdhci0_clk {
+                                               #clock-cells = <0>;
+                                               reg = <12>;
+                                       };
+
+                                       dbgu_clk: dbgu_clk {
+                                               #clock-cells = <0>;
+                                               reg = <47>;
+                                       };
+
+                                       macb0_clk: macb0_clk {
+                                               #clock-cells = <0>;
+                                               reg = <24>;
+                                       };
+
+                                       qspi_clk: qspi_clk {
+                                               #clock-cells = <0>;
+                                               reg = <35>;
+                                       };
+                               };
+
+                               generic: gck {
+                                       compatible = "microchip,sam9x60-clk-generated";
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       clocks = <&md_slck>, <&td_slck>, <&main>, <&mck>, <&plla>;
+
+                                       sdhci0_gclk: sdhci0_gclk {
+                                               #clock-cells = <0>;
+                                               reg = <12>;
+                                       };
+                               };
+                       };
+
+                       pit: timer@fffffe40 {
+                               compatible = "atmel,at91sam9260-pit";
+                               reg = <0xfffffe40 0x10>;
+                               clocks = <&mck>;
+                       };
+
+                       slowckc: sckc@fffffe50 {
+                               compatible = "atmel,at91sam9x5-sckc";
+                               reg = <0xfffffe50 0x4>;
+
+                               slow_osc: slow_osc {
+                                       compatible = "atmel,at91sam9x5-clk-slow-osc";
+                                       #clock-cells = <0>;
+                                       clocks = <&slow_xtal>;
+                               };
+
+                               slow_rc_osc: slow_rc_osc {
+                                       compatible = "atmel,at91sam9x5-clk-slow-rc-osc";
+                                       #clock-cells = <0>;
+                                       clock-frequency = <32768>;
+                               };
+
+                               td_slck: td_slck {
+                                       compatible = "atmel,at91sam9x5-clk-slow";
+                                       #clock-cells = <0>;
+                                       clocks = <&slow_rc_osc>, <&slow_osc>;
+                               };
+
+                               md_slck: md_slck {
+                                       compatible = "atmel,at91sam9x5-clk-slow";
+                                       #clock-cells = <0>;
+                                       clocks = <&slow_rc_osc>;
+                               };
+                       };
+               };
+       };
+
+       onewire_tm: onewire {
+               compatible = "w1-gpio";
+               status = "disabled";
+       };
+};
diff --git a/arch/arm/dts/sam9x60ek-u-boot.dtsi b/arch/arm/dts/sam9x60ek-u-boot.dtsi
new file mode 100644 (file)
index 0000000..93cf126
--- /dev/null
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * sam9x60-u-boot.dts - Device Tree file for SAM9X60 SoC.
+ *
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ */
+
+/ {
+       chosen {
+               u-boot,dm-pre-reloc;
+       };
+
+       ahb {
+               u-boot,dm-pre-reloc;
+
+               apb {
+                       u-boot,dm-pre-reloc;
+
+                       pinctrl {
+                               u-boot,dm-pre-reloc;
+                       };
+               };
+       };
+};
+
+&sdhci0 {
+       u-boot,dm-pre-reloc;
+};
+
+&dbgu {
+       u-boot,dm-pre-reloc;
+};
+
+&qspi {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_dbgu {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_sdhci0 {
+       u-boot,dm-pre-reloc;
+};
+
+&pinctrl_qspi {
+       u-boot,dm-pre-reloc;
+};
+
+&pioA {
+       u-boot,dm-pre-reloc;
+};
+
+&pioB {
+       u-boot,dm-pre-reloc;
+};
+
+&pmc {
+       u-boot,dm-pre-reloc;
+};
+
+&main {
+       u-boot,dm-pre-reloc;
+};
+
+&plla {
+       u-boot,dm-pre-reloc;
+};
+
+&mck {
+       u-boot,dm-pre-reloc;
+};
+
+&system {
+       u-boot,dm-pre-reloc;
+};
+
+&qspick {
+       u-boot,dm-pre-reloc;
+};
+
+&periph {
+       u-boot,dm-pre-reloc;
+};
+
+&pioA_clk {
+       u-boot,dm-pre-reloc;
+};
+
+&pioB_clk {
+       u-boot,dm-pre-reloc;
+};
+
+&sdhci0_clk {
+       u-boot,dm-pre-reloc;
+};
+
+&dbgu_clk {
+       u-boot,dm-pre-reloc;
+};
+
+&qspi_clk {
+       u-boot,dm-pre-reloc;
+};
+
+&generic {
+       u-boot,dm-pre-reloc;
+};
+
+&sdhci0_gclk {
+       u-boot,dm-pre-reloc;
+};
+
+&slowckc {
+       u-boot,dm-pre-reloc;
+};
+
+&slow_osc {
+       u-boot,dm-pre-reloc;
+};
+
+&slow_rc_osc {
+       u-boot,dm-pre-reloc;
+};
+
+&td_slck {
+       u-boot,dm-pre-reloc;
+};
+
+&md_slck {
+       u-boot,dm-pre-reloc;
+};
diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
new file mode 100644 (file)
index 0000000..bed59f3
--- /dev/null
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * sam9x60ek.dts - Device Tree file for SAM9X60 EK board
+ *
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Sandeep Sheriker M <Sandeepsheriker.mallikarjun@microchip.com>
+ */
+/dts-v1/;
+#include "sam9x60.dtsi"
+
+/ {
+       model = "Microchip SAM9X60-Ek";
+       compatible = "microchip,sam9x60ek", "microchip,sam9x60", "atmel,at91sam9";
+
+       chosen {
+               stdout-path = &dbgu;
+       };
+
+       onewire_tm: onewire {
+               gpios = <&pioD 14 0>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_onewire_tm_default>;
+               status = "okay";
+
+               w1_eeprom: w1_eeprom@0 {
+                       compatible = "maxim,ds24b33";
+                       status = "okay";
+               };
+       };
+
+       ahb {
+               apb {
+                       qspi: spi@f0014000 {
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_qspi>;
+                               status = "okay";
+
+                               nor_flash: sst26vf064@0 {
+                                       compatible = "spi-flash";
+                                       reg = <0>;
+                                       spi-max-frequency = <80000000>;
+                                       spi-rx-bus-width = <4>;
+                                       spi-tx-bus-width = <4>;
+                               };
+                       };
+
+                       pinctrl {
+                                       pinctrl_qspi: qspi {
+                                               atmel,pins =
+                                                       <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
+                                                        AT91_PIOB 20 AT91_PERIPH_A AT91_PINCTRL_NONE
+                                                        AT91_PIOB 21 AT91_PERIPH_A AT91_PINCTRL_PULL_UP
+                                                        AT91_PIOB 22 AT91_PERIPH_A AT91_PINCTRL_PULL_UP
+                                                        AT91_PIOB 23 AT91_PERIPH_A AT91_PINCTRL_PULL_UP
+                                                        AT91_PIOB 24 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>;
+                                       };
+
+                                       pinctrl_onewire_tm_default: onewire_tm_default {
+                                               atmel,pins =
+                                                       <AT91_PIOD 14 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
+                                       };
+
+                       };
+               };
+       };
+};
+
+&macb0 {
+       phy-mode = "rmii";
+       status = "okay";
+};
index 830251a5393cb8f413a97b94441dc58cfeac5284..5adc47b906b1711c0bffbc7467b01f93c23c8c3c 100644 (file)
@@ -7,6 +7,7 @@
        aliases {
                spi0 = &spi0;
                spi1 = &qspi0;
+               spi2 = &qspi1;
                i2c0 = &i2c0;
                i2c1 = &i2c1;
        };
diff --git a/arch/arm/dts/sama5d27_wlsom1.dtsi b/arch/arm/dts/sama5d27_wlsom1.dtsi
new file mode 100644 (file)
index 0000000..889a003
--- /dev/null
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * sama5d27_wlsom1.dtsi - Device Tree file for SAMA5D27 WLSOM1
+ *
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Nicolas Ferre <nicolas.ferre@microcihp.com>
+ */
+#include "sama5d2.dtsi"
+#include "sama5d2-pinfunc.h"
+/ {
+       model = "Microchip SAMA5D27 WLSOM1";
+       compatible = "microchip,sama5d27-wlsom1", "atmel,sama5d2", "atmel,sama5";
+
+       memory {
+               reg = <0x20000000 0x10000000>;
+       };
+
+       ahb {
+               apb {
+                       qspi1: spi@f0024000 {
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_qspi1_default>;
+
+                               qspi1_flash: spi_flash@0 {
+                                       compatible = "jedec,spi-nor";
+                                       reg = <0>;
+                                       spi-max-frequency = <50000000>;
+                                       spi-rx-bus-width = <4>;
+                                       spi-tx-bus-width = <4>;
+                               };
+                       };
+
+                       macb0: ethernet@f8008000 {
+                               pinctrl-names = "default";
+                               pinctrl-0 = <&pinctrl_macb0_rmii &pinctrl_macb0_phy_irq>;
+                               phy-mode = "rmii";
+
+                               ethernet-phy@0 {
+                                       reg = <0x0>;
+                               };
+                       };
+
+                       pioA: gpio@fc038000 {
+                               pinctrl {
+                                       pinctrl_macb0_phy_irq: macb0_phy_irq {
+                                               pinmux = <PIN_PB24__GPIO>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_macb0_rmii: macb0_rmii {
+                                               pinmux = <PIN_PB14__GTXCK>,
+                                                        <PIN_PB15__GTXEN>,
+                                                        <PIN_PB16__GRXDV>,
+                                                        <PIN_PB17__GRXER>,
+                                                        <PIN_PB18__GRX0>,
+                                                        <PIN_PB19__GRX1>,
+                                                        <PIN_PB20__GTX0>,
+                                                        <PIN_PB21__GTX1>,
+                                                        <PIN_PB22__GMDC>,
+                                                        <PIN_PB23__GMDIO>;
+                                               bias-disable;
+                                       };
+
+                                       pinctrl_qspi1_default: qspi1_default {
+                                               pinmux = <PIN_PB5__QSPI1_SCK>,
+                                                        <PIN_PB6__QSPI1_CS>,
+                                                        <PIN_PB7__QSPI1_IO0>,
+                                                        <PIN_PB8__QSPI1_IO1>,
+                                                        <PIN_PB9__QSPI1_IO2>,
+                                                        <PIN_PB10__QSPI1_IO3>;
+                                               bias-pull-up;
+                                       };
+                               };
+                       };
+               };
+       };
+};
index c3b21b7557e64828d2d37a237ba5493b0354ea54..85524004f9e4791a8c275fb63364f2a11ef4463e 100644 (file)
@@ -43,9 +43,14 @@ config AT91SAM9X5
        bool
        select CPU_ARM926EJS
 
+config SAM9X60
+       bool
+       select CPU_ARM926EJS
+
 config SAMA5D2
        bool
        select CPU_V7A
+       select ATMEL_SFR
 
 config SAMA5D3
        bool
@@ -54,6 +59,7 @@ config SAMA5D3
 config SAMA5D4
        bool
        select CPU_V7A
+       select ATMEL_SFR
 
 choice
        prompt "Atmel AT91 board select"
@@ -154,6 +160,12 @@ config TARGET_GARDENA_SMART_GATEWAY_AT91SAM
        select BOARD_LATE_INIT
        select SUPPORT_SPL
 
+config TARGET_SAM9X60EK
+       bool "SAM9X60-EK board"
+       select SAM9X60
+       select BOARD_EARLY_INIT_F
+       select BOARD_LATE_INIT
+
 config TARGET_SAMA5D2_PTC_EK
        bool "SAMA5D2 PTC EK board"
        select BOARD_EARLY_INIT_F
@@ -173,6 +185,7 @@ config TARGET_SAMA5D27_SOM1_EK
        select BOARD_LATE_INIT
        select CPU_V7A
        select SUPPORT_SPL
+       select ATMEL_SFR
        help
          The SAMA5D27 SOM1 embeds SAMA5D2 SiP(System in Package),
          a 64Mbit QSPI flash, KSZ8081 Phy and a Mac-address EEPROM
@@ -180,9 +193,24 @@ config TARGET_SAMA5D27_SOM1_EK
          processor-based SAMA5D2 MPU with up to 1 Gbit DDR2-SDRAM
          in a single package.
 
+config TARGET_SAMA5D27_WLSOM1_EK
+       bool "SAMA5D27 WLSOM1 EK board"
+       select SAMA5D2
+       select BOARD_EARLY_INIT_F
+       select BOARD_LATE_INIT
+       select CPU_V7A
+       select SUPPORT_SPL
+       help
+         The SAMA5D27 WLSOM1 embeds SAMA5D2 SiP (System in Package),
+         a 64Mbit QSPI flash with Mac-address, KSZ8081 Phy. A wireless
+         module providing bluetooth and wifi is also embedded.
+         The SAMA5D2 SiP integrates the ARM Cortex-A5
+         processor-based SAMA5D2 MPU with 2 Gbit LPDDR2-SDRAM
+         in a single package.
+
 config TARGET_SAMA5D2_ICP
        bool "SAMA5D2 Industrial Connectivity Platform (ICP)"
-       select CPU_V7A
+       select SAMA5D2
        select SUPPORT_SPL
        select BOARD_EARLY_INIT_F
        select BOARD_LATE_INIT
@@ -275,9 +303,14 @@ config TARGET_WB50N
        select BOARD_LATE_INIT
        select CPU_V7A
        select SUPPORT_SPL
+       select ATMEL_SFR
 
 endchoice
 
+config ATMEL_SFR
+       bool
+       default n
+
 config SYS_SOC
        default "at91"
 
@@ -289,9 +322,11 @@ source "board/atmel/at91sam9m10g45ek/Kconfig"
 source "board/atmel/at91sam9n12ek/Kconfig"
 source "board/atmel/at91sam9rlek/Kconfig"
 source "board/atmel/at91sam9x5ek/Kconfig"
+source "board/atmel/sam9x60ek/Kconfig"
 source "board/atmel/sama5d2_ptc_ek/Kconfig"
 source "board/atmel/sama5d2_xplained/Kconfig"
 source "board/atmel/sama5d27_som1_ek/Kconfig"
+source "board/atmel/sama5d27_wlsom1_ek/Kconfig"
 source "board/atmel/sama5d2_icp/Kconfig"
 source "board/atmel/sama5d3_xplained/Kconfig"
 source "board/atmel/sama5d3xek/Kconfig"
index 045ac88806f9d13aba889727c75fc35b3400d240..cbd0ed68c2759ad15c51862023eef2bcb7ffb9ae 100644 (file)
@@ -7,10 +7,11 @@ obj-$(CONFIG_AT91SAM9G20) += sdram.o spl_at91.o
 obj-$(CONFIG_AT91SAM9M10G45) += mpddrc.o spl_at91.o
 obj-$(CONFIG_AT91SAM9N12) += mpddrc.o spl_at91.o
 obj-$(CONFIG_AT91SAM9X5) += mpddrc.o spl_at91.o
-obj-$(CONFIG_SAMA5D2) += bootparams_atmel.o mpddrc.o spl_atmel.o matrix.o atmel_sfr.o
+obj-$(CONFIG_SAMA5D2) += bootparams_atmel.o mpddrc.o spl_atmel.o matrix.o
 obj-$(CONFIG_SAMA5D3) += bootparams_atmel.o mpddrc.o spl_atmel.o
-obj-$(CONFIG_SAMA5D4) += bootparams_atmel.o mpddrc.o spl_atmel.o matrix.o atmel_sfr.o
+obj-$(CONFIG_SAMA5D4) += bootparams_atmel.o mpddrc.o spl_atmel.o matrix.o
 obj-y += spl.o
+obj-$(CONFIG_ATMEL_SFR) += atmel_sfr.o
 endif
 
 obj-y += clock.o
index 6b0b28957af593c815e27b7001f798d7a4029c2d..8de6a2f9661e00857870b18b6c2fdd658ae19c53 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_AT91SAM9M10G45)  += at91sam9m10g45_devices.o
 obj-$(CONFIG_AT91SAM9G45)      += at91sam9m10g45_devices.o
 obj-$(CONFIG_AT91SAM9N12)      += at91sam9n12_devices.o
 obj-$(CONFIG_AT91SAM9X5)       += at91sam9x5_devices.o
+obj-$(CONFIG_SAM9X60)          += sam9x60_devices.o
 obj-$(CONFIG_AT91_EFLASH)      += eflash.o
 obj-$(CONFIG_AT91_LED) += led.o
 obj-y += clock.o
diff --git a/arch/arm/mach-at91/arm926ejs/sam9x60_devices.c b/arch/arm/mach-at91/arm926ejs/sam9x60_devices.c
new file mode 100644 (file)
index 0000000..d463bbc
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ */
+
+#include <common.h>
+#include <asm/arch/at91_common.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpio.h>
+#include <asm/io.h>
+
+unsigned int get_chip_id(void)
+{
+       /* The 0x40 is the offset of cidr in DBGU */
+       return readl(ATMEL_BASE_DBGU + 0x40) & ~ARCH_ID_VERSION_MASK;
+}
+
+unsigned int get_extension_chip_id(void)
+{
+       /* The 0x44 is the offset of exid in DBGU */
+       return readl(ATMEL_BASE_DBGU + 0x44);
+}
+
+unsigned int has_emac1(void)
+{
+       return cpu_is_sam9x60();
+}
+
+unsigned int has_emac0(void)
+{
+       return cpu_is_sam9x60();
+}
+
+unsigned int has_lcdc(void)
+{
+       return cpu_is_sam9x60();
+}
+
+char *get_cpu_name(void)
+{
+       unsigned int extension_id = get_extension_chip_id();
+
+       if (cpu_is_sam9x60()) {
+               switch (extension_id) {
+               case ARCH_EXID_SAM9X60:
+                       return "SAM9X60";
+               default:
+                       return "Unknown CPU type";
+               }
+       } else {
+               return "Unknown CPU type";
+       }
+}
+
+void at91_seriald_hw_init(void)
+{
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 9, 1);   /* DRXD */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 10, 1);  /* DTXD */
+
+       at91_periph_clk_enable(ATMEL_ID_DBGU);
+}
+
+void at91_mci_hw_init(void)
+{
+       /* Initialize the SDMMC0 */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 17, 1);  /* CLK */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 16, 1);  /* CMD */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 15, 1);  /* DAT0 */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 18, 1);  /* DAT1 */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 19, 1);  /* DAT2 */
+       at91_pio3_set_a_periph(AT91_PIO_PORTA, 20, 1);  /* DAT3 */
+
+       at91_periph_clk_enable(ATMEL_ID_SDMMC0);
+}
+
+#ifdef CONFIG_MACB
+void at91_macb_hw_init(void)
+{
+       if (has_emac0()) {
+               /* Enable EMAC0 clock */
+               at91_periph_clk_enable(ATMEL_ID_EMAC0);
+               /* EMAC0 pins setup */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 4, 0);   /* ETXCK */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 3, 0);   /* ERXDV */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 0, 0);   /* ERX0 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 1, 0);   /* ERX1 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 2, 0);   /* ERXER */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 7, 0);   /* ETXEN */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 9, 0);   /* ETX0 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 10, 0);  /* ETX1 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 5, 0);   /* EMDIO */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 6, 0);   /* EMDC */
+       }
+
+       if (has_emac1()) {
+               /* Enable EMAC1 clock */
+               at91_periph_clk_enable(ATMEL_ID_EMAC1);
+               /* EMAC1 pins setup */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 29, 0);  /* ETXCK */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 28, 0);  /* ECRSDV */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 20, 0);  /* ERXO */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 21, 0);  /* ERX1 */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 16, 0);  /* ERXER */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 27, 0);  /* ETXEN */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 18, 0);  /* ETX0 */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 19, 0);  /* ETX1 */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 31, 0);  /* EMDIO */
+               at91_pio3_set_b_periph(AT91_PIO_PORTC, 30, 0);  /* EMDC */
+       }
+
+#ifndef CONFIG_RMII
+       /* Only emac0 support MII */
+       if (has_emac0()) {
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 16, 0);  /* ECRS */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 17, 0);  /* ECOL */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 13, 0);  /* ERX2 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 14, 0);  /* ERX3 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 15, 0);  /* ERXCK */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 11, 0);  /* ETX2 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 12, 0);  /* ETX3 */
+               at91_pio3_set_a_periph(AT91_PIO_PORTB, 8, 0);   /* ETXER */
+       }
+#endif
+}
+#endif
index 59a0c449132817e85d930118158e8609c1351973..9e9d026c3e064e46a36a1bd70abd9c653dd15459 100644 (file)
@@ -57,8 +57,16 @@ char *get_cpu_name(void)
                        return "SAMA5D27 512M bits DDR2 SDRAM";
                case ARCH_EXID_SAMA5D27C_D1G:
                        return "SAMA5D27 1G bits DDR2 SDRAM";
+               case ARCH_EXID_SAMA5D27C_LD1G:
+                       return "SAMA5D27 1G bits LPDDR2 SDRAM";
+               case ARCH_EXID_SAMA5D27C_LD2G:
+                       return "SAMA5D27 2G bits LPDDR2 SDRAM";
                case ARCH_EXID_SAMA5D28C_D1G:
                        return "SAMA5D28 1G bits DDR2 SDRAM";
+               case ARCH_EXID_SAMA5D28C_LD1G:
+                       return "SAMA5D28 1G bits LPDDR2 SDRAM";
+               case ARCH_EXID_SAMA5D28C_LD2G:
+                       return "SAMA5D28 2G bits LPDDR2 SDRAM";
                }
        }
 
index 5c693df2ecf062bf28e7ab53dc976e6e6a5059d0..e68ae994078858e11d1dfe5e550b28e32fd3b883 100644 (file)
@@ -8,7 +8,7 @@
 #include <asm/io.h>
 #include <asm/arch/at91_common.h>
 #include <asm/arch/clk.h>
-#include <asm/arch/sama5_sfr.h>
+#include <asm/arch/at91_sfr.h>
 #include <asm/arch/sama5d4.h>
 
 char *get_cpu_name()
index 22251153a10d3686265cb4d20632a7e11b62bd4f..b14222460f3acd74f888774fdcaceaafd5ed6b9b 100644 (file)
@@ -7,8 +7,9 @@
 #include <common.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/arch/sama5_sfr.h>
+#include <asm/arch/at91_sfr.h>
 
+#if defined(CONFIG_SAMA5D2) || defined(CONFIG_SAMA5D4)
 void redirect_int_from_saic_to_aic(void)
 {
        struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
@@ -26,3 +27,16 @@ void configure_2nd_sram_as_l2_cache(void)
 
        writel(1, &sfr->l2cc_hramc);
 }
+#endif
+
+void configure_ddrcfg_input_buffers(bool open)
+{
+       struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
+
+       if (open)
+               writel(ATMEL_SFR_DDRCFG_FDQIEN | ATMEL_SFR_DDRCFG_FDQSIEN,
+                      &sfr->ddrcfg);
+       else
+               writel(0, &sfr->ddrcfg);
+}
+
index df7d0e7051ff0b4805b2024b4614016d7e007277..e929b5e1d207e53286f0ddbd8f52b68014a45395 100644 (file)
@@ -35,6 +35,9 @@ void at91_disable_wdt(void);
 void matrix_init(void);
 void redirect_int_from_saic_to_aic(void);
 void configure_2nd_sram_as_l2_cache(void);
+#ifdef CONFIG_ATMEL_SFR
+void configure_ddrcfg_input_buffers(bool open);
+#endif
 
 int at91_set_ethaddr(int offset);
 int at91_video_show_board_info(void);
diff --git a/arch/arm/mach-at91/include/mach/at91_sfr.h b/arch/arm/mach-at91/include/mach/at91_sfr.h
new file mode 100644 (file)
index 0000000..0300c33
--- /dev/null
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Special Function Register (SFR)
+ *
+ * Copyright (C) 2014 Atmel
+ *                   Bo Shen <voice.shen@atmel.com>
+ */
+
+#ifndef __AT91_SFR_H
+#define __AT91_SFR_H
+
+struct atmel_sfr {
+       u32 reserved1;  /* 0x00 */
+       union {
+               u32 ddrcfg;     /* 0x04: DDR Configuration Register */
+               u32 ebicsa;     /* 0x04: EBI Chip Select Register */
+       };
+       u32 reserved2;  /* 0x08 */
+       u32 reserved3;  /* 0x0c */
+       u32 ohciicr;    /* 0x10: OHCI Interrupt Configuration Register */
+       u32 ohciisr;    /* 0x14: OHCI Interrupt Status Register */
+       u32 reserved4[4];       /* 0x18 ~ 0x24 */
+       u32 secure;             /* 0x28: Security Configuration Register */
+       u32 reserved5[5];       /* 0x2c ~ 0x3c */
+       u32 ebicfg;             /* 0x40: EBI Configuration Register */
+       u32 reserved6[2];       /* 0x44 ~ 0x48 */
+       u32 sn0;                /* 0x4c */
+       u32 sn1;                /* 0x50 */
+       u32 aicredir;   /* 0x54 */
+       u32 l2cc_hramc; /* 0x58 */
+};
+
+/* Register Mapping*/
+#define AT91_SFR_DDRCFG                0x04    /* DDR Configuration Register */
+#define AT91_SFR_CCFG_EBICSA   0x04    /* EBI Chip Select Register */
+/* 0x08 ~ 0x0c: Reserved */
+#define AT91_SFR_OHCIICR       0x10    /* OHCI INT Configuration Register */
+#define AT91_SFR_OHCIISR       0x14    /* OHCI INT Status Register */
+#define AT91_SFR_UTMICKTRIM    0x30    /* UTMI Clock Trimming Register */
+#define AT91_SFR_UTMISWAP      0x3c    /* UTMI DP/DM Pin Swapping Register */
+#define AT91_SFR_LS            0x7c    /* Light Sleep Register */
+#define AT91_SFR_I2SCLKSEL     0x90    /* I2SC Register */
+#define AT91_SFR_WPMR          0xe4    /* Write Protection Mode Register */
+
+/* Bit field in DDRCFG */
+#define ATMEL_SFR_DDRCFG_FDQIEN                0x00010000
+#define ATMEL_SFR_DDRCFG_FDQSIEN       0x00020000
+
+/* Bit field in EBICFG */
+#define AT91_SFR_EBICFG_DRIVE0         (0x3 << 0)
+#define AT91_SFR_EBICFG_DRIVE0_LOW             (0x0 << 0)
+#define AT91_SFR_EBICFG_DRIVE0_MEDIUM          (0x2 << 0)
+#define AT91_SFR_EBICFG_DRIVE0_HIGH            (0x3 << 0)
+#define AT91_SFR_EBICFG_PULL0          (0x3 << 2)
+#define AT91_SFR_EBICFG_PULL0_UP               (0x0 << 2)
+#define AT91_SFR_EBICFG_PULL0_NONE             (0x1 << 2)
+#define AT91_SFR_EBICFG_PULL0_DOWN             (0x3 << 2)
+#define AT91_SFR_EBICFG_SCH0           (0x1 << 4)
+#define AT91_SFR_EBICFG_SCH0_OFF               (0x0 << 4)
+#define AT91_SFR_EBICFG_SCH0_ON                        (0x1 << 4)
+#define AT91_SFR_EBICFG_DRIVE1         (0x3 << 8)
+#define AT91_SFR_EBICFG_DRIVE1_LOW             (0x0 << 8)
+#define AT91_SFR_EBICFG_DRIVE1_MEDIUM          (0x2 << 8)
+#define AT91_SFR_EBICFG_DRIVE1_HIGH            (0x3 << 8)
+#define AT91_SFR_EBICFG_PULL1          (0x3 << 10)
+#define AT91_SFR_EBICFG_PULL1_UP               (0x0 << 10)
+#define AT91_SFR_EBICFG_PULL1_NONE             (0x1 << 10)
+#define AT91_SFR_EBICFG_PULL1_DOWN             (0x3 << 10)
+#define AT91_SFR_EBICFG_SCH1           (0x1 << 12)
+#define AT91_SFR_EBICFG_SCH1_OFF               (0x0 << 12)
+#define AT91_SFR_EBICFG_SCH1_ON                        (0x1 << 12)
+
+/* Bit field in AICREDIR */
+#define ATMEL_SFR_AICREDIR_NSAIC       0x00000001
+
+/* Bit field in DDRCFG */
+#define ATMEL_SFR_DDRCFG_FDQIEN                0x00010000
+#define ATMEL_SFR_DDRCFG_FDQSIEN       0x00020000
+
+#define AT91_SFR_CCFG_EBI_CSA(cs, val)         ((val) << (cs))
+#define AT91_SFR_CCFG_EBI_DBPUC                        BIT(8)
+#define AT91_SFR_CCFG_EBI_DBPDC                        BIT(9)
+#define AT91_SFR_CCFG_EBI_DRIVE_SAM9X60                BIT(16)
+#define AT91_SFR_CCFG_EBI_DRIVE                        BIT(17)
+#define AT91_SFR_CCFG_DQIEN_F                  BIT(20)
+#define AT91_SFR_CCFG_NFD0_ON_D16              BIT(24)
+#define AT91_SFR_CCFG_DDR_MP_EN                        BIT(25)
+
+#define AT91_SFR_OHCIICR_RES(x)                        BIT(x)
+#define AT91_SFR_OHCIICR_ARIE                  BIT(4)
+#define AT91_SFR_OHCIICR_APPSTART              BIT(5)
+#define AT91_SFR_OHCIICR_USB_SUSP(x)           BIT(8 + (x))
+#define AT91_SFR_OHCIICR_UDPPUDIS              BIT(23)
+#define AT91_OHCIICR_USB_SUSPEND               GENMASK(10, 8)
+
+#define AT91_SFR_OHCIISR_RIS(x)                        BIT(x)
+
+#define AT91_UTMICKTRIM_FREQ                   GENMASK(1, 0)
+
+#define AT91_SFR_UTMISWAP_PORT(x)              BIT(x)
+
+#define AT91_SFR_LS_VALUE(x)                   BIT(x)
+#define AT91_SFR_LS_MEM_POWER_GATING_ULP1_EN   BIT(16)
+
+#define AT91_SFR_WPMR_WPEN                     BIT(0)
+#define AT91_SFR_WPMR_WPKEY_MASK               GENMASK(31, 8)
+
+#endif
index 45a76a60fa2c09aaee81a285904ebbbe4add1507..40ec87e2ff94651a1d69589df8fb84b2afc98b68 100644 (file)
@@ -18,6 +18,9 @@ struct atmel_mpddrc_config {
        u32 tpr1;
        u32 tpr2;
        u32 md;
+       u32 lpddr23_lpr;
+       u32 cal_mr4;
+       u32 tim_cal;
 };
 
 /*
@@ -61,6 +64,10 @@ int ddr2_init(const unsigned int base,
              const unsigned int ram_address,
              const struct atmel_mpddrc_config *mpddr_value);
 
+int lpddr2_init(const unsigned int base,
+               const unsigned int ram_address,
+               const struct atmel_mpddrc_config *mpddr_value);
+
 int ddr3_init(const unsigned int base,
              const unsigned int ram_address,
              const struct atmel_mpddrc_config *mpddr_value);
@@ -74,6 +81,11 @@ int ddr3_init(const unsigned int base,
 #define ATMEL_MPDDRC_MR_MODE_EXT_LMR_CMD       0x5
 #define ATMEL_MPDDRC_MR_MODE_DEEP_CMD          0x6
 #define ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD                0x7
+#define ATMEL_MPDDRC_MR_MRS(v)                 (((v) & 0xFF) << 0x8)
+
+/* Bit field in refresh timer register */
+#define ATMEL_MPDDRC_RTR_ADJ_REF               (0x1 << 16)
+#define ATMEL_MPDDRC_RTR_MR4VALUE(v)           (((v) & 0x7) << 20)
 
 /* Bit field in configuration register */
 #define ATMEL_MPDDRC_CR_NC_MASK                        0x3
@@ -157,6 +169,7 @@ int ddr3_init(const unsigned int base,
 #define ATMEL_MPDDRC_MD_DDR3_SDRAM     0x4
 #define ATMEL_MPDDRC_MD_LPDDR3_SDRAM   0x5
 #define ATMEL_MPDDRC_MD_DDR2_SDRAM     0x6
+#define ATMEL_MPDDRC_MD_LPDDR2_SDRAM   0x7
 #define ATMEL_MPDDRC_MD_DBW_MASK       (0x1 << 4)
 #define ATMEL_MPDDRC_MD_DBW_32_BITS    (0x0 << 4)
 #define ATMEL_MPDDRC_MD_DBW_16_BITS    (0x1 << 4)
@@ -206,4 +219,14 @@ int ddr3_init(const unsigned int base,
 #define ATMEL_MPDDRC_RD_DATA_PATH_SHIFT_TWO_CYCLE      0x2
 #define ATMEL_MPDDRC_RD_DATA_PATH_SHIFT_THREE_CYCLE    0x3
 
+/* Bit field in LPDDR2 - LPDDR3 Low Power Register */
+#define ATMEL_MPDDRC_LPDDR23_LPR_DS(x)                 (((x) & 0xf) << 24)
+
+/* Bit field in CAL_MR4 Calibration and MR4 Register */
+#define ATMEL_MPDDRC_CAL_MR4_COUNT_CAL(x)              (((x) & 0xffff) << 0)
+#define ATMEL_MPDDRC_CAL_MR4_MR4R(x)                   (((x) & 0xffff) << 16)
+
+/* Bit field in TIM_CAL Timing Calibration Register */
+#define ATMEL_MPDDRC_CALR_ZQCS(x)                      (((x) & 0xff) << 0)
+
 #endif
index 3a7752b999f64c5a9e34e75f06e700e3677e6e67..88acca85499836ae6a21d8ec7d2233532a6a86f1 100644 (file)
@@ -22,6 +22,8 @@
 # include <asm/arch/at91sam9g45.h>
 #elif defined(CONFIG_AT91SAM9N12) || defined(CONFIG_AT91SAM9X5)
 # include <asm/arch/at91sam9x5.h>
+#elif defined(CONFIG_SAM9X60)
+# include <asm/arch/sam9x60.h>
 #elif defined(CONFIG_SAMA5D2)
 # include <asm/arch/sama5d2.h>
 #elif defined(CONFIG_SAMA5D3)
diff --git a/arch/arm/mach-at91/include/mach/sam9x60.h b/arch/arm/mach-at91/include/mach/sam9x60.h
new file mode 100644 (file)
index 0000000..0f00a9a
--- /dev/null
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Chip-specific header file for the SAM9X60 SoC.
+ *
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ */
+
+#ifndef __SAM9X60_H__
+#define __SAM9X60_H__
+
+/*
+ * Peripheral identifiers/interrupts.
+ */
+#define ATMEL_ID_FIQ           0       /* Advanced Interrupt Controller */
+#define ATMEL_ID_SYS           1       /* System Controller Interrupt */
+#define ATMEL_ID_PIOA          2       /* Parallel I/O Controller A */
+#define ATMEL_ID_PIOB          3       /* Parallel I/O Controller B */
+#define ATMEL_ID_PIOC          4       /* Parallel I/O Controller C */
+#define ATMEL_ID_FLEXCOM0      5       /* FLEXCOM 0 */
+#define ATMEL_ID_FLEXCOM1      6       /* FLEXCOM 1 */
+#define ATMEL_ID_FLEXCOM2      7       /* FLEXCOM 2 */
+#define ATMEL_ID_FLEXCOM3      8       /* FLEXCOM 3 */
+#define ATMEL_ID_FLEXCOM6      9       /* FLEXCOM 6 */
+#define ATMEL_ID_FLEXCOM7      10      /* FLEXCOM 7 */
+#define ATMEL_ID_FLEXCOM8      11      /* FLEXCOM 8 */
+#define ATMEL_ID_SDMMC0                12      /* SDMMC 0 */
+#define ATMEL_ID_FLEXCOM4      13      /* FLEXCOM 4 */
+#define ATMEL_ID_FLEXCOM5      14      /* FLEXCOM 5 */
+#define ATMEL_ID_FLEXCOM9      15      /* FLEXCOM 9 */
+#define ATMEL_ID_FLEXCOM10     16      /* FLEXCOM 10 */
+#define ATMEL_ID_TC01          17      /* Timer Counter 0, 1, 2, 3, 4 and 5 */
+#define ATMEL_ID_PWM           18      /* Pulse Width Modulation Controller */
+#define ATMEL_ID_ADC           19      /* ADC Controller */
+#define ATMEL_ID_XDMAC0                20      /* XDMA Controller 0 */
+#define ATMEL_ID_MATRIX                21      /* BUS Matrix */
+#define ATMEL_ID_UHPHS         22      /* USB Host High Speed */
+#define ATMEL_ID_UDPHS         23      /* USB Device High Speed */
+#define ATMEL_ID_EMAC0         24      /* Ethernet MAC 0 */
+#define ATMEL_ID_LCDC          25      /* LCD Controller */
+#define ATMEL_ID_SDMMC1                26      /* SDMMC 1 */
+#define ATMEL_ID_EMAC1         27      /* Ethernet MAC `1 */
+#define ATMEL_ID_SSC           28      /* Synchronous Serial Controller */
+#define ATMEL_ID_IRQ           31      /* Advanced Interrupt Controller */
+#define ATMEL_ID_TRNG          38      /* True Random Number Generator */
+#define ATMEL_ID_PIOD          44      /* Parallel I/O Controller D */
+#define ATMEL_ID_DBGU          47      /* Debug unit */
+
+/*
+ * User Peripheral physical base addresses.
+ */
+#define ATMEL_BASE_FLEXCOM4    0xf0000000
+#define ATMEL_BASE_FLEXCOM5    0xf0004000
+#define ATMEL_BASE_XDMA0       0xf0008000
+#define ATMEL_BASE_SSC         0xf0010000
+#define ATMEL_BASE_QSPI                0xf0014000
+#define ATMEL_BASE_CAN0                0xf8000000
+#define ATMEL_BASE_CAN1                0xf8004000
+#define ATMEL_BASE_TC0         0xf8008000
+#define ATMEL_BASE_TC1         0xf8008040
+#define ATMEL_BASE_TC2         0xf8008080
+#define ATMEL_BASE_TC3         0xf800c000
+#define ATMEL_BASE_TC4         0xf800c040
+#define ATMEL_BASE_TC5         0xf800c080
+#define ATMEL_BASE_FLEXCOM6    0xf8010000
+#define ATMEL_BASE_FLEXCOM7    0xf8014000
+#define ATMEL_BASE_FLEXCOM8    0xf8018000
+#define ATMEL_BASE_FLEXCOM0    0xf801c000
+#define ATMEL_BASE_FLEXCOM1    0xf8020000
+#define ATMEL_BASE_FLEXCOM2    0xf8024000
+#define ATMEL_BASE_FLEXCOM3    0xf8028000
+#define ATMEL_BASE_EMAC0       0xf802c000
+#define ATMEL_BASE_EMAC1       0xf8030000
+#define ATMEL_BASE_PWM         0xf8034000
+#define ATMEL_BASE_LCDC                0xf8038000
+#define ATMEL_BASE_UDPHS       0xf803c000
+#define ATMEL_BASE_FLEXCOM9    0xf8040000
+#define ATMEL_BASE_FLEXCOM10 0xf8044000
+#define ATMEL_BASE_ISI         0xf8048000
+#define ATMEL_BASE_ADC         0xf804c000
+#define ATMEL_BASE_SFR         0xf8050000
+#define ATMEL_BASE_SYS         0xffffc000
+
+/*
+ * System Peripherals
+ */
+#define ATMEL_BASE_MATRIX      0xffffde00
+#define ATMEL_BASE_PMECC       0xffffe000
+#define ATMEL_BASE_PMERRLOC    0xffffe600
+#define ATMEL_BASE_MPDDRC      0xffffe800
+#define ATMEL_BASE_SMC         0xffffea00
+#define ATMEL_BASE_SDRAMC      0xffffec00
+#define ATMEL_BASE_AIC         0xfffff100
+#define ATMEL_BASE_DBGU                0xfffff200
+#define ATMEL_BASE_PIOA                0xfffff400
+#define ATMEL_BASE_PIOB                0xfffff600
+#define ATMEL_BASE_PIOC                0xfffff800
+#define ATMEL_BASE_PIOD                0xfffffa00
+#define ATMEL_BASE_PMC         0xfffffc00
+#define ATMEL_BASE_RSTC                0xfffffe00
+#define ATMEL_BASE_SHDWC       0xfffffe10
+#define ATMEL_BASE_PIT         0xfffffe40
+#define ATMEL_BASE_GPBR                0xfffffe60
+#define ATMEL_BASE_RTC         0xfffffea8
+#define ATMEL_BASE_WDT         0xffffff80
+
+/*
+ * Internal Memory.
+ */
+#define ATMEL_BASE_ROM         0x00100000 /* Internal ROM base address */
+#define ATMEL_BASE_SRAM                0x00300000 /* Internal SRAM base address */
+#define ATMEL_BASE_UDPHS_FIFO  0x00500000 /* USB Device HS controller */
+#define ATMEL_BASE_OHCI                0x00600000 /* USB Host controller (OHCI) */
+#define ATMEL_BASE_EHCI                0x00700000 /* USB Host controller (EHCI) */
+
+/*
+ * External memory
+ */
+#define ATMEL_BASE_CS0         0x10000000
+#define ATMEL_BASE_CS1         0x20000000
+#define ATMEL_BASE_CS2         0x30000000
+#define ATMEL_BASE_CS3         0x40000000
+#define ATMEL_BASE_CS4         0x50000000
+#define ATMEL_BASE_CS5         0x60000000
+#define ATMEL_BASE_SDMMC0      0x80000000
+#define ATMEL_BASE_SDMMC1      0x90000000
+
+/* 9x60 series chip id definitions */
+#define ARCH_ID_SAM9X60                0x819b35a0
+#define ARCH_ID_VERSION_MASK   0x1f
+#define ARCH_EXID_SAM9X60      0x00000000
+
+#define cpu_is_sam9x60()       (get_chip_id() == ARCH_ID_SAM9X60)
+
+/*
+ * Cpu Name
+ */
+#define ATMEL_CPU_NAME get_cpu_name()
+
+/* Timer */
+#define CONFIG_SYS_TIMER_COUNTER       0xfffffe4c
+
+/*
+ * Other misc defines
+ */
+#define ATMEL_PIO_PORTS        4
+#define CPU_HAS_PCR
+#define CPU_NO_PLLB
+#define PLL_ID_PLLA 0
+#define PLL_ID_UPLL 1
+
+/*
+ * PMECC table in ROM
+ */
+#define ATMEL_PMECC_INDEX_OFFSET_512   0x8000
+#define ATMEL_PMECC_INDEX_OFFSET_1024  0x10000
+
+/*
+ * SAM9X60 specific prototypes
+ */
+#ifndef __ASSEMBLY__
+unsigned int get_chip_id(void);
+unsigned int get_extension_chip_id(void);
+unsigned int has_emac1(void);
+unsigned int has_emac0(void);
+unsigned int has_lcdc(void);
+char *get_cpu_name(void);
+#endif
+
+#endif
diff --git a/arch/arm/mach-at91/include/mach/sama5_sfr.h b/arch/arm/mach-at91/include/mach/sama5_sfr.h
deleted file mode 100644 (file)
index f9c412f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Special Function Register (SFR)
- *
- * Copyright (C) 2014 Atmel
- *                   Bo Shen <voice.shen@atmel.com>
- */
-
-#ifndef __SAMA5_SFR_H
-#define __SAMA5_SFR_H
-
-struct atmel_sfr {
-       u32 reserved1;  /* 0x00 */
-       u32 ddrcfg;     /* 0x04: DDR Configuration Register */
-       u32 reserved2;  /* 0x08 */
-       u32 reserved3;  /* 0x0c */
-       u32 ohciicr;    /* 0x10: OHCI Interrupt Configuration Register */
-       u32 ohciisr;    /* 0x14: OHCI Interrupt Status Register */
-       u32 reserved4[4];       /* 0x18 ~ 0x24 */
-       u32 secure;             /* 0x28: Security Configuration Register */
-       u32 reserved5[5];       /* 0x2c ~ 0x3c */
-       u32 ebicfg;             /* 0x40: EBI Configuration Register */
-       u32 reserved6[2];       /* 0x44 ~ 0x48 */
-       u32 sn0;                /* 0x4c */
-       u32 sn1;                /* 0x50 */
-       u32 aicredir;   /* 0x54 */
-       u32 l2cc_hramc; /* 0x58 */
-};
-
-/* Register Mapping*/
-#define AT91_SFR_UTMICKTRIM    0x30    /* UTMI Clock Trimming Register */
-
-/* Bit field in DDRCFG */
-#define ATMEL_SFR_DDRCFG_FDQIEN                0x00010000
-#define ATMEL_SFR_DDRCFG_FDQSIEN       0x00020000
-
-/* Bit field in EBICFG */
-#define AT91_SFR_EBICFG_DRIVE0         (0x3 << 0)
-#define AT91_SFR_EBICFG_DRIVE0_LOW             (0x0 << 0)
-#define AT91_SFR_EBICFG_DRIVE0_MEDIUM          (0x2 << 0)
-#define AT91_SFR_EBICFG_DRIVE0_HIGH            (0x3 << 0)
-#define AT91_SFR_EBICFG_PULL0          (0x3 << 2)
-#define AT91_SFR_EBICFG_PULL0_UP               (0x0 << 2)
-#define AT91_SFR_EBICFG_PULL0_NONE             (0x1 << 2)
-#define AT91_SFR_EBICFG_PULL0_DOWN             (0x3 << 2)
-#define AT91_SFR_EBICFG_SCH0           (0x1 << 4)
-#define AT91_SFR_EBICFG_SCH0_OFF               (0x0 << 4)
-#define AT91_SFR_EBICFG_SCH0_ON                        (0x1 << 4)
-#define AT91_SFR_EBICFG_DRIVE1         (0x3 << 8)
-#define AT91_SFR_EBICFG_DRIVE1_LOW             (0x0 << 8)
-#define AT91_SFR_EBICFG_DRIVE1_MEDIUM          (0x2 << 8)
-#define AT91_SFR_EBICFG_DRIVE1_HIGH            (0x3 << 8)
-#define AT91_SFR_EBICFG_PULL1          (0x3 << 10)
-#define AT91_SFR_EBICFG_PULL1_UP               (0x0 << 10)
-#define AT91_SFR_EBICFG_PULL1_NONE             (0x1 << 10)
-#define AT91_SFR_EBICFG_PULL1_DOWN             (0x3 << 10)
-#define AT91_SFR_EBICFG_SCH1           (0x1 << 12)
-#define AT91_SFR_EBICFG_SCH1_OFF               (0x0 << 12)
-#define AT91_SFR_EBICFG_SCH1_ON                        (0x1 << 12)
-
-#define AT91_UTMICKTRIM_FREQ           GENMASK(1, 0)
-
-/* Bit field in AICREDIR */
-#define ATMEL_SFR_AICREDIR_NSAIC       0x00000001
-
-#endif
index c7d9bb5ad31454ed54f0bb31363ed0abf5e461f0..d1b2e01cdd0dccd6abdb0fd91a8da08d10b5920a 100644 (file)
 #define ARCH_EXID_SAMA5D225C_D1M       0x00000053
 #define ARCH_EXID_SAMA5D27C_D5M                0x00000032
 #define ARCH_EXID_SAMA5D27C_D1G                0x00000033
+#define ARCH_EXID_SAMA5D27C_LD1G       0x00000061
+#define ARCH_EXID_SAMA5D27C_LD2G       0x00000062
 #define ARCH_EXID_SAMA5D28C_D1G                0x00000013
+#define ARCH_EXID_SAMA5D28C_LD1G       0x00000071
+#define ARCH_EXID_SAMA5D28C_LD2G       0x00000072
 
 /* Checked if defined in ethernet driver macb */
 #define cpu_is_sama5d2 _cpu_is_sama5d2
index 81ccd6ab9a1f816159f2419fc6d48cb07530984d..3df0ea7c7924f5ce0c298a7a14ed96b0f261c9c4 100644 (file)
@@ -10,6 +10,7 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/atmel_mpddrc.h>
+#include <asm/arch/at91_common.h>
 
 #define SAMA5D3_MPDDRC_VERSION         0x140
 
@@ -18,6 +19,7 @@ static inline void atmel_mpddr_op(const struct atmel_mpddr *mpddr,
              u32 ram_address)
 {
        writel(mode, &mpddr->mr);
+       dmb();
        writel(0, ram_address);
 }
 
@@ -227,3 +229,163 @@ int ddr3_init(const unsigned int base,
 
        return 0;
 }
+
+int lpddr2_init(const unsigned int base,
+               const unsigned int ram_address,
+               const struct atmel_mpddrc_config *mpddr_value)
+{
+       struct atmel_mpddr *mpddr = (struct atmel_mpddr *)base;
+       u32 reg;
+
+       writel(mpddr_value->lpddr23_lpr, &mpddr->lpddr23_lpr);
+
+       writel(mpddr_value->tim_cal, &mpddr->tim_cal);
+
+       /* 1. Program the memory device type */
+       writel(mpddr_value->md, &mpddr->md);
+
+       /*
+        * 2. Program features of the LPDDR2-SDRAM device and timing parameters
+        */
+       writel(mpddr_value->cr, &mpddr->cr);
+
+       writel(mpddr_value->tpr0, &mpddr->tpr0);
+       writel(mpddr_value->tpr1, &mpddr->tpr1);
+       writel(mpddr_value->tpr2, &mpddr->tpr2);
+
+       /* 3. A NOP command is issued to the LPDDR2-SDRAM */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_NOP_CMD, ram_address);
+
+       /*
+        * 3bis. Add memory barrier then Perform a write access to
+        * any low-power DDR2-SDRAM address to acknowledge the command.
+       */
+
+       dmb();
+       writel(0, ram_address);
+
+       /* 4. A pause of at least 100 ns must be observed before a single toggle */
+       udelay(1);
+
+       /* 5. A NOP command is issued to the LPDDR2-SDRAM */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_NOP_CMD, ram_address);
+
+       /*  6. A pause of at least 200 us must be observed before a Reset Command */
+       udelay(200);
+
+       /* 7. A Reset command is issued to the low-power DDR2-SDRAM. */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(63), ram_address);
+
+       /*
+        * 8. A pause of at least tINIT5 must be observed before issuing
+        * any commands
+        */
+       udelay(1);
+
+       /* 9. A Calibration command is issued to the low-power DDR2-SDRAM. */
+       reg = readl(&mpddr->cr);
+       reg &= ~ATMEL_MPDDRC_CR_ZQ_RESET;
+       reg |= ATMEL_MPDDRC_CR_ZQ_RESET;
+       writel(reg, &mpddr->cr);
+
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(10), ram_address);
+
+       /*
+        * 9bis: The ZQ Calibration command is now issued.
+        * Program the type of calibration in the MPDDRC_CR: set the
+        * ZQ field to the SHORT value.
+        */
+       reg = readl(&mpddr->cr);
+       reg &= ~ATMEL_MPDDRC_CR_ZQ_RESET;
+       reg |= ATMEL_MPDDRC_CR_ZQ_SHORT;
+       writel(reg, &mpddr->cr);
+
+       /*
+        * 10: A Mode Register Write command with 1 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(1), ram_address);
+
+       /*
+        * 11: A Mode Register Write command with 2 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(2), ram_address);
+
+       /*
+        * 12: A Mode Register Write command with 3 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(3), ram_address);
+
+       /*
+        * 13: A Mode Register Write command with 16 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(16), ram_address);
+
+       /*
+        * 14: In the DDR Configuration Register, open the input buffers.
+        */
+#ifdef CONFIG_ATMEL_SFR
+       configure_ddrcfg_input_buffers(true);
+#endif
+
+       /* 15. A NOP command is issued to the LPDDR2-SDRAM */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_NOP_CMD, ram_address);
+
+       /*
+        * 16: A Mode Register Write command with 5 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(5), ram_address);
+
+       /*
+        * 17: A Mode Register Write command with 6 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(6), ram_address);
+
+       /*
+        * 18: A Mode Register Write command with 8 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(8), ram_address);
+
+       /*
+        * 19: A Mode Register Write command with 0 to the MRS field
+        * is issued to the low-power DDR2-SDRAM.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_LPDDR2_CMD |
+                        ATMEL_MPDDRC_MR_MRS(0), ram_address);
+
+       /*
+        * 20: A Normal Mode command is provided.
+        */
+       atmel_mpddr_op(mpddr, ATMEL_MPDDRC_MR_MODE_NORMAL_CMD, ram_address);
+
+        /* 21: In the DDR Configuration Register, close the input buffers. */
+#ifdef CONFIG_ATMEL_SFR
+       configure_ddrcfg_input_buffers(false);
+#endif
+
+       /*
+        * 22: Write the refresh rate into the COUNT field in the MPDDRC
+        * Refresh Timer Register.
+        */
+       writel(mpddr_value->rtr, &mpddr->rtr);
+
+       /* 23. Configre CAL MR4 register */
+       writel(mpddr_value->cal_mr4, &mpddr->cal_mr4);
+
+       return 0;
+}
index ed8056e87186777eaac8fbc91831bb62838aabc2..4c87cbc00f743e4c13e1b6f792054796442874e0 100644 (file)
@@ -11,7 +11,7 @@ config OMAP34XX
        select ARM_ERRATA_454179
        select ARM_ERRATA_621766
        select ARM_ERRATA_725233
-       select USE_TINY_PRINTF if SPL
+       select SPL_USE_TINY_PRINTF if SPL
        imply NAND_OMAP_GPMC
        imply SPL_FS_EXT4
        imply SPL_FS_FAT
@@ -31,7 +31,7 @@ config OMAP34XX
 
 config OMAP44XX
        bool "OMAP44XX SoC"
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply NAND_OMAP_ELM
        imply NAND_OMAP_GPMC
        imply SPL_DISPLAY_PRINT
@@ -124,7 +124,7 @@ config AM33XX
        imply SPL_NAND_SUPPORT
        imply SYS_I2C_OMAP24XX
        imply SYS_THUMB_BUILD
-       imply USE_TINY_PRINTF
+       imply SPL_USE_TINY_PRINTF
        help
          Support for AM335x SOC from Texas Instruments.
          The AM335x high performance SOC features a Cortex-A8
index 52ab89142578fbb819a912398516ea98d1a5ec6d..8343000c8faae5055419af0f94d64e38546612c1 100644 (file)
@@ -32,7 +32,7 @@ config RCAR_GEN3
        imply SPL_SYS_MALLOC_SIMPLE
        imply SPL_TINY_MEMSET
        imply SPL_YMODEM_SUPPORT
-       imply USE_TINY_PRINTF
+       imply SPL_USE_TINY_PRINTF
 
 config RZA1
        prompt "Renesas ARM SoCs RZ/A1 (32bit)"
index 1441c8069207bb066ef1d3910ea1e0075ce92e7b..d5e437f0d2e2b4e67df9f6f9282c29a5b75d1ce8 100644 (file)
@@ -65,7 +65,7 @@ config TARGET_GOSE
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_KOELSCH
@@ -74,7 +74,7 @@ config TARGET_KOELSCH
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_LAGER
@@ -83,7 +83,7 @@ config TARGET_LAGER
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_KZM9G
@@ -95,7 +95,7 @@ config TARGET_ALT
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_SILK
@@ -104,7 +104,7 @@ config TARGET_SILK
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_PORTER
@@ -113,7 +113,7 @@ config TARGET_PORTER
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 config TARGET_STOUT
@@ -122,7 +122,7 @@ config TARGET_STOUT
        select DM_SERIAL
        select SPL_TINY_MEMSET
        select SUPPORT_SPL
-       select USE_TINY_PRINTF
+       select SPL_USE_TINY_PRINTF
        imply CMD_DM
 
 endchoice
index 1d914648e314fbfde805c3947c2a1d927b3fb486..45de153aa5ab84b4faa948cc560219ccee8c9872 100644 (file)
@@ -4,7 +4,7 @@ config NR_DRAM_BANKS
        default 1
 
 config SPL_SIZE_LIMIT
-       default 65536 if TARGET_SOCFPGA_GEN5
+       default 0x10000 if TARGET_SOCFPGA_GEN5
 
 config SPL_SIZE_LIMIT_PROVIDE_STACK
        default 0x200 if TARGET_SOCFPGA_GEN5
@@ -45,7 +45,7 @@ config TARGET_SOCFPGA_ARRIA10
        select SPL_SYSCON if SPL
        select ETH_DESIGNWARE_SOCFPGA
        imply FPGA_SOCFPGA
-       imply USE_TINY_PRINTF
+       imply SPL_USE_TINY_PRINTF
 
 config TARGET_SOCFPGA_CYCLONE5
        bool
@@ -59,7 +59,7 @@ config TARGET_SOCFPGA_GEN5
        imply SPL_SIZE_LIMIT_SUBTRACT_MALLOC
        imply SPL_STACK_R
        imply SPL_SYS_MALLOC_SIMPLE
-       imply USE_TINY_PRINTF
+       imply SPL_USE_TINY_PRINTF
 
 config TARGET_SOCFPGA_STRATIX10
        bool
index f13bd256cc2aba5fc15d95e3fbcb280b726ebf6e..07f54f0684888b58d155f0d0e39faf558b139035 100644 (file)
@@ -6,7 +6,6 @@
 
 #include <common.h>
 #include <dm.h>
-#include <efi_loader.h>
 #include <env.h>
 #include <errno.h>
 #include <ns16550.h>
@@ -224,19 +223,6 @@ int board_early_init_f(void)
 
 int board_late_init(void)
 {
-#if CONFIG_IS_ENABLED(EFI_LOADER)
-       if (gd->bd->bi_dram[1].start) {
-               /*
-                * Only bank 0 is below board_get_usable_ram_top(), so all of
-                * bank 1 is not mapped by the U-Boot MMU configuration, and so
-                * we must prevent EFI from using it.
-                */
-               efi_add_memory_map(gd->bd->bi_dram[1].start,
-                                  gd->bd->bi_dram[1].size >> EFI_PAGE_SHIFT,
-                                  EFI_BOOT_SERVICES_DATA, false);
-       }
-#endif
-
 #if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
        if (tegra_cpu_is_non_secure()) {
                printf("CPU is in NS mode\n");
index c4d3b17c39d8457817ea514acc6552bbca1f41c3..b45f72f59a77862b8256f4bd19d6d2db77694d5a 100644 (file)
 
 int uniphier_pin_init(const char *pinconfig_name)
 {
-       struct udevice *pctldev, *config, *next;
+       struct udevice *pctldev, *config;
        int ret;
 
        ret = uclass_first_device(UCLASS_PINCTRL, &pctldev);
        if (ret)
                return ret;
 
-       device_foreach_child_safe(config, next, pctldev) {
+       device_foreach_child(config, pctldev) {
                if (strcmp(config->name, pinconfig_name))
                        continue;
 
index fdfb209f77dbd16b30c05d9b20505983e96430e3..2046cb53c4ae6cc4008d29e5e23371996d2a0e40 100644 (file)
@@ -225,6 +225,58 @@ phys_addr_t map_to_sysmem(const void *ptr)
        return mentry->tag;
 }
 
+unsigned int sandbox_read(const void *addr, enum sandboxio_size_t size)
+{
+       struct sandbox_state *state = state_get_current();
+
+       if (!state->allow_memio)
+               return 0;
+
+       switch (size) {
+       case SB_SIZE_8:
+               return *(u8 *)addr;
+       case SB_SIZE_16:
+               return *(u16 *)addr;
+       case SB_SIZE_32:
+               return *(u32 *)addr;
+       case SB_SIZE_64:
+               return *(u64 *)addr;
+       }
+
+       return 0;
+}
+
+void sandbox_write(const void *addr, unsigned int val,
+                  enum sandboxio_size_t size)
+{
+       struct sandbox_state *state = state_get_current();
+
+       if (!state->allow_memio)
+               return;
+
+       switch (size) {
+       case SB_SIZE_8:
+               *(u8 *)addr = val;
+               break;
+       case SB_SIZE_16:
+               *(u16 *)addr = val;
+               break;
+       case SB_SIZE_32:
+               *(u32 *)addr = val;
+               break;
+       case SB_SIZE_64:
+               *(u64 *)addr = val;
+               break;
+       }
+}
+
+void sandbox_set_enable_memio(bool enable)
+{
+       struct sandbox_state *state = state_get_current();
+
+       state->allow_memio = enable;
+}
+
 void sandbox_set_enable_pci_map(int enable)
 {
        enable_pci_map = enable;
index 4f415c71d63227272db33cb7a761409f9814d8f1..44c68a39bc628e75115b6e693c9d5c30061a79a3 100644 (file)
@@ -78,3 +78,10 @@ void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
        }
        hang();
 }
+
+int handoff_arch_save(struct spl_handoff *ho)
+{
+       ho->arch.magic = TEST_HANDOFF_MAGIC;
+
+       return 0;
+}
index 82828f0c1d45f5dcbb7fa7e28c6656e468189a3b..cfc542c8066f84f0d4cd15ac69b30687f60162be 100644 (file)
@@ -147,6 +147,31 @@ static int sandbox_cmdline_cb_default_fdt(struct sandbox_state *state,
 SANDBOX_CMDLINE_OPT_SHORT(default_fdt, 'D', 0,
                "Use the default u-boot.dtb control FDT in U-Boot directory");
 
+static int sandbox_cmdline_cb_test_fdt(struct sandbox_state *state,
+                                      const char *arg)
+{
+       const char *fmt = "/arch/sandbox/dts/test.dtb";
+       char *p;
+       char *fname;
+       int len;
+
+       len = strlen(state->argv[0]) + strlen(fmt) + 1;
+       fname = os_malloc(len);
+       if (!fname)
+               return -ENOMEM;
+       strcpy(fname, state->argv[0]);
+       p = strrchr(fname, '/');
+       if (!p)
+               p = fname + strlen(fname);
+       len -= p - fname;
+       snprintf(p, len, fmt, p);
+       state->fdt_fname = fname;
+
+       return 0;
+}
+SANDBOX_CMDLINE_OPT_SHORT(test_fdt, 'T', 0,
+                         "Use the test.dtb control FDT in U-Boot directory");
+
 static int sandbox_cmdline_cb_interactive(struct sandbox_state *state,
                                          const char *arg)
 {
index c6d5650c20b7fe45b67cdfc4dd37e9ae8846462d..f09bc70b0da71dc2b73f0989b367d7ecbb791356 100644 (file)
                pci@1f,0 {
                        compatible = "pci-generic";
                        reg = <0xf800 0 0 0 0>;
-                       emul@1f,0 {
-                               compatible = "sandbox,swap-case";
-                       };
+                       sandbox,emul = <&swap_case_emul>;
+               };
+       };
+
+       emul {
+               compatible = "sandbox,pci-emul-parent";
+               swap_case_emul: emul@1f,0 {
+                       compatible = "sandbox,swap-case";
                };
        };
 
index 27b0baab2781717ddbaf2a9128666275f4bb6ef0..25cac056bbdf2952fa383d79bf72ed88f465bb42 100644 (file)
                device_type = "pci";
                #address-cells = <3>;
                #size-cells = <2>;
-               ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000
+               ranges = <0x02000000 0 0x10000000 0x10000000 0 0x2000000
                                0x01000000 0 0x20000000 0x20000000 0 0x2000>;
                pci@0,0 {
                        compatible = "pci-generic";
                        reg = <0x0000 0 0 0 0>;
-                       emul@0,0 {
-                               compatible = "sandbox,swap-case";
-                       };
+                       sandbox,emul = <&swap_case_emul0_0>;
                };
                pci@1,0 {
                        compatible = "pci-generic";
-                       reg = <0x0800 0 0 0 0>;
-                       emul@0,0 {
-                               compatible = "sandbox,swap-case";
-                               use-ea;
-                       };
+                       /* reg 0 is at 0x14, using FDT_PCI_SPACE_MEM32 */
+                       reg = <0x02000814 0 0 0 0
+                              0x01000810 0 0 0 0>;
+                       sandbox,emul = <&swap_case_emul0_1>;
                };
                pci@1f,0 {
                        compatible = "pci-generic";
-                       reg = <0xf800 0 0 0 0>;
-                       emul@1f,0 {
-                               compatible = "sandbox,swap-case";
-                       };
+                       /* reg 0 is at 0x10, using FDT_PCI_SPACE_IO */
+                       reg = <0x0100f810 0 0 0 0>;
+                       sandbox,emul = <&swap_case_emul0_1f>;
+               };
+       };
+
+       pci-emul0 {
+               compatible = "sandbox,pci-emul-parent";
+               swap_case_emul0_0: emul0@0,0 {
+                       compatible = "sandbox,swap-case";
+               };
+               swap_case_emul0_1: emul0@1,0 {
+                       compatible = "sandbox,swap-case";
+                       use-ea;
+               };
+               swap_case_emul0_1f: emul0@1f,0 {
+                       compatible = "sandbox,swap-case";
                };
        };
 
                pci@1f,0 {
                        compatible = "pci-generic";
                        reg = <0xf800 0 0 0 0>;
-                       emul@1f,0 {
-                               compatible = "sandbox,swap-case";
-                       };
+                       sandbox,emul = <&swap_case_emul2_1f>;
+               };
+       };
+
+       pci-emul2 {
+               compatible = "sandbox,pci-emul-parent";
+               swap_case_emul2_1f: emul2@1f,0 {
+                       compatible = "sandbox,swap-case";
                };
        };
 
                compatible = "sandbox,spmi";
                #address-cells = <0x1>;
                #size-cells = <0x1>;
+               ranges;
                pm8916@0 {
                        compatible = "qcom,spmi-pmic";
                        reg = <0x0 0x1>;
                        #address-cells = <0x1>;
                        #size-cells = <0x1>;
+                       ranges;
 
                        spmi_gpios: gpios@c000 {
                                compatible = "qcom,pm8916-gpio";
index 2a350a826c4bc240a61479273f0cb1fc43c4e90a..4a35c419723edbeb9aff2e4a926c2fe636960a8a 100644 (file)
@@ -6,6 +6,13 @@
 #ifndef __SANDBOX_ASM_IO_H
 #define __SANDBOX_ASM_IO_H
 
+enum sandboxio_size_t {
+       SB_SIZE_8,
+       SB_SIZE_16,
+       SB_SIZE_32,
+       SB_SIZE_64,
+};
+
 void *phys_to_virt(phys_addr_t paddr);
 #define phys_to_virt phys_to_virt
 
@@ -38,18 +45,21 @@ static inline void unmap_sysmem(const void *vaddr)
 /* Map from a pointer to our RAM buffer */
 phys_addr_t map_to_sysmem(const void *ptr);
 
-/* Define nops for sandbox I/O access */
-#define readb(addr) ((void)addr, 0)
-#define readw(addr) ((void)addr, 0)
-#define readl(addr) ((void)addr, 0)
+unsigned int sandbox_read(const void *addr, enum sandboxio_size_t size);
+void sandbox_write(const void *addr, unsigned int val,
+                  enum sandboxio_size_t size);
+
+#define readb(addr) sandbox_read((const void *)addr, SB_SIZE_8)
+#define readw(addr) sandbox_read((const void *)addr, SB_SIZE_16)
+#define readl(addr) sandbox_read((const void *)addr, SB_SIZE_32)
 #ifdef CONFIG_SANDBOX64
-#define readq(addr) ((void)addr, 0)
+#define readq(addr) sandbox_read((const void *)addr, SB_SIZE_64)
 #endif
-#define writeb(v, addr) ((void)addr)
-#define writew(v, addr) ((void)addr)
-#define writel(v, addr) ((void)addr)
+#define writeb(v, addr) sandbox_write((const void *)addr, v, SB_SIZE_8)
+#define writew(v, addr) sandbox_write((const void *)addr, v, SB_SIZE_16)
+#define writel(v, addr) sandbox_write((const void *)addr, v, SB_SIZE_32)
 #ifdef CONFIG_SANDBOX64
-#define writeq(v, addr) ((void)addr)
+#define writeq(v, addr) sandbox_write((const void *)addr, v, SB_SIZE_64)
 #endif
 
 /*
@@ -110,13 +120,21 @@ phys_addr_t map_to_sysmem(const void *ptr);
 #define clrsetbits_8(addr, clear, set) clrsetbits(8, addr, clear, set)
 
 /* I/O access functions */
-int inl(unsigned int addr);
-int inw(unsigned int addr);
-int inb(unsigned int addr);
+int _inl(unsigned int addr);
+int _inw(unsigned int addr);
+int _inb(unsigned int addr);
+
+void _outl(unsigned int value, unsigned int addr);
+void _outw(unsigned int value, unsigned int addr);
+void _outb(unsigned int value, unsigned int addr);
 
-void outl(unsigned int value, unsigned int addr);
-void outw(unsigned int value, unsigned int addr);
-void outb(unsigned int value, unsigned int addr);
+#define inb(port)      _inb((uintptr_t)(port))
+#define inw(port)      _inw((uintptr_t)(port))
+#define inl(port)      _inl((uintptr_t)(port))
+
+#define outb(val, port)        _outb(val, (uintptr_t)(port))
+#define outw(val, port)        _outw(val, (uintptr_t)(port))
+#define outl(val, port)        _outl(val, (uintptr_t)(port))
 
 #define out_arch(type,endian,a,v)      write##type(cpu_to_##endian(v),a)
 #define in_arch(type,endian,a)         endian##_to_cpu(read##type(a))
@@ -188,6 +206,28 @@ static inline void memcpy_toio(volatile void *dst, const void *src, int count)
 #define insw(port, buf, ns)            _insw((u16 *)port, buf, ns)
 #define outsw(port, buf, ns)           _outsw((u16 *)port, buf, ns)
 
+/* IO space accessors */
+#define clrio(type, addr, clear) \
+       out##type(in##type(addr) & ~(clear), (addr))
+
+#define setio(type, addr, set) \
+       out##type(in##type(addr) | (set), (addr))
+
+#define clrsetio(type, addr, clear, set) \
+       out##type((in##type(addr) & ~(clear)) | (set), (addr))
+
+#define clrio_32(addr, clear) clrio(l, addr, clear)
+#define clrio_16(addr, clear) clrio(w, addr, clear)
+#define clrio_8(addr, clear) clrio(b, addr, clear)
+
+#define setio_32(addr, set) setio(l, addr, set)
+#define setio_16(addr, set) setio(w, addr, set)
+#define setio_8(addr, set) setio(b, addr, set)
+
+#define clrsetio_32(addr, clear, set) clrsetio(l, addr, clear, set)
+#define clrsetio_16(addr, clear, set) clrsetio(w, addr, clear, set)
+#define clrsetio_8(addr, clear, set) clrsetio(b, addr, clear, set)
+
 #include <iotrace.h>
 #include <asm/types.h>
 
index 2d773d3fa6b53c9f0477161b4b352a8c8c4125ca..ad3e94beb9a77e79e6167cf2852b2eb7614ecf52 100644 (file)
@@ -102,6 +102,7 @@ struct sandbox_state {
        ulong next_tag;                 /* Next address tag to allocate */
        struct list_head mapmem_head;   /* struct sandbox_mapmem_entry */
        bool hwspinlock;                /* Hardware Spinlock status */
+       bool allow_memio;               /* Allow readl() etc. to work */
 
        /*
         * This struct is getting large.
index cbf209693daeacdb5c84dcc625c134df043c1dae..cd2b9e3155d73adb1a38c3191cb00477f22c2f7c 100644 (file)
@@ -12,7 +12,7 @@
 #define SANDBOX_I2C_TEST_ADDR          0x59
 
 #define SANDBOX_PCI_VENDOR_ID          0x1234
-#define SANDBOX_PCI_DEVICE_ID          0x5678
+#define SANDBOX_PCI_SWAP_CASE_EMUL_ID  0x5678
 #define SANDBOX_PCI_CLASS_CODE         PCI_CLASS_CODE_COMM
 #define SANDBOX_PCI_CLASS_SUB_CODE     PCI_CLASS_SUB_CODE_COMM_SERIAL
 
@@ -198,4 +198,19 @@ int sandbox_get_pch_spi_protect(struct udevice *dev);
  */
 int sandbox_get_pci_ep_irq_count(struct udevice *dev);
 
+/**
+ * sandbox_pci_read_bar() - Read the BAR value for a read_config operation
+ *
+ * This is used in PCI emulators to read a base address reset. This has special
+ * rules because when the register is set to 0xffffffff it can be used to
+ * discover the type and size of the BAR.
+ *
+ * @barval: Current value of the BAR
+ * @type: Type of BAR (PCI_BASE_ADDRESS_SPACE_IO or
+ *             PCI_BASE_ADDRESS_MEM_TYPE_32)
+ * @size: Size of BAR in bytes
+ * @return BAR value to return from emulator
+ */
+uint sandbox_pci_read_bar(u32 barval, int type, uint size);
+
 #endif
index 01822c606956cb04e74fd425afca688f2c331cb4..f22e47c7f6da1f65dabd13c9aec4d18d3b0d7407 100644 (file)
@@ -91,7 +91,7 @@ static int pci_io_write(unsigned int addr, ulong value, pci_size_t size)
        return -ENOSYS;
 }
 
-int inl(unsigned int addr)
+int _inl(unsigned int addr)
 {
        unsigned long value;
        int ret;
@@ -101,7 +101,7 @@ int inl(unsigned int addr)
        return ret ? 0 : value;
 }
 
-int inw(unsigned int addr)
+int _inw(unsigned int addr)
 {
        unsigned long value;
        int ret;
@@ -111,7 +111,7 @@ int inw(unsigned int addr)
        return ret ? 0 : value;
 }
 
-int inb(unsigned int addr)
+int _inb(unsigned int addr)
 {
        unsigned long value;
        int ret;
@@ -121,17 +121,17 @@ int inb(unsigned int addr)
        return ret ? 0 : value;
 }
 
-void outl(unsigned int value, unsigned int addr)
+void _outl(unsigned int value, unsigned int addr)
 {
        pci_io_write(addr, value, PCI_SIZE_32);
 }
 
-void outw(unsigned int value, unsigned int addr)
+void _outw(unsigned int value, unsigned int addr)
 {
        pci_io_write(addr, value, PCI_SIZE_16);
 }
 
-void outb(unsigned int value, unsigned int addr)
+void _outb(unsigned int value, unsigned int addr)
 {
        pci_io_write(addr, value, PCI_SIZE_8);
 }
index 218e817cf3f8126415693eeb7eea40ff77698cfd..47bf28c434f173a1a9dc9f7ea3875e8b8e9b959f 100644 (file)
@@ -364,6 +364,37 @@ config HAVE_FSP
          Note: Without this binary U-Boot will not be able to set up its
          SDRAM so will not boot.
 
+config USE_CAR
+       bool "Use Cache-As-RAM (CAR) to get temporary RAM at start-up"
+       default y if !HAVE_FSP
+       help
+         Select this option if your board uses CAR init code, typically in a
+         car.S file, to get some initial memory for code execution. This is
+         common with Intel CPUs which don't use FSP.
+
+choice
+       prompt "FSP version"
+       depends on HAVE_FSP
+       default FSP_VERSION1
+       help
+         Selects the FSP version to use. Intel has published several versions
+         of the FSP External Architecture Specification and this allows
+         selection of the version number used by a particular SoC.
+
+config FSP_VERSION1
+       bool "FSP version 1.x"
+       help
+         This covers versions 1.0 and 1.1a. See here for details:
+         https://github.com/IntelFsp/fsp/wiki
+
+config FSP_VERSION2
+       bool "FSP version 2.x"
+       help
+         This covers versions 2.0 and 2.1. See here for details:
+         https://github.com/IntelFsp/fsp/wiki
+
+endchoice
+
 config FSP_FILE
        string "Firmware Support Package binary filename"
        depends on HAVE_FSP
@@ -429,7 +460,7 @@ config ENABLE_MRC_CACHE
 
          For platforms that use Intel FSP for the memory initialization,
          please check FSP output HOB via U-Boot command 'fsp hob' to see
-         if there is FSP_NON_VOLATILE_STORAGE_HOB_GUID (asm/fsp/fsp_hob.h).
+         if there is FSP_NON_VOLATILE_STORAGE_HOB_GUID (asm/fsp1/fsp_hob.h).
          If such GUID does not exist, MRC cache is not available on such
          platform (eg: Intel Queensbay), which means selecting this option
          here does not make any difference.
index 3f1f62da2b1d23ff7b9096708bd5ae87ee5ab885..6296b55ff8ad2d26928c4c4b2b4ccf47e6f36b59 100644 (file)
@@ -26,7 +26,10 @@ endif
 
 extra-$(CONFIG_$(SPL_TPL_)X86_16BIT_INIT) += resetvec.o start16.o
 
-obj-y  += cpu.o cpu_x86.o
+obj-y  += cpu.o
+ifndef CONFIG_TPL_BUILD
+obj-y  += cpu_x86.o
+endif
 
 ifndef CONFIG_$(SPL_)X86_64
 AFLAGS_REMOVE_call32.o := -mregparm=3 \
index 445e4ba2d7f6c2369a60f6b6587849149b6bdf49..f44228e693980080868f3646e2a6ca454934b60a 100644 (file)
@@ -4,10 +4,10 @@
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <cpu.h>
 #include <dm.h>
 #include <dm/uclass-internal.h>
-#include <asm/acpi_s3.h>
 #include <asm/acpi_table.h>
 #include <asm/io.h>
 #include <asm/tables.h>
@@ -167,7 +167,7 @@ void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
  * and PMC_BASE_ADDRESS are accessed, so we need make sure the base addresses
  * of these two blocks are programmed by either U-Boot or FSP.
  *
- * It has been verified that 1st phase API (see arch/x86/lib/fsp/fsp_car.S)
+ * It has been verified that 1st phase API (see arch/x86/lib/fsp1/fsp_car.S)
  * on Intel BayTrail SoC already initializes these two base addresses so
  * we are safe to access these registers here.
  */
index 2eb917283bc00b932de1850541170a9c2d86bc7b..9394eab956ba1fca12e1f08ed9aa13b8396c4838 100644 (file)
@@ -68,9 +68,9 @@ static void set_max_freq(void)
        msr_t msr;
 
        /* Enable speed step */
-       msr = msr_read(MSR_IA32_MISC_ENABLES);
-       msr.lo |= (1 << 16);
-       msr_write(MSR_IA32_MISC_ENABLES, msr);
+       msr = msr_read(MSR_IA32_MISC_ENABLE);
+       msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
+       msr_write(MSR_IA32_MISC_ENABLE, msr);
 
        /*
         * Set guaranteed ratio [21:16] from IACORE_RATIOS to bits [15:8] of
index cefd26299a438ea1dacfdc5b9ff304e5d96d8127..1d1948c91a8d1808b9e1d9cfabbdcd5e3face682 100644 (file)
@@ -7,7 +7,7 @@
 
 #include <common.h>
 #include <fdtdec.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -27,7 +27,7 @@ __weak void update_fsp_azalia_configs(struct azalia_config **azalia)
  * If the device tree does not specify an integer setting, use the default
  * provided in Intel's Baytrail_FSP_Gold4.tgz release FSP/BayleyBayFsp.bsf file.
  */
-void update_fsp_configs(struct fsp_config_data *config,
+void fsp_update_configs(struct fsp_config_data *config,
                        struct fspinit_rtbuf *rt_buf)
 {
        struct upd_region *fsp_upd = &config->fsp_upd;
index 7fe6fa7995a82c71ca445da9bb15d4a05219d5a5..60101d742d15e34a29d02a76d9a9b1af6360b63d 100644 (file)
@@ -5,7 +5,7 @@
 
 #include <common.h>
 #include <fdtdec.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -40,7 +40,7 @@ __weak void update_fsp_gpio_configs(struct gpio_family **family,
  * If the device tree does not specify an integer setting, use the default
  * provided in Intel's Braswell release FSP/BraswellFsp.bsf file.
  */
-void update_fsp_configs(struct fsp_config_data *config,
+void fsp_update_configs(struct fsp_config_data *config,
                        struct fspinit_rtbuf *rt_buf)
 {
        struct upd_region *fsp_upd = &config->fsp_upd;
index bb7c3614081ecece400de3752f22f6d138c136b2..55a7439f1c103b0dacda17b27437c65c5ffd62cf 100644 (file)
@@ -41,12 +41,9 @@ int arch_cpu_init_dm(void)
 
 void set_max_freq(void)
 {
-       msr_t msr, perf_ctl, platform_info;
+       msr_t msr, perf_ctl;
 
-       /* Check for configurable TDP option */
-       platform_info = msr_read(MSR_PLATFORM_INFO);
-
-       if ((platform_info.hi >> 1) & 3) {
+       if (cpu_config_tdp_levels()) {
                /* Set to nominal TDP ratio */
                msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
                perf_ctl.lo = (msr.lo & 0xff) << 8;
@@ -57,17 +54,22 @@ void set_max_freq(void)
        }
 
        perf_ctl.hi = 0;
-       msr_write(IA32_PERF_CTL, perf_ctl);
+       msr_write(MSR_IA32_PERF_CTL, perf_ctl);
 
        debug("CPU: frequency set to %d MHz\n",
-             ((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK);
+             ((perf_ctl.lo >> 8) & 0xff) * INTEL_BCLK_MHZ);
 }
 
 int arch_cpu_init(void)
 {
        post_code(POST_CPU_INIT);
 
+#ifdef CONFIG_TPL
+       /* Do a mini-init if TPL has already done the full init */
+       return x86_cpu_reinit_f();
+#else
        return x86_cpu_init_f();
+#endif
 }
 
 int checkcpu(void)
@@ -98,11 +100,8 @@ int print_cpuinfo(void)
 
 void board_debug_uart_init(void)
 {
-       struct udevice *bus = NULL;
-
        /* com1 / com2 decode range */
-       pci_x86_write_config(bus, PCH_DEV_LPC, LPC_IO_DEC, 1 << 4, PCI_SIZE_16);
+       pci_x86_write_config(PCH_DEV_LPC, LPC_IO_DEC, 1 << 4, PCI_SIZE_16);
 
-       pci_x86_write_config(bus, PCH_DEV_LPC, LPC_EN, COMA_LPC_EN,
-                            PCI_SIZE_16);
+       pci_x86_write_config(PCH_DEV_LPC, LPC_EN, COMA_LPC_EN, PCI_SIZE_16);
 }
index c1db18454987a7433815ae37a528c165e0df7e32..895edeb4bc423edd8cf17759240365d8d4c7938c 100644 (file)
@@ -81,6 +81,13 @@ static const u8 power_limit_time_msr_to_sec[] = {
        [0x11] = 128,
 };
 
+#if defined(CONFIG_SPL_BUILD) && !defined(CONFIG_TPL_BUILD)
+int arch_cpu_init(void)
+{
+       return 0;
+}
+#endif
+
 /*
  * The core 100MHz BLCK is disabled in deeper c-states. One needs to calibrate
  * the 100MHz BCLCK against the 24MHz BLCK to restore the clocks properly
@@ -322,15 +329,6 @@ static int bsp_init_before_ap_bringup(struct udevice *dev)
        return 0;
 }
 
-static int cpu_config_tdp_levels(void)
-{
-       msr_t platform_info;
-
-       /* Bits 34:33 indicate how many levels supported */
-       platform_info = msr_read(MSR_PLATFORM_INFO);
-       return (platform_info.hi >> 1) & 3;
-}
-
 static void set_max_ratio(void)
 {
        msr_t msr, perf_ctl;
@@ -339,7 +337,7 @@ static void set_max_ratio(void)
 
        /* Check for configurable TDP option */
        if (turbo_get_state() == TURBO_ENABLED) {
-               msr = msr_read(MSR_NHM_TURBO_RATIO_LIMIT);
+               msr = msr_read(MSR_TURBO_RATIO_LIMIT);
                perf_ctl.lo = (msr.lo & 0xff) << 8;
        } else if (cpu_config_tdp_levels()) {
                /* Set to nominal TDP ratio */
@@ -350,10 +348,10 @@ static void set_max_ratio(void)
                msr = msr_read(MSR_PLATFORM_INFO);
                perf_ctl.lo = msr.lo & 0xff00;
        }
-       msr_write(IA32_PERF_CTL, perf_ctl);
+       msr_write(MSR_IA32_PERF_CTL, perf_ctl);
 
        debug("cpu: frequency set to %d\n",
-             ((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK);
+             ((perf_ctl.lo >> 8) & 0xff) * INTEL_BCLK_MHZ);
 }
 
 int broadwell_init(struct udevice *dev)
@@ -472,9 +470,9 @@ static void configure_misc(void)
        msr_t msr;
 
        msr = msr_read(MSR_IA32_MISC_ENABLE);
-       msr.lo |= (1 << 0);       /* Fast String enable */
-       msr.lo |= (1 << 3);       /* TM1/TM2/EMTTM enable */
-       msr.lo |= (1 << 16);      /* Enhanced SpeedStep Enable */
+       msr.lo |= MISC_ENABLE_FAST_STRING;
+       msr.lo |= MISC_ENABLE_TM1;
+       msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
        msr_write(MSR_IA32_MISC_ENABLE, msr);
 
        /* Disable thermal interrupts */
@@ -488,24 +486,6 @@ static void configure_misc(void)
        msr_write(MSR_IA32_PACKAGE_THERM_INTERRUPT, msr);
 }
 
-static void configure_thermal_target(struct udevice *dev)
-{
-       int tcc_offset;
-       msr_t msr;
-
-       tcc_offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
-                                   "intel,tcc-offset", 0);
-
-       /* Set TCC activaiton offset if supported */
-       msr = msr_read(MSR_PLATFORM_INFO);
-       if ((msr.lo & (1 << 30)) && tcc_offset) {
-               msr = msr_read(MSR_TEMPERATURE_TARGET);
-               msr.lo &= ~(0xf << 24); /* Bits 27:24 */
-               msr.lo |= (tcc_offset & 0xf) << 24;
-               msr_write(MSR_TEMPERATURE_TARGET, msr);
-       }
-}
-
 static void configure_dca_cap(void)
 {
        struct cpuid_result cpuid_regs;
@@ -555,7 +535,7 @@ static void cpu_core_init(struct udevice *dev)
        configure_misc();
 
        /* Thermal throttle activation offset */
-       configure_thermal_target(dev);
+       cpu_configure_thermal_target(dev);
 
        /* Enable Direct Cache Access */
        configure_dca_cap();
@@ -645,14 +625,7 @@ void cpu_set_power_limits(int power_limit_1_time)
 
 static int broadwell_get_info(struct udevice *dev, struct cpu_info *info)
 {
-       msr_t msr;
-
-       msr = msr_read(IA32_PERF_CTL);
-       info->cpu_freq = ((msr.lo >> 8) & 0xff) * BROADWELL_BCLK * 1000000;
-       info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
-               1 << CPU_FEAT_UCODE | 1 << CPU_FEAT_DEVICE_ID;
-
-       return 0;
+       return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
 }
 
 static int broadwell_get_count(struct udevice *dev)
index 290ee084e5e9ac7726c1115e3ffd84390bd95aeb..9ee4b0294aef6f3cc5ebf869e7ccfd17e141603d 100644 (file)
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <command.h>
 #include <dm.h>
 #include <errno.h>
 #include <malloc.h>
 #include <syscon.h>
 #include <asm/acpi.h>
-#include <asm/acpi_s3.h>
 #include <asm/acpi_table.h>
 #include <asm/control_regs.h>
 #include <asm/coreboot_tables.h>
index d0ac17808c3e3d9f55407446821eb0c4ad378426..4d093a539129ed35cccf69f9111f66f515565593 100644 (file)
@@ -1,11 +1,17 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
+ * Copyright (C) 2014 Google Inc.
  * Copyright (c) 2016 Google, Inc
+ * Copyright (C) 2015-2018 Intel Corporation.
+ * Copyright (C) 2018 Siemens AG
+ * Some code taken from coreboot cpulib.c
  */
 
 #include <common.h>
+#include <cpu.h>
 #include <dm.h>
 #include <errno.h>
+#include <asm/cpu.h>
 #include <asm/cpu_common.h>
 #include <asm/intel_regs.h>
 #include <asm/lapic.h>
@@ -110,3 +116,113 @@ int cpu_set_flex_ratio_to_tdp_nominal(void)
        /* Not reached */
        return -EINVAL;
 }
+
+int cpu_intel_get_info(struct cpu_info *info, int bclk)
+{
+       msr_t msr;
+
+       msr = msr_read(MSR_IA32_PERF_CTL);
+       info->cpu_freq = ((msr.lo >> 8) & 0xff) * bclk * 1000000;
+       info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
+               1 << CPU_FEAT_UCODE | 1 << CPU_FEAT_DEVICE_ID;
+
+       return 0;
+}
+
+int cpu_configure_thermal_target(struct udevice *dev)
+{
+       u32 tcc_offset;
+       msr_t msr;
+       int ret;
+
+       ret = dev_read_u32(dev, "tcc-offset", &tcc_offset);
+       if (!ret)
+               return -ENOENT;
+
+       /* Set TCC activaiton offset if supported */
+       msr = msr_read(MSR_PLATFORM_INFO);
+       if (msr.lo & (1 << 30)) {
+               msr = msr_read(MSR_TEMPERATURE_TARGET);
+               msr.lo &= ~(0xf << 24); /* Bits 27:24 */
+               msr.lo |= (tcc_offset & 0xf) << 24;
+               msr_write(MSR_TEMPERATURE_TARGET, msr);
+       }
+
+       return 0;
+}
+
+void cpu_set_perf_control(uint clk_ratio)
+{
+       msr_t perf_ctl;
+
+       perf_ctl.lo = (clk_ratio & 0xff) << 8;
+       perf_ctl.hi = 0;
+       msr_write(MSR_IA32_PERF_CTL, perf_ctl);
+       debug("CPU: frequency set to %d MHz\n", clk_ratio * INTEL_BCLK_MHZ);
+}
+
+bool cpu_config_tdp_levels(void)
+{
+       msr_t platform_info;
+
+       /* Bits 34:33 indicate how many levels supported */
+       platform_info = msr_read(MSR_PLATFORM_INFO);
+
+       return ((platform_info.hi >> 1) & 3) != 0;
+}
+
+void cpu_set_p_state_to_turbo_ratio(void)
+{
+       msr_t msr;
+
+       msr = msr_read(MSR_TURBO_RATIO_LIMIT);
+       cpu_set_perf_control(msr.lo);
+}
+
+enum burst_mode_t cpu_get_burst_mode_state(void)
+{
+       enum burst_mode_t state;
+       int burst_en, burst_cap;
+       msr_t msr;
+       uint eax;
+
+       eax = cpuid_eax(0x6);
+       burst_cap = eax & 0x2;
+       msr = msr_read(MSR_IA32_MISC_ENABLE);
+       burst_en = !(msr.hi & BURST_MODE_DISABLE);
+
+       if (!burst_cap && burst_en)
+               state = BURST_MODE_UNAVAILABLE;
+       else if (burst_cap && !burst_en)
+               state = BURST_MODE_DISABLED;
+       else if (burst_cap && burst_en)
+               state = BURST_MODE_ENABLED;
+       else
+               state = BURST_MODE_UNKNOWN;
+
+       return state;
+}
+
+void cpu_set_burst_mode(bool burst_mode)
+{
+       msr_t msr;
+
+       msr = msr_read(MSR_IA32_MISC_ENABLE);
+       if (burst_mode)
+               msr.hi &= ~BURST_MODE_DISABLE;
+       else
+               msr.hi |= BURST_MODE_DISABLE;
+       msr_write(MSR_IA32_MISC_ENABLE, msr);
+}
+
+void cpu_set_eist(bool eist_status)
+{
+       msr_t msr;
+
+       msr = msr_read(MSR_IA32_MISC_ENABLE);
+       if (eist_status)
+               msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
+       else
+               msr.lo &= ~MISC_ENABLE_ENHANCED_SPEEDSTEP;
+       msr_write(MSR_IA32_MISC_ENABLE, msr);
+}
index a6233c75ce2cc2c0b1a18d05e70de9ec73cbe71e..b7bb524162f6f0e48cf2a38005cdd8c5d56a41c4 100644 (file)
@@ -6,6 +6,7 @@
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
+#include <handoff.h>
 #include <asm/cpu_common.h>
 #include <asm/intel_regs.h>
 #include <asm/lapic.h>
@@ -21,6 +22,11 @@ int arch_cpu_init(void)
 {
        int ret;
 
+#if CONFIG_IS_ENABLED(HANDOFF) && IS_ENABLED(CONFIG_USE_HOB)
+       struct spl_handoff *ho = gd->spl_handoff;
+
+       gd->arch.hob_list = ho->arch.hob_list;
+#endif
        ret = x86_cpu_reinit_f();
 
        return ret;
index c8b16e32c0342ed02d5f110b165d41d74a69ccca..6db9da81b71b0e7a58dfa7e7c517b14d9fceec99 100644 (file)
@@ -199,6 +199,5 @@ int print_cpuinfo(void)
 void board_debug_uart_init(void)
 {
        /* This enables the debug UART */
-       pci_x86_write_config(NULL, PCH_LPC_DEV, LPC_EN, COMA_LPC_EN,
-                            PCI_SIZE_16);
+       pci_x86_write_config(PCH_LPC_DEV, LPC_EN, COMA_LPC_EN, PCI_SIZE_16);
 }
index 2fd06b3bed76dacce498cc2a6585af8cbd4d6b66..0e6453c84778f9227219d6bfda62dc4f36d56f1b 100644 (file)
@@ -5,11 +5,11 @@
 
 #include <common.h>
 #include <fdtdec.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
-void update_fsp_configs(struct fsp_config_data *config,
+void fsp_update_configs(struct fsp_config_data *config,
                        struct fspinit_rtbuf *rt_buf)
 {
        struct platform_config *plat_config = &config->plat_config;
index 6edc3e233c29f4cceaadfa7a6f80c7ee8a575f31..56ab6bf4acf5c7db82c94cb03d69aacd4aabd509 100644 (file)
@@ -12,6 +12,7 @@
 #include <fdtdec.h>
 #include <malloc.h>
 #include <asm/cpu.h>
+#include <asm/cpu_common.h>
 #include <asm/cpu_x86.h>
 #include <asm/msr.h>
 #include <asm/msr-index.h>
@@ -139,19 +140,16 @@ static const u8 power_limit_time_msr_to_sec[] = {
        [0x11] = 128,
 };
 
-int cpu_config_tdp_levels(void)
+bool cpu_ivybridge_config_tdp_levels(void)
 {
        struct cpuid_result result;
-       msr_t platform_info;
 
        /* Minimum CPU revision */
        result = cpuid(1);
        if (result.eax < IVB_CONFIG_TDP_MIN_CPUID)
-               return 0;
+               return false;
 
-       /* Bits 34:33 indicate how many levels supported */
-       platform_info = msr_read(MSR_PLATFORM_INFO);
-       return (platform_info.hi >> 1) & 3;
+       return cpu_config_tdp_levels();
 }
 
 /*
@@ -212,7 +210,7 @@ void set_power_limits(u8 power_limit_1_time)
        msr_write(MSR_PKG_POWER_LIMIT, limit);
 
        /* Use nominal TDP values for CPUs with configurable TDP */
-       if (cpu_config_tdp_levels()) {
+       if (cpu_ivybridge_config_tdp_levels()) {
                msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
                limit.hi = 0;
                limit.lo = msr.lo & 0xff;
@@ -282,26 +280,6 @@ static void configure_c_states(void)
        msr_write(MSR_PP1_CURRENT_CONFIG, msr);
 }
 
-static int configure_thermal_target(struct udevice *dev)
-{
-       int tcc_offset;
-       msr_t msr;
-
-       tcc_offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
-                                   "tcc-offset", 0);
-
-       /* Set TCC activaiton offset if supported */
-       msr = msr_read(MSR_PLATFORM_INFO);
-       if ((msr.lo & (1 << 30)) && tcc_offset) {
-               msr = msr_read(MSR_TEMPERATURE_TARGET);
-               msr.lo &= ~(0xf << 24); /* Bits 27:24 */
-               msr.lo |= (tcc_offset & 0xf) << 24;
-               msr_write(MSR_TEMPERATURE_TARGET, msr);
-       }
-
-       return 0;
-}
-
 static void configure_misc(void)
 {
        msr_t msr;
@@ -348,24 +326,20 @@ static void configure_dca_cap(void)
 
 static void set_max_ratio(void)
 {
-       msr_t msr, perf_ctl;
-
-       perf_ctl.hi = 0;
+       msr_t msr;
+       uint ratio;
 
        /* Check for configurable TDP option */
-       if (cpu_config_tdp_levels()) {
+       if (cpu_ivybridge_config_tdp_levels()) {
                /* Set to nominal TDP ratio */
                msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
-               perf_ctl.lo = (msr.lo & 0xff) << 8;
+               ratio = msr.lo & 0xff;
        } else {
                /* Platform Info bits 15:8 give max ratio */
                msr = msr_read(MSR_PLATFORM_INFO);
-               perf_ctl.lo = msr.lo & 0xff00;
+               ratio = (msr.lo & 0xff00) >> 8;
        }
-       msr_write(MSR_IA32_PERF_CTL, perf_ctl);
-
-       debug("model_x06ax: frequency set to %d\n",
-             ((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK);
+       cpu_set_perf_control(ratio);
 }
 
 static void set_energy_perf_bias(u8 policy)
@@ -413,10 +387,11 @@ static int model_206ax_init(struct udevice *dev)
        configure_misc();
 
        /* Thermal throttle activation offset */
-       ret = configure_thermal_target(dev);
+       ret = cpu_configure_thermal_target(dev);
        if (ret) {
                debug("Cannot set thermal target\n");
-               return ret;
+               if (ret != -ENOENT)
+                       return ret;
        }
 
        /* Enable Direct Cache Access */
@@ -436,12 +411,7 @@ static int model_206ax_init(struct udevice *dev)
 
 static int model_206ax_get_info(struct udevice *dev, struct cpu_info *info)
 {
-       msr_t msr;
-
-       msr = msr_read(MSR_IA32_PERF_CTL);
-       info->cpu_freq = ((msr.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK * 1000000;
-       info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
-               1 << CPU_FEAT_UCODE;
+       return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
 
        return 0;
 }
index a809b823b3bcf8997756527c09834d32b5c4be2b..0f427afcb826b8be6e04c10929899df7ccb1b98d 100644 (file)
@@ -141,7 +141,7 @@ static void northbridge_init(struct udevice *dev, int rev)
         * CPUs with configurable TDP also need power limits set
         * in MCHBAR.  Use same values from MSR_PKG_POWER_LIMIT.
         */
-       if (cpu_config_tdp_levels()) {
+       if (cpu_ivybridge_config_tdp_levels()) {
                msr_t msr = msr_read(MSR_PKG_POWER_LIMIT);
 
                writel(msr.lo, MCHBAR_REG(0x59A0));
index 0939736164d496ea8f2ad0ffa107837302db230d..a00db422e7a91ace8f28032ebdd0c845bc453158 100644 (file)
@@ -50,11 +50,20 @@ void mtrr_close(struct mtrr_state *state, bool do_caches)
                enable_caches();
 }
 
+static void set_var_mtrr(uint reg, uint type, uint64_t start, uint64_t size)
+{
+       u64 mask;
+
+       wrmsrl(MTRR_PHYS_BASE_MSR(reg), start | type);
+       mask = ~(size - 1);
+       mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
+       wrmsrl(MTRR_PHYS_MASK_MSR(reg), mask | MTRR_PHYS_MASK_VALID);
+}
+
 int mtrr_commit(bool do_caches)
 {
        struct mtrr_request *req = gd->arch.mtrr_req;
        struct mtrr_state state;
-       uint64_t mask;
        int i;
 
        debug("%s: enabled=%d, count=%d\n", __func__, gd->arch.has_mtrr,
@@ -65,12 +74,8 @@ int mtrr_commit(bool do_caches)
        debug("open\n");
        mtrr_open(&state, do_caches);
        debug("open done\n");
-       for (i = 0; i < gd->arch.mtrr_req_count; i++, req++) {
-               mask = ~(req->size - 1);
-               mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
-               wrmsrl(MTRR_PHYS_BASE_MSR(i), req->start | req->type);
-               wrmsrl(MTRR_PHYS_MASK_MSR(i), mask | MTRR_PHYS_MASK_VALID);
-       }
+       for (i = 0; i < gd->arch.mtrr_req_count; i++, req++)
+               set_var_mtrr(i, req->type, req->start, req->size);
 
        /* Clear the ones that are unused */
        debug("clear\n");
@@ -107,3 +112,41 @@ int mtrr_add_request(int type, uint64_t start, uint64_t size)
 
        return 0;
 }
+
+static int get_var_mtrr_count(void)
+{
+       return msr_read(MSR_MTRR_CAP_MSR).lo & MSR_MTRR_CAP_VCNT;
+}
+
+static int get_free_var_mtrr(void)
+{
+       struct msr_t maskm;
+       int vcnt;
+       int i;
+
+       vcnt = get_var_mtrr_count();
+
+       /* Identify the first var mtrr which is not valid */
+       for (i = 0; i < vcnt; i++) {
+               maskm = msr_read(MTRR_PHYS_MASK_MSR(i));
+               if ((maskm.lo & MTRR_PHYS_MASK_VALID) == 0)
+                       return i;
+       }
+
+       /* No free var mtrr */
+       return -ENOSPC;
+}
+
+int mtrr_set_next_var(uint type, uint64_t start, uint64_t size)
+{
+       int mtrr;
+
+       mtrr = get_free_var_mtrr();
+       if (mtrr < 0)
+               return mtrr;
+
+       set_var_mtrr(mtrr, type, start, size);
+       debug("MTRR %x: start=%x, size=%x\n", mtrr, (uint)start, (uint)size);
+
+       return 0;
+}
index c6218250e161f12e65d190daa3f5b9f3344b6e3e..e1aae158ce577318a559a5332a8b4fe9de0ad0d3 100644 (file)
@@ -16,8 +16,8 @@
 #include <asm/io.h>
 #include <asm/pci.h>
 
-int pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
-                       ulong *valuep, enum pci_size_t size)
+int pci_x86_read_config(pci_dev_t bdf, uint offset, ulong *valuep,
+                       enum pci_size_t size)
 {
        outl(bdf | (offset & 0xfc) | PCI_CFG_EN, PCI_REG_ADDR);
        switch (size) {
@@ -35,8 +35,8 @@ int pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
        return 0;
 }
 
-int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset,
-                        ulong value, enum pci_size_t size)
+int pci_x86_write_config(pci_dev_t bdf, uint offset, ulong value,
+                        enum pci_size_t size)
 {
        outl(bdf | (offset & 0xfc) | PCI_CFG_EN, PCI_REG_ADDR);
        switch (size) {
@@ -54,6 +54,21 @@ int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset,
        return 0;
 }
 
+int pci_x86_clrset_config(pci_dev_t bdf, uint offset, ulong clr, ulong set,
+                         enum pci_size_t size)
+{
+       ulong value;
+       int ret;
+
+       ret = pci_x86_read_config(bdf, offset, &value, size);
+       if (ret)
+               return ret;
+       value &= ~clr;
+       value |= set;
+
+       return pci_x86_write_config(bdf, offset, value, size);
+}
+
 void pci_assign_irqs(int bus, int device, u8 irq[4])
 {
        pci_dev_t bdf;
index c4d117783cbf3ada7f8743d503937dc838662ec9..381edd0761587b21747b33ee874f92c0f9f02f88 100644 (file)
@@ -5,9 +5,9 @@
  */
 
 #include <common.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
-void update_fsp_configs(struct fsp_config_data *config,
+void fsp_update_configs(struct fsp_config_data *config,
                        struct fspinit_rtbuf *rt_buf)
 {
        /* Initialize runtime buffer for fsp_init() */
index 76556fc7f79425964f913ddaa678092e6d6fd669..66737e655bbe26df3e4ce69a3fbcf24fd911103c 100644 (file)
@@ -12,7 +12,7 @@
 #include <asm/post.h>
 #include <asm/arch/device.h>
 #include <asm/arch/tnc.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 #include <asm/processor.h>
 
 static int __maybe_unused disable_igd(void)
index 3c9bdf2a9d6f026356495f2e5ed03626dc48e157..01524635e9c82d9a498653605dc36d2107ea8d90 100644 (file)
@@ -2,6 +2,18 @@
 /*
  *  U-Boot - x86 Startup Code
  *
+ * This is always the first code to run from the U-Boot source. To spell it out:
+ *
+ * 1. When TPL (Tertiary Program Loader) is enabled, the boot flow is
+ * TPL->SPL->U-Boot and this file is used for TPL. Then start_from_tpl.S is used
+ * for SPL and start_from_spl.S is used for U-Boot proper.
+ *
+ * 2. When SPL (Secondary Program Loader) is enabled, but not TPL, the boot
+ * flow is SPL->U-Boot and this file is used for SPL. Then start_from_spl.S is
+ * used for U-Boot proper.
+ *
+ * 3. When neither TPL nor SPL is used, this file is used for U-Boot proper.
+ *
  * (C) Copyright 2008-2011
  * Graeme Russ, <graeme.russ@gmail.com>
  *
@@ -90,7 +102,7 @@ early_board_init_ret:
        jmp     car_init
 .globl car_init_ret
 car_init_ret:
-#ifndef CONFIG_USE_HOB
+#ifdef CONFIG_USE_CAR
        /*
         * We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM,
         * or fully initialised SDRAM - we really don't care which)
@@ -130,7 +142,7 @@ car_init_ret:
 
        /* Get address of global_data */
        mov     %fs:0, %edx
-#ifdef CONFIG_USE_HOB
+#if defined(CONFIG_USE_HOB) && !defined(CONFIG_USE_CAR)
        /* Store the HOB list if we have one */
        test    %esi, %esi
        jz      skip_hob
index 4d4e5d0758d44b26720edc16afd1b0f981654499..a73b4d7c4597f7a5df030aa46d0e41b66fa08cc5 100644 (file)
@@ -1,7 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * 32-bit x86 Startup Code when running from SPL
- *
+ * 32-bit x86 Startup Code when running from SPL. This is the startup code in
+ * U-Boot proper, when SPL is used.
+
  * Copyright 2018 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
  */
index 44b5363a6852db19a1cb191da54eacf855e366d6..9a4974a5f1b7110bb9296c03f805506406526003 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * 32-bit x86 Startup Code when running from TPL
+ * 32-bit x86 Startup Code when running from TPL. This is the startup code in
+ * SPL, when TPL is used.
  *
  * Copyright 2018 Google, Inc
  * Written by Simon Glass <sjg@chromium.org>
index a41d511238dffb8e6d710034fbd59dfac7980bd3..be468d2b2cac5f5b28ce83effb6eacce0e0a46a1 100644 (file)
@@ -60,8 +60,8 @@ int turbo_get_state(void)
        cpuid_regs = cpuid(CPUID_LEAF_PM);
        turbo_cap = !!(cpuid_regs.eax & PM_CAP_TURBO_MODE);
 
-       msr = msr_read(MSR_IA32_MISC_ENABLES);
-       turbo_en = !(msr.hi & H_MISC_DISABLE_TURBO);
+       msr = msr_read(MSR_IA32_MISC_ENABLE);
+       turbo_en = !(msr.hi & MISC_DISABLE_TURBO);
 
        if (!turbo_cap && turbo_en) {
                /* Unavailable */
@@ -86,9 +86,9 @@ void turbo_enable(void)
        /* Only possible if turbo is available but hidden */
        if (turbo_get_state() == TURBO_DISABLED) {
                /* Clear Turbo Disable bit in Misc Enables */
-               msr = msr_read(MSR_IA32_MISC_ENABLES);
-               msr.hi &= ~H_MISC_DISABLE_TURBO;
-               msr_write(MSR_IA32_MISC_ENABLES, msr);
+               msr = msr_read(MSR_IA32_MISC_ENABLE);
+               msr.hi &= ~MISC_DISABLE_TURBO;
+               msr_write(MSR_IA32_MISC_ENABLE, msr);
 
                /* Update cached turbo state */
                set_global_turbo_state(TURBO_ENABLED);
index f20c0b810d3cd4750ea31de2d7b5de4014096fe1..c1e9bfbf66f726d7e5a6734c379e9374b5902e73 100644 (file)
@@ -35,6 +35,12 @@ SECTIONS
        . = ALIGN(4);
        __data_end = .;
        __init_end = .;
+       . = ALIGN(4);
+       .binman_sym_table : {
+               __binman_sym_start = .;
+               KEEP(*(SORT(.binman_sym*)));
+               __binman_sym_end = .;
+       }
 
         _image_binary_end = .;
 
index 663b02f27d852975d843dfcc13276cb7dece368c..244ca1276af70834879f9d29e21a6df3dcf3c386 100644 (file)
@@ -5,7 +5,7 @@
  * From coreboot src/arch/x86/wakeup.S
  */
 
-#include <asm/acpi_s3.h>
+#include <acpi_s3.h>
 #include <asm/processor.h>
 #include <asm/processor-flags.h>
 
diff --git a/arch/x86/include/asm/acpi_s3.h b/arch/x86/include/asm/acpi_s3.h
deleted file mode 100644 (file)
index baa848d..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#ifndef __ASM_ACPI_S3_H__
-#define __ASM_ACPI_S3_H__
-
-#define WAKEUP_BASE    0x600
-
-/* PM1_STATUS register */
-#define WAK_STS                (1 << 15)
-#define PCIEXPWAK_STS  (1 << 14)
-#define RTC_STS                (1 << 10)
-#define SLPBTN_STS     (1 << 9)
-#define PWRBTN_STS     (1 << 8)
-#define GBL_STS                (1 << 5)
-#define BM_STS         (1 << 4)
-#define TMR_STS                (1 << 0)
-
-/* PM1_CNT register */
-#define SLP_EN         (1 << 13)
-#define SLP_TYP_SHIFT  10
-#define SLP_TYP                (7 << SLP_TYP_SHIFT)
-#define SLP_TYP_S0     0
-#define SLP_TYP_S1     1
-#define SLP_TYP_S3     5
-#define SLP_TYP_S4     6
-#define SLP_TYP_S5     7
-
-/* Memory size reserved for S3 resume */
-#define S3_RESERVE_SIZE        0x1000
-
-#ifndef __ASSEMBLY__
-
-extern char __wakeup[];
-extern int __wakeup_size;
-
-enum acpi_sleep_state {
-       ACPI_S0,
-       ACPI_S1,
-       ACPI_S2,
-       ACPI_S3,
-       ACPI_S4,
-       ACPI_S5,
-};
-
-/**
- * acpi_ss_string() - get ACPI-defined sleep state string
- *
- * @pm1_cnt:   ACPI-defined sleep state
- * @return:    a pointer to the sleep state string.
- */
-static inline char *acpi_ss_string(enum acpi_sleep_state state)
-{
-       char *ss_string[] = { "S0", "S1", "S2", "S3", "S4", "S5"};
-
-       return ss_string[state];
-}
-
-/**
- * acpi_sleep_from_pm1() - get ACPI-defined sleep state from PM1_CNT register
- *
- * @pm1_cnt:   PM1_CNT register value
- * @return:    ACPI-defined sleep state if given valid PM1_CNT register value,
- *             -EINVAL otherwise.
- */
-static inline enum acpi_sleep_state acpi_sleep_from_pm1(u32 pm1_cnt)
-{
-       switch ((pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
-       case SLP_TYP_S0:
-               return ACPI_S0;
-       case SLP_TYP_S1:
-               return ACPI_S1;
-       case SLP_TYP_S3:
-               return ACPI_S3;
-       case SLP_TYP_S4:
-               return ACPI_S4;
-       case SLP_TYP_S5:
-               return ACPI_S5;
-       }
-
-       return -EINVAL;
-}
-
-/**
- * chipset_prev_sleep_state() - Get chipset previous sleep state
- *
- * This returns chipset previous sleep state from ACPI registers.
- * Platform codes must supply this routine in order to support ACPI S3.
- *
- * @return ACPI_S0/S1/S2/S3/S4/S5.
- */
-enum acpi_sleep_state chipset_prev_sleep_state(void);
-
-/**
- * chipset_clear_sleep_state() - Clear chipset sleep state
- *
- * This clears chipset sleep state in ACPI registers.
- * Platform codes must supply this routine in order to support ACPI S3.
- */
-void chipset_clear_sleep_state(void);
-
-struct acpi_fadt;
-/**
- * acpi_resume() - Do ACPI S3 resume
- *
- * This calls U-Boot wake up assembly stub and jumps to OS's wake up vector.
- *
- * @fadt:      FADT table pointer in the ACPI table
- * @return:    Never returns
- */
-void acpi_resume(struct acpi_fadt *fadt);
-
-/**
- * acpi_s3_reserve() - Reserve memory for ACPI S3 resume
- *
- * This copies memory where real mode interrupt handler stubs reside to the
- * reserved place on the stack.
- *
- * This routine should be called by reserve_arch() before U-Boot is relocated
- * when ACPI S3 resume is enabled.
- *
- * @return:    0 always
- */
-int acpi_s3_reserve(void);
-
-#endif /* __ASSEMBLY__ */
-
-#endif /* __ASM_ACPI_S3_H__ */
index ca22a7999680519699df350c03f734e60b8362b9..3bc3bd6609e2a49301f5fd03a2e35aa81f9511fa 100644 (file)
@@ -21,9 +21,6 @@
 #define CPUID_BROADWELL_D0     0x306d3
 #define CPUID_BROADWELL_E0     0x306d4
 
-/* Broadwell bus clock is fixed at 100MHz */
-#define BROADWELL_BCLK         100
-
 #define BROADWELL_FAMILY_ULT   0x306d0
 
 #define CORE_THREAD_COUNT_MSR          0x35
index 23153a040fa960e74a8c2f2ab6ae23c87b6e5416..ecdf6d16f94279c6896370fd1be232510a728b94 100644 (file)
@@ -6,9 +6,6 @@
 #ifndef __ASM_ARCH_PCH_H
 #define __ASM_ARCH_PCH_H
 
-/* CPU bus clock is fixed at 100MHz */
-#define CPU_BCLK               100
-
 #define PMBASE                 0x40
 #define ACPI_CNTL              0x44
 #define  ACPI_EN               (1 << 7)
index 850d96bdd9aa1b5a0f572a81dc7d86f8044bfdfb..4839ebc3124413805f6b0076c11ab00a1db04fd1 100644 (file)
@@ -8,9 +8,6 @@
 #ifndef _ASM_ARCH_MODEL_206AX_H
 #define _ASM_ARCH_MODEL_206AX_H
 
-/* SandyBridge/IvyBridge bus clock is fixed at 100MHz */
-#define SANDYBRIDGE_BCLK               100
-
 #define  CPUID_VMX                     (1 << 5)
 #define  CPUID_SMX                     (1 << 6)
 #define MSR_FEATURE_CONFIG             0x13c
@@ -61,6 +58,6 @@
 
 /* Configure power limits for turbo mode */
 void set_power_limits(u8 power_limit_1_time);
-int cpu_config_tdp_levels(void);
+bool cpu_ivybridge_config_tdp_levels(void);
 
 #endif
index 4c91a5daced87871449ad234c1ce1a88cc6b39e5..cdd99a90b768d7801c8ab39e2680ea69dbc42adc 100644 (file)
@@ -1,12 +1,19 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
+ * Common code for Intel CPUs
+ *
  * Copyright (c) 2016 Google, Inc
  */
 
 #ifndef __ASM_CPU_COMMON_H
 #define __ASM_CPU_COMMON_H
 
-#define IA32_PERF_CTL                  0x199
+/* Standard Intel bus clock is fixed at 100MHz */
+enum {
+       INTEL_BCLK_MHZ          = 100
+};
+
+struct cpu_info;
 
 /**
  * cpu_common_init() - Set up common CPU init
@@ -31,4 +38,94 @@ int cpu_common_init(void);
  */
 int cpu_set_flex_ratio_to_tdp_nominal(void);
 
+/**
+ * cpu_intel_get_info() - Obtain CPU info for Intel CPUs
+ *
+ * Most Intel CPUs use the same MSR to obtain the clock speed, and use the same
+ * features. This function fills in these values, given the value of the base
+ * clock in MHz (typically this should be set to 100).
+ *
+ * @info:      cpu_info struct to fill in
+ * @bclk_mz:   the base clock in MHz
+ *
+ * @return 0 always
+ */
+int cpu_intel_get_info(struct cpu_info *info, int bclk_mz);
+
+/**
+ * cpu_configure_thermal_target() - Set the thermal target for a CPU
+ *
+ * This looks up the tcc-offset property and uses it to set the
+ * MSR_TEMPERATURE_TARGET value.
+ *
+ * @dev: CPU device
+ * @return 0 if OK, -ENOENT if no target is given in device tree
+ */
+int cpu_configure_thermal_target(struct udevice *dev);
+
+/**
+ * cpu_set_perf_control() - Set the nominal CPU clock speed
+ *
+ * This sets the clock speed as a multiplier of BCLK
+ *
+ * @clk_ratio: Ratio to use
+ */
+void cpu_set_perf_control(uint clk_ratio);
+
+/**
+ * cpu_config_tdp_levels() - Check for configurable TDP option
+ *
+ * @return true if the CPU has configurable TDP (Thermal-design power)
+ */
+bool cpu_config_tdp_levels(void);
+
+/** enum burst_mode_t - Burst-mode states */
+enum burst_mode_t {
+       BURST_MODE_UNKNOWN,
+       BURST_MODE_UNAVAILABLE,
+       BURST_MODE_DISABLED,
+       BURST_MODE_ENABLED
+};
+
+/*
+ * cpu_get_burst_mode_state() - Get the Burst/Turbo Mode State
+ *
+ * This reads MSR IA32_MISC_ENABLE 0x1A0
+ * Bit 38 - TURBO_MODE_DISABLE Bit to get state ENABLED / DISABLED.
+ * Also checks cpuid 0x6 to see whether burst mode is supported.
+ *
+ * @return current burst mode status
+ */
+enum burst_mode_t cpu_get_burst_mode_state(void);
+
+/**
+ * cpu_set_burst_mode() - Set CPU burst mode
+ *
+ * @burst_mode: true to enable burst mode, false to disable
+ */
+void cpu_set_burst_mode(bool burst_mode);
+
+/**
+ * cpu_set_eist() - Enable Enhanced Intel Speed Step Technology
+ *
+ * @eist_status: true to enable EIST, false to disable
+ */
+void cpu_set_eist(bool eist_status);
+
+/**
+ * cpu_set_p_state_to_turbo_ratio() - Set turbo ratio
+ *
+ * TURBO_RATIO_LIMIT MSR (0x1AD) Bits 31:0 indicates the
+ * factory configured values for of 1-core, 2-core, 3-core
+ * and 4-core turbo ratio limits for all processors.
+ *
+ * 7:0 -       MAX_TURBO_1_CORE
+ * 15:8 -      MAX_TURBO_2_CORES
+ * 23:16 -     MAX_TURBO_3_CORES
+ * 31:24 -     MAX_TURBO_4_CORES
+ *
+ * Set PERF_CTL MSR (0x199) P_Req with that value.
+ */
+void cpu_set_p_state_to_turbo_ratio(void);
+
 #endif
diff --git a/arch/x86/include/asm/fsp/fsp_api.h b/arch/x86/include/asm/fsp/fsp_api.h
deleted file mode 100644 (file)
index f2d7079..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* SPDX-License-Identifier: Intel */
-/*
- * Copyright (C) 2013, Intel Corporation
- * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#ifndef __FSP_API_H__
-#define __FSP_API_H__
-
-#include <linux/linkage.h>
-
-/*
- * FSP common configuration structure.
- * This needs to be included in the platform-specific struct fsp_config_data.
- */
-struct fsp_cfg_common {
-       struct fsp_header       *fsp_hdr;
-       u32                     stack_top;
-       u32                     boot_mode;
-};
-
-/*
- * FspInit continuation function prototype.
- * Control will be returned to this callback function after FspInit API call.
- */
-typedef void (*fsp_continuation_f)(u32 status, void *hob_list);
-
-struct fsp_init_params {
-       /* Non-volatile storage buffer pointer */
-       void                    *nvs_buf;
-       /* Runtime buffer pointer */
-       void                    *rt_buf;
-       /* Continuation function address */
-       fsp_continuation_f      continuation;
-};
-
-struct common_buf {
-       /*
-        * Stack top pointer used by the bootloader. The new stack frame will be
-        * set up at this location after FspInit API call.
-        */
-       u32     stack_top;
-       u32     boot_mode;      /* Current system boot mode */
-       void    *upd_data;      /* User platform configuraiton data region */
-       u32     tolum_size;     /* Top of low usable memory size (FSP 1.1) */
-       u32     reserved[6];    /* Reserved */
-};
-
-enum fsp_phase {
-       /* Notification code for post PCI enuermation */
-       INIT_PHASE_PCI  = 0x20,
-       /* Notification code before transfering control to the payload */
-       INIT_PHASE_BOOT = 0x40
-};
-
-struct fsp_notify_params {
-       /* Notification phase used for NotifyPhase API */
-       enum fsp_phase  phase;
-};
-
-/* FspInit API function prototype */
-typedef asmlinkage u32 (*fsp_init_f)(struct fsp_init_params *params);
-
-/* FspNotify API function prototype */
-typedef asmlinkage u32 (*fsp_notify_f)(struct fsp_notify_params *params);
-
-#endif
diff --git a/arch/x86/include/asm/fsp/fsp_ffs.h b/arch/x86/include/asm/fsp/fsp_ffs.h
deleted file mode 100644 (file)
index b7558e5..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/* SPDX-License-Identifier: Intel */
-/*
- * Copyright (C) 2013, Intel Corporation
- * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#ifndef __FSP_FFS_H__
-#define __FSP_FFS_H__
-
-/* Used to verify the integrity of the file */
-union __packed ffs_integrity {
-       struct {
-               /*
-                * The IntegrityCheck.checksum.header field is an 8-bit
-                * checksum of the file header. The State and
-                * IntegrityCheck.checksum.file fields are assumed to be zero
-                * and the checksum is calculated such that the entire header
-                * sums to zero.
-                */
-               u8      header;
-               /*
-                * If the FFS_ATTRIB_CHECKSUM (see definition below) bit of
-                * the Attributes field is set to one, the
-                * IntegrityCheck.checksum.file field is an 8-bit checksum of
-                * the file data. If the FFS_ATTRIB_CHECKSUM bit of the
-                * Attributes field is cleared to zero, the
-                * IntegrityCheck.checksum.file field must be initialized with
-                * a value of 0xAA. The IntegrityCheck.checksum.file field is
-                * valid any time the EFI_FILE_DATA_VALID bit is set in the
-                * State field.
-                */
-               u8      file;
-       } checksum;
-
-       /* This is the full 16 bits of the IntegrityCheck field */
-       u16     checksum16;
-};
-
-/*
- * Each file begins with the header that describe the
- * contents and state of the files.
- */
-struct __packed ffs_file_header {
-       /*
-        * This GUID is the file name.
-        * It is used to uniquely identify the file.
-        */
-       efi_guid_t              name;
-       /* Used to verify the integrity of the file */
-       union ffs_integrity     integrity;
-       /* Identifies the type of file */
-       u8                      type;
-       /* Declares various file attribute bits */
-       u8                      attr;
-       /* The length of the file in bytes, including the FFS header */
-       u8                      size[3];
-       /*
-        * Used to track the state of the file throughout the life of
-        * the file from creation to deletion.
-        */
-       u8                      state;
-};
-
-struct __packed ffs_file_header2 {
-       /*
-        * This GUID is the file name. It is used to uniquely identify the file.
-        * There may be only one instance of a file with the file name GUID of
-        * Name in any given firmware volume, except if the file type is
-        * EFI_FV_FILE_TYPE_FFS_PAD.
-        */
-       efi_guid_t              name;
-       /* Used to verify the integrity of the file */
-       union ffs_integrity     integrity;
-       /* Identifies the type of file */
-       u8                      type;
-       /* Declares various file attribute bits */
-       u8                      attr;
-       /*
-        * The length of the file in bytes, including the FFS header.
-        * The length of the file data is either
-        * (size - sizeof(struct ffs_file_header)). This calculation means a
-        * zero-length file has a size of 24 bytes, which is
-        * sizeof(struct ffs_file_header). Size is not required to be a
-        * multiple of 8 bytes. Given a file F, the next file header is located
-        * at the next 8-byte aligned firmware volume offset following the last
-        * byte of the file F.
-        */
-       u8                      size[3];
-       /*
-        * Used to track the state of the file throughout the life of
-        * the file from creation to deletion.
-        */
-       u8                      state;
-       /*
-        * If FFS_ATTRIB_LARGE_FILE is set in attr, then ext_size exists
-        * and size must be set to zero.
-        * If FFS_ATTRIB_LARGE_FILE is not set then
-        * struct ffs_file_header is used.
-        */
-       u32                     ext_size;
-};
-
-/*
- * Pseudo type. It is used as a wild card when retrieving sections.
- * The section type EFI_SECTION_ALL matches all section types.
- */
-#define EFI_SECTION_ALL                                0x00
-
-/* Encapsulation section Type values */
-#define EFI_SECTION_COMPRESSION                        0x01
-#define EFI_SECTION_GUID_DEFINED               0x02
-#define EFI_SECTION_DISPOSABLE                 0x03
-
-/* Leaf section Type values */
-#define EFI_SECTION_PE32                       0x10
-#define EFI_SECTION_PIC                                0x11
-#define EFI_SECTION_TE                         0x12
-#define EFI_SECTION_DXE_DEPEX                  0x13
-#define EFI_SECTION_VERSION                    0x14
-#define EFI_SECTION_USER_INTERFACE             0x15
-#define EFI_SECTION_COMPATIBILITY16            0x16
-#define EFI_SECTION_FIRMWARE_VOLUME_IMAGE      0x17
-#define EFI_SECTION_FREEFORM_SUBTYPE_GUID      0x18
-#define EFI_SECTION_RAW                                0x19
-#define EFI_SECTION_PEI_DEPEX                  0x1B
-#define EFI_SECTION_SMM_DEPEX                  0x1C
-
-/* Common section header */
-struct __packed raw_section {
-       /*
-        * A 24-bit unsigned integer that contains the total size of
-        * the section in bytes, including the EFI_COMMON_SECTION_HEADER.
-        */
-       u8      size[3];
-       u8      type;
-};
-
-struct __packed raw_section2 {
-       /*
-        * A 24-bit unsigned integer that contains the total size of
-        * the section in bytes, including the EFI_COMMON_SECTION_HEADER.
-        */
-       u8      size[3];
-       u8      type;
-       /*
-        * If size is 0xFFFFFF, then ext_size contains the size of
-        * the section. If size is not equal to 0xFFFFFF, then this
-        * field does not exist.
-        */
-       u32     ext_size;
-};
-
-#endif
index 3bb79c4b67a45c89c26e5ce59cb509c3ef6ee1f4..d248520e972695c82f2360746071b63d2abc159d 100644 (file)
@@ -69,6 +69,10 @@ struct __packed hob_graphics_info {
        EFI_GUID(0x721acf02, 0x4d77, 0x4c2a, \
                0xb3, 0xdc, 0x27, 0x0b, 0x7b, 0xa9, 0xe4, 0xb0)
 
+#define FSP_VARIABLE_NV_DATA_HOB_GUID \
+       EFI_GUID(0xa034147d, 0x690c, 0x4154, \
+               0x8d, 0xe6, 0xc0, 0x44, 0x64, 0x1d, 0xe9, 0x42)
+
 #define FSP_BOOTLOADER_TEMP_MEM_HOB_GUID \
        EFI_GUID(0xbbcff46c, 0xc8d3, 0x4113, \
                0x89, 0x85, 0xb9, 0xd4, 0xf3, 0xb3, 0xf6, 0x4e)
index 86f78014b7b623c248b0889b38139bc42cedd5fb..e72c052ed1ecf4e240c9f7c45183dfc45d55f380 100644 (file)
@@ -33,6 +33,19 @@ struct __packed fsp_header {
 #define FSP_HEADER_REVISION_1          1
 #define FSP_HEADER_REVISION_2          2
 
-#define FSP_ATTR_GRAPHICS_SUPPORT      (1 << 0)
+enum fsp_type {
+       FSP_ATTR_COMP_TYPE_FSP_T        = 1,
+       FSP_ATTR_COMP_TYPE_FSP_M        = 2,
+       FSP_ATTR_COMP_TYPE_FSP_S        = 3,
+};
+
+enum {
+       FSP_ATTR_GRAPHICS_SUPPORT       = 1 << 0,
+       FSP_ATTR_COMP_TYPE_SHIFT        = 28,
+       FSP_ATTR_COMP_TYPE_MASK         = 0xfU << FSP_ATTR_COMP_TYPE_SHIFT,
+
+};
+
+#define EFI_FSPH_SIGNATURE             SIGNATURE_32('F', 'S', 'P', 'H')
 
 #endif
index 7b92392a2772060eaea651aca27bdda4895612f3..4ac27d26f5537970968a566c088cda61f90d862b 100644 (file)
 #ifndef __FSP_SUPPORT_H__
 #define __FSP_SUPPORT_H__
 
-#include "fsp_types.h"
-#include "fsp_hob.h"
-#include "fsp_fv.h"
-#include "fsp_ffs.h"
-#include "fsp_api.h"
-#include "fsp_infoheader.h"
-#include "fsp_bootmode.h"
-#include "fsp_azalia.h"
-#include <asm/arch/fsp/fsp_vpd.h>
-#include <asm/arch/fsp/fsp_configs.h>
+#include <asm/fsp/fsp_bootmode.h>
+#include <asm/fsp/fsp_fv.h>
+#include <asm/fsp/fsp_hob.h>
+#include <asm/fsp/fsp_infoheader.h>
+#include <asm/fsp/fsp_types.h>
+#include <asm/fsp_arch.h>
+#include <asm/fsp/fsp_azalia.h>
 
 #define FSP_LOWMEM_BASE                0x100000UL
 #define FSP_HIGHMEM_BASE       0x100000000ULL
 #define UPD_TERMINATOR         0x55AA
 
-
-/**
- * FSP Continuation assembly helper routine
- *
- * This routine jumps to the C version of FSP continuation function
- */
-void asm_continuation(void);
-
-/**
- * FSP initialization complete
- *
- * This is the function that indicates FSP initialization is complete and jumps
- * back to the bootloader with HOB list pointer as the parameter.
- *
- * @hob_list:    HOB list pointer
- */
-void fsp_init_done(void *hob_list);
-
-/**
- * FSP Continuation function
- *
- * @status:      Always 0
- * @hob_list:    HOB list pointer
- *
- * @retval:      Never returns
- */
-void fsp_continue(u32 status, void *hob_list);
-
-/**
- * Find FSP header offset in FSP image
- *
- * @retval: the offset of FSP header. If signature is invalid, returns 0.
- */
-struct fsp_header *find_fsp_header(void);
-
 /**
- * FSP initialization wrapper function.
+ * fsp_find_header() - Find FSP header offset in FSP image
  *
- * @stack_top: bootloader stack top address
- * @boot_mode: boot mode defined in fsp_bootmode.h
- * @nvs_buf:   Non-volatile memory buffer pointer
+ * @return the offset of FSP header. If signature is invalid, returns 0.
  */
-void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf);
+struct fsp_header *fsp_find_header(void);
 
 /**
- * FSP notification wrapper function
+ * fsp_notify() - FSP notification wrapper function
  *
  * @fsp_hdr: Pointer to FSP information header
  * @phase:   FSP initialization phase defined in enum fsp_phase
  *
- * @retval:  compatible status code with EFI_STATUS defined in PI spec
+ * @return compatible status code with EFI_STATUS defined in PI spec
  */
 u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase);
 
 /**
- * This function retrieves the top of usable low memory.
+ * fsp_get_usable_lowmem_top() - retrieves the top of usable low memory
  *
  * @hob_list: A HOB list pointer.
  *
- * @retval:   Usable low memory top.
+ * @return Usable low memory top.
  */
 u32 fsp_get_usable_lowmem_top(const void *hob_list);
 
 /**
- * This function retrieves the top of usable high memory.
+ * fsp_get_usable_highmem_top() - retrieves the top of usable high memory
  *
  * @hob_list: A HOB list pointer.
  *
- * @retval:   Usable high memory top.
+ * @return Usable high memory top.
  */
 u64 fsp_get_usable_highmem_top(const void *hob_list);
 
 /**
- * This function retrieves a special reserved memory region.
+ * fsp_get_reserved_mem_from_guid() - retrieves a special reserved memory region
  *
  * @hob_list: A HOB list pointer.
  * @len:      A pointer to the GUID HOB data buffer length.
  *            If the GUID HOB is located, the length will be updated.
  * @guid:     A pointer to the owner guild.
  *
- * @retval:   Reserved region start address.
+ * @return Reserved region start address.
  *            0 if this region does not exist.
  */
 u64 fsp_get_reserved_mem_from_guid(const void *hob_list,
                                   u64 *len, const efi_guid_t *guid);
 
 /**
- * This function retrieves the FSP reserved normal memory.
+ * fsp_get_fsp_reserved_mem() - retrieves the FSP reserved normal memory
  *
  * @hob_list: A HOB list pointer.
  * @len:      A pointer to the FSP reserved memory length buffer.
  *            If the GUID HOB is located, the length will be updated.
- * @retval:   FSP reserved memory base
+ * @return FSP reserved memory base
  *            0 if this region does not exist.
  */
 u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len);
 
 /**
- * This function retrieves the TSEG reserved normal memory.
+ * fsp_get_tseg_reserved_mem() - retrieves the TSEG reserved normal memory
  *
  * @hob_list:      A HOB list pointer.
  * @len:           A pointer to the TSEG reserved memory length buffer.
  *                 If the GUID HOB is located, the length will be updated.
  *
- * @retval NULL:   Failed to find the TSEG reserved memory.
- * @retval others: TSEG reserved memory base.
+ * @return NULL:   Failed to find the TSEG reserved memory.
+ * @return others: TSEG reserved memory base.
  */
 u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len);
 
 /**
- * This function retrieves FSP Non-volatile Storage HOB buffer and size.
+ * fsp_get_nvs_data() - retrieves FSP Non-volatile Storage HOB buffer and size
  *
  * @hob_list:      A HOB list pointer.
  * @len:           A pointer to the NVS data buffer length.
  *                 If the HOB is located, the length will be updated.
  *
- * @retval NULL:   Failed to find the NVS HOB.
- * @retval others: FSP NVS data buffer pointer.
+ * @return NULL:   Failed to find the NVS HOB.
+ * @return others: FSP NVS data buffer pointer.
  */
 void *fsp_get_nvs_data(const void *hob_list, u32 *len);
 
 /**
- * This function retrieves Bootloader temporary stack buffer and size.
+ * fsp_get_var_nvs_data() - get FSP variable Non-volatile Storage HOB buffer
  *
  * @hob_list:      A HOB list pointer.
- * @len:           A pointer to the bootloader temporary stack length.
+ * @len:           A pointer to the NVS data buffer length.
  *                 If the HOB is located, the length will be updated.
  *
- * @retval NULL:   Failed to find the bootloader temporary stack HOB.
- * @retval others: Bootloader temporary stackbuffer pointer.
+ * @return NULL:   Failed to find the NVS HOB.
+ * @return others: FSP NVS data buffer pointer.
  */
-void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len);
+void *fsp_get_var_nvs_data(const void *hob_list, u32 *len);
 
 /**
- * This function retrieves graphics information.
+ * fsp_get_graphics_info() - retrieves graphics information.
  *
  * @hob_list:      A HOB list pointer.
  * @len:           A pointer to the graphics info HOB length.
  *                 If the HOB is located, the length will be updated.
  *
- * @retval NULL:   Failed to find the graphics info HOB.
- * @retval others: A pointer to struct hob_graphics_info.
+ * @return NULL:   Failed to find the graphics info HOB.
+ * @return others: A pointer to struct hob_graphics_info.
  */
 void *fsp_get_graphics_info(const void *hob_list, u32 *len);
 
 /**
- * This function overrides the default configurations of FSP.
+ * fsp_init_phase_pci() - Tell the FSP that we have completed PCI init
+ *
+ * @return 0 if OK, -EPERM if the FSP gave an error.
+ */
+int fsp_init_phase_pci(void);
+
+/**
+ * fsp_scan_for_ram_size() - Scan the HOB list to find the RAM size
  *
- * @config:  A pointer to the FSP configuration data structure
- * @rt_buf:  A pointer to the FSP runtime buffer data structure
+ * This sets gd->ram_size based on what it finds.
  *
- * @return:  None
+ * @return 0 if OK, -ve on error
  */
-void update_fsp_configs(struct fsp_config_data *config,
-                       struct fspinit_rtbuf *rt_buf);
+int fsp_scan_for_ram_size(void);
 
 /**
- * fsp_init_phase_pci() - Tell the FSP that we have completed PCI init
+ * fsp_prepare_mrc_cache() - Find the DRAM training data from the MRC cache
  *
- * @return 0 if OK, -EPERM if the FSP gave an error.
+ * @return pointer to data, or NULL if no cache or no data found in the cache
  */
-int fsp_init_phase_pci(void);
+void *fsp_prepare_mrc_cache(void);
+
+/**
+ * fsp_notify() - FSP notification wrapper function
+ *
+ * @fsp_hdr: Pointer to FSP information header
+ * @phase:   FSP initialization phase defined in enum fsp_phase
+ *
+ * @return compatible status code with EFI_STATUS defined in PI spec
+ */
+u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase);
 
 #endif
diff --git a/arch/x86/include/asm/fsp1/fsp_api.h b/arch/x86/include/asm/fsp1/fsp_api.h
new file mode 100644 (file)
index 0000000..f2d7079
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: Intel */
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#ifndef __FSP_API_H__
+#define __FSP_API_H__
+
+#include <linux/linkage.h>
+
+/*
+ * FSP common configuration structure.
+ * This needs to be included in the platform-specific struct fsp_config_data.
+ */
+struct fsp_cfg_common {
+       struct fsp_header       *fsp_hdr;
+       u32                     stack_top;
+       u32                     boot_mode;
+};
+
+/*
+ * FspInit continuation function prototype.
+ * Control will be returned to this callback function after FspInit API call.
+ */
+typedef void (*fsp_continuation_f)(u32 status, void *hob_list);
+
+struct fsp_init_params {
+       /* Non-volatile storage buffer pointer */
+       void                    *nvs_buf;
+       /* Runtime buffer pointer */
+       void                    *rt_buf;
+       /* Continuation function address */
+       fsp_continuation_f      continuation;
+};
+
+struct common_buf {
+       /*
+        * Stack top pointer used by the bootloader. The new stack frame will be
+        * set up at this location after FspInit API call.
+        */
+       u32     stack_top;
+       u32     boot_mode;      /* Current system boot mode */
+       void    *upd_data;      /* User platform configuraiton data region */
+       u32     tolum_size;     /* Top of low usable memory size (FSP 1.1) */
+       u32     reserved[6];    /* Reserved */
+};
+
+enum fsp_phase {
+       /* Notification code for post PCI enuermation */
+       INIT_PHASE_PCI  = 0x20,
+       /* Notification code before transfering control to the payload */
+       INIT_PHASE_BOOT = 0x40
+};
+
+struct fsp_notify_params {
+       /* Notification phase used for NotifyPhase API */
+       enum fsp_phase  phase;
+};
+
+/* FspInit API function prototype */
+typedef asmlinkage u32 (*fsp_init_f)(struct fsp_init_params *params);
+
+/* FspNotify API function prototype */
+typedef asmlinkage u32 (*fsp_notify_f)(struct fsp_notify_params *params);
+
+#endif
diff --git a/arch/x86/include/asm/fsp1/fsp_ffs.h b/arch/x86/include/asm/fsp1/fsp_ffs.h
new file mode 100644 (file)
index 0000000..b7558e5
--- /dev/null
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: Intel */
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#ifndef __FSP_FFS_H__
+#define __FSP_FFS_H__
+
+/* Used to verify the integrity of the file */
+union __packed ffs_integrity {
+       struct {
+               /*
+                * The IntegrityCheck.checksum.header field is an 8-bit
+                * checksum of the file header. The State and
+                * IntegrityCheck.checksum.file fields are assumed to be zero
+                * and the checksum is calculated such that the entire header
+                * sums to zero.
+                */
+               u8      header;
+               /*
+                * If the FFS_ATTRIB_CHECKSUM (see definition below) bit of
+                * the Attributes field is set to one, the
+                * IntegrityCheck.checksum.file field is an 8-bit checksum of
+                * the file data. If the FFS_ATTRIB_CHECKSUM bit of the
+                * Attributes field is cleared to zero, the
+                * IntegrityCheck.checksum.file field must be initialized with
+                * a value of 0xAA. The IntegrityCheck.checksum.file field is
+                * valid any time the EFI_FILE_DATA_VALID bit is set in the
+                * State field.
+                */
+               u8      file;
+       } checksum;
+
+       /* This is the full 16 bits of the IntegrityCheck field */
+       u16     checksum16;
+};
+
+/*
+ * Each file begins with the header that describe the
+ * contents and state of the files.
+ */
+struct __packed ffs_file_header {
+       /*
+        * This GUID is the file name.
+        * It is used to uniquely identify the file.
+        */
+       efi_guid_t              name;
+       /* Used to verify the integrity of the file */
+       union ffs_integrity     integrity;
+       /* Identifies the type of file */
+       u8                      type;
+       /* Declares various file attribute bits */
+       u8                      attr;
+       /* The length of the file in bytes, including the FFS header */
+       u8                      size[3];
+       /*
+        * Used to track the state of the file throughout the life of
+        * the file from creation to deletion.
+        */
+       u8                      state;
+};
+
+struct __packed ffs_file_header2 {
+       /*
+        * This GUID is the file name. It is used to uniquely identify the file.
+        * There may be only one instance of a file with the file name GUID of
+        * Name in any given firmware volume, except if the file type is
+        * EFI_FV_FILE_TYPE_FFS_PAD.
+        */
+       efi_guid_t              name;
+       /* Used to verify the integrity of the file */
+       union ffs_integrity     integrity;
+       /* Identifies the type of file */
+       u8                      type;
+       /* Declares various file attribute bits */
+       u8                      attr;
+       /*
+        * The length of the file in bytes, including the FFS header.
+        * The length of the file data is either
+        * (size - sizeof(struct ffs_file_header)). This calculation means a
+        * zero-length file has a size of 24 bytes, which is
+        * sizeof(struct ffs_file_header). Size is not required to be a
+        * multiple of 8 bytes. Given a file F, the next file header is located
+        * at the next 8-byte aligned firmware volume offset following the last
+        * byte of the file F.
+        */
+       u8                      size[3];
+       /*
+        * Used to track the state of the file throughout the life of
+        * the file from creation to deletion.
+        */
+       u8                      state;
+       /*
+        * If FFS_ATTRIB_LARGE_FILE is set in attr, then ext_size exists
+        * and size must be set to zero.
+        * If FFS_ATTRIB_LARGE_FILE is not set then
+        * struct ffs_file_header is used.
+        */
+       u32                     ext_size;
+};
+
+/*
+ * Pseudo type. It is used as a wild card when retrieving sections.
+ * The section type EFI_SECTION_ALL matches all section types.
+ */
+#define EFI_SECTION_ALL                                0x00
+
+/* Encapsulation section Type values */
+#define EFI_SECTION_COMPRESSION                        0x01
+#define EFI_SECTION_GUID_DEFINED               0x02
+#define EFI_SECTION_DISPOSABLE                 0x03
+
+/* Leaf section Type values */
+#define EFI_SECTION_PE32                       0x10
+#define EFI_SECTION_PIC                                0x11
+#define EFI_SECTION_TE                         0x12
+#define EFI_SECTION_DXE_DEPEX                  0x13
+#define EFI_SECTION_VERSION                    0x14
+#define EFI_SECTION_USER_INTERFACE             0x15
+#define EFI_SECTION_COMPATIBILITY16            0x16
+#define EFI_SECTION_FIRMWARE_VOLUME_IMAGE      0x17
+#define EFI_SECTION_FREEFORM_SUBTYPE_GUID      0x18
+#define EFI_SECTION_RAW                                0x19
+#define EFI_SECTION_PEI_DEPEX                  0x1B
+#define EFI_SECTION_SMM_DEPEX                  0x1C
+
+/* Common section header */
+struct __packed raw_section {
+       /*
+        * A 24-bit unsigned integer that contains the total size of
+        * the section in bytes, including the EFI_COMMON_SECTION_HEADER.
+        */
+       u8      size[3];
+       u8      type;
+};
+
+struct __packed raw_section2 {
+       /*
+        * A 24-bit unsigned integer that contains the total size of
+        * the section in bytes, including the EFI_COMMON_SECTION_HEADER.
+        */
+       u8      size[3];
+       u8      type;
+       /*
+        * If size is 0xFFFFFF, then ext_size contains the size of
+        * the section. If size is not equal to 0xFFFFFF, then this
+        * field does not exist.
+        */
+       u32     ext_size;
+};
+
+#endif
diff --git a/arch/x86/include/asm/fsp1/fsp_support.h b/arch/x86/include/asm/fsp1/fsp_support.h
new file mode 100644 (file)
index 0000000..a44a550
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: Intel */
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#ifndef __FSP1_SUPPORT_H__
+#define __FSP1_SUPPORT_H__
+
+#include <asm/fsp/fsp_support.h>
+#include "fsp_ffs.h"
+
+/**
+ * fsp_asm_continuation() - FSP Continuation assembly helper routine
+ *
+ * This routine jumps to the C version of FSP continuation function
+ */
+void fsp_asm_continuation(void);
+
+/**
+ * fsp_init_done() - FSP initialization complete
+ *
+ * This is the function that indicates FSP initialization is complete and jumps
+ * back to the bootloader with HOB list pointer as the parameter.
+ *
+ * @hob_list:    HOB list pointer
+ */
+void fsp_init_done(void *hob_list);
+
+/**
+ * fsp_continue() - FSP Continuation function
+ *
+ * @status:      Always 0
+ * @hob_list:    HOB list pointer
+ *
+ * @return Never returns
+ */
+void fsp_continue(u32 status, void *hob_list);
+
+/**
+ * fsp_init() - FSP initialization wrapper function
+ *
+ * @stack_top: bootloader stack top address
+ * @boot_mode: boot mode defined in fsp_bootmode.h
+ * @nvs_buf:   Non-volatile memory buffer pointer
+ */
+void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf);
+
+/**
+ * fsp_get_bootloader_tmp_mem() - retrieves temporary stack buffer and size
+ *
+ * @hob_list:      A HOB list pointer.
+ * @len:           A pointer to the bootloader temporary stack length.
+ *                 If the HOB is located, the length will be updated.
+ *
+ * @return NULL:   Failed to find the bootloader temporary stack HOB.
+ * @return others: Bootloader temporary stackbuffer pointer.
+ */
+void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len);
+
+/**
+ * fsp_update_configs() - overrides the default configurations of FSP
+ *
+ * @config:  A pointer to the FSP configuration data structure
+ * @rt_buf:  A pointer to the FSP runtime buffer data structure
+ *
+ * @return None
+ */
+void fsp_update_configs(struct fsp_config_data *config,
+                       struct fspinit_rtbuf *rt_buf);
+
+#endif
diff --git a/arch/x86/include/asm/fsp_arch.h b/arch/x86/include/asm/fsp_arch.h
new file mode 100644 (file)
index 0000000..3b2077b
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * Architecture-specific definitions (FSP config and VPD/UPD)
+ */
+
+#ifndef __FSP_ARCH_H__
+#define __FSP_ARCH_H__
+
+/*
+ * Note: use #ifndef __ASSEMBLY__ around any struct definitions or other C code
+ * since this file can be included from assembly.
+ */
+
+#include <asm/fsp1/fsp_api.h>
+#include <asm/fsp1/fsp_ffs.h>
+#include <asm/arch/fsp/fsp_vpd.h>
+#include <asm/arch/fsp/fsp_configs.h>
+
+#endif
index 4d18d59efedb4ca49bf55d515a4318aef9224c6e..aec49b9b815c6e1a1a683c516e9ceeecd1ae844c 100644 (file)
@@ -9,7 +9,15 @@
 #ifndef __x86_asm_handoff_h
 #define __x86_asm_handoff_h
 
+/**
+ * struct arch_spl_handoff - architecture-specific handoff info
+ *
+ * @usable_ram_top: Value returned by board_get_usable_ram_top() in SPL
+ * @hob_list: Start of FSP hand-off blocks (HOBs)
+ */
 struct arch_spl_handoff {
+       ulong usable_ram_top;
+       void *hob_list;
 };
 
 #endif
index b4239821aaa3401ba7381048fd596b28ea8b0cd9..56e11dbb28f02aab661cfd7a634d9cf3565b263c 100644 (file)
@@ -135,7 +135,7 @@ struct hob_guid {
  *
  * @hdr:    A pointer to a HOB.
  *
- * @return: A pointer to the next HOB in the HOB list.
+ * @return A pointer to the next HOB in the HOB list.
  */
 static inline const struct hob_header *get_next_hob(const struct hob_header
                                                    *hdr)
@@ -152,8 +152,8 @@ static inline const struct hob_header *get_next_hob(const struct hob_header
  *
  * @hdr:          A pointer to a HOB.
  *
- * @retval true:  The HOB specified by hdr is the last HOB in the HOB list.
- * @retval false: The HOB specified by hdr is not the last HOB in the HOB list.
+ * @return true:  The HOB specified by hdr is the last HOB in the HOB list.
+ * @return false: The HOB specified by hdr is not the last HOB in the HOB list.
  */
 static inline bool end_of_hob(const struct hob_header *hdr)
 {
@@ -169,7 +169,7 @@ static inline bool end_of_hob(const struct hob_header *hdr)
  *
  * @hdr:    A pointer to a HOB.
  *
- * @return: A pointer to the data buffer in a HOB.
+ * @return A pointer to the data buffer in a HOB.
  */
 static inline void *get_guid_hob_data(const struct hob_header *hdr)
 {
@@ -185,7 +185,7 @@ static inline void *get_guid_hob_data(const struct hob_header *hdr)
  *
  * @hdr:    A pointer to a HOB.
  *
- * @return: The size of the data buffer.
+ * @return The size of the data buffer.
  */
 static inline u16 get_guid_hob_data_size(const struct hob_header *hdr)
 {
@@ -198,7 +198,7 @@ static inline u16 get_guid_hob_data_size(const struct hob_header *hdr)
  * @type:     HOB type to search
  * @hob_list: A pointer to the HOB list
  *
- * @retval:   A HOB object with matching type; Otherwise NULL.
+ * @return A HOB object with matching type; Otherwise NULL.
  */
 const struct hob_header *hob_get_next_hob(uint type, const void *hob_list);
 
@@ -208,7 +208,7 @@ const struct hob_header *hob_get_next_hob(uint type, const void *hob_list);
  * @guid:     GUID to search
  * @hob_list: A pointer to the HOB list
  *
- * @retval:   A HOB object with matching GUID; Otherwise NULL.
+ * @return A HOB object with matching GUID; Otherwise NULL.
  */
 const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
                                               const void *hob_list);
@@ -221,8 +221,8 @@ const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
  *                 If the GUID HOB is located, the length will be updated.
  * @guid           A pointer to HOB GUID.
  *
- * @retval NULL:   Failed to find the GUID HOB.
- * @retval others: GUID HOB data buffer pointer.
+ * @return NULL:   Failed to find the GUID HOB.
+ * @return others: GUID HOB data buffer pointer.
  */
 void *hob_get_guid_hob_data(const void *hob_list, u32 *len,
                            const efi_guid_t *guid);
index 9c1dbe61d5965b21b4f31629e929e4c6aed89481..5bc8b6c22c7eab17ed1c41446bc5e356b8029b6b 100644 (file)
 #define MSR_PIC_MSG_CONTROL            0x2e
 #define  PLATFORM_INFO_SET_TDP         (1 << 29)
 
+#define MSR_MTRR_CAP_MSR               0x0fe
+#define MSR_MTRR_CAP_SMRR              (1 << 11)
+#define MSR_MTRR_CAP_WC                        (1 << 10)
+#define MSR_MTRR_CAP_FIX               (1 << 8)
+#define MSR_MTRR_CAP_VCNT              0xff
+
 #define MSR_IA32_PERFCTR0              0x000000c1
 #define MSR_IA32_PERFCTR1              0x000000c2
 #define MSR_FSB_FREQ                   0x000000cd
 #define ENABLE_ULFM_AUTOCM_MASK                (1 << 2)
 #define ENABLE_INDP_AUTOCM_MASK                (1 << 3)
 
+#define MSR_EMULATE_PM_TIMER           0x121
+#define  EMULATE_DELAY_OFFSET_VALUE    20
+#define  EMULATE_PM_TMR_EN             (1 << 16)
+#define  EMULATE_DELAY_VALUE           0x13
+
 #define MSR_IA32_SYSENTER_CS           0x00000174
 #define MSR_IA32_SYSENTER_ESP          0x00000175
 #define MSR_IA32_SYSENTER_EIP          0x00000176
 #define MSR_FLEX_RATIO                 0x194
 #define  FLEX_RATIO_LOCK               (1 << 20)
 #define  FLEX_RATIO_EN                 (1 << 16)
+/* This is burst mode BIT 38 in IA32_MISC_ENABLE MSR at offset 1A0h */
+#define BURST_MODE_DISABLE             (1 << 6)
+
+#define MSR_IA32_MISC_ENABLE           0x000001a0
+
+/* MISC_ENABLE bits: architectural */
+#define MISC_ENABLE_FAST_STRING                BIT_ULL(0)
+#define MISC_ENABLE_TCC                        BIT_ULL(1)
+#define MISC_DISABLE_TURBO             BIT_ULL(6)
+#define MISC_ENABLE_EMON               BIT_ULL(7)
+#define MISC_ENABLE_BTS_UNAVAIL                BIT_ULL(11)
+#define MISC_ENABLE_PEBS_UNAVAIL       BIT_ULL(12)
+#define MISC_ENABLE_ENHANCED_SPEEDSTEP BIT_ULL(16)
+#define MISC_ENABLE_MWAIT              BIT_ULL(18)
+#define MISC_ENABLE_LIMIT_CPUID                BIT_ULL(22)
+#define MISC_ENABLE_XTPR_DISABLE       BIT_ULL(23)
+#define MISC_ENABLE_XD_DISABLE         BIT_ULL(34)
+
+/* MISC_ENABLE bits: model-specific, meaning may vary from core to core */
+#define MISC_ENABLE_X87_COMPAT         BIT_ULL(2)
+#define MISC_ENABLE_TM1                        BIT_ULL(3)
+#define MISC_ENABLE_SPLIT_LOCK_DISABLE BIT_ULL(4)
+#define MISC_ENABLE_L3CACHE_DISABLE    BIT_ULL(6)
+#define MISC_ENABLE_SUPPRESS_LOCK      BIT_ULL(8)
+#define MISC_ENABLE_PREFETCH_DISABLE   BIT_ULL(9)
+#define MISC_ENABLE_FERR               BIT_ULL(10)
+#define MISC_ENABLE_FERR_MULTIPLEX     BIT_ULL(10)
+#define MISC_ENABLE_TM2                        BIT_ULL(13)
+#define MISC_ENABLE_ADJ_PREF_DISABLE   BIT_ULL(19)
+#define MISC_ENABLE_SPEEDSTEP_LOCK     BIT_ULL(20)
+#define MISC_ENABLE_L1D_CONTEXT                BIT_ULL(24)
+#define MISC_ENABLE_DCU_PREF_DISABLE   BIT_ULL(37)
+#define MISC_ENABLE_TURBO_DISABLE      BIT_ULL(38)
+#define MISC_ENABLE_IP_PREF_DISABLE    BIT_ULL(39)
 
-#define MSR_IA32_MISC_ENABLES          0x000001a0
 #define MSR_TEMPERATURE_TARGET         0x1a2
+#define MSR_PREFETCH_CTL               0x1a4
+#define  PREFETCH_L1_DISABLE           (1 << 0)
+#define  PREFETCH_L2_DISABLE           (1 << 2)
 #define MSR_OFFCORE_RSP_0              0x000001a6
 #define MSR_OFFCORE_RSP_1              0x000001a7
 #define MSR_MISC_PWR_MGMT              0x1aa
 #define  MISC_PWR_MGMT_EIST_HW_DIS     (1 << 0)
-#define MSR_NHM_TURBO_RATIO_LIMIT      0x000001ad
-#define MSR_IVT_TURBO_RATIO_LIMIT      0x000001ae
+#define MSR_TURBO_RATIO_LIMIT          0x000001ad
 
 #define MSR_IA32_ENERGY_PERFORMANCE_BIAS       0x1b0
 #define  ENERGY_POLICY_PERFORMANCE     0
 #define  ENERGY_POLICY_NORMAL          6
 #define  ENERGY_POLICY_POWERSAVE       15
 
+#define MSR_IA32_PACKAGE_THERM_STATUS          0x000001b1
+
+#define PACKAGE_THERM_STATUS_PROCHOT           BIT(0)
+#define PACKAGE_THERM_STATUS_POWER_LIMIT       BIT(10)
+
+#define MSR_IA32_PACKAGE_THERM_INTERRUPT       0x000001b2
+
+#define PACKAGE_THERM_INT_HIGH_ENABLE          BIT(0)
+#define PACKAGE_THERM_INT_LOW_ENABLE           BIT(1)
+#define PACKAGE_THERM_INT_PLN_ENABLE           BIT(24)
+
 #define MSR_LBR_SELECT                 0x000001c8
 #define MSR_LBR_TOS                    0x000001c9
 #define MSR_IA32_PLATFORM_DCA_CAP      0x1f8
 
 #define MSR_THERM2_CTL_TM_SELECT       (1ULL << 16)
 
-#define MSR_IA32_MISC_ENABLE           0x000001a0
-#define H_MISC_DISABLE_TURBO           (1 << 6)
-
-#define MSR_IA32_TEMPERATURE_TARGET    0x000001a2
-
-#define MSR_IA32_ENERGY_PERF_BIAS      0x000001b0
-#define ENERGY_PERF_BIAS_PERFORMANCE   0
-#define ENERGY_PERF_BIAS_NORMAL                6
-#define ENERGY_PERF_BIAS_POWERSAVE     15
-
-#define MSR_IA32_PACKAGE_THERM_STATUS          0x000001b1
-
-#define PACKAGE_THERM_STATUS_PROCHOT           (1 << 0)
-#define PACKAGE_THERM_STATUS_POWER_LIMIT       (1 << 10)
-
-#define MSR_IA32_PACKAGE_THERM_INTERRUPT       0x000001b2
-
-#define PACKAGE_THERM_INT_HIGH_ENABLE          (1 << 0)
-#define PACKAGE_THERM_INT_LOW_ENABLE           (1 << 1)
-#define PACKAGE_THERM_INT_PLN_ENABLE           (1 << 24)
-
-/* Thermal Thresholds Support */
-#define THERM_INT_THRESHOLD0_ENABLE    (1 << 15)
-#define THERM_SHIFT_THRESHOLD0        8
-#define THERM_MASK_THRESHOLD0          (0x7f << THERM_SHIFT_THRESHOLD0)
-#define THERM_INT_THRESHOLD1_ENABLE    (1 << 23)
-#define THERM_SHIFT_THRESHOLD1        16
-#define THERM_MASK_THRESHOLD1          (0x7f << THERM_SHIFT_THRESHOLD1)
-#define THERM_STATUS_THRESHOLD0        (1 << 6)
-#define THERM_LOG_THRESHOLD0           (1 << 7)
-#define THERM_STATUS_THRESHOLD1        (1 << 8)
-#define THERM_LOG_THRESHOLD1           (1 << 9)
-
-/* MISC_ENABLE bits: architectural */
-#define MSR_IA32_MISC_ENABLE_FAST_STRING       (1ULL << 0)
-#define MSR_IA32_MISC_ENABLE_TCC               (1ULL << 1)
-#define MSR_IA32_MISC_ENABLE_EMON              (1ULL << 7)
-#define MSR_IA32_MISC_ENABLE_BTS_UNAVAIL       (1ULL << 11)
-#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL      (1ULL << 12)
-#define MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP        (1ULL << 16)
-#define MSR_IA32_MISC_ENABLE_MWAIT             (1ULL << 18)
-#define MSR_IA32_MISC_ENABLE_LIMIT_CPUID       (1ULL << 22)
-#define MSR_IA32_MISC_ENABLE_XTPR_DISABLE      (1ULL << 23)
-#define MSR_IA32_MISC_ENABLE_XD_DISABLE                (1ULL << 34)
-
-/* MISC_ENABLE bits: model-specific, meaning may vary from core to core */
-#define MSR_IA32_MISC_ENABLE_X87_COMPAT                (1ULL << 2)
-#define MSR_IA32_MISC_ENABLE_TM1               (1ULL << 3)
-#define MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE        (1ULL << 4)
-#define MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE   (1ULL << 6)
-#define MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK     (1ULL << 8)
-#define MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE  (1ULL << 9)
-#define MSR_IA32_MISC_ENABLE_FERR              (1ULL << 10)
-#define MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX    (1ULL << 10)
-#define MSR_IA32_MISC_ENABLE_TM2               (1ULL << 13)
-#define MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE  (1ULL << 19)
-#define MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK    (1ULL << 20)
-#define MSR_IA32_MISC_ENABLE_L1D_CONTEXT       (1ULL << 24)
-#define MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE  (1ULL << 37)
-#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE     (1ULL << 38)
-#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE   (1ULL << 39)
-
 #define MSR_IA32_TSC_DEADLINE          0x000006E0
 
 /* P4/Xeon+ specific */
 #define MSR_IA32_VMX_TRUE_ENTRY_CTLS     0x00000490
 #define MSR_IA32_VMX_VMFUNC             0x00000491
 
+#define MSR_IA32_PQR_ASSOC             0xc8f
+/* MSR bits 33:32 encode slot number 0-3 */
+#define MSR_IA32_PQR_ASSOC_MASK                (1 << 0 | 1 << 1)
+
+#define MSR_L2_QOS_MASK(reg)           (0xd10 + (reg))
+
 /* VMX_BASIC bits and bitmasks */
 #define VMX_BASIC_VMCS_SIZE_SHIFT      32
 #define VMX_BASIC_64           0x0001000000000000LLU
index 2d897f82ef791157210f9142d1dbaddeeedb36a9..672617256e9bdea94ed1a5be0aed755f6fdb77f9 100644 (file)
@@ -25,6 +25,7 @@
 #define MTRR_CAP_FIX           (1 << 8)
 #define MTRR_CAP_VCNT_MASK     0xff
 
+#define MTRR_DEF_TYPE_MASK     0xff
 #define MTRR_DEF_TYPE_EN       (1 << 11)
 #define MTRR_DEF_TYPE_FIX_EN   (1 << 10)
 
@@ -116,6 +117,18 @@ int mtrr_add_request(int type, uint64_t start, uint64_t size);
  */
 int mtrr_commit(bool do_caches);
 
+/**
+ * mtrr_set_next_var() - set up a variable MTRR
+ *
+ * This finds the first free variable MTRR and sets to the given area
+ *
+ * @type:      Requested type (MTRR_TYPE_)
+ * @start:     Start address
+ * @size:      Size
+ * @return 0 on success, -ENOSPC if there are no more MTRRs
+ */
+int mtrr_set_next_var(uint type, uint64_t base, uint64_t size);
+
 #endif
 
 #if ((CONFIG_XIP_ROM_SIZE & (CONFIG_XIP_ROM_SIZE - 1)) != 0)
index 118ac937d93366a260be320a84b65200de259f50..2a7207357284f68c810fc06c7f0fc0a74716f046 100644 (file)
 
 #ifndef __ASSEMBLY__
 
-int pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
-                       ulong *valuep, enum pci_size_t size);
+/**
+ * pci_x86_read_config() - Read a configuration value from a device
+ *
+ * This function can be called before PCI is set up in driver model.
+ *
+ * @bdf:       PCI device address: bus, device and function -see PCI_BDF()
+ * @offset:    Register offset to read
+ * @valuep:    Place to put the returned value
+ * @size:      Access size
+ * @return 0 if OK, -ve on error
+ */
+int pci_x86_read_config(pci_dev_t bdf, uint offset, ulong *valuep,
+                       enum pci_size_t size);
 
-int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset,
-                        ulong value, enum pci_size_t size);
+/**
+ * pci_bus_write_config() - Write a configuration value to a device
+ *
+ * This function can be called before PCI is set up in driver model.
+ *
+ * @bdf:       PCI device address: bus, device and function -see PCI_BDF()
+ * @offset:    Register offset to write
+ * @value:     Value to write
+ * @size:      Access size
+ * @return 0 if OK, -ve on error
+ */
+int pci_x86_write_config(pci_dev_t bdf, uint offset, ulong value,
+                        enum pci_size_t size);
+
+/**
+ * pci_bus_clrset_config32() - Update a configuration value for a device
+ *
+ * The register at @offset is updated to (oldvalue & ~clr) | set. This function
+ * can be called before PCI is set up in driver model.
+ *
+ * @bdf:       PCI device address: bus, device and function -see PCI_BDF()
+ * @offset:    Register offset to update
+ * @clr:       Bits to clear
+ * @set:       Bits to set
+ * @return 0 if OK, -ve on error
+ */
+int pci_x86_clrset_config(pci_dev_t bdf, uint offset, ulong clr, ulong set,
+                         enum pci_size_t size);
 
 /**
  * Assign IRQ number to a PCI device
index 27432b2897913daadc6c7bfb701b87eaa074a486..1bef4877eb33ce57485a4cfe7c58ba09e95bcba5 100644 (file)
@@ -10,8 +10,7 @@
 #define CONFIG_SPL_BOARD_LOAD_IMAGE
 
 enum {
-       BOOT_DEVICE_SPI         = 10,
-       BOOT_DEVICE_BOARD,
+       BOOT_DEVICE_SPI_MMAP    = 10,
        BOOT_DEVICE_CROS_VBOOT,
 };
 
index c252192bf41455bf09a7aa04d0a2e10feb155e93..2466ad2ad305cc1bef73ff5e511d74eb6dc9b7d4 100644 (file)
@@ -74,7 +74,7 @@ u32 isa_map_rom(u32 bus_addr, int size);
 /* arch/x86/lib/... */
 int video_bios_init(void);
 
-/* arch/x86/lib/fsp/... */
+/* arch/x86/lib/fsp1,2/... */
 
 /**
  * fsp_save_s3_stack() - save stack address to CMOS for next S3 boot
index 906be5eab98571d528a7353a63fef84131e36279..ca0ca1066b04856a1d052aac4b03e67aeff5fd31 100644 (file)
@@ -44,6 +44,8 @@ obj-$(CONFIG_CMD_ZBOOT)       += zimage.o
 endif
 obj-$(CONFIG_USE_HOB) += hob.o
 obj-$(CONFIG_HAVE_FSP) += fsp/
+obj-$(CONFIG_FSP_VERSION1) += fsp1/
+obj-$(CONFIG_FSP_VERSION2) += fsp2/
 
 ifdef CONFIG_SPL_BUILD
 ifdef CONFIG_TPL_BUILD
index 03917188a9bc6ff8df37268663a5d7fde2c6bcd8..197636c4b507eb3fd11eb09fae08a344b7d17a9a 100644 (file)
@@ -4,8 +4,8 @@
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <asm/acpi.h>
-#include <asm/acpi_s3.h>
 #include <asm/acpi_table.h>
 #include <asm/post.h>
 #include <linux/linkage.h>
index 2d08a2db0dba68062e5fc7f5f3b7a4c516e052eb..8685aa30467bf4e419e7074c0ad8b68f0020bb02 100644 (file)
@@ -4,8 +4,8 @@
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <vbe.h>
-#include <asm/acpi_s3.h>
 #include <asm/coreboot_tables.h>
 #include <asm/e820.h>
 
index 870de71bd71a940f6e326e648579167cd868655f..9e348564737b0347d1f46d034542575663f73a0e 100644 (file)
@@ -1,9 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0+
 #
-# Copyright (C) 2015 Google, Inc
+# Copyright 2019 Google LLC
 
-obj-y += fsp_car.o
 obj-y += fsp_common.o
 obj-y += fsp_dram.o
-obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o
 obj-y += fsp_support.o
diff --git a/arch/x86/lib/fsp/fsp_car.S b/arch/x86/lib/fsp/fsp_car.S
deleted file mode 100644 (file)
index 8c54cea..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#include <config.h>
-#include <asm/post.h>
-
-.globl car_init
-car_init:
-       /*
-        * Note: ebp holds the BIST value (built-in self test) so far, but ebp
-        * will be destroyed through the FSP call, thus we have to test the
-        * BIST value here before we call into FSP.
-        */
-       test    %ebp, %ebp
-       jz      car_init_start
-       post_code(POST_BIST_FAILURE)
-       jmp     die
-
-car_init_start:
-       post_code(POST_CAR_START)
-       lea     find_fsp_header_romstack, %esp
-       jmp     find_fsp_header
-
-find_fsp_header_ret:
-       /* EAX points to FSP_INFO_HEADER */
-       mov     %eax, %ebp
-
-       /* sanity test */
-       cmp     $CONFIG_FSP_ADDR, %eax
-       jb      die
-
-       /* calculate TempRamInitEntry address */
-       mov     0x30(%ebp), %eax
-       add     0x1c(%ebp), %eax
-
-       /* call FSP TempRamInitEntry to setup temporary stack */
-       lea     temp_ram_init_romstack, %esp
-       jmp     *%eax
-
-temp_ram_init_ret:
-       addl    $4, %esp
-       cmp     $0, %eax
-       jnz     car_init_fail
-
-       post_code(POST_CAR_CPU_CACHE)
-
-       /*
-        * The FSP TempRamInit initializes the ecx and edx registers to
-        * point to a temporary but writable memory range (Cache-As-RAM).
-        * ecx: the start of this temporary memory range,
-        * edx: the end of this range.
-        */
-
-       /* stack grows down from top of CAR */
-       movl    %edx, %esp
-       subl    $4, %esp
-
-       xor     %esi, %esi
-       jmp     car_init_done
-
-.global fsp_init_done
-fsp_init_done:
-       /*
-        * We come here from fsp_continue() with eax pointing to the HOB list.
-        * Save eax to esi temporarily.
-        */
-       movl    %eax, %esi
-
-car_init_done:
-       /*
-        * Re-initialize the ebp (BIST) to zero, as we already reach here
-        * which means we passed BIST testing before.
-        */
-       xorl    %ebp, %ebp
-       jmp     car_init_ret
-
-car_init_fail:
-       post_code(POST_CAR_FAILURE)
-
-die:
-       hlt
-       jmp     die
-       hlt
-
-       /*
-        * The function call before CAR initialization is tricky. It cannot
-        * be called using the 'call' instruction but only the 'jmp' with
-        * the help of a handcrafted stack in the ROM. The stack needs to
-        * contain the function return address as well as the parameters.
-        */
-       .balign 4
-find_fsp_header_romstack:
-       .long   find_fsp_header_ret
-
-       .balign 4
-temp_ram_init_romstack:
-       .long   temp_ram_init_ret
-       .long   temp_ram_init_params
-temp_ram_init_params:
-_dt_ucode_base_size:
-       /* These next two fields are filled in by binman */
-.globl ucode_base
-ucode_base:    /* Declared in microcode.h */
-       .long   0                       /* microcode base */
-.globl ucode_size
-ucode_size:    /* Declared in microcode.h */
-       .long   0                       /* microcode size */
-       .long   CONFIG_SYS_MONITOR_BASE /* code region base */
-       .long   CONFIG_SYS_MONITOR_LEN  /* code region size */
index ed0827c6e921901d440c1733dbef10a40462087e..40ba866d77c0845a0044eecfb881c9d0887b3d30 100644 (file)
@@ -4,10 +4,10 @@
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <dm.h>
 #include <errno.h>
 #include <rtc.h>
-#include <asm/acpi_s3.h>
 #include <asm/cmos_layout.h>
 #include <asm/early_cmos.h>
 #include <asm/io.h>
@@ -55,11 +55,9 @@ void board_final_cleanup(void)
                debug("fail, error code %x\n", status);
        else
                debug("OK\n");
-
-       return;
 }
 
-static __maybe_unused void *fsp_prepare_mrc_cache(void)
+void *fsp_prepare_mrc_cache(void)
 {
        struct mrc_data_container *cache;
        struct mrc_region entry;
@@ -104,62 +102,3 @@ int fsp_save_s3_stack(void)
        return 0;
 }
 #endif
-
-int arch_fsp_init(void)
-{
-       void *nvs;
-       int stack = CONFIG_FSP_TEMP_RAM_ADDR;
-       int boot_mode = BOOT_FULL_CONFIG;
-#ifdef CONFIG_HAVE_ACPI_RESUME
-       int prev_sleep_state = chipset_prev_sleep_state();
-       gd->arch.prev_sleep_state = prev_sleep_state;
-#endif
-
-       if (!gd->arch.hob_list) {
-#ifdef CONFIG_ENABLE_MRC_CACHE
-               nvs = fsp_prepare_mrc_cache();
-#else
-               nvs = NULL;
-#endif
-
-#ifdef CONFIG_HAVE_ACPI_RESUME
-               if (prev_sleep_state == ACPI_S3) {
-                       if (nvs == NULL) {
-                               /* If waking from S3 and no cache then */
-                               debug("No MRC cache found in S3 resume path\n");
-                               post_code(POST_RESUME_FAILURE);
-                               /* Clear Sleep Type */
-                               chipset_clear_sleep_state();
-                               /* Reboot */
-                               debug("Rebooting..\n");
-                               outb(SYS_RST | RST_CPU, IO_PORT_RESET);
-                               /* Should not reach here.. */
-                               panic("Reboot System");
-                       }
-
-                       /*
-                        * DM is not available yet at this point, hence call
-                        * CMOS access library which does not depend on DM.
-                        */
-                       stack = cmos_read32(CMOS_FSP_STACK_ADDR);
-                       boot_mode = BOOT_ON_S3_RESUME;
-               }
-#endif
-               /*
-                * The first time we enter here, call fsp_init().
-                * Note the execution does not return to this function,
-                * instead it jumps to fsp_continue().
-                */
-               fsp_init(stack, boot_mode, nvs);
-       } else {
-               /*
-                * The second time we enter here, adjust the size of malloc()
-                * pool before relocation. Given gd->malloc_base was adjusted
-                * after the call to board_init_f_init_reserve() in arch/x86/
-                * cpu/start.S, we should fix up gd->malloc_limit here.
-                */
-               gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN;
-       }
-
-       return 0;
-}
index 3a23b70410b44e4fc6443f460c350f4277a0a4f8..2d1023068feb856015aab667dd319b46f3e04004 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <common.h>
+#include <handoff.h>
 #include <asm/fsp/fsp_support.h>
 #include <asm/e820.h>
 #include <asm/mrccache.h>
@@ -11,7 +12,7 @@
 
 DECLARE_GLOBAL_DATA_PTR;
 
-int dram_init(void)
+int fsp_scan_for_ram_size(void)
 {
        phys_size_t ram_size = 0;
        const struct hob_header *hdr;
@@ -22,9 +23,8 @@ int dram_init(void)
                if (hdr->type == HOB_TYPE_RES_DESC) {
                        res_desc = (struct hob_res_desc *)hdr;
                        if (res_desc->type == RES_SYS_MEM ||
-                           res_desc->type == RES_MEM_RESERVED) {
+                           res_desc->type == RES_MEM_RESERVED)
                                ram_size += res_desc->len;
-                       }
                }
                hdr = get_next_hob(hdr);
        }
@@ -32,13 +32,8 @@ int dram_init(void)
        gd->ram_size = ram_size;
        post_code(POST_DRAM);
 
-#ifdef CONFIG_ENABLE_MRC_CACHE
-       gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list,
-                                              &gd->arch.mrc_output_len);
-#endif
-
        return 0;
-}
+};
 
 int dram_init_banksize(void)
 {
@@ -48,19 +43,6 @@ int dram_init_banksize(void)
        return 0;
 }
 
-/*
- * This function looks for the highest region of memory lower than 4GB which
- * has enough space for U-Boot where U-Boot is aligned on a page boundary.
- * It overrides the default implementation found elsewhere which simply
- * picks the end of ram, wherever that may be. The location of the stack,
- * the relocation address, and how far U-Boot is moved by relocation are
- * set in the global data structure.
- */
-ulong board_get_usable_ram_top(ulong total_size)
-{
-       return fsp_get_usable_lowmem_top(gd->arch.hob_list);
-}
-
 unsigned int install_e820_map(unsigned int max_entries,
                              struct e820_entry *entries)
 {
@@ -98,7 +80,7 @@ unsigned int install_e820_map(unsigned int max_entries,
         * reserved in order for ACPI S3 resume to work.
         */
        entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
-       entries[num_entries].size = gd->ram_top - gd->start_addr_sp + \
+       entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
                CONFIG_STACK_SIZE;
        entries[num_entries].type = E820_RESERVED;
        num_entries++;
@@ -106,3 +88,13 @@ unsigned int install_e820_map(unsigned int max_entries,
 
        return num_entries;
 }
+
+#if CONFIG_IS_ENABLED(HANDOFF) && IS_ENABLED(CONFIG_USE_HOB)
+int handoff_arch_save(struct spl_handoff *ho)
+{
+       ho->arch.usable_ram_top = fsp_get_usable_lowmem_top(gd->arch.hob_list);
+       ho->arch.hob_list = gd->arch.hob_list;
+
+       return 0;
+}
+#endif
diff --git a/arch/x86/lib/fsp/fsp_graphics.c b/arch/x86/lib/fsp/fsp_graphics.c
deleted file mode 100644 (file)
index 91d2d08..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
- */
-
-#include <common.h>
-#include <dm.h>
-#include <vbe.h>
-#include <video.h>
-#include <asm/fsp/fsp_support.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-struct pixel {
-       u8 pos;
-       u8 size;
-};
-
-static const struct fsp_framebuffer {
-       struct pixel red;
-       struct pixel green;
-       struct pixel blue;
-       struct pixel rsvd;
-} fsp_framebuffer_format_map[] = {
-       [pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
-       [pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
-};
-
-static int save_vesa_mode(struct vesa_mode_info *vesa)
-{
-       const struct hob_graphics_info *ginfo;
-       const struct fsp_framebuffer *fbinfo;
-
-       ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL);
-
-       /*
-        * If there is no graphics info structure, bail out and keep
-        * running on the serial console.
-        *
-        * Note: on some platforms (eg: Braswell), the FSP will not produce
-        * the graphics info HOB unless you plug some cables to the display
-        * interface (eg: HDMI) on the board.
-        */
-       if (!ginfo) {
-               debug("FSP graphics hand-off block not found\n");
-               return -ENXIO;
-       }
-
-       vesa->x_resolution = ginfo->width;
-       vesa->y_resolution = ginfo->height;
-       vesa->bits_per_pixel = 32;
-       vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4;
-       vesa->phys_base_ptr = ginfo->fb_base;
-
-       if (ginfo->pixel_format >= pixel_bitmask) {
-               debug("FSP set unknown framebuffer format: %d\n",
-                     ginfo->pixel_format);
-               return -EINVAL;
-       }
-       fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format];
-       vesa->red_mask_size = fbinfo->red.size;
-       vesa->red_mask_pos = fbinfo->red.pos;
-       vesa->green_mask_size = fbinfo->green.size;
-       vesa->green_mask_pos = fbinfo->green.pos;
-       vesa->blue_mask_size = fbinfo->blue.size;
-       vesa->blue_mask_pos = fbinfo->blue.pos;
-       vesa->reserved_mask_size = fbinfo->rsvd.size;
-       vesa->reserved_mask_pos = fbinfo->rsvd.pos;
-
-       return 0;
-}
-
-static int fsp_video_probe(struct udevice *dev)
-{
-       struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
-       struct video_priv *uc_priv = dev_get_uclass_priv(dev);
-       struct vesa_mode_info *vesa = &mode_info.vesa;
-       int ret;
-
-       printf("Video: ");
-
-       /* Initialize vesa_mode_info structure */
-       ret = save_vesa_mode(vesa);
-       if (ret)
-               goto err;
-
-       /*
-        * The framebuffer base address in the FSP graphics info HOB reflects
-        * the value assigned by the FSP. After PCI enumeration the framebuffer
-        * base address may be relocated. Let's get the updated one from device.
-        *
-        * For IGD, it seems to be always on BAR2.
-        */
-       vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
-
-       ret = vbe_setup_video_priv(vesa, uc_priv, plat);
-       if (ret)
-               goto err;
-
-       printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
-              vesa->bits_per_pixel);
-
-       return 0;
-
-err:
-       printf("No video mode configured in FSP!\n");
-       return ret;
-}
-
-static const struct udevice_id fsp_video_ids[] = {
-       { .compatible = "fsp-fb" },
-       { }
-};
-
-U_BOOT_DRIVER(fsp_video) = {
-       .name   = "fsp_video",
-       .id     = UCLASS_VIDEO,
-       .of_match = fsp_video_ids,
-       .probe  = fsp_video_probe,
-};
-
-static struct pci_device_id fsp_video_supported[] = {
-       { PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) },
-       { },
-};
-
-U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported);
index 0eaa9b232b16689a353c37640ff03a1664ddc00b..983888fd74301cca2b5b13c068eaf41c5bf9e573 100644 (file)
@@ -5,199 +5,9 @@
  */
 
 #include <common.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 #include <asm/post.h>
 
-struct fsp_header *__attribute__((optimize("O0"))) find_fsp_header(void)
-{
-       /*
-        * This function may be called before the a stack is established,
-        * so special care must be taken. First, it cannot declare any local
-        * variable using stack. Only register variable can be used here.
-        * Secondly, some compiler version will add prolog or epilog code
-        * for the C function. If so the function call may not work before
-        * stack is ready.
-        *
-        * GCC 4.8.1 has been verified to be working for the following codes.
-        */
-       volatile register u8 *fsp asm("eax");
-
-       /* Initalize the FSP base */
-       fsp = (u8 *)CONFIG_FSP_ADDR;
-
-       /* Check the FV signature, _FVH */
-       if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
-               /* Go to the end of the FV header and align the address */
-               fsp += ((struct fv_header *)fsp)->ext_hdr_off;
-               fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
-               fsp  = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
-       } else {
-               fsp  = 0;
-       }
-
-       /* Check the FFS GUID */
-       if (fsp &&
-           ((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 &&
-           ((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 &&
-           ((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 &&
-           ((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 &&
-           ((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 &&
-           ((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 &&
-           ((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 &&
-           ((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 &&
-           ((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 &&
-           ((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 &&
-           ((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 &&
-           ((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 &&
-           ((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 &&
-           ((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 &&
-           ((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 &&
-           ((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) {
-               /* Add the FFS header size to find the raw section header */
-               fsp += sizeof(struct ffs_file_header);
-       } else {
-               fsp = 0;
-       }
-
-       if (fsp &&
-           ((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
-               /* Add the raw section header size to find the FSP header */
-               fsp += sizeof(struct raw_section);
-       } else {
-               fsp = 0;
-       }
-
-       return (struct fsp_header *)fsp;
-}
-
-void fsp_continue(u32 status, void *hob_list)
-{
-       post_code(POST_MRC);
-
-       assert(status == 0);
-
-       /* The boot loader main function entry */
-       fsp_init_done(hob_list);
-}
-
-void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
-{
-       struct fsp_config_data config_data;
-       fsp_init_f init;
-       struct fsp_init_params params;
-       struct fspinit_rtbuf rt_buf;
-       struct fsp_header *fsp_hdr;
-       struct fsp_init_params *params_ptr;
-#ifdef CONFIG_FSP_USE_UPD
-       struct vpd_region *fsp_vpd;
-       struct upd_region *fsp_upd;
-#endif
-
-       fsp_hdr = find_fsp_header();
-       if (fsp_hdr == NULL) {
-               /* No valid FSP info header was found */
-               panic("Invalid FSP header");
-       }
-
-       config_data.common.fsp_hdr = fsp_hdr;
-       config_data.common.stack_top = stack_top;
-       config_data.common.boot_mode = boot_mode;
-
-#ifdef CONFIG_FSP_USE_UPD
-       /* Get VPD region start */
-       fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
-                       fsp_hdr->cfg_region_off);
-
-       /* Verify the VPD data region is valid */
-       assert(fsp_vpd->sign == VPD_IMAGE_ID);
-
-       fsp_upd = &config_data.fsp_upd;
-
-       /* Copy default data from Flash */
-       memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
-              sizeof(struct upd_region));
-
-       /* Verify the UPD data region is valid */
-       assert(fsp_upd->terminator == UPD_TERMINATOR);
-#endif
-
-       memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
-
-       /* Override any configuration if required */
-       update_fsp_configs(&config_data, &rt_buf);
-
-       memset(&params, 0, sizeof(struct fsp_init_params));
-       params.nvs_buf = nvs_buf;
-       params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
-       params.continuation = (fsp_continuation_f)asm_continuation;
-
-       init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
-       params_ptr = &params;
-
-       post_code(POST_PRE_MRC);
-
-       /* Load GDT for FSP */
-       setup_fsp_gdt();
-
-       /*
-        * Use ASM code to ensure the register value in EAX & EDX
-        * will be passed into fsp_continue
-        */
-       asm volatile (
-               "pushl  %0;"
-               "call   *%%eax;"
-               ".global asm_continuation;"
-               "asm_continuation:;"
-               "movl   4(%%esp), %%eax;"       /* status */
-               "movl   8(%%esp), %%edx;"       /* hob_list */
-               "jmp    fsp_continue;"
-               : : "m"(params_ptr), "a"(init)
-       );
-
-       /*
-        * Should never get here.
-        * Control will continue from fsp_continue.
-        * This line below is to prevent the compiler from optimizing
-        * structure intialization.
-        *
-        * DO NOT REMOVE!
-        */
-       init(&params);
-}
-
-u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
-{
-       fsp_notify_f notify;
-       struct fsp_notify_params params;
-       struct fsp_notify_params *params_ptr;
-       u32 status;
-
-       if (!fsp_hdr)
-               fsp_hdr = (struct fsp_header *)find_fsp_header();
-
-       if (fsp_hdr == NULL) {
-               /* No valid FSP info header */
-               panic("Invalid FSP header");
-       }
-
-       notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
-       params.phase = phase;
-       params_ptr = &params;
-
-       /*
-        * Use ASM code to ensure correct parameter is on the stack for
-        * FspNotify as U-Boot is using different ABI from FSP
-        */
-       asm volatile (
-               "pushl  %1;"            /* push notify phase */
-               "call   *%%eax;"        /* call FspNotify */
-               "addl   $4, %%esp;"     /* clean up the stack */
-               : "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
-       );
-
-       return status;
-}
-
 u32 fsp_get_usable_lowmem_top(const void *hob_list)
 {
        const struct hob_header *hdr;
@@ -324,7 +134,7 @@ u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len)
 
        base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
                        &length, &guid);
-       if ((len != 0) && (base != 0))
+       if (len && base)
                *len = (u32)length;
 
        return base;
@@ -338,7 +148,7 @@ u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len)
 
        base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
                        &length, &guid);
-       if ((len != 0) && (base != 0))
+       if (len && base)
                *len = (u32)length;
 
        return base;
@@ -351,6 +161,13 @@ void *fsp_get_nvs_data(const void *hob_list, u32 *len)
        return hob_get_guid_hob_data(hob_list, len, &guid);
 }
 
+void *fsp_get_var_nvs_data(const void *hob_list, u32 *len)
+{
+       const efi_guid_t guid = FSP_VARIABLE_NV_DATA_HOB_GUID;
+
+       return hob_get_guid_hob_data(hob_list, len, &guid);
+}
+
 void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len)
 {
        const efi_guid_t guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID;
diff --git a/arch/x86/lib/fsp1/Makefile b/arch/x86/lib/fsp1/Makefile
new file mode 100644 (file)
index 0000000..870de71
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2015 Google, Inc
+
+obj-y += fsp_car.o
+obj-y += fsp_common.o
+obj-y += fsp_dram.o
+obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o
+obj-y += fsp_support.o
diff --git a/arch/x86/lib/fsp1/fsp_car.S b/arch/x86/lib/fsp1/fsp_car.S
new file mode 100644 (file)
index 0000000..a64a653
--- /dev/null
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <config.h>
+#include <asm/post.h>
+
+.globl car_init
+car_init:
+       /*
+        * Note: ebp holds the BIST value (built-in self test) so far, but ebp
+        * will be destroyed through the FSP call, thus we have to test the
+        * BIST value here before we call into FSP.
+        */
+       test    %ebp, %ebp
+       jz      car_init_start
+       post_code(POST_BIST_FAILURE)
+       jmp     die
+
+car_init_start:
+       post_code(POST_CAR_START)
+       lea     fsp_find_header_romstack, %esp
+       jmp     fsp_find_header
+
+fsp_find_header_ret:
+       /* EAX points to FSP_INFO_HEADER */
+       mov     %eax, %ebp
+
+       /* sanity test */
+       cmp     $CONFIG_FSP_ADDR, %eax
+       jb      die
+
+       /* calculate TempRamInitEntry address */
+       mov     0x30(%ebp), %eax
+       add     0x1c(%ebp), %eax
+
+       /* call FSP TempRamInitEntry to setup temporary stack */
+       lea     temp_ram_init_romstack, %esp
+       jmp     *%eax
+
+temp_ram_init_ret:
+       addl    $4, %esp
+       cmp     $0, %eax
+       jnz     car_init_fail
+
+       post_code(POST_CAR_CPU_CACHE)
+
+       /*
+        * The FSP TempRamInit initializes the ecx and edx registers to
+        * point to a temporary but writable memory range (Cache-As-RAM).
+        * ecx: the start of this temporary memory range,
+        * edx: the end of this range.
+        */
+
+       /* stack grows down from top of CAR */
+       movl    %edx, %esp
+       subl    $4, %esp
+
+       xor     %esi, %esi
+       jmp     car_init_done
+
+.global fsp_init_done
+fsp_init_done:
+       /*
+        * We come here from fsp_continue() with eax pointing to the HOB list.
+        * Save eax to esi temporarily.
+        */
+       movl    %eax, %esi
+
+car_init_done:
+       /*
+        * Re-initialize the ebp (BIST) to zero, as we already reach here
+        * which means we passed BIST testing before.
+        */
+       xorl    %ebp, %ebp
+       jmp     car_init_ret
+
+car_init_fail:
+       post_code(POST_CAR_FAILURE)
+
+die:
+       hlt
+       jmp     die
+       hlt
+
+       /*
+        * The function call before CAR initialization is tricky. It cannot
+        * be called using the 'call' instruction but only the 'jmp' with
+        * the help of a handcrafted stack in the ROM. The stack needs to
+        * contain the function return address as well as the parameters.
+        */
+       .balign 4
+fsp_find_header_romstack:
+       .long   fsp_find_header_ret
+
+       .balign 4
+temp_ram_init_romstack:
+       .long   temp_ram_init_ret
+       .long   temp_ram_init_params
+temp_ram_init_params:
+_dt_ucode_base_size:
+       /* These next two fields are filled in by binman */
+.globl ucode_base
+ucode_base:    /* Declared in microcode.h */
+       .long   0                       /* microcode base */
+.globl ucode_size
+ucode_size:    /* Declared in microcode.h */
+       .long   0                       /* microcode size */
+       .long   CONFIG_SYS_MONITOR_BASE /* code region base */
+       .long   CONFIG_SYS_MONITOR_LEN  /* code region size */
diff --git a/arch/x86/lib/fsp1/fsp_common.c b/arch/x86/lib/fsp1/fsp_common.c
new file mode 100644 (file)
index 0000000..e8066d8
--- /dev/null
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <acpi_s3.h>
+#include <dm.h>
+#include <errno.h>
+#include <rtc.h>
+#include <asm/cmos_layout.h>
+#include <asm/early_cmos.h>
+#include <asm/io.h>
+#include <asm/mrccache.h>
+#include <asm/post.h>
+#include <asm/processor.h>
+#include <asm/fsp1/fsp_support.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int arch_fsp_init(void)
+{
+       void *nvs;
+       int stack = CONFIG_FSP_TEMP_RAM_ADDR;
+       int boot_mode = BOOT_FULL_CONFIG;
+#ifdef CONFIG_HAVE_ACPI_RESUME
+       int prev_sleep_state = chipset_prev_sleep_state();
+       gd->arch.prev_sleep_state = prev_sleep_state;
+#endif
+
+       if (!gd->arch.hob_list) {
+               if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
+                       nvs = fsp_prepare_mrc_cache();
+               else
+                       nvs = NULL;
+
+#ifdef CONFIG_HAVE_ACPI_RESUME
+               if (prev_sleep_state == ACPI_S3) {
+                       if (nvs == NULL) {
+                               /* If waking from S3 and no cache then */
+                               debug("No MRC cache found in S3 resume path\n");
+                               post_code(POST_RESUME_FAILURE);
+                               /* Clear Sleep Type */
+                               chipset_clear_sleep_state();
+                               /* Reboot */
+                               debug("Rebooting..\n");
+                               outb(SYS_RST | RST_CPU, IO_PORT_RESET);
+                               /* Should not reach here.. */
+                               panic("Reboot System");
+                       }
+
+                       /*
+                        * DM is not available yet at this point, hence call
+                        * CMOS access library which does not depend on DM.
+                        */
+                       stack = cmos_read32(CMOS_FSP_STACK_ADDR);
+                       boot_mode = BOOT_ON_S3_RESUME;
+               }
+#endif
+               /*
+                * The first time we enter here, call fsp_init().
+                * Note the execution does not return to this function,
+                * instead it jumps to fsp_continue().
+                */
+               fsp_init(stack, boot_mode, nvs);
+       } else {
+               /*
+                * The second time we enter here, adjust the size of malloc()
+                * pool before relocation. Given gd->malloc_base was adjusted
+                * after the call to board_init_f_init_reserve() in arch/x86/
+                * cpu/start.S, we should fix up gd->malloc_limit here.
+                */
+               gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN;
+       }
+
+       return 0;
+}
diff --git a/arch/x86/lib/fsp1/fsp_dram.c b/arch/x86/lib/fsp1/fsp_dram.c
new file mode 100644 (file)
index 0000000..6a3349b
--- /dev/null
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <asm/fsp/fsp_support.h>
+
+int dram_init(void)
+{
+       int ret;
+
+       /* The FSP has already set up DRAM, so grab the info we need */
+       ret = fsp_scan_for_ram_size();
+       if (ret)
+               return ret;
+
+       if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
+               gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list,
+                                              &gd->arch.mrc_output_len);
+
+       return 0;
+}
+
+/*
+ * This function looks for the highest region of memory lower than 4GB which
+ * has enough space for U-Boot where U-Boot is aligned on a page boundary.
+ * It overrides the default implementation found elsewhere which simply
+ * picks the end of ram, wherever that may be. The location of the stack,
+ * the relocation address, and how far U-Boot is moved by relocation are
+ * set in the global data structure.
+ */
+ulong board_get_usable_ram_top(ulong total_size)
+{
+       return fsp_get_usable_lowmem_top(gd->arch.hob_list);
+}
diff --git a/arch/x86/lib/fsp1/fsp_graphics.c b/arch/x86/lib/fsp1/fsp_graphics.c
new file mode 100644 (file)
index 0000000..52e7133
--- /dev/null
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <vbe.h>
+#include <video.h>
+#include <asm/fsp1/fsp_support.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pixel {
+       u8 pos;
+       u8 size;
+};
+
+static const struct fsp_framebuffer {
+       struct pixel red;
+       struct pixel green;
+       struct pixel blue;
+       struct pixel rsvd;
+} fsp_framebuffer_format_map[] = {
+       [pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
+       [pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
+};
+
+static int save_vesa_mode(struct vesa_mode_info *vesa)
+{
+       const struct hob_graphics_info *ginfo;
+       const struct fsp_framebuffer *fbinfo;
+
+       ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL);
+
+       /*
+        * If there is no graphics info structure, bail out and keep
+        * running on the serial console.
+        *
+        * Note: on some platforms (eg: Braswell), the FSP will not produce
+        * the graphics info HOB unless you plug some cables to the display
+        * interface (eg: HDMI) on the board.
+        */
+       if (!ginfo) {
+               debug("FSP graphics hand-off block not found\n");
+               return -ENXIO;
+       }
+
+       vesa->x_resolution = ginfo->width;
+       vesa->y_resolution = ginfo->height;
+       vesa->bits_per_pixel = 32;
+       vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4;
+       vesa->phys_base_ptr = ginfo->fb_base;
+
+       if (ginfo->pixel_format >= pixel_bitmask) {
+               debug("FSP set unknown framebuffer format: %d\n",
+                     ginfo->pixel_format);
+               return -EINVAL;
+       }
+       fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format];
+       vesa->red_mask_size = fbinfo->red.size;
+       vesa->red_mask_pos = fbinfo->red.pos;
+       vesa->green_mask_size = fbinfo->green.size;
+       vesa->green_mask_pos = fbinfo->green.pos;
+       vesa->blue_mask_size = fbinfo->blue.size;
+       vesa->blue_mask_pos = fbinfo->blue.pos;
+       vesa->reserved_mask_size = fbinfo->rsvd.size;
+       vesa->reserved_mask_pos = fbinfo->rsvd.pos;
+
+       return 0;
+}
+
+static int fsp_video_probe(struct udevice *dev)
+{
+       struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+       struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+       struct vesa_mode_info *vesa = &mode_info.vesa;
+       int ret;
+
+       printf("Video: ");
+
+       /* Initialize vesa_mode_info structure */
+       ret = save_vesa_mode(vesa);
+       if (ret)
+               goto err;
+
+       /*
+        * The framebuffer base address in the FSP graphics info HOB reflects
+        * the value assigned by the FSP. After PCI enumeration the framebuffer
+        * base address may be relocated. Let's get the updated one from device.
+        *
+        * For IGD, it seems to be always on BAR2.
+        */
+       vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
+
+       ret = vbe_setup_video_priv(vesa, uc_priv, plat);
+       if (ret)
+               goto err;
+
+       printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
+              vesa->bits_per_pixel);
+
+       return 0;
+
+err:
+       printf("No video mode configured in FSP!\n");
+       return ret;
+}
+
+static const struct udevice_id fsp_video_ids[] = {
+       { .compatible = "fsp-fb" },
+       { }
+};
+
+U_BOOT_DRIVER(fsp_video) = {
+       .name   = "fsp_video",
+       .id     = UCLASS_VIDEO,
+       .of_match = fsp_video_ids,
+       .probe  = fsp_video_probe,
+};
+
+static struct pci_device_id fsp_video_supported[] = {
+       { PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) },
+       { },
+};
+
+U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported);
diff --git a/arch/x86/lib/fsp1/fsp_support.c b/arch/x86/lib/fsp1/fsp_support.c
new file mode 100644 (file)
index 0000000..c7a2c73
--- /dev/null
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: Intel
+/*
+ * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <common.h>
+#include <asm/fsp1/fsp_support.h>
+#include <asm/post.h>
+
+struct fsp_header *__attribute__((optimize("O0"))) fsp_find_header(void)
+{
+       /*
+        * This function may be called before the a stack is established,
+        * so special care must be taken. First, it cannot declare any local
+        * variable using stack. Only register variable can be used here.
+        * Secondly, some compiler version will add prolog or epilog code
+        * for the C function. If so the function call may not work before
+        * stack is ready.
+        *
+        * GCC 4.8.1 has been verified to be working for the following codes.
+        */
+       volatile register u8 *fsp asm("eax");
+
+       /* Initalize the FSP base */
+       fsp = (u8 *)CONFIG_FSP_ADDR;
+
+       /* Check the FV signature, _FVH */
+       if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
+               /* Go to the end of the FV header and align the address */
+               fsp += ((struct fv_header *)fsp)->ext_hdr_off;
+               fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
+               fsp  = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
+       } else {
+               fsp  = 0;
+       }
+
+       /* Check the FFS GUID */
+       if (fsp &&
+           ((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 &&
+           ((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 &&
+           ((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 &&
+           ((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 &&
+           ((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 &&
+           ((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 &&
+           ((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 &&
+           ((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 &&
+           ((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 &&
+           ((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 &&
+           ((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 &&
+           ((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 &&
+           ((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 &&
+           ((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 &&
+           ((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 &&
+           ((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) {
+               /* Add the FFS header size to find the raw section header */
+               fsp += sizeof(struct ffs_file_header);
+       } else {
+               fsp = 0;
+       }
+
+       if (fsp &&
+           ((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
+               /* Add the raw section header size to find the FSP header */
+               fsp += sizeof(struct raw_section);
+       } else {
+               fsp = 0;
+       }
+
+       return (struct fsp_header *)fsp;
+}
+
+void fsp_continue(u32 status, void *hob_list)
+{
+       post_code(POST_MRC);
+
+       assert(status == 0);
+
+       /* The boot loader main function entry */
+       fsp_init_done(hob_list);
+}
+
+void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
+{
+       struct fsp_config_data config_data;
+       fsp_init_f init;
+       struct fsp_init_params params;
+       struct fspinit_rtbuf rt_buf;
+       struct fsp_header *fsp_hdr;
+       struct fsp_init_params *params_ptr;
+#ifdef CONFIG_FSP_USE_UPD
+       struct vpd_region *fsp_vpd;
+       struct upd_region *fsp_upd;
+#endif
+
+       fsp_hdr = fsp_find_header();
+       if (fsp_hdr == NULL) {
+               /* No valid FSP info header was found */
+               panic("Invalid FSP header");
+       }
+
+       config_data.common.fsp_hdr = fsp_hdr;
+       config_data.common.stack_top = stack_top;
+       config_data.common.boot_mode = boot_mode;
+
+#ifdef CONFIG_FSP_USE_UPD
+       /* Get VPD region start */
+       fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
+                       fsp_hdr->cfg_region_off);
+
+       /* Verify the VPD data region is valid */
+       assert(fsp_vpd->sign == VPD_IMAGE_ID);
+
+       fsp_upd = &config_data.fsp_upd;
+
+       /* Copy default data from Flash */
+       memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
+              sizeof(struct upd_region));
+
+       /* Verify the UPD data region is valid */
+       assert(fsp_upd->terminator == UPD_TERMINATOR);
+#endif
+
+       memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
+
+       /* Override any configuration if required */
+       fsp_update_configs(&config_data, &rt_buf);
+
+       memset(&params, 0, sizeof(struct fsp_init_params));
+       params.nvs_buf = nvs_buf;
+       params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
+       params.continuation = (fsp_continuation_f)fsp_asm_continuation;
+
+       init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
+       params_ptr = &params;
+
+       post_code(POST_PRE_MRC);
+
+       /* Load GDT for FSP */
+       setup_fsp_gdt();
+
+       /*
+        * Use ASM code to ensure the register value in EAX & EDX
+        * will be passed into fsp_continue
+        */
+       asm volatile (
+               "pushl  %0;"
+               "call   *%%eax;"
+               ".global fsp_asm_continuation;"
+               "fsp_asm_continuation:;"
+               "movl   4(%%esp), %%eax;"       /* status */
+               "movl   8(%%esp), %%edx;"       /* hob_list */
+               "jmp    fsp_continue;"
+               : : "m"(params_ptr), "a"(init)
+       );
+
+       /*
+        * Should never get here.
+        * Control will continue from fsp_continue.
+        * This line below is to prevent the compiler from optimizing
+        * structure intialization.
+        *
+        * DO NOT REMOVE!
+        */
+       init(&params);
+}
+
+u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
+{
+       fsp_notify_f notify;
+       struct fsp_notify_params params;
+       struct fsp_notify_params *params_ptr;
+       u32 status;
+
+       if (!fsp_hdr)
+               fsp_hdr = (struct fsp_header *)fsp_find_header();
+
+       if (fsp_hdr == NULL) {
+               /* No valid FSP info header */
+               panic("Invalid FSP header");
+       }
+
+       notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
+       params.phase = phase;
+       params_ptr = &params;
+
+       /*
+        * Use ASM code to ensure correct parameter is on the stack for
+        * FspNotify as U-Boot is using different ABI from FSP
+        */
+       asm volatile (
+               "pushl  %1;"            /* push notify phase */
+               "call   *%%eax;"        /* call FspNotify */
+               "addl   $4, %%esp;"     /* clean up the stack */
+               : "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
+       );
+
+       return status;
+}
index dcee29b04cfe409fc889a1ea0ac47f0799788d39..f2c47240ee8ee4062b2d89fc8d8aed75a0fada66 100644 (file)
@@ -13,7 +13,7 @@
  * @type:     HOB type to search
  * @hob_list: A pointer to the HOB list
  *
- * @retval:   A HOB object with matching type; Otherwise NULL.
+ * @return A HOB object with matching type; Otherwise NULL.
  */
 const struct hob_header *hob_get_next_hob(uint type, const void *hob_list)
 {
@@ -38,7 +38,7 @@ const struct hob_header *hob_get_next_hob(uint type, const void *hob_list)
  * @guid:     GUID to search
  * @hob_list: A pointer to the HOB list
  *
- * @retval:   A HOB object with matching GUID; Otherwise NULL.
+ * @return A HOB object with matching GUID; Otherwise NULL.
  */
 const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
                                               const void *hob_list)
@@ -65,8 +65,8 @@ const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
  *                 If the GUID HOB is located, the length will be updated.
  * @guid           A pointer to HOB GUID.
  *
- * @retval NULL:   Failed to find the GUID HOB.
- * @retval others: GUID HOB data buffer pointer.
+ * @return NULL:   Failed to find the GUID HOB.
+ * @return others: GUID HOB data buffer pointer.
  */
 void *hob_get_guid_hob_data(const void *hob_list, u32 *len,
                            const efi_guid_t *guid)
index 4774a9bdb78d03339667948f5fab6ba1e70dce08..3e3a11ac2fa93e4bd2675f2cdc012917407182b7 100644 (file)
@@ -12,15 +12,23 @@ DECLARE_GLOBAL_DATA_PTR;
 
 int init_cache_f_r(void)
 {
-#if CONFIG_IS_ENABLED(X86_32BIT_INIT) && !defined(CONFIG_HAVE_FSP) && \
-               !defined(CONFIG_SYS_SLIMBOOTLOADER)
+       bool do_mtrr = CONFIG_IS_ENABLED(X86_32BIT_INIT) ||
+                IS_ENABLED(CONFIG_FSP_VERSION2);
        int ret;
 
-       ret = mtrr_commit(false);
-       /* If MTRR MSR is not implemented by the processor, just ignore it */
-       if (ret && ret != -ENOSYS)
-               return ret;
-#endif
+       do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) &&
+               !IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER);
+
+       if (do_mtrr) {
+               ret = mtrr_commit(false);
+               /*
+                * If MTRR MSR is not implemented by the processor, just ignore
+                * it
+                */
+               if (ret && ret != -ENOSYS)
+                       return ret;
+       }
+
        /* Initialise the CPU cache(s) */
        return init_cache();
 }
index 505d7a943d2e1e75aa9e6609d8df229abc1b72ee..1302a6e34a144a9f3441ae92d7ecce9768b00666 100644 (file)
@@ -10,5 +10,7 @@
 UCLASS_DRIVER(lpc) = {
        .id             = UCLASS_LPC,
        .name           = "lpc",
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
        .post_bind      = dm_scan_fdt_dev,
+#endif
 };
index 5d5d1a9ca749d9ad4acc1b83f27af4892034631e..2baac91383763391c66daa927ecb21d58744f602 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/mrccache.h>
 #include <asm/mtrr.h>
 #include <asm/processor.h>
+#include <asm/spl.h>
 #include <asm-generic/sections.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -39,12 +40,7 @@ static int x86_spl_init(void)
                debug("%s: spl_init() failed\n", __func__);
                return ret;
        }
-#ifdef CONFIG_TPL
-       /* Do a mini-init if TPL has already done the full init */
-       ret = x86_cpu_reinit_f();
-#else
        ret = arch_cpu_init();
-#endif
        if (ret) {
                debug("%s: arch_cpu_init() failed\n", __func__);
                return ret;
@@ -142,7 +138,7 @@ void board_init_f_r(void)
 
 u32 spl_boot_device(void)
 {
-       return BOOT_DEVICE_BOARD;
+       return BOOT_DEVICE_SPI_MMAP;
 }
 
 int spl_start_uboot(void)
@@ -168,7 +164,7 @@ static int spl_board_load_image(struct spl_image_info *spl_image,
 
        return 0;
 }
-SPL_LOAD_IMAGE_METHOD("SPI", 0, BOOT_DEVICE_BOARD, spl_board_load_image);
+SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
 
 int spl_spi_load_image(void)
 {
@@ -183,8 +179,7 @@ void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
        printf("Jumping to 64-bit U-Boot: Note many features are missing\n");
        ret = cpu_jump_to_64bit_uboot(spl_image->entry_point);
        debug("ret=%d\n", ret);
-       while (1)
-               ;
+       hang();
 }
 #endif
 
index 492a2d65216db3a8a895aba2110c26bd16f4404d..cfefa78045e37120d9e42f121ee63a8d6bc95c13 100644 (file)
@@ -71,7 +71,7 @@ void board_init_f_r(void)
 u32 spl_boot_device(void)
 {
        return IS_ENABLED(CONFIG_CHROMEOS) ? BOOT_DEVICE_CROS_VBOOT :
-               BOOT_DEVICE_BOARD;
+               BOOT_DEVICE_SPI_MMAP;
 }
 
 int spl_start_uboot(void)
@@ -97,7 +97,7 @@ static int spl_board_load_image(struct spl_image_info *spl_image,
 
        return 0;
 }
-SPL_LOAD_IMAGE_METHOD("SPI", 0, BOOT_DEVICE_BOARD, spl_board_load_image);
+SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
 
 int spl_spi_load_image(void)
 {
@@ -108,8 +108,7 @@ void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
 {
        printf("Jumping to U-Boot SPL at %lx\n", (ulong)spl_image->entry_point);
        jump_to_spl(spl_image->entry_point);
-       while (1)
-               ;
+       hang();
 }
 
 void spl_board_init(void)
index ac12f303a3befa4f751c9e25decf4991f950bbcf..8f4c587371d920af5c9d18d28fe745ce4535258c 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 #include <common.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 /* ALC262 Verb Table - 10EC0262 */
 static const u32 verb_table_data13[] = {
index c7d3f8addca17a7fb1db75f24bb68d1628ba8300..cc051d2e0cc2bc0e7ab9f975814ae3a0f42cd1d8 100644 (file)
@@ -23,7 +23,7 @@ int at91_video_show_board_info(void)
        int i;
        u32 len = 0;
        char buf[255];
-       char *corp = "2017 Microchip Technology Inc.\n";
+       char *corp = "Microchip Technology Inc.\n";
        char temp[32];
        struct udevice *dev, *con;
        const char *s;
diff --git a/board/atmel/sam9x60ek/Kconfig b/board/atmel/sam9x60ek/Kconfig
new file mode 100644 (file)
index 0000000..32fae21
--- /dev/null
@@ -0,0 +1,12 @@
+if TARGET_SAM9X60EK
+
+config SYS_BOARD
+       default "sam9x60ek"
+
+config SYS_VENDOR
+       default "atmel"
+
+config SYS_CONFIG_NAME
+       default "sam9x60ek"
+
+endif
diff --git a/board/atmel/sam9x60ek/MAINTAINERS b/board/atmel/sam9x60ek/MAINTAINERS
new file mode 100644 (file)
index 0000000..d209249
--- /dev/null
@@ -0,0 +1,9 @@
+SAM9X60EK BOARD
+M:     Sandeep Sheriker M <sandeep.sheriker@microchip.com>
+M:     Eugen Hristev <eugen.hristev@microchip.com>
+S:     Maintained
+F:     board/atmel/sam9x60ek/
+F:     include/configs/sam9x60ek.h
+F:     configs/sam9x60ek_mmc_defconfig
+F:     configs/sam9x60ek_nandflash_defconfig
+F:     configs/sam9x60ek_qspiflash_defconfig
diff --git a/board/atmel/sam9x60ek/Makefile b/board/atmel/sam9x60ek/Makefile
new file mode 100644 (file)
index 0000000..12a406a
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+#
+# Author: Sandeep Sheriker M <sandeep.sheriker@microchip.com>
+
+obj-y += sam9x60ek.o
diff --git a/board/atmel/sam9x60ek/sam9x60ek.c b/board/atmel/sam9x60ek/sam9x60ek.c
new file mode 100644 (file)
index 0000000..182b3ae
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Sandeep Sheriker M <sandeep.sheriker@microchip.com>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/at91sam9_smc.h>
+#include <asm/arch/at91_common.h>
+#include <asm/arch/at91_rstc.h>
+#include <asm/arch/at91_sfr.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpio.h>
+#include <debug_uart.h>
+#include <asm/mach-types.h>
+
+extern void at91_pda_detect(void);
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void at91_prepare_cpu_var(void);
+
+#ifdef CONFIG_CMD_NAND
+static void sam9x60ek_nand_hw_init(void)
+{
+       struct at91_smc *smc = (struct at91_smc *)ATMEL_BASE_SMC;
+       struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
+       unsigned int csa;
+
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 0, 1);   /* NAND OE */
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 1, 1);   /* NAND WE */
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 2, 0);   /* NAND ALE */
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 3, 0);   /* NAND CLE */
+       /* Enable NandFlash */
+       at91_set_gpio_output(CONFIG_SYS_NAND_ENABLE_PIN, 1);
+       /* Configure RDY/BSY */
+       at91_set_gpio_input(CONFIG_SYS_NAND_READY_PIN, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 6, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 7, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 8, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 9, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 10, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 11, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 12, 1);
+       at91_pio3_set_a_periph(AT91_PIO_PORTD, 13, 1);
+
+       at91_periph_clk_enable(ATMEL_ID_PIOD);
+
+       /* Enable CS3 */
+       csa = readl(&sfr->ebicsa);
+       csa |= AT91_SFR_CCFG_EBI_CSA(3, 1) | AT91_SFR_CCFG_NFD0_ON_D16;
+
+       /* Configure IO drive */
+       csa &= ~AT91_SFR_CCFG_EBI_DRIVE_SAM9X60;
+
+       writel(csa, &sfr->ebicsa);
+
+       /* Configure SMC CS3 for NAND/SmartMedia */
+       writel(AT91_SMC_SETUP_NWE(4), &smc->cs[3].setup);
+
+       writel(AT91_SMC_PULSE_NWE(10) | AT91_SMC_PULSE_NCS_WR(20) |
+              AT91_SMC_PULSE_NRD(10) | AT91_SMC_PULSE_NCS_RD(20),
+              &smc->cs[3].pulse);
+
+       writel(AT91_SMC_CYCLE_NWE(20) | AT91_SMC_CYCLE_NRD(20),
+              &smc->cs[3].cycle);
+
+       writel(AT91_SMC_MODE_RM_NRD | AT91_SMC_MODE_WM_NWE |
+#ifdef CONFIG_SYS_NAND_DBW_16
+              AT91_SMC_MODE_DBW_16 |
+#else /* CONFIG_SYS_NAND_DBW_8 */
+              AT91_SMC_MODE_DBW_8 |
+#endif
+              AT91_SMC_MODE_TDF | AT91_SMC_MODE_TDF_CYCLE(15),
+              &smc->cs[3].mode);
+}
+#endif
+
+#ifdef CONFIG_BOARD_LATE_INIT
+int board_late_init(void)
+{
+       at91_prepare_cpu_var();
+
+       at91_pda_detect();
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_DEBUG_UART_BOARD_INIT
+void board_debug_uart_init(void)
+{
+       at91_seriald_hw_init();
+}
+#endif
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+#ifdef CONFIG_DEBUG_UART
+       debug_uart_init();
+#endif
+       return 0;
+}
+#endif
+
+int board_init(void)
+{
+       /* address of boot parameters */
+       gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
+
+#ifdef CONFIG_CMD_NAND
+       sam9x60ek_nand_hw_init();
+#endif
+       return 0;
+}
+
+int dram_init(void)
+{
+       gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
+                                   CONFIG_SYS_SDRAM_SIZE);
+       return 0;
+}
diff --git a/board/atmel/sama5d27_wlsom1_ek/Kconfig b/board/atmel/sama5d27_wlsom1_ek/Kconfig
new file mode 100644 (file)
index 0000000..4b192b0
--- /dev/null
@@ -0,0 +1,15 @@
+if TARGET_SAMA5D27_WLSOM1_EK
+
+config SYS_BOARD
+       default "sama5d27_wlsom1_ek"
+
+config SYS_VENDOR
+       default "atmel"
+
+config SYS_SOC
+       default "at91"
+
+config SYS_CONFIG_NAME
+       default "sama5d27_wlsom1_ek"
+
+endif
diff --git a/board/atmel/sama5d27_wlsom1_ek/MAINTAINERS b/board/atmel/sama5d27_wlsom1_ek/MAINTAINERS
new file mode 100644 (file)
index 0000000..ff68cf0
--- /dev/null
@@ -0,0 +1,8 @@
+SAMA5D27 WLSOM1 EK BOARD
+M:     Nicolas Ferre <nicolas.ferre@microchip.com>
+M:     Eugen Hristev <eugen.hristev@microchip.com>
+S:     Maintained
+F:     board/atmel/sama5d27_wlsom1_ek/
+F:     include/configs/sama5d27_wlsom1_ek.h
+F:     configs/sama5d27_wlsom1_ek_mmc_defconfig
+F:     configs/sama5d27_wlsom1_ek_qspiflash_defconfig
diff --git a/board/atmel/sama5d27_wlsom1_ek/Makefile b/board/atmel/sama5d27_wlsom1_ek/Makefile
new file mode 100644 (file)
index 0000000..cf827ae
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+#
+# Author: Nicolas Ferre <nicolas.ferre@microcihp.com>
+
+obj-y += sama5d27_wlsom1_ek.o
diff --git a/board/atmel/sama5d27_wlsom1_ek/sama5d27_wlsom1_ek.c b/board/atmel/sama5d27_wlsom1_ek/sama5d27_wlsom1_ek.c
new file mode 100644 (file)
index 0000000..fda06c8
--- /dev/null
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Nicolas Ferre <nicolas.ferre@microcihp.com>
+ */
+
+#include <common.h>
+#include <debug_uart.h>
+#include <asm/io.h>
+#include <asm/arch/at91_common.h>
+#include <asm/arch/atmel_pio4.h>
+#include <asm/arch/atmel_mpddrc.h>
+#include <asm/arch/atmel_sdhci.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/sama5d2.h>
+
+extern void at91_pda_detect(void);
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_BOARD_LATE_INIT
+int board_late_init(void)
+{
+#ifdef CONFIG_DM_VIDEO
+       at91_video_show_board_info();
+#endif
+       at91_pda_detect();
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_DEBUG_UART_BOARD_INIT
+static void board_uart0_hw_init(void)
+{
+       atmel_pio4_set_c_periph(AT91_PIO_PORTB, 26, ATMEL_PIO_PUEN_MASK);       /* URXD0 */
+       atmel_pio4_set_c_periph(AT91_PIO_PORTB, 27, 0);                         /* UTXD0 */
+
+       at91_periph_clk_enable(ATMEL_ID_UART0);
+}
+
+void board_debug_uart_init(void)
+{
+       board_uart0_hw_init();
+}
+#endif
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+#ifdef CONFIG_DEBUG_UART
+       debug_uart_init();
+#endif
+
+       return 0;
+}
+#endif
+
+int board_init(void)
+{
+       /* address of boot parameters */
+       gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
+
+       return 0;
+}
+
+#ifdef CONFIG_MISC_INIT_R
+int misc_init_r(void)
+{
+       return 0;
+}
+#endif
+
+int dram_init(void)
+{
+       gd->ram_size = get_ram_size((void *)CONFIG_SYS_SDRAM_BASE,
+                                   CONFIG_SYS_SDRAM_SIZE);
+       return 0;
+}
+
+/* SPL */
+#ifdef CONFIG_SPL_BUILD
+
+static void board_leds_init(void)
+{
+       atmel_pio4_set_pio_output(AT91_PIO_PORTA, 6, 0); /* RED */
+       atmel_pio4_set_pio_output(AT91_PIO_PORTA, 7, 1); /* GREEN */
+       atmel_pio4_set_pio_output(AT91_PIO_PORTA, 8, 0); /* BLUE */
+}
+
+#ifdef CONFIG_SD_BOOT
+void spl_mmc_init(void)
+{
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 1, 0);  /* CMD */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 2, 0);  /* DAT0 */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 3, 0);  /* DAT1 */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 4, 0);  /* DAT2 */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 5, 0);  /* DAT3 */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 0, 0);  /* CK */
+       atmel_pio4_set_a_periph(AT91_PIO_PORTA, 13, 0); /* CD */
+
+       at91_periph_clk_enable(ATMEL_ID_SDMMC0);
+}
+#endif
+
+#ifdef CONFIG_QSPI_BOOT
+void spl_qspi_init(void)
+{
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 5, 0);  /* SCK */
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 6, 0);  /* CS */
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 7, 0);  /* IO0 */
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 8, 0);  /* IO1 */
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 9, 0);  /* IO2 */
+       atmel_pio4_set_d_periph(AT91_PIO_PORTB, 10, 0); /* IO3 */
+
+       at91_periph_clk_enable(ATMEL_ID_QSPI1);
+}
+#endif
+
+void spl_board_init(void)
+{
+       board_leds_init();
+#ifdef CONFIG_SD_BOOT
+       spl_mmc_init();
+#endif
+#ifdef CONFIG_QSPI_BOOT
+       spl_qspi_init();
+#endif
+}
+
+void spl_display_print(void)
+{
+}
+
+static void ddrc_conf(struct atmel_mpddrc_config *ddrc)
+{
+       ddrc->md = (ATMEL_MPDDRC_MD_DBW_32_BITS | ATMEL_MPDDRC_MD_LPDDR2_SDRAM);
+
+       ddrc->cr = (ATMEL_MPDDRC_CR_NC_COL_9 |
+                   ATMEL_MPDDRC_CR_NR_ROW_14 |
+                   ATMEL_MPDDRC_CR_CAS_DDR_CAS3 |
+                   ATMEL_MPDDRC_CR_ZQ_SHORT |
+                   ATMEL_MPDDRC_CR_NB_8BANKS |
+                   ATMEL_MPDDRC_CR_DECOD_INTERLEAVED |
+                   ATMEL_MPDDRC_CR_UNAL_SUPPORTED);
+
+       ddrc->lpddr23_lpr = ATMEL_MPDDRC_LPDDR23_LPR_DS(0x3);
+
+       /*
+        * The AD220032D average time between REFRESH commands (Trefi): 3.9us
+        * 3.9us * 164MHz = 639.6 = 0x27F.
+        */
+       ddrc->rtr = 0x27f;
+       /* Enable Adjust Refresh Rate */
+       ddrc->rtr |= ATMEL_MPDDRC_RTR_ADJ_REF;
+
+       ddrc->tpr0 = ((7 << ATMEL_MPDDRC_TPR0_TRAS_OFFSET) |
+                     (3 << ATMEL_MPDDRC_TPR0_TRCD_OFFSET) |
+                     (4 << ATMEL_MPDDRC_TPR0_TWR_OFFSET) |
+                     (11 << ATMEL_MPDDRC_TPR0_TRC_OFFSET) |
+                     (4 << ATMEL_MPDDRC_TPR0_TRP_OFFSET) |
+                     (2 << ATMEL_MPDDRC_TPR0_TRRD_OFFSET) |
+                     (2 << ATMEL_MPDDRC_TPR0_TWTR_OFFSET) |
+                     (5 << ATMEL_MPDDRC_TPR0_TMRD_OFFSET));
+
+       ddrc->tpr1 = ((21 << ATMEL_MPDDRC_TPR1_TRFC_OFFSET) |
+                     (0 << ATMEL_MPDDRC_TPR1_TXSNR_OFFSET) |
+                     (23 << ATMEL_MPDDRC_TPR1_TXSRD_OFFSET) |
+                     (2 << ATMEL_MPDDRC_TPR1_TXP_OFFSET));
+
+       ddrc->tpr2 = ((0 << ATMEL_MPDDRC_TPR2_TXARD_OFFSET) |
+                     (0 << ATMEL_MPDDRC_TPR2_TXARDS_OFFSET) |
+                     (4 << ATMEL_MPDDRC_TPR2_TRPA_OFFSET) |
+                     (2 << ATMEL_MPDDRC_TPR2_TRTP_OFFSET) |
+                     (10 << ATMEL_MPDDRC_TPR2_TFAW_OFFSET));
+
+       ddrc->tim_cal = ATMEL_MPDDRC_CALR_ZQCS(15);
+
+       /*
+        * According to the sama5d2 datasheet and the following values:
+        * T Sens = 0.75%/C, V Sens = 0.2%/mV, T driftrate = 1C/sec and V driftrate = 15 mV/s
+        * Warning: note that the values T driftrate and V driftrate are dependent on
+        * the application environment.
+        * ZQCS period is 1.5 / ((0.75 x 1) + (0.2 x 15)) = 0.4s
+        * If Trefi is 3.9us, we have: 400000 / 3.9 = 102564: we can maximize
+        * this timer to 0xFFFE.
+        */
+       ddrc->cal_mr4 = ATMEL_MPDDRC_CAL_MR4_COUNT_CAL(0xFFFE);
+
+       /*
+        * MR4 Read interval is dependent on the application environment.
+        * Here, we want to maximize this value as temperature is supposed
+        * to vary slowly in the application chosen.
+        * If Trefi is 3.9us, we have:
+        * (0xFFFE) 65534 x 3.9 = 0.25s between MR4 reads.
+        */
+       ddrc->cal_mr4 |= ATMEL_MPDDRC_CAL_MR4_MR4R(0xFFFE);
+}
+
+void mem_init(void)
+{
+       struct at91_pmc *pmc = (struct at91_pmc *)ATMEL_BASE_PMC;
+       struct atmel_mpddr *mpddrc = (struct atmel_mpddr *)ATMEL_BASE_MPDDRC;
+       struct atmel_mpddrc_config ddrc_config;
+       u32 reg;
+
+       at91_periph_clk_enable(ATMEL_ID_MPDDRC);
+       writel(AT91_PMC_DDR, &pmc->scer);
+
+       ddrc_conf(&ddrc_config);
+
+       reg = readl(&mpddrc->io_calibr);
+       reg &= ~ATMEL_MPDDRC_IO_CALIBR_RDIV;
+       reg |= ATMEL_MPDDRC_IO_CALIBR_LPDDR2_RZQ_48;
+       reg &= ~ATMEL_MPDDRC_IO_CALIBR_TZQIO;
+       reg |= ATMEL_MPDDRC_IO_CALIBR_TZQIO_(100);
+       writel(reg, &mpddrc->io_calibr);
+
+       writel(ATMEL_MPDDRC_RD_DATA_PATH_SHIFT_ONE_CYCLE,
+              &mpddrc->rd_data_path);
+
+       lpddr2_init(ATMEL_BASE_MPDDRC, ATMEL_BASE_DDRCS, &ddrc_config);
+}
+
+void at91_pmc_init(void)
+{
+       u32 tmp;
+
+       /*
+        * while coming from the ROM code, we run on PLLA @ 492 MHz / 164 MHz
+        * so we need to slow down and configure MCKR accordingly.
+        * This is why we have a special flavor of the switching function.
+        */
+       tmp = AT91_PMC_MCKR_PLLADIV_2 |
+             AT91_PMC_MCKR_MDIV_3 |
+             AT91_PMC_MCKR_CSS_MAIN;
+       at91_mck_init_down(tmp);
+
+       tmp = AT91_PMC_PLLAR_29 |
+             AT91_PMC_PLLXR_PLLCOUNT(0x3f) |
+             AT91_PMC_PLLXR_MUL(40) |
+             AT91_PMC_PLLXR_DIV(1);
+       at91_plla_init(tmp);
+
+       tmp = AT91_PMC_MCKR_H32MXDIV |
+             AT91_PMC_MCKR_PLLADIV_2 |
+             AT91_PMC_MCKR_MDIV_3 |
+             AT91_PMC_MCKR_CSS_PLLA;
+       at91_mck_init(tmp);
+}
+#endif
index 695af6bb7ecaddfc9aa6a7138689611b53733948..c037d5b14cd71c10ca58079b1463acb08f9794f7 100644 (file)
@@ -5,7 +5,7 @@
 
 #include <common.h>
 #include <asm/arch/gpio.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 static const struct gpio_family gpio_family[] = {
        GPIO_FAMILY_CONF("SOUTHEAST_2_hshvfamily_2x3_rcomp_7_0", NA, 0,
index a2f8eaf0ba34f05fc3e462f608b1c979d3aad862..13563abb49ef87904c6f480e75d5cf961a41310d 100644 (file)
@@ -4,7 +4,7 @@
 
 #include <common.h>
 #include <asm/io.h>
-#include <asm/arch/sama5_sfr.h>
+#include <asm/arch/at91_sfr.h>
 #include <asm/arch/sama5d3_smc.h>
 #include <asm/arch/at91_common.h>
 #include <asm/arch/at91_pmc.h>
@@ -173,13 +173,11 @@ static void ddr2_conf(struct atmel_mpddrc_config *ddr2)
 
 void mem_init(void)
 {
-       struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
        struct atmel_mpddrc_config ddr2;
 
        ddr2_conf(&ddr2);
 
-       writel(ATMEL_SFR_DDRCFG_FDQIEN | ATMEL_SFR_DDRCFG_FDQSIEN,
-              &sfr->ddrcfg);
+       configure_ddrcfg_input_buffers(true);
 
        /* enable MPDDR clock */
        at91_periph_clk_enable(ATMEL_ID_MPDDRC);
index 4e61565aab169c7ba408b80673eec7b844a8293e..8e55b34c961ff3a44abc4552df832e01cf7f4fc1 100644 (file)
@@ -531,7 +531,6 @@ config CMD_ENV_FLAGS
 config CMD_NVEDIT_EFI
        bool "env [set|print] -e - set/print UEFI variables"
        depends on EFI_LOADER
-       default y
        imply HEXDUMP
        help
          UEFI variables are encoded as some form of U-Boot variables.
index 79faf814ff739d689825a76b3492f14d30705cc1..7fee9674a2862731c1e81f993ef88afd8745c04c 100644 (file)
--- a/cmd/io.c
+++ b/cmd/io.c
 #include <command.h>
 #include <asm/io.h>
 
+/* Display values from last command */
+static ulong last_addr, last_size;
+static ulong last_length = 0x40;
+static ulong base_address;
+
+#define DISP_LINE_LEN  16
+
 /*
  * IO Display
  *
  */
 int do_io_iod(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
 {
-       ulong addr;
-       int size;
-
-       if (argc != 2)
+       ulong addr, length, bytes;
+       u8 buf[DISP_LINE_LEN];
+       int size, todo;
+
+       /*
+        * We use the last specified parameters, unless new ones are
+        * entered.
+        */
+       addr = last_addr;
+       size = last_size;
+       length = last_length;
+
+       if (argc < 2)
                return CMD_RET_USAGE;
 
-       size = cmd_get_data_size(argv[0], 4);
-       if (size < 0)
-               return 1;
-
-       addr = simple_strtoul(argv[1], NULL, 16);
-
-       printf("%04x: ", (u16) addr);
-
-       if (size == 4)
-               printf("%08x\n", inl(addr));
-       else if (size == 2)
-               printf("%04x\n", inw(addr));
-       else
-               printf("%02x\n", inb(addr));
+       if ((flag & CMD_FLAG_REPEAT) == 0) {
+               /*
+                * New command specified.  Check for a size specification.
+                * Defaults to long if no or incorrect specification.
+                */
+               size = cmd_get_data_size(argv[0], 4);
+               if (size < 0)
+                       return 1;
+
+               /* Address is specified since argc > 1 */
+               addr = simple_strtoul(argv[1], NULL, 16);
+               addr += base_address;
+
+               /*
+                * If another parameter, it is the length to display.
+                * Length is the number of objects, not number of bytes.
+                */
+               if (argc > 2)
+                       length = simple_strtoul(argv[2], NULL, 16);
+       }
+
+       bytes = size * length;
+
+       /* Print the lines */
+       for (; bytes > 0; addr += todo) {
+               u8 *ptr = buf;
+               int i;
+
+               todo = min(bytes, (ulong)DISP_LINE_LEN);
+               for (i = 0; i < todo; i += size, ptr += size) {
+                       if (size == 4)
+                               *(u32 *)ptr = inl(addr + i);
+                       else if (size == 2)
+                               *(u16 *)ptr = inw(addr + i);
+                       else
+                               *ptr = inb(addr + i);
+               }
+               print_buffer(addr, buf, size, todo / size,
+                            DISP_LINE_LEN / size);
+               bytes -= todo;
+       }
+
+       last_addr = addr;
+       last_length = length;
+       last_size = size;
 
        return 0;
 }
@@ -69,7 +116,7 @@ int do_io_iow(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
 }
 
 /**************************************************/
-U_BOOT_CMD(iod, 2, 0, do_io_iod,
+U_BOOT_CMD(iod, 3, 1, do_io_iod,
           "IO space display", "[.b, .w, .l] address");
 
 U_BOOT_CMD(iow, 3, 0, do_io_iow,
index ed6d09a530466e9fe8e6817e6876bb6709e32df7..ede4cd5235ea822de6d6bb2aea13185b9b506eac 100644 (file)
@@ -330,7 +330,7 @@ out:
 }
 
 /**
- * do_env_print_efi() - set UEFI variable
+ * do_env_set_efi() - set UEFI variable
  *
  * @cmdtp:     Command table
  * @flag:      Command flag
index efa183854b7415f5435221013f7a8161ce33dea9..b3b663021bc1e2b984c9ffda919a0ecf0670427a 100644 (file)
@@ -5,13 +5,13 @@
 
 #include <common.h>
 #include <command.h>
-#include <asm/fsp/fsp_support.h>
+#include <asm/fsp1/fsp_support.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
 static int do_hdr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-       struct fsp_header *hdr = find_fsp_header();
+       struct fsp_header *hdr = fsp_find_header();
        u32 img_addr = hdr->img_base;
        char *sign = (char *)&hdr->sign;
        int i;
index f59b6f5b51f41b80073ab72ac5ebf847983651f3..57d06ccece5952126b5ffea348a3cc23f373a3c2 100644 (file)
@@ -26,7 +26,7 @@ config SPL_FRAMEWORK
          and the Linux Kernel.  If unsure, say Y.
 
 config SPL_SIZE_LIMIT
-       int "Maximum size of SPL image"
+       hex "Maximum size of SPL image"
        depends on SPL
        default 69632 if ARCH_MX6 && !MX6_OCRAM_256KB
        default 200704 if ARCH_MX6 && MX6_OCRAM_256KB
@@ -116,7 +116,7 @@ if SPL
 
 config SPL_HANDOFF
        bool "Pass hand-off information from SPL to U-Boot proper"
-       depends on HANDOFF
+       depends on HANDOFF && SPL_BLOBLIST
        default y
        help
          This option enables SPL to write handoff information. This can be
@@ -973,7 +973,7 @@ config SPL_SERIAL_SUPPORT
          for displaying messages while SPL is running. It also brings in
          printf() and panic() functions. This should normally be enabled
          unless there are space reasons not to. Even then, consider
-         enabling USE_TINY_PRINTF which is a small printf() version.
+         enabling SPL_USE_TINY_PRINTF which is a small printf() version.
 
 config SPL_SPI_FLASH_SUPPORT
        bool "Support SPI flash drivers"
@@ -1195,7 +1195,7 @@ if TPL
 
 config TPL_HANDOFF
        bool "Pass hand-off information from TPL to SPL and U-Boot proper"
-       depends on HANDOFF
+       depends on HANDOFF && TPL_BLOBLIST
        default y
        help
          This option enables TPL to write handoff information. This can be
index 082fa2bd94d7ce47a235a4841bced1fbd307871d..5fdd6d0d032181cfd6e841ede1c2dd47fd6b9701 100644 (file)
@@ -356,17 +356,23 @@ static int setup_spl_handoff(void)
        return 0;
 }
 
+__weak int handoff_arch_save(struct spl_handoff *ho)
+{
+       return 0;
+}
+
 static int write_spl_handoff(void)
 {
        struct spl_handoff *ho;
+       int ret;
 
        ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(struct spl_handoff));
        if (!ho)
                return -ENOENT;
        handoff_save_dram(ho);
-#ifdef CONFIG_SANDBOX
-       ho->arch.magic = TEST_HANDOFF_MAGIC;
-#endif
+       ret = handoff_arch_save(ho);
+       if (ret)
+               return ret;
        debug(SPL_TPL_PROMPT "Wrote SPL handoff\n");
 
        return 0;
@@ -404,23 +410,6 @@ static int spl_common_init(bool setup_malloc)
                return ret;
        }
 #endif
-       if (CONFIG_IS_ENABLED(BLOBLIST)) {
-               ret = bloblist_init();
-               if (ret) {
-                       debug("%s: Failed to set up bloblist: ret=%d\n",
-                             __func__, ret);
-                       return ret;
-               }
-       }
-       if (CONFIG_IS_ENABLED(HANDOFF)) {
-               int ret;
-
-               ret = setup_spl_handoff();
-               if (ret) {
-                       puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n");
-                       hang();
-               }
-       }
        if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
                ret = fdtdec_setup();
                if (ret) {
@@ -598,6 +587,24 @@ void board_init_r(gd_t *dummy1, ulong dummy2)
         */
        timer_init();
 #endif
+       if (CONFIG_IS_ENABLED(BLOBLIST)) {
+               ret = bloblist_init();
+               if (ret) {
+                       debug("%s: Failed to set up bloblist: ret=%d\n",
+                             __func__, ret);
+                       puts(SPL_TPL_PROMPT "Cannot set up bloblist\n");
+                       hang();
+               }
+       }
+       if (CONFIG_IS_ENABLED(HANDOFF)) {
+               int ret;
+
+               ret = setup_spl_handoff();
+               if (ret) {
+                       puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n");
+                       hang();
+               }
+       }
 
 #if CONFIG_IS_ENABLED(BOARD_INIT)
        spl_board_init();
index e85da74a698f373c837987228f2a99b4a318a136..6bf2375671d2825668457472e29c9e81a12fef28 100644 (file)
@@ -173,7 +173,7 @@ parse_num (char *s, unsigned long *val, char **es, char *delim)
 }
 
 
-#if defined(DEBUG) && !defined(CONFIG_USE_TINY_PRINTF)
+#if defined(DEBUG) && !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
 /*
  * Note: this debug setup works by storing the strings in a fixed buffer
  */
index e69f81b6e549f84b53fbde0f0a4777c87143b50f..1a1d58d3cc8aee1f334345445f08823e1511b00f 100644 (file)
@@ -51,5 +51,5 @@ CONFIG_SPL_DM_REGULATOR=y
 CONFIG_DM_REGULATOR_FIXED=y
 CONFIG_DM_REGULATOR_TPS65910=y
 CONFIG_CONS_INDEX=4
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 # CONFIG_EFI_LOADER is not set
index f459af07f8d8e8ed554f12770d0d3c52d7351511..db4753c5c7fb1fe122b362fcb866913a04bc21d0 100644 (file)
@@ -85,4 +85,5 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0525
 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
 CONFIG_CI_UDC=y
 CONFIG_USB_GADGET_DOWNLOAD=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=60000
 CONFIG_IMX_WATCHDOG=y
index 46bcdaffc835a3d9d8d9bb376285a652bcbbc1b9..3227249413e43b490db8cb512370c563c33d5f6f 100644 (file)
@@ -108,6 +108,7 @@ CONFIG_DM_REGULATOR_PFUZE100=y
 CONFIG_MXC_UART=y
 CONFIG_SPI=y
 CONFIG_MXC_SPI=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=15000
 CONFIG_SYSRESET=y
 CONFIG_SYSRESET_WATCHDOG=y
 CONFIG_I2C_EDID=y
index c515d14a80eae91464fa5d7f9e87995906b3974a..a41a6329cb8cf772299fd97ccb82cee83f77cf8b 100644 (file)
@@ -121,6 +121,7 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0525
 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
 CONFIG_CI_UDC=y
 CONFIG_USB_GADGET_DOWNLOAD=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=15000
 CONFIG_I2C_EDID=y
 CONFIG_IMX_WATCHDOG=y
 CONFIG_PANIC_HANG=y
index 7b0e908f715c8d994076752ba8268c470ed8a6b3..043ee32bb441186c8f959a58fe4c8d18108706df 100644 (file)
@@ -4,7 +4,7 @@ CONFIG_SYS_TEXT_BASE=0x01000000
 CONFIG_ROCKCHIP_RK3288=y
 CONFIG_TARGET_EVB_RK3288=y
 CONFIG_NR_DRAM_BANKS=1
-CONFIG_SPL_SIZE_LIMIT=307200
+CONFIG_SPL_SIZE_LIMIT=0x4b000
 CONFIG_SPL_STACK_R_ADDR=0x80000
 CONFIG_DEBUG_UART_BASE=0xff690000
 CONFIG_DEBUG_UART_CLOCK=24000000
index 735165916a8a4aa3d72dc84e387db57d15602f6a..75fdbf7e72a7118d55885a53a426056f58438d4c 100644 (file)
@@ -61,5 +61,6 @@ CONFIG_DM_SPI=y
 CONFIG_MXC_SPI=y
 CONFIG_DM_VIDEO=y
 CONFIG_VIDEO_IPUV3=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=6000
 CONFIG_IMX_WATCHDOG=y
 # CONFIG_EFI_LOADER is not set
index 75dd9d7c7a0908ca2ebf6c89e04b6c58dac06135..fdfb89990375cc8aace201837f44b8d730a93d62 100644 (file)
@@ -41,5 +41,6 @@ CONFIG_FEC_MXC=y
 CONFIG_MII=y
 CONFIG_IMX_THERMAL=y
 CONFIG_USB=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=60000
 CONFIG_IMX_WATCHDOG=y
 CONFIG_OF_LIBFDT=y
index 7aa8c54a128b2c4eed02e03c026dfebf3673b00b..c45fc68ac3f7198499e78672e32c97b8ccbd048a 100644 (file)
@@ -67,7 +67,7 @@ CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_XHCI_DWC3=y
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_RSA=y
 CONFIG_SPL_RSA=y
 CONFIG_EFI_LOADER_BOUNCE_BUFFER=y
index 22ccf526f69640eaecc6799a7e713a9aea5a1385..a9e58047acc4d428f962a62ae25138ac52bc231c 100644 (file)
@@ -67,5 +67,5 @@ CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_XHCI_DWC3=y
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_EFI_LOADER_BOUNCE_BUFFER=y
index 486a2e089b775e73c1ef317d9fc1324d6ea03de7..bdfa13a0a71a7512bfd3473204b4ad2c84422897 100644 (file)
@@ -65,7 +65,7 @@ CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_XHCI_DWC3=y
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_RSA=y
 CONFIG_SPL_RSA=y
 CONFIG_EFI_LOADER_BOUNCE_BUFFER=y
index b3c2970207c8cfda6fc06e4b73d50ff3cfb167f7..01adbf0b192fd48cd134aeb288ab3cfe8c517a75 100644 (file)
@@ -65,5 +65,5 @@ CONFIG_USB=y
 CONFIG_DM_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_XHCI_DWC3=y
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_EFI_LOADER_BOUNCE_BUFFER=y
index 50047952a71e7106db08b31da39d17e7864d8f74..7e5bcf94b90e8308573819f55a4b56ff3754bccd 100644 (file)
@@ -89,4 +89,5 @@ CONFIG_DM_VIDEO=y
 CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_VIDEO_IPUV3=y
 CONFIG_IMX_WATCHDOG=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=8000
 CONFIG_FAT_WRITE=y
index cad798a4166b56ba02822c402d2699ab1b4a98b4..7f293c8e9a88e1724512bbef332a76961cb1ecd5 100644 (file)
@@ -50,4 +50,5 @@ CONFIG_USB_EHCI_MX5=y
 CONFIG_VIDEO_IPUV3=y
 CONFIG_VIDEO=y
 # CONFIG_VIDEO_SW_CURSOR is not set
+CONFIG_WATCHDOG_TIMEOUT_MSECS=8000
 CONFIG_IMX_WATCHDOG=y
diff --git a/configs/sam9x60ek_mmc_defconfig b/configs/sam9x60ek_mmc_defconfig
new file mode 100644 (file)
index 0000000..0d3746a
--- /dev/null
@@ -0,0 +1,56 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AT91=y
+CONFIG_SYS_TEXT_BASE=0x23f00000
+CONFIG_TARGET_SAM9X60EK=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_ENV_SIZE=0x4000
+CONFIG_NR_DRAM_BANKS=8
+CONFIG_DEBUG_UART_BOARD_INIT=y
+CONFIG_DEBUG_UART_BASE=0xfffff200
+CONFIG_DEBUG_UART_CLOCK=200000000
+CONFIG_DEBUG_UART=y
+CONFIG_FIT=y
+CONFIG_SD_BOOT=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_BOOTARGS="mem=256M console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4 rootwait"
+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_CMD_BOOTZ=y
+CONFIG_CMD_MMC=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_FAT=y
+CONFIG_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="sam9x60ek"
+CONFIG_ENV_IS_IN_FAT=y
+CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
+CONFIG_DM=y
+CONFIG_CLK=y
+CONFIG_CLK_AT91=y
+CONFIG_AT91_GENERIC_CLK=y
+CONFIG_DM_GPIO=y
+CONFIG_AT91_GPIO=y
+CONFIG_DM_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_ATMEL=y
+CONFIG_PHY_MICREL=y
+CONFIG_DM_ETH=y
+CONFIG_MACB=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_AT91=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEBUG_UART_ATMEL=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_ATMEL_USART=y
+CONFIG_TIMER=y
+CONFIG_ATMEL_PIT_TIMER=y
+CONFIG_W1=y
+CONFIG_W1_GPIO=y
+CONFIG_W1_EEPROM=y
+CONFIG_W1_EEPROM_DS24XXX=y
+CONFIG_OF_LIBFDT_OVERLAY=y
diff --git a/configs/sam9x60ek_nandflash_defconfig b/configs/sam9x60ek_nandflash_defconfig
new file mode 100644 (file)
index 0000000..cdba103
--- /dev/null
@@ -0,0 +1,57 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AT91=y
+CONFIG_SYS_TEXT_BASE=0x23f00000
+CONFIG_TARGET_SAM9X60EK=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_NR_DRAM_BANKS=8
+CONFIG_DEBUG_UART_BOARD_INIT=y
+CONFIG_DEBUG_UART_BASE=0xfffff200
+CONFIG_DEBUG_UART_CLOCK=200000000
+CONFIG_DEBUG_UART=y
+CONFIG_FIT=y
+CONFIG_NAND_BOOT=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk mtdparts=atmel_nand:256k(bootstrap)ro,768k(uboot)ro,256k(env_redundant),256k(env),512k(dtb),6M(kernel)ro,-(rootfs) rootfstype=ubifs ubi.mtd=12 root=ubi0:rootfs rw"
+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_NAND=y
+CONFIG_CMD_NAND_TRIMFFS=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_UBI=y
+CONFIG_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="sam9x60ek"
+CONFIG_ENV_IS_IN_NAND=y
+CONFIG_DM=y
+CONFIG_CLK=y
+CONFIG_CLK_AT91=y
+CONFIG_AT91_GENERIC_CLK=y
+CONFIG_DM_GPIO=y
+CONFIG_AT91_GPIO=y
+CONFIG_DM_MMC=y
+CONFIG_GENERIC_ATMEL_MCI=y
+CONFIG_PHY_MICREL=y
+CONFIG_DM_ETH=y
+CONFIG_MACB=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_AT91=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEBUG_UART_ATMEL=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_ATMEL_USART=y
+CONFIG_TIMER=y
+CONFIG_ATMEL_PIT_TIMER=y
+CONFIG_W1=y
+CONFIG_W1_GPIO=y
+CONFIG_W1_EEPROM=y
+CONFIG_W1_EEPROM_DS24XXX=y
+CONFIG_OF_LIBFDT_OVERLAY=y
diff --git a/configs/sam9x60ek_qspiflash_defconfig b/configs/sam9x60ek_qspiflash_defconfig
new file mode 100644 (file)
index 0000000..e1b292e
--- /dev/null
@@ -0,0 +1,79 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AT91=y
+CONFIG_SYS_TEXT_BASE=0x23f00000
+CONFIG_TARGET_SAM9X60EK=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_NR_DRAM_BANKS=8
+CONFIG_DEBUG_UART_BOARD_INIT=y
+CONFIG_DEBUG_UART_BASE=0xfffff200
+CONFIG_DEBUG_UART_CLOCK=200000000
+CONFIG_ENV_SECT_SIZE=0x1000
+CONFIG_DEBUG_UART=y
+CONFIG_ENV_VARS_UBOOT_CONFIG=y
+CONFIG_FIT=y
+CONFIG_QSPI_BOOT=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_BOOTARGS="console=ttyS0,115200 earlyprintk mtdparts=atmel_nand:256k(bootstrap)ro,768k(uboot)ro,256k(env_redundant),256k(env),512k(dtb),6M(kernel)ro,-(rootfs) rootfstype=ubifs ubi.mtd=12 root=ubi0:rootfs rw"
+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_HUSH_PARSER=y
+CONFIG_SYS_PROMPT="U-Boot> "
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_NAND=y
+CONFIG_CMD_NAND_TRIMFFS=y
+CONFIG_CMD_SF=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_UBI=y
+CONFIG_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="sam9x60ek"
+CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_USE_ENV_SPI_BUS=y
+CONFIG_ENV_SPI_BUS=0
+CONFIG_USE_ENV_SPI_CS=y
+CONFIG_ENV_SPI_CS=0
+CONFIG_USE_ENV_SPI_MAX_HZ=y
+CONFIG_ENV_SPI_MAX_HZ=50000000
+CONFIG_USE_ENV_SPI_MODE=y
+CONFIG_ENV_SPI_MODE=0x0
+CONFIG_DM=y
+CONFIG_CLK=y
+CONFIG_CLK_AT91=y
+CONFIG_AT91_GENERIC_CLK=y
+CONFIG_DM_GPIO=y
+CONFIG_AT91_GPIO=y
+CONFIG_DM_MMC=y
+CONFIG_GENERIC_ATMEL_MCI=y
+CONFIG_MTD=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_SST=y
+CONFIG_SPI_FLASH_MTD=y
+CONFIG_PHY_MICREL=y
+CONFIG_DM_ETH=y
+CONFIG_MACB=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_AT91=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEBUG_UART_ATMEL=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_ATMEL_USART=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_ATMEL_QSPI=y
+CONFIG_TIMER=y
+CONFIG_ATMEL_PIT_TIMER=y
+CONFIG_W1=y
+CONFIG_W1_GPIO=y
+CONFIG_W1_EEPROM=y
+CONFIG_W1_EEPROM_DS24XXX=y
+CONFIG_OF_LIBFDT_OVERLAY=y
diff --git a/configs/sama5d27_wlsom1_ek_mmc_defconfig b/configs/sama5d27_wlsom1_ek_mmc_defconfig
new file mode 100644 (file)
index 0000000..50a8a8e
--- /dev/null
@@ -0,0 +1,102 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AT91=y
+CONFIG_SYS_TEXT_BASE=0x26f00000
+CONFIG_TARGET_SAMA5D27_WLSOM1_EK=y
+CONFIG_SPL_GPIO_SUPPORT=y
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_SPL_MMC_SUPPORT=y
+CONFIG_SPL_SERIAL_SUPPORT=y
+CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
+CONFIG_ENV_SIZE=0x4000
+CONFIG_SPL=y
+CONFIG_DEBUG_UART_BOARD_INIT=y
+CONFIG_DEBUG_UART_BASE=0xf801c000
+CONFIG_DEBUG_UART_CLOCK=82000000
+CONFIG_SPL_FS_FAT=y
+CONFIG_SPL_LIBDISK_SUPPORT=y
+CONFIG_DEBUG_UART=y
+CONFIG_ENV_VARS_UBOOT_CONFIG=y
+CONFIG_FIT=y
+CONFIG_SYS_EXTRA_OPTIONS="SAMA5D2"
+CONFIG_SD_BOOT=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_MISC_INIT_R=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_SPL_TEXT_BASE=0x200000
+CONFIG_SPL_SEPARATE_BSS=y
+CONFIG_SPL_DISPLAY_PRINT=y
+# CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR is not set
+CONFIG_SPL_AT91_MCK_BYPASS=y
+CONFIG_HUSH_PARSER=y
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_I2C=y
+# CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_OF_CONTROL=y
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="at91-sama5d27_wlsom1_ek"
+CONFIG_OF_SPL_REMOVE_PROPS="interrupts interrupt-parent dmas dma-names"
+CONFIG_ENV_IS_IN_FAT=y
+CONFIG_DM=y
+CONFIG_SPL_DM=y
+CONFIG_SPL_DM_SEQ_ALIAS=y
+CONFIG_CLK=y
+CONFIG_SPL_CLK=y
+CONFIG_CLK_AT91=y
+CONFIG_AT91_UTMI=y
+CONFIG_AT91_H32MX=y
+CONFIG_AT91_GENERIC_CLK=y
+CONFIG_DM_GPIO=y
+CONFIG_ATMEL_PIO4=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_AT91=y
+CONFIG_I2C_EEPROM=y
+CONFIG_DM_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_ATMEL=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH=y
+CONFIG_SPI_FLASH_ATMEL=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_SST=y
+CONFIG_PHY_MICREL=y
+CONFIG_DM_ETH=y
+CONFIG_MACB=y
+CONFIG_PINCTRL=y
+CONFIG_SPL_PINCTRL=y
+CONFIG_PINCTRL_AT91PIO4=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEBUG_UART_ATMEL=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_ATMEL_USART=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_TIMER=y
+CONFIG_SPL_TIMER=y
+CONFIG_ATMEL_PIT_TIMER=y
+CONFIG_USB=y
+CONFIG_DM_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_ATMEL_USBA=y
+CONFIG_DM_VIDEO=y
+CONFIG_ATMEL_HLCD=y
+CONFIG_W1=y
+CONFIG_W1_GPIO=y
+CONFIG_W1_EEPROM=y
+CONFIG_W1_EEPROM_DS24XXX=y
+CONFIG_OF_LIBFDT_OVERLAY=y
+# CONFIG_EFI_LOADER_HII is not set
diff --git a/configs/sama5d27_wlsom1_ek_qspiflash_defconfig b/configs/sama5d27_wlsom1_ek_qspiflash_defconfig
new file mode 100644 (file)
index 0000000..82568e2
--- /dev/null
@@ -0,0 +1,117 @@
+CONFIG_ARM=y
+CONFIG_ARCH_AT91=y
+CONFIG_SYS_TEXT_BASE=0x26f00000
+CONFIG_TARGET_SAMA5D27_WLSOM1_EK=y
+CONFIG_SPL_GPIO_SUPPORT=y
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_SYS_MALLOC_F_LEN=0x2000
+CONFIG_SPL_SERIAL_SUPPORT=y
+CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
+CONFIG_SPL=y
+CONFIG_DEBUG_UART_BOARD_INIT=y
+CONFIG_DEBUG_UART_BASE=0xf801c000
+CONFIG_DEBUG_UART_CLOCK=82000000
+CONFIG_ENV_SECT_SIZE=0x1000
+CONFIG_SPL_SPI_FLASH_SUPPORT=y
+CONFIG_SPL_SPI_SUPPORT=y
+CONFIG_DEBUG_UART=y
+CONFIG_SPL_TEXT_BASE=0x200000
+CONFIG_ENV_VARS_UBOOT_CONFIG=y
+CONFIG_FIT=y
+CONFIG_SYS_EXTRA_OPTIONS="SAMA5D2"
+CONFIG_QSPI_BOOT=y
+CONFIG_SPI_BOOT=y
+CONFIG_BOOTDELAY=3
+CONFIG_USE_BOOTARGS=y
+CONFIG_MISC_INIT_R=y
+# CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_SPL_SEPARATE_BSS=y
+CONFIG_SPL_DISPLAY_PRINT=y
+# CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR is not set
+CONFIG_SPL_SPI_LOAD=y
+CONFIG_SPL_AT91_MCK_BYPASS=y
+CONFIG_SYS_SPI_U_BOOT_OFFS=0x40000
+CONFIG_HUSH_PARSER=y
+CONFIG_CMD_BOOTZ=y
+# CONFIG_CMD_FLASH is not set
+CONFIG_CMD_I2C=y
+# CONFIG_CMD_LOADS is not set
+CONFIG_CMD_MMC=y
+CONFIG_CMD_SF=y
+CONFIG_CMD_USB=y
+CONFIG_CMD_DHCP=y
+CONFIG_CMD_MII=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_EXT4=y
+CONFIG_CMD_FAT=y
+CONFIG_OF_CONTROL=y
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_DEFAULT_DEVICE_TREE="at91-sama5d27_wlsom1_ek"
+CONFIG_OF_SPL_REMOVE_PROPS="interrupts interrupt-parent dmas dma-names"
+CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_USE_ENV_SPI_BUS=y
+CONFIG_ENV_SPI_BUS=2
+CONFIG_USE_ENV_SPI_CS=y
+CONFIG_ENV_SPI_CS=0
+CONFIG_USE_ENV_SPI_MAX_HZ=y
+CONFIG_ENV_SPI_MAX_HZ=50000000
+CONFIG_USE_ENV_SPI_MODE=y
+CONFIG_ENV_SPI_MODE=0x0
+CONFIG_DM=y
+CONFIG_SPL_DM=y
+CONFIG_SPL_DM_SEQ_ALIAS=y
+CONFIG_CLK=y
+CONFIG_SPL_CLK=y
+CONFIG_CLK_AT91=y
+CONFIG_AT91_UTMI=y
+CONFIG_AT91_H32MX=y
+CONFIG_AT91_GENERIC_CLK=y
+CONFIG_DM_GPIO=y
+CONFIG_ATMEL_PIO4=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_AT91=y
+CONFIG_I2C_EEPROM=y
+CONFIG_DM_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_ATMEL=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH=y
+CONFIG_SF_DEFAULT_BUS=2
+CONFIG_SF_DEFAULT_SPEED=50000000
+CONFIG_SPI_FLASH_ATMEL=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_SST=y
+CONFIG_PHY_MICREL=y
+CONFIG_DM_ETH=y
+CONFIG_MACB=y
+CONFIG_PINCTRL=y
+CONFIG_SPL_PINCTRL=y
+CONFIG_PINCTRL_AT91PIO4=y
+CONFIG_DM_SERIAL=y
+CONFIG_DEBUG_UART_ATMEL=y
+CONFIG_DEBUG_UART_ANNOUNCE=y
+CONFIG_ATMEL_USART=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_ATMEL_QSPI=y
+CONFIG_TIMER=y
+CONFIG_SPL_TIMER=y
+CONFIG_ATMEL_PIT_TIMER=y
+CONFIG_USB=y
+CONFIG_DM_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_ATMEL_USBA=y
+CONFIG_DM_VIDEO=y
+CONFIG_ATMEL_HLCD=y
+CONFIG_W1=y
+CONFIG_W1_GPIO=y
+CONFIG_W1_EEPROM=y
+CONFIG_W1_EEPROM_DS24XXX=y
+CONFIG_OF_LIBFDT_OVERLAY=y
+# CONFIG_EFI_LOADER_HII is not set
index 0b3391a2a6b7caa29ae862bb340ff014493e87ee..409b8a38d5e0c0205272a67855ce539c94ee3075 100644 (file)
@@ -191,7 +191,7 @@ CONFIG_OSD=y
 CONFIG_SANDBOX_OSD=y
 CONFIG_FS_CBFS=y
 CONFIG_FS_CRAMFS=y
-# CONFIG_USE_TINY_PRINTF is not set
+# CONFIG_SPL_USE_TINY_PRINTF is not set
 CONFIG_CMD_DHRYSTONE=y
 CONFIG_TPM=y
 CONFIG_LZ4=y
index f9fecff45ea068a03d5fa679431bd75b0135b30f..3cbb83c7a47a3867f8473b50c2d727b5f0baa85a 100644 (file)
@@ -18,3 +18,4 @@ CONFIG_REGMAP=y
 CONFIG_SYSCON=y
 # CONFIG_PCI_PNP is not set
 CONFIG_CONSOLE_SCROLL_LINES=5
+# CONFIG_USE_CAR is not set
index 93286084525dad92c6d7248d6ae50a1e704ba25a..eff3b06b5ce274844a95c95dc5ab4452a43dbeaf 100644 (file)
@@ -5,7 +5,7 @@ CONFIG_SPL_GPIO_SUPPORT=y
 CONFIG_ROCKCHIP_RK3288=y
 CONFIG_TARGET_TINKER_RK3288=y
 CONFIG_NR_DRAM_BANKS=1
-CONFIG_SPL_SIZE_LIMIT=307200
+CONFIG_SPL_SIZE_LIMIT=0x4b000
 CONFIG_SPL_STACK_R_ADDR=0x80000
 CONFIG_DEBUG_UART_BASE=0xff690000
 CONFIG_DEBUG_UART_CLOCK=24000000
index 03e893044b2e88a4b998c45a855b46d0e7473dda..1dab5af41320aff7c6a56a73a5f1b9ec77755d95 100644 (file)
@@ -66,5 +66,6 @@ CONFIG_USB=y
 CONFIG_USB_STORAGE=y
 CONFIG_USB_HOST_ETHER=y
 CONFIG_USB_ETHER_SMSC95XX=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=60000
 CONFIG_IMX_WATCHDOG=y
 CONFIG_OF_LIBFDT=y
index 7a6ea6f8c6d4242e6fa6eb2ee38ae91f9d5f0e7b..7da64e6c6f6391da61b9684128675d5d328b01d6 100644 (file)
@@ -39,5 +39,6 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0525
 CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
 CONFIG_CI_UDC=y
 CONFIG_USB_GADGET_DOWNLOAD=y
+CONFIG_WATCHDOG_TIMEOUT_MSECS=30000
 CONFIG_IMX_WATCHDOG=y
 CONFIG_OF_LIBFDT=y
index 5c0caebcbf0eaaf2773a8d4ac75bb010ab5d7ecf..54933b56759b414d484ab5dc141f7d86ff26d343 100644 (file)
@@ -103,6 +103,8 @@ A device tree binary file can be provided with -d. If you edit the source
 (it is stored at arch/sandbox/dts/sandbox.dts) you must rebuild U-Boot to
 recreate the binary file.
 
+To use the default device tree, use -D. To use the test device tree, use -T.
+
 To execute commands directly, use the -c option. You can specify a single
 command, or multiple commands separated by a semicolon, as is normal in
 U-Boot. Be careful with quoting as the shell will normally process and
@@ -499,6 +501,13 @@ run natively on your board if desired (and enabled).
 
 To run all tests use "make check".
 
+To run a single test in an existing sandbox build, you can use -T to use the
+test device tree, and -c to select the test:
+
+  /tmp/b/sandbox/u-boot -T -c "ut dm pci_busdev"
+
+This runs dm_test_pci_busdev() which is in test/dm/pci.c
+
 
 Memory Map
 ----------
diff --git a/doc/driver-model/debugging.rst b/doc/driver-model/debugging.rst
new file mode 100644 (file)
index 0000000..4f4a8d4
--- /dev/null
@@ -0,0 +1,62 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. sectionauthor:: Simon Glass <sjg@chromium.org>
+
+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
+     ofdata_to_platdata() 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, #define DEBUG at the top of lists.c should show you
+what is going on.
index ea32c36335f5502733139311fc0364fe07d42893..6d55774b4c2edf9853e5605067414e70a09c1494 100644 (file)
@@ -6,6 +6,7 @@ Driver Model
 .. toctree::
    :maxdepth: 2
 
+   debugging
    design
    fdt-fixup
    fs_firmware_loader
index d93ab8b61d5dc0adb5e8de814aa23cd21d08a057..3c1b1adf077eb608e41c41146fff71aeb2ad49f9 100644 (file)
@@ -103,7 +103,7 @@ 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. Plese note with U_BOOT_PCI_DEVICE, only drivers with
+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.
@@ -113,14 +113,17 @@ 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 a
-driver. For example::
-
+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>;
-               emul@1f,0 {
+               sandbox,emul = <&emul_1f>;
+       };
+       pci-emul {
+               compatible = "sandbox,pci-emul-parent";
+               emul_1f: emul@1f,0 {
                        compatible = "sandbox,swap-case";
                };
        };
@@ -130,14 +133,16 @@ 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-controller @ 05c660c8, 0
     `-   pci@1f,0 @ 05c661c8, 63488
-     `-   emul@1f,0 @ 05c662c8
+   `-   emul@1f,0 @ 05c662c8
 
-When accesses go to the pci@1f,0 device they are forwarded to its child, the
-emulator.
+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(),
@@ -164,7 +169,3 @@ When this bus is scanned we will end up with something like this::
  pci        [ + ]   pci_sandbo  |-- pci-controller1
  pci_emul   [   ]   sandbox_sw  |   |-- sandbox_swap_case_emul
  pci_emul   [   ]   sandbox_sw  |   `-- sandbox_swap_case_emul
-
-Note the difference from the statically declared device nodes is that the
-device is directly attached to the host controller, instead of via a container
-device like pci@1f,0.
index baaf431e5e0c3ce64af9b934fa140af496e0b2b5..e8f58b3f5e471b815c619a0a091fab87d5e7d762 100644 (file)
@@ -142,9 +142,9 @@ struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
  */
 struct blk_desc *blk_get_by_device(struct udevice *dev)
 {
-       struct udevice *child_dev, *next;
+       struct udevice *child_dev;
 
-       device_foreach_child_safe(child_dev, next, dev) {
+       device_foreach_child(child_dev, dev) {
                if (device_get_uclass_id(child_dev) != UCLASS_BLK)
                        continue;
 
index e8506099fd323a4f54813818f682d84ecc134cdc..18af0bfeaad1601a39f221ed2ae9b284e7ffd95c 100644 (file)
@@ -10,7 +10,7 @@
 #include <syscon.h>
 #include <linux/io.h>
 #include <mach/at91_pmc.h>
-#include <mach/sama5_sfr.h>
+#include <mach/at91_sfr.h>
 #include "pmc.h"
 
 /*
index 05dadf98f95950f0b5c29a4ff113dcab7ce3ee74..84f0f0fbf0e87b450f33752706577b51e92c05f7 100644 (file)
@@ -404,7 +404,8 @@ int device_probe(struct udevice *dev)
                        goto fail;
        }
 
-       if (drv->ofdata_to_platdata && dev_has_of_node(dev)) {
+       if (drv->ofdata_to_platdata &&
+           (CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_of_node(dev))) {
                ret = drv->ofdata_to_platdata(dev);
                if (ret)
                        goto fail;
index 6850003a287b8b4490e9baf14b5397150324c30e..575798fae93c3d97a8984c62c60493a4adfb2b07 100644 (file)
@@ -190,3 +190,33 @@ void *devfdt_map_physmem(struct udevice *dev, unsigned long size)
 
        return map_physmem(addr, size, MAP_NOCACHE);
 }
+
+fdt_addr_t devfdt_get_addr_pci(struct udevice *dev)
+{
+       ulong addr;
+
+       addr = devfdt_get_addr(dev);
+       if (CONFIG_IS_ENABLED(PCI) && IS_ENABLED(CONFIG_DM_PCI) &&
+           addr == FDT_ADDR_T_NONE) {
+               struct fdt_pci_addr pci_addr;
+               u32 bar;
+               int ret;
+
+               ret = ofnode_read_pci_addr(dev_ofnode(dev), FDT_PCI_SPACE_MEM32,
+                                          "reg", &pci_addr);
+               if (ret) {
+                       /* try if there is any i/o-mapped register */
+                       ret = ofnode_read_pci_addr(dev_ofnode(dev),
+                                                  FDT_PCI_SPACE_IO, "reg",
+                                                  &pci_addr);
+                       if (ret)
+                               return FDT_ADDR_T_NONE;
+               }
+               ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar);
+               if (ret)
+                       return FDT_ADDR_T_NONE;
+               addr = bar;
+       }
+
+       return addr;
+}
index a1f828463ecd615ba1e9175fc68cabd35543615a..4681b3e5dd1a02fd4268a089ea75941e94087be7 100644 (file)
@@ -6,6 +6,8 @@
  * Marek Vasut <marex@denx.de>
  */
 
+#define LOG_CATEGORY LOGC_DM
+
 #include <common.h>
 #include <errno.h>
 #include <dm/device.h>
@@ -139,13 +141,13 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
        if (devp)
                *devp = NULL;
        name = ofnode_get_name(node);
-       pr_debug("bind node %s\n", name);
+       log_debug("bind node %s\n", name);
 
        compat_list = ofnode_get_property(node, "compatible", &compat_length);
        if (!compat_list) {
                if (compat_length == -FDT_ERR_NOTFOUND) {
-                       pr_debug("Device '%s' has no compatible string\n",
-                                name);
+                       log_debug("Device '%s' has no compatible string\n",
+                                 name);
                        return 0;
                }
 
@@ -160,8 +162,8 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
         */
        for (i = 0; i < compat_length; i += strlen(compat) + 1) {
                compat = compat_list + i;
-               pr_debug("   - attempt to match compatible string '%s'\n",
-                        compat);
+               log_debug("   - attempt to match compatible string '%s'\n",
+                         compat);
 
                for (entry = driver; entry != driver + n_ents; entry++) {
                        ret = driver_check_compatible(entry->of_match, &id,
@@ -178,11 +180,13 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
                                return 0;
                }
 
-               pr_debug("   - found match at '%s'\n", entry->name);
+               log_debug("   - found match at '%s': '%s' matches '%s'\n",
+                         entry->name, entry->of_match->compatible,
+                         id->compatible);
                ret = device_bind_with_driver_data(parent, entry, name,
                                                   id->data, node, &dev);
                if (ret == -ENODEV) {
-                       pr_debug("Driver '%s' refuses to bind\n", entry->name);
+                       log_debug("Driver '%s' refuses to bind\n", entry->name);
                        continue;
                }
                if (ret) {
@@ -198,7 +202,7 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
        }
 
        if (!found && !result && ret != -ENODEV)
-               pr_debug("No match for node '%s'\n", name);
+               log_debug("No match for node '%s'\n", name);
 
        return result;
 }
index fb3dcd9a7905d5178074de82313f8ce5a3758418..9602e52d1b1d3dcd8afbe8d5ef31c9fffa044936 100644 (file)
@@ -307,3 +307,14 @@ int dev_read_alias_highest_id(const char *stem)
 
        return fdtdec_get_alias_highest_id(gd->fdt_blob, stem);
 }
+
+fdt_addr_t dev_read_addr_pci(struct udevice *dev)
+{
+       ulong addr;
+
+       addr = dev_read_addr(dev);
+       if (addr == FDT_ADDR_T_NONE && !of_live_active())
+               addr = devfdt_get_addr_pci(dev);
+
+       return addr;
+}
index b33296542f63ddfed35739ea891483b03d5504e6..f217876cd2c0ab70a7e81d38c705b39da1c4d040 100644 (file)
@@ -225,7 +225,7 @@ int uclass_find_first_device(enum uclass_id id, struct udevice **devp)
        if (ret)
                return ret;
        if (list_empty(&uc->dev_head))
-               return -ENODEV;
+               return 0;
 
        *devp = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
 
@@ -714,8 +714,11 @@ int uclass_pre_probe_device(struct udevice *dev)
        if (!dev->parent)
                return 0;
        uc_drv = dev->parent->uclass->uc_drv;
-       if (uc_drv->child_pre_probe)
-               return uc_drv->child_pre_probe(dev);
+       if (uc_drv->child_pre_probe) {
+               ret = uc_drv->child_pre_probe(dev);
+               if (ret)
+                       return ret;
+       }
 
        return 0;
 }
@@ -735,8 +738,11 @@ int uclass_post_probe_device(struct udevice *dev)
        }
 
        uc_drv = dev->uclass->uc_drv;
-       if (uc_drv->post_probe)
-               return uc_drv->post_probe(dev);
+       if (uc_drv->post_probe) {
+               ret = uc_drv->post_probe(dev);
+               if (ret)
+                       return ret;
+       }
 
        return 0;
 }
index 01cfa2f7884884d0a6c61674da5992f2e7a04555..90fbed455b8da8269f49a3062032e199dab2beb7 100644 (file)
@@ -294,7 +294,7 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label)
 
 static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
 {
-#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF)
+#if !defined(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
        va_list args;
        char buf[40];
 
@@ -343,7 +343,7 @@ int gpio_request(unsigned gpio, const char *label)
  */
 int gpio_requestf(unsigned gpio, const char *fmt, ...)
 {
-#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF)
+#if !defined(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
        va_list args;
        char buf[40];
 
index bbe214d5eea972c71596794ffba62d2fdb3e1602..74a773c099b9d5871ea078564bc6e9cf53cc6265 100644 (file)
@@ -172,16 +172,16 @@ static int pm8916_gpio_probe(struct udevice *dev)
 
        priv->pid = dev_read_addr(dev);
        if (priv->pid == FDT_ADDR_T_NONE)
-               return -EINVAL;
+               return log_msg_ret("bad address", -EINVAL);
 
        /* Do a sanity check */
        reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
        if (reg != 0x10)
-               return -ENODEV;
+               return log_msg_ret("bad type", -ENXIO);
 
        reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
        if (reg != 0x5 && reg != 0x1)
-               return -ENODEV;
+               return log_msg_ret("bad subtype", -ENXIO);
 
        return 0;
 }
@@ -257,16 +257,16 @@ static int pm8941_pwrkey_probe(struct udevice *dev)
 
        priv->pid = devfdt_get_addr(dev);
        if (priv->pid == FDT_ADDR_T_NONE)
-               return -EINVAL;
+               return log_msg_ret("bad address", -EINVAL);
 
        /* Do a sanity check */
        reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
        if (reg != 0x1)
-               return -ENODEV;
+               return log_msg_ret("bad type", -ENXIO);
 
        reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
        if (reg != 0x1)
-               return -ENODEV;
+               return log_msg_ret("bad subtype", -ENXIO);
 
        return 0;
 }
index 509c588582d2e0f51ca0ca85b8f7b1e2bc3ac26c..0001d105baee00870d5fe47edce1dd3212424e01 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(SPL_TPL_)CROS_EC_LPC) += cros_ec_lpc.o
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o
 obj-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o
+obj-$(CONFIG_SANDBOX) += swap_case.o
 endif
 
 ifdef CONFIG_DM_I2C
@@ -52,7 +53,6 @@ obj-$(CONFIG_PCA9551_LED) += pca9551_led.o
 obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o
 obj-$(CONFIG_QFW) += qfw.o
 obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o
-obj-$(CONFIG_SANDBOX) += swap_case.o
 obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o
 obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o
 obj-$(CONFIG_SMSC_SIO1007) += smsc_sio1007.o
index 6afc6d9466bb92869be8117170ede81b04bceea0..11189d16c83ae87fa5227d3b198368db8e288048 100644 (file)
@@ -24,9 +24,6 @@ struct swap_case_platdata {
        u32 bar[6];
 };
 
-#define offset_to_barnum(offset)       \
-               (((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32))
-
 enum {
        MEM_TEXT_SIZE   = 0x100,
 };
@@ -54,13 +51,6 @@ struct swap_case_priv {
        char mem_text[MEM_TEXT_SIZE];
 };
 
-static int sandbox_swap_case_get_devfn(struct udevice *dev)
-{
-       struct pci_child_platdata *plat = dev_get_parent_platdata(dev);
-
-       return plat->devfn;
-}
-
 static int sandbox_swap_case_use_ea(struct udevice *dev)
 {
        return !!ofnode_get_property(dev->node, "use-ea", NULL);
@@ -129,7 +119,7 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
                *valuep = SANDBOX_PCI_VENDOR_ID;
                break;
        case PCI_DEVICE_ID:
-               *valuep = SANDBOX_PCI_DEVICE_ID;
+               *valuep = SANDBOX_PCI_SWAP_CASE_EMUL_ID;
                break;
        case PCI_CLASS_DEVICE:
                if (size == PCI_SIZE_8) {
@@ -149,25 +139,13 @@ static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
        case PCI_BASE_ADDRESS_4:
        case PCI_BASE_ADDRESS_5: {
                int barnum;
-               u32 *bar, result;
+               u32 *bar;
 
-               barnum = offset_to_barnum(offset);
+               barnum = pci_offset_to_barnum(offset);
                bar = &plat->bar[barnum];
 
-               result = *bar;
-               if (*bar == 0xffffffff) {
-                       if (barinfo[barnum].type) {
-                               result = (~(barinfo[barnum].size - 1) &
-                                       PCI_BASE_ADDRESS_IO_MASK) |
-                                       PCI_BASE_ADDRESS_SPACE_IO;
-                       } else {
-                               result = (~(barinfo[barnum].size - 1) &
-                                       PCI_BASE_ADDRESS_MEM_MASK) |
-                                       PCI_BASE_ADDRESS_MEM_TYPE_32;
-                       }
-               }
-               debug("r bar %d=%x\n", barnum, result);
-               *valuep = result;
+               *valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type,
+                                              barinfo[barnum].size);
                break;
        }
        case PCI_CAPABILITY_LIST:
@@ -231,7 +209,7 @@ static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
                int barnum;
                u32 *bar;
 
-               barnum = offset_to_barnum(offset);
+               barnum = pci_offset_to_barnum(offset);
                bar = &plat->bar[barnum];
 
                debug("w bar %d=%lx\n", barnum, value);
@@ -286,8 +264,8 @@ static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
        }
 }
 
-int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
-                             ulong *valuep, enum pci_size_t size)
+static int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
+                                    ulong *valuep, enum pci_size_t size)
 {
        struct swap_case_priv *priv = dev_get_priv(dev);
        unsigned int offset;
@@ -304,8 +282,8 @@ int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
        return 0;
 }
 
-int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
-                              ulong value, enum pci_size_t size)
+static int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
+                                     ulong value, enum pci_size_t size)
 {
        struct swap_case_priv *priv = dev_get_priv(dev);
        unsigned int offset;
@@ -392,8 +370,7 @@ static int sandbox_swap_case_unmap_physmem(struct udevice *dev,
        return 0;
 }
 
-struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
-       .get_devfn = sandbox_swap_case_get_devfn,
+static struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
        .read_config = sandbox_swap_case_read_config,
        .write_config = sandbox_swap_case_write_config,
        .read_io = sandbox_swap_case_read_io,
@@ -417,7 +394,8 @@ U_BOOT_DRIVER(sandbox_swap_case_emul) = {
 };
 
 static struct pci_device_id sandbox_swap_case_supported[] = {
-       { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_DEVICE_ID), SWAP_CASE_DRV_DATA },
+       { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_SWAP_CASE_EMUL_ID),
+               SWAP_CASE_DRV_DATA },
        {},
 };
 
index d930ed8da0e20b2955f035252bc938abca207521..2b797c9bd4005485a5fac7210cfa753e1c0f73b1 100644 (file)
@@ -112,6 +112,7 @@ static int atmel_sdhci_bind(struct udevice *dev)
 
 static const struct udevice_id atmel_sdhci_ids[] = {
        { .compatible = "atmel,sama5d2-sdhci" },
+       { .compatible = "microchip,sam9x60-sdhci" },
        { }
 };
 
index c8f71cd0c1b6e34883aa77d6ba54e7f41500ffcf..6bece7f3073a51fd75c9c996d5d0680dfc3f40cc 100644 (file)
@@ -2577,7 +2577,7 @@ static int mmc_startup(struct mmc *mmc)
        bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
 #if !defined(CONFIG_SPL_BUILD) || \
                (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
-               !defined(CONFIG_USE_TINY_PRINTF))
+               !CONFIG_IS_ENABLED(USE_TINY_PRINTF))
        sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
                mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
                (mmc->cid[3] >> 16) & 0xffff);
index 2fa7d8c3dcde8633e5d275e209477880bf3b71bf..899952d773cdd063122cbb37382f5e8bf82c6231 100644 (file)
@@ -27,6 +27,7 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 {
        switch (cmd->cmdidx) {
        case MMC_CMD_ALL_SEND_CID:
+               memset(cmd->response, '\0', sizeof(cmd->response));
                break;
        case SD_CMD_SEND_RELATIVE_ADDR:
                cmd->response[0] = 0 << 16; /* mmc->rca */
@@ -43,11 +44,14 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
        case MMC_CMD_SEND_CSD:
                cmd->response[0] = 0;
                cmd->response[1] = 10 << 16;    /* 1 << block_len */
+               cmd->response[2] = 0;
+               cmd->response[3] = 0;
                break;
        case SD_CMD_SWITCH_FUNC: {
                if (!data)
                        break;
                u32 *resp = (u32 *)data->dest;
+               resp[3] = 0;
                resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY);
                if ((cmd->cmdarg & 0xF) == UHS_SDR12_BUS_SPEED)
                        resp[4] = (cmd->cmdarg & 0xF) << 24;
index 719a2fd23ae29fb83d080a9b59faec0ee426ab9c..c6107522be033eb8c0a0ed3dd94f093e2cf04bf6 100644 (file)
@@ -66,7 +66,7 @@ int spi_flash_probe_bus_cs(unsigned int busnum, unsigned int cs,
        char *str;
        int ret;
 
-#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_USE_TINY_PRINTF)
+#if defined(CONFIG_SPL_BUILD) && CONFIG_IS_ENABLED(USE_TINY_PRINTF)
        str = "spi_flash";
 #else
        char name[30];
index 377188e361c4f9fe98f423f64018b9945f94c7fe..1a532b0e5a4f603a75deca3edb51fc7c389a7c75 100644 (file)
@@ -1321,6 +1321,7 @@ static const struct macb_config sifive_config = {
 static const struct udevice_id macb_eth_ids[] = {
        { .compatible = "cdns,macb" },
        { .compatible = "cdns,at91sam9260-macb" },
+       { .compatible = "cdns,sam9x60-macb" },
        { .compatible = "atmel,sama5d2-gem" },
        { .compatible = "atmel,sama5d3-gem" },
        { .compatible = "atmel,sama5d4-gem", .data = (ulong)&sama5d4_config },
index 47f101e2808be51e93e87de9e8d4cd33f6b83c6e..ee6b581d9e17f9240165a892ca128f764ab7617f 100644 (file)
@@ -621,6 +621,18 @@ static int nvme_get_info_from_identify(struct nvme_dev *dev)
        return 0;
 }
 
+int nvme_get_namespace_id(struct udevice *udev, u32 *ns_id, u8 *eui64)
+{
+       struct nvme_ns *ns = dev_get_priv(udev);
+
+       if (ns_id)
+               *ns_id = ns->ns_id;
+       if (eui64)
+               memcpy(eui64, ns->eui64, sizeof(ns->eui64));
+
+       return 0;
+}
+
 int nvme_scan_namespace(void)
 {
        struct uclass *uc;
@@ -657,6 +669,7 @@ static int nvme_blk_probe(struct udevice *udev)
        if (nvme_identify(ndev, ns->ns_id, 0, (dma_addr_t)(long)id))
                return -EIO;
 
+       memcpy(&ns->eui64, &id->eui64, sizeof(id->eui64));
        flbas = id->flbas & NVME_NS_FLBAS_LBA_MASK;
        ns->flbas = flbas;
        ns->lba_shift = id->lbaf[flbas].ds;
index 922f7abfe8562bdf891f43f30a17ce47afeb627c..0e8cb221a7a2d5cda4ae84badf8c68d199d04bb3 100644 (file)
@@ -637,6 +637,7 @@ struct nvme_ns {
        struct list_head list;
        struct nvme_dev *dev;
        unsigned ns_id;
+       u8 eui64[8];
        int devnum;
        int lba_shift;
        u8 flbas;
index caf8b72803cac90d7ed8fa0522ef65d07ecde69f..ad4906aa58b41c55859180d0572866413d53460b 100644 (file)
@@ -64,5 +64,7 @@ int pch_ioctl(struct udevice *dev, ulong req, void *data, int size)
 UCLASS_DRIVER(pch) = {
        .id             = UCLASS_PCH,
        .name           = "pch",
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
        .post_bind      = dm_scan_fdt_dev,
+#endif
 };
index 38227583547341f080fa124818521d2dad08277d..0dcf937d9a67bc28a562e6496d3c5cbb68935416 100644 (file)
@@ -30,17 +30,38 @@ int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
        }
        *containerp = dev;
 
-       if (device_get_uclass_id(dev) == UCLASS_PCI_GENERIC) {
-               ret = device_find_first_child(dev, emulp);
-               if (ret)
-                       return ret;
-       } else {
+       /*
+        * See commit 4345998ae9df,
+        * "pci: sandbox: Support dynamically binding device driver"
+        */
+       ret = uclass_get_device_by_phandle(UCLASS_PCI_EMUL, dev, "sandbox,emul",
+                                          emulp);
+       if (ret && device_get_uclass_id(dev) != UCLASS_PCI_GENERIC)
                *emulp = dev;
-       }
 
        return *emulp ? 0 : -ENODEV;
 }
 
+uint sandbox_pci_read_bar(u32 barval, int type, uint size)
+{
+       u32 result;
+
+       result = barval;
+       if (result == 0xffffffff) {
+               if (type == PCI_BASE_ADDRESS_SPACE_IO) {
+                       result = (~(size - 1) &
+                               PCI_BASE_ADDRESS_IO_MASK) |
+                               PCI_BASE_ADDRESS_SPACE_IO;
+               } else {
+                       result = (~(size - 1) &
+                               PCI_BASE_ADDRESS_MEM_MASK) |
+                               PCI_BASE_ADDRESS_MEM_TYPE_32;
+               }
+       }
+
+       return result;
+}
+
 static int sandbox_pci_emul_post_probe(struct udevice *dev)
 {
        struct sandbox_pci_emul_priv *priv = dev->uclass->priv;
@@ -68,3 +89,25 @@ UCLASS_DRIVER(pci_emul) = {
        .pre_remove     = sandbox_pci_emul_pre_remove,
        .priv_auto_alloc_size   = sizeof(struct sandbox_pci_emul_priv),
 };
+
+/*
+ * This uclass is a child of the pci bus. Its platdata is not defined here so
+ * is defined by its parent, UCLASS_PCI, which uses struct pci_child_platdata.
+ * See per_child_platdata_auto_alloc_size in UCLASS_DRIVER(pci).
+ */
+UCLASS_DRIVER(pci_emul_parent) = {
+       .id             = UCLASS_PCI_EMUL_PARENT,
+       .name           = "pci_emul_parent",
+       .post_bind      = dm_scan_fdt_dev,
+};
+
+static const struct udevice_id pci_emul_parent_ids[] = {
+       { .compatible = "sandbox,pci-emul-parent" },
+       { }
+};
+
+U_BOOT_DRIVER(pci_emul_parent_drv) = {
+       .name           = "pci_emul_parent_drv",
+       .id             = UCLASS_PCI_EMUL_PARENT,
+       .of_match       = pci_emul_parent_ids,
+};
index ab3e1310eb546d030139d8ead14094890b7cff35..896cb6b23a1a23516fd5b6664b5bf62dd3654beb 100644 (file)
@@ -790,7 +790,7 @@ int pci_bind_bus_devices(struct udevice *bus)
                if (!PCI_FUNC(bdf))
                        found_multi = header_type & 0x80;
 
-               debug("%s: bus %d/%s: found device %x, function %d\n", __func__,
+               debug("%s: bus %d/%s: found device %x, function %d", __func__,
                      bus->seq, bus->name, PCI_DEV(bdf), PCI_FUNC(bdf));
                pci_bus_read_config(bus, bdf, PCI_DEVICE_ID, &device,
                                    PCI_SIZE_16);
@@ -800,6 +800,7 @@ int pci_bind_bus_devices(struct udevice *bus)
 
                /* Find this device in the device tree */
                ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), &dev);
+               debug(": find ret=%d\n", ret);
 
                /* If nothing in the device tree, bind a device */
                if (ret == -ENODEV) {
@@ -982,7 +983,7 @@ static int pci_uclass_post_probe(struct udevice *bus)
        if (ret)
                return ret;
 
-#ifdef CONFIG_PCI_PNP
+#if CONFIG_IS_ENABLED(PCI_PNP)
        ret = pci_auto_config_devices(bus);
        if (ret < 0)
                return ret;
index 1a3bf708347d2269cff25aa7b4f566335ac6ad15..28667bde8da79d6167296072ea6ed898010dd40f 100644 (file)
@@ -39,6 +39,8 @@ void dm_pciauto_setup_device(struct udevice *dev, int bars_num,
 
        for (bar = PCI_BASE_ADDRESS_0;
             bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) {
+               int ret = 0;
+
                /* Tickle the BAR and get the response */
                if (!enum_only)
                        dm_pci_write_config32(dev, bar, 0xffffffff);
@@ -97,9 +99,13 @@ void dm_pciauto_setup_device(struct udevice *dev, int bars_num,
                              (unsigned long long)bar_size);
                }
 
-               if (!enum_only && pciauto_region_allocate(bar_res, bar_size,
-                                                         &bar_value,
-                                                         found_mem64) == 0) {
+               if (!enum_only) {
+                       ret = pciauto_region_allocate(bar_res, bar_size,
+                                                     &bar_value, found_mem64);
+                       if (ret)
+                               printf("PCI: Failed autoconfig bar %x\n", bar);
+               }
+               if (!enum_only && !ret) {
                        /* Write it out and update our limit */
                        dm_pci_write_config32(dev, bar, (u32)bar_value);
 
index 84908e6154c568a3382381aeda2baeaa3ebef01e..86903166101b6732bae0d19c82c69a0ff50d7fae 100644 (file)
@@ -45,7 +45,9 @@ int pciauto_region_allocate(struct pci_region *res, pci_size_t size,
        addr = ((res->bus_lower - 1) | (size - 1)) + 1;
 
        if (addr - res->bus_start + size > res->size) {
-               debug("No room in resource");
+               debug("No room in resource, avail start=%llx / size=%llx, "
+                     "need=%llx\n", (unsigned long long)res->bus_lower,
+                     (unsigned long long)res->size, (unsigned long long)size);
                goto error;
        }
 
index 2cede1211bbed341986d6bd1d999c3b83a052bbd..1d4064e3769281537cbd563f0f903555c5c3e435 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/screen_info.h>
 
 #ifdef CONFIG_X86
-#include <asm/acpi_s3.h>
+#include <acpi_s3.h>
 DECLARE_GLOBAL_DATA_PTR;
 #endif
 
index 520ea4649e11c1bcda350f02623bbfee9770f3bb..e76a9c6e44fe50ddc4aff96d8bb52a383a31c3bf 100644 (file)
@@ -8,9 +8,21 @@
 #include <pci.h>
 #include <asm/pci.h>
 
+static int _pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
+                               ulong *valuep, enum pci_size_t size)
+{
+       return pci_x86_read_config(bdf, offset, valuep, size);
+}
+
+static int _pci_x86_write_config(struct udevice *bus, pci_dev_t bdf,
+                                uint offset, ulong value, enum pci_size_t size)
+{
+       return pci_x86_write_config(bdf, offset, value, size);
+}
+
 static const struct dm_pci_ops pci_x86_ops = {
-       .read_config    = pci_x86_read_config,
-       .write_config   = pci_x86_write_config,
+       .read_config    = _pci_x86_read_config,
+       .write_config   = _pci_x86_write_config,
 };
 
 static const struct udevice_id pci_x86_ids[] = {
index 6cf2be8f2b892eb0fd7cfea13ddf825b4380724b..754b6e99215822190b1b2eecb7c592302895904b 100644 (file)
@@ -21,7 +21,7 @@ DECLARE_GLOBAL_DATA_PTR;
 #define UART_MCRVAL (UART_MCR_DTR | \
                     UART_MCR_RTS)              /* RTS/DTR */
 
-#ifndef CONFIG_DM_SERIAL
+#if !CONFIG_IS_ENABLED(DM_SERIAL)
 #ifdef CONFIG_SYS_NS16550_PORT_MAPPED
 #define serial_out(x, y)       outb(x, (ulong)y)
 #define serial_in(y)           inb((ulong)y)
@@ -86,7 +86,7 @@ static inline int serial_in_shift(void *addr, int shift)
 #endif
 }
 
-#ifdef CONFIG_DM_SERIAL
+#if CONFIG_IS_ENABLED(DM_SERIAL)
 
 #ifndef CONFIG_SYS_NS16550_CLK
 #define CONFIG_SYS_NS16550_CLK  0
@@ -301,7 +301,7 @@ DEBUG_UART_FUNCS
 
 #endif
 
-#ifdef CONFIG_DM_SERIAL
+#if CONFIG_IS_ENABLED(DM_SERIAL)
 static int ns16550_serial_putc(struct udevice *dev, const char ch)
 {
        struct NS16550 *const com_port = dev_get_priv(dev);
@@ -440,36 +440,7 @@ int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
        int err;
 
        /* try Processor Local Bus device first */
-       addr = dev_read_addr(dev);
-#if CONFIG_IS_ENABLED(PCI) && defined(CONFIG_DM_PCI)
-       if (addr == FDT_ADDR_T_NONE) {
-               /* then try pci device */
-               struct fdt_pci_addr pci_addr;
-               u32 bar;
-               int ret;
-
-               /* we prefer to use a memory-mapped register */
-               ret = fdtdec_get_pci_addr(gd->fdt_blob, dev_of_offset(dev),
-                                         FDT_PCI_SPACE_MEM32, "reg",
-                                         &pci_addr);
-               if (ret) {
-                       /* try if there is any i/o-mapped register */
-                       ret = fdtdec_get_pci_addr(gd->fdt_blob,
-                                                 dev_of_offset(dev),
-                                                 FDT_PCI_SPACE_IO,
-                                                 "reg", &pci_addr);
-                       if (ret)
-                               return ret;
-               }
-
-               ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar);
-               if (ret)
-                       return ret;
-
-               addr = bar;
-       }
-#endif
-
+       addr = dev_read_addr_pci(dev);
        if (addr == FDT_ADDR_T_NONE)
                return -EINVAL;
 
index 33102fc872f20fd1b1533403ed9c33022fc7df41..2f7bc248871ff8a2e8ce836a9804beb6563b5f11 100644 (file)
@@ -220,6 +220,8 @@ static int sandbox_serial_ofdata_to_platdata(struct udevice *dev)
        const char *colour;
        int i;
 
+       if (CONFIG_IS_ENABLED(OF_PLATDATA))
+               return 0;
        plat->colour = -1;
        colour = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
                             "sandbox,text-colour", NULL);
index f565ae031095c3d40f95675acafc357320b243f3..5e6293ae69b949aca2b52aabe8e3667bd7f492f7 100644 (file)
@@ -107,6 +107,18 @@ config SYSRESET_X86
        help
          Reboot support for generic x86 processor reset.
 
+config SYSRESET_SPL_X86
+       bool "Enable support for x86 processor reboot driver in SPL"
+       depends on X86
+       help
+         Reboot support for generic x86 processor reset in SPL.
+
+config SYSRESET_TPL_X86
+       bool "Enable support for x86 processor reboot driver in TPL"
+       depends on X86
+       help
+         Reboot support for generic x86 processor reset in TPL.
+
 config SYSRESET_MCP83XX
        bool "Enable support MPC83xx SoC family reboot driver"
        help
index cf01492295f7dccbafaa6a3fd6198e68c37c2cef..fff4a184a02fd47922fe60b02436fe4ab69f3d90 100644 (file)
@@ -16,5 +16,5 @@ obj-$(CONFIG_SYSRESET_SOCFPGA_S10) += sysreset_socfpga_s10.o
 obj-$(CONFIG_SYSRESET_TI_SCI) += sysreset-ti-sci.o
 obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o
 obj-$(CONFIG_SYSRESET_WATCHDOG) += sysreset_watchdog.o
-obj-$(CONFIG_SYSRESET_X86) += sysreset_x86.o
+obj-$(CONFIG_$(SPL_TPL_)SYSRESET_X86) += sysreset_x86.o
 obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o
index 072f7948efa1be8aa7305b82c58fbc9df5a0e47b..8e2d1eaa7a1312e10cc56711d5f2737cdcb31b5e 100644 (file)
@@ -6,11 +6,11 @@
  */
 
 #include <common.h>
+#include <acpi_s3.h>
 #include <dm.h>
 #include <efi_loader.h>
 #include <pch.h>
 #include <sysreset.h>
-#include <asm/acpi_s3.h>
 #include <asm/io.h>
 #include <asm/processor.h>
 
index a66a9bcbe23f898c0e784db5213f2072c58ba34a..8c16d69d333054fe2c2d6fe7838d28207b363944 100644 (file)
@@ -8,6 +8,15 @@ config WATCHDOG
          this option if you want to service enabled watchdog by U-Boot. Disable
          this option if you want U-Boot to start watchdog but never service it.
 
+config WATCHDOG_TIMEOUT_MSECS
+       int "Watchdog timeout in msec"
+       default 128000 if ARCH_MX25 || ARCH_MX31 || ARCH_MX5 || ARCH_MX6
+       default 128000 if ARCH_MX7 || ARCH_VF610
+       default 30000 if ARCH_SOCFPGA
+       default 60000
+       help
+         Watchdog timeout in msec
+
 config HW_WATCHDOG
        bool
 
diff --git a/include/acpi_s3.h b/include/acpi_s3.h
new file mode 100644 (file)
index 0000000..baa848d
--- /dev/null
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#ifndef __ASM_ACPI_S3_H__
+#define __ASM_ACPI_S3_H__
+
+#define WAKEUP_BASE    0x600
+
+/* PM1_STATUS register */
+#define WAK_STS                (1 << 15)
+#define PCIEXPWAK_STS  (1 << 14)
+#define RTC_STS                (1 << 10)
+#define SLPBTN_STS     (1 << 9)
+#define PWRBTN_STS     (1 << 8)
+#define GBL_STS                (1 << 5)
+#define BM_STS         (1 << 4)
+#define TMR_STS                (1 << 0)
+
+/* PM1_CNT register */
+#define SLP_EN         (1 << 13)
+#define SLP_TYP_SHIFT  10
+#define SLP_TYP                (7 << SLP_TYP_SHIFT)
+#define SLP_TYP_S0     0
+#define SLP_TYP_S1     1
+#define SLP_TYP_S3     5
+#define SLP_TYP_S4     6
+#define SLP_TYP_S5     7
+
+/* Memory size reserved for S3 resume */
+#define S3_RESERVE_SIZE        0x1000
+
+#ifndef __ASSEMBLY__
+
+extern char __wakeup[];
+extern int __wakeup_size;
+
+enum acpi_sleep_state {
+       ACPI_S0,
+       ACPI_S1,
+       ACPI_S2,
+       ACPI_S3,
+       ACPI_S4,
+       ACPI_S5,
+};
+
+/**
+ * acpi_ss_string() - get ACPI-defined sleep state string
+ *
+ * @pm1_cnt:   ACPI-defined sleep state
+ * @return:    a pointer to the sleep state string.
+ */
+static inline char *acpi_ss_string(enum acpi_sleep_state state)
+{
+       char *ss_string[] = { "S0", "S1", "S2", "S3", "S4", "S5"};
+
+       return ss_string[state];
+}
+
+/**
+ * acpi_sleep_from_pm1() - get ACPI-defined sleep state from PM1_CNT register
+ *
+ * @pm1_cnt:   PM1_CNT register value
+ * @return:    ACPI-defined sleep state if given valid PM1_CNT register value,
+ *             -EINVAL otherwise.
+ */
+static inline enum acpi_sleep_state acpi_sleep_from_pm1(u32 pm1_cnt)
+{
+       switch ((pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
+       case SLP_TYP_S0:
+               return ACPI_S0;
+       case SLP_TYP_S1:
+               return ACPI_S1;
+       case SLP_TYP_S3:
+               return ACPI_S3;
+       case SLP_TYP_S4:
+               return ACPI_S4;
+       case SLP_TYP_S5:
+               return ACPI_S5;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * chipset_prev_sleep_state() - Get chipset previous sleep state
+ *
+ * This returns chipset previous sleep state from ACPI registers.
+ * Platform codes must supply this routine in order to support ACPI S3.
+ *
+ * @return ACPI_S0/S1/S2/S3/S4/S5.
+ */
+enum acpi_sleep_state chipset_prev_sleep_state(void);
+
+/**
+ * chipset_clear_sleep_state() - Clear chipset sleep state
+ *
+ * This clears chipset sleep state in ACPI registers.
+ * Platform codes must supply this routine in order to support ACPI S3.
+ */
+void chipset_clear_sleep_state(void);
+
+struct acpi_fadt;
+/**
+ * acpi_resume() - Do ACPI S3 resume
+ *
+ * This calls U-Boot wake up assembly stub and jumps to OS's wake up vector.
+ *
+ * @fadt:      FADT table pointer in the ACPI table
+ * @return:    Never returns
+ */
+void acpi_resume(struct acpi_fadt *fadt);
+
+/**
+ * acpi_s3_reserve() - Reserve memory for ACPI S3 resume
+ *
+ * This copies memory where real mode interrupt handler stubs reside to the
+ * reserved place on the stack.
+ *
+ * This routine should be called by reserve_arch() before U-Boot is relocated
+ * when ACPI S3 resume is enabled.
+ *
+ * @return:    0 always
+ */
+int acpi_s3_reserve(void);
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ACPI_S3_H__ */
index c516e6ed4c2d3af114c2f57cd181831dd85a78b0..a854d0b53152d1df7b2ffb1f254b3d54d2b7f6a6 100644 (file)
@@ -88,7 +88,6 @@
 #endif
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS  60000
 
 /* allow to overwrite serial and ethaddr */
 #define CONFIG_ENV_OVERWRITE
index de11ff61c66121514c3115da33975f9599f916fb..d80641568eb44c8294cbc7713762c984b9305574 100644 (file)
        (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS   15000
 #if defined(CONFIG_SPL_BUILD)
 #undef CONFIG_WDT
 #undef CONFIG_WATCHDOG
 #define CONFIG_HW_WATCHDOG
 #endif
+
 /* ENV config */
 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
 #define CONFIG_ENV_SIZE                (SZ_64K)
index 31214a6aa78e2beb43bf4ca30aac03dd88626606..6de5119d7fbe5eabc31baa5925b0610cb146223d 100644 (file)
@@ -28,8 +28,6 @@
 #define CONFIG_REVISION_TAG
 #define CONFIG_SYS_MALLOC_LEN          (10 * SZ_1M)
 
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS 6000
-
 #define CONFIG_MXC_UART
 
 /* SATA Configs */
index 2435ebbc7f05b375973f7b8191b982c76d11eb57..c160ad5bf6bff583e81c0cdc4773cee142d2df95 100644 (file)
@@ -60,7 +60,6 @@
 #endif
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS  60000
 
 /* allow to overwrite serial and ethaddr */
 #define CONFIG_ENV_OVERWRITE
index 49f11ea4b8d1429537bed754829752fa51b49118..113e84456d388548ee06e46a84920ca718b34057 100644 (file)
 #define CONFIG_FSL_IIM
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS 8000
 
 /*
  * Boot Linux
index d5b54dfa15e0cc01f35a181632a4adf8f422918d..59988efc9b328384d389d3b29897a2fc46ab44fe 100644 (file)
@@ -22,8 +22,6 @@
 /* Size of malloc() pool */
 #define CONFIG_SYS_MALLOC_LEN          (10 * 1024 * 1024)
 
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS 8000
-
 #define CONFIG_BOARD_LATE_INIT
 #define CONFIG_REVISION_TAG
 
index 6cadd720d2b958cc01821f4934f40b7eb4dbe573..e079f8035b8baf8fc1f822c761dd6f0d26b0614c 100644 (file)
@@ -45,7 +45,7 @@
 
 /* SPL */
 #ifndef CONFIG_SPL_FRAMEWORK
-#define CONFIG_SPL_NO_CPU_SUPPORT_CODE
+#define CONFIG_SPL_NO_CPU_SUPPORT
 #define CONFIG_SPL_START_S_PATH        "arch/arm/cpu/arm926ejs/mxs"
 #endif
 
diff --git a/include/configs/sam9x60ek.h b/include/configs/sam9x60ek.h
new file mode 100644 (file)
index 0000000..5f89ae4
--- /dev/null
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Configuation settings for the SAM9X60EK board.
+ *
+ * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Sandeep Sheriker M <sandeep.sheriker@microchip.com>
+ */
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+/* ARM asynchronous clock */
+#define CONFIG_SYS_AT91_SLOW_CLOCK     32768
+#define CONFIG_SYS_AT91_MAIN_CLOCK     24000000        /* 24 MHz crystal */
+
+#define CONFIG_CMDLINE_TAG             /* enable passing of ATAGs */
+#define CONFIG_SETUP_MEMORY_TAGS
+#define CONFIG_INITRD_TAG
+#define CONFIG_SKIP_LOWLEVEL_INIT
+
+#define CONFIG_USART_BASE   ATMEL_BASE_DBGU
+#define CONFIG_USART_ID     0 /* ignored in arm */
+
+/* general purpose I/O */
+#define CONFIG_ATMEL_LEGACY            /* required until (g)pio is fixed */
+
+/*
+ * BOOTP options
+ */
+#define CONFIG_BOOTP_BOOTFILESIZE
+
+/*
+ * define CONFIG_USB_EHCI_HCD to enable USB Hi-Speed (aka 2.0)
+ * NB: in this case, USB 1.1 devices won't be recognized.
+ */
+
+/* SDRAM */
+#define CONFIG_SYS_SDRAM_BASE          0x20000000
+#define CONFIG_SYS_SDRAM_SIZE          0x10000000      /* 256 megs */
+
+#define CONFIG_SYS_INIT_SP_ADDR \
+       (CONFIG_SYS_SDRAM_BASE + 16 * 1024 - GENERATED_GBL_DATA_SIZE)
+
+/* NAND flash */
+#ifdef CONFIG_CMD_NAND
+#define CONFIG_NAND_ATMEL
+#define CONFIG_SYS_MAX_NAND_DEVICE     1
+#define CONFIG_SYS_NAND_BASE           0x40000000
+#define CONFIG_SYS_NAND_MASK_ALE       BIT(21)
+#define CONFIG_SYS_NAND_MASK_CLE       BIT(22)
+#define CONFIG_SYS_NAND_ENABLE_PIN     AT91_PIN_PD4
+#define CONFIG_SYS_NAND_READY_PIN      AT91_PIN_PD5
+#define CONFIG_SYS_NAND_ONFI_DETECTION
+
+#define CONFIG_MTD_DEVICE
+#endif
+
+/* PMECC & PMERRLOC */
+#define CONFIG_ATMEL_NAND_HWECC
+#define CONFIG_ATMEL_NAND_HW_PMECC
+#define CONFIG_PMECC_CAP               8
+#define CONFIG_PMECC_SECTOR_SIZE       512
+
+#define CONFIG_SYS_LOAD_ADDR           0x22000000      /* load address */
+
+#ifdef CONFIG_SD_BOOT
+/* bootstrap + u-boot + env + linux in sd card */
+#define CONFIG_BOOTCOMMAND  \
+                       "fatload mmc 0:1 0x21000000 at91-sam9x60ek.dtb;" \
+                       "fatload mmc 0:1 0x22000000 zImage;" \
+                       "bootz 0x22000000 - 0x21000000"
+
+#elif defined(CONFIG_NAND_BOOT)
+/* bootstrap + u-boot + env + linux in nandflash */
+#define CONFIG_ENV_OFFSET_REDUND       0x100000
+#define CONFIG_BOOTCOMMAND     "nand read " \
+                               "0x22000000 0x200000 0x600000; " \
+                               "nand read 0x21000000 0x180000 0x20000; " \
+                               "bootz 0x22000000 - 0x21000000"
+
+#elif defined(CONFIG_QSPI_BOOT)
+/* bootstrap + u-boot + env + linux in SPI NOR flash */
+#define CONFIG_BOOTCOMMAND     "sf probe 0; "                                  \
+                               "sf read 0x21000000 0x180000 0x80000; "         \
+                               "sf read 0x22000000 0x200000 0x600000; "        \
+                               "bootz 0x22000000 - 0x21000000"
+#endif
+
+/*
+ * Size of malloc() pool
+ */
+#define CONFIG_SYS_MALLOC_LEN          (512 * 1024 + 0x1000)
+
+#endif
diff --git a/include/configs/sama5d27_wlsom1_ek.h b/include/configs/sama5d27_wlsom1_ek.h
new file mode 100644 (file)
index 0000000..6bcbc06
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Configuration file for the SAMA5D27 WLSOM1 EK Board.
+ *
+ * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Nicolas Ferre <nicolas.ferre@microcihp.com>
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include "at91-sama5_common.h"
+
+#undef CONFIG_SYS_AT91_MAIN_CLOCK
+#define CONFIG_SYS_AT91_MAIN_CLOCK      24000000 /* from 24 MHz crystal */
+
+/* SDRAM */
+#define CONFIG_SYS_SDRAM_BASE          0x20000000
+#define CONFIG_SYS_SDRAM_SIZE          0x10000000
+
+#ifdef CONFIG_SPL_BUILD
+#define CONFIG_SYS_INIT_SP_ADDR                0x218000
+#else
+#define CONFIG_SYS_INIT_SP_ADDR \
+       (CONFIG_SYS_SDRAM_BASE + 16 * 1024 - GENERATED_GBL_DATA_SIZE)
+#endif
+
+#define CONFIG_SYS_LOAD_ADDR           0x22000000 /* load address */
+
+/* SPL */
+#define CONFIG_SPL_TEXT_BASE           0x200000
+#define CONFIG_SPL_MAX_SIZE            0x10000
+#define CONFIG_SPL_BSS_START_ADDR      0x20000000
+#define CONFIG_SPL_BSS_MAX_SIZE                0x80000
+#define CONFIG_SYS_SPL_MALLOC_START    0x20080000
+#define CONFIG_SYS_SPL_MALLOC_SIZE     0x80000
+
+#define CONFIG_SYS_MONITOR_LEN         (512 << 10)
+
+#ifdef CONFIG_SD_BOOT
+#define CONFIG_SYS_MMCSD_FS_BOOT_PARTITION     1
+#define CONFIG_SPL_FS_LOAD_PAYLOAD_NAME                "u-boot.img"
+#endif
+
+#endif
index b11fe021a72677cf7fc11fb4f4c841041414e591..94268ed7a310ca99f61cb4b6a4ed7be5c5dcb673 100644 (file)
 #define CONFIG_DESIGNWARE_WATCHDOG
 #define CONFIG_DW_WDT_BASE             SOCFPGA_L4WD0_ADDRESS
 #define CONFIG_DW_WDT_CLOCK_KHZ                25000
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS  30000
 #endif
 
 /*
index 7b55dd14dabc405bd969b5f2fa7a79748a81c542..8e6ecf4bed34c6b51d329579260174c5af5dbe91 100644 (file)
@@ -168,7 +168,6 @@ unsigned int cm_get_qspi_controller_clk_hz(void);
 unsigned int cm_get_l4_sys_free_clk_hz(void);
 #define CONFIG_DW_WDT_CLOCK_KHZ                (cm_get_l4_sys_free_clk_hz() / 1000)
 #endif
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS  3000
 #endif
 
 /*
index 34f000f2145a9d148a9538da1e5b6e9eb21d1fc3..0af52e5565ea7446f9290282d8725d800049f539 100644 (file)
@@ -17,7 +17,6 @@
 #define CONSOLE_DEV            "ttymxc3"
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS  60000
 
 /* Config on-board RTC */
 #define CONFIG_RTC_DS1337
index 5345f5314d66607af64fe52e9c525ee0ba2640cb..41fd6c759eba42534dd5771ce0bfa183715e5851 100644 (file)
@@ -25,7 +25,6 @@
 #define CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE
 
 /* Watchdog */
-#define CONFIG_WATCHDOG_TIMEOUT_MSECS 30000 /* 30s */
 
 #define CONFIG_SYS_MEMTEST_START       0x80000000
 #define CONFIG_SYS_MEMTEST_END         (CONFIG_SYS_MEMTEST_START + SZ_256M)
index 27a6d7b9fdb0da75049a285bb14fb562fca710c3..d1210429e9230788afe4c59af7d418b2f6d21352 100644 (file)
@@ -679,6 +679,15 @@ static inline bool device_is_on_pci_bus(struct udevice *dev)
 #define device_foreach_child_safe(pos, next, parent)   \
        list_for_each_entry_safe(pos, next, &parent->child_head, sibling_node)
 
+/**
+ * device_foreach_child() - iterate through child devices
+ *
+ * @pos: struct udevice * for the current device
+ * @parent: parent device to scan
+ */
+#define device_foreach_child(pos, parent)      \
+       list_for_each_entry(pos, &parent->child_head, sibling_node)
+
 /**
  * dm_scan_fdt_dev() - Bind child device in a the device tree
  *
index 57b326cb3362d109bcea76cf0118ddb31b48252e..959d3bc2d69934937b217932867c53ffc60fcbaa 100644 (file)
@@ -138,4 +138,12 @@ fdt_addr_t devfdt_get_addr_name(struct udevice *dev, const char *name);
 fdt_addr_t devfdt_get_addr_size_name(struct udevice *dev, const char *name,
                                     fdt_size_t *size);
 
+/**
+ * devfdt_get_addr_pci() - Read an address and handle PCI address translation
+ *
+ * @dev: Device to read from
+ * @return address or FDT_ADDR_T_NONE if not found
+ */
+fdt_addr_t devfdt_get_addr_pci(struct udevice *dev);
+
 #endif
index 803daf7620c8ab623aa2b0e67fc0991849e49e69..d37fcb504d3471d48455fb769008a1321af820ab 100644 (file)
@@ -248,6 +248,26 @@ fdt_addr_t dev_read_addr(struct udevice *dev);
  */
 void *dev_read_addr_ptr(struct udevice *dev);
 
+/**
+ * dev_read_addr_pci() - Read an address and handle PCI address translation
+ *
+ * At present U-Boot does not have address translation logic for PCI in the
+ * livetree implementation (of_addr.c). This special function supports this for
+ * the flat tree implementation.
+ *
+ * This function should be removed (and code should use dev_read() instead)
+ * once:
+ *
+ * 1. PCI address translation is added; and either
+ * 2. everything uses livetree where PCI translation is used (which is feasible
+ *    in SPL and U-Boot proper) or PCI address translation is added to
+ *    fdtdec_get_addr() and friends.
+ *
+ * @dev: Device to read from
+ * @return address or FDT_ADDR_T_NONE if not found
+ */
+fdt_addr_t dev_read_addr_pci(struct udevice *dev);
+
 /**
  * dev_remap_addr() - Get the reg property of a device as a
  *                         memory-mapped I/O pointer
@@ -691,6 +711,11 @@ static inline void *dev_read_addr_ptr(struct udevice *dev)
        return devfdt_get_addr_ptr(dev);
 }
 
+static inline fdt_addr_t dev_read_addr_pci(struct udevice *dev)
+{
+       return devfdt_get_addr_pci(dev);
+}
+
 static inline void *dev_remap_addr(struct udevice *dev)
 {
        return devfdt_remap_addr(dev);
index d4d96106b3728231c4e1f0abae97c1fac8b697f1..f431f3bf294dcd46e7f9e119f19eea42037d95fc 100644 (file)
@@ -23,6 +23,7 @@ enum uclass_id {
        UCLASS_I2C_EMUL,        /* sandbox I2C device emulator */
        UCLASS_I2C_EMUL_PARENT, /* parent for I2C device emulators */
        UCLASS_PCI_EMUL,        /* sandbox PCI device emulator */
+       UCLASS_PCI_EMUL_PARENT, /* parent for PCI device emulators */
        UCLASS_USB_EMUL,        /* sandbox USB bus device emulator */
        UCLASS_AXI_EMUL,        /* sandbox AXI bus device emulator */
 
index 6977995246da138e394c553807ea65dd704ba01a..6e3f15c2b0804c07f91b03e6f20f77e9b6527596 100644 (file)
@@ -69,7 +69,7 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);
  * The device is not prepared for use - this is an internal function.
  * The function uclass_get_device_tail() can be used to probe the device.
  *
- * @return 0 if OK (found or not found), -1 on error
+ * @return 0 if OK (found or not found), -ve on error
  */
 int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
 
@@ -81,7 +81,7 @@ int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
  * The device is not prepared for use - this is an internal function.
  * The function uclass_get_device_tail() can be used to probe the device.
  *
- * @return 0 if OK (found or not found), -1 on error
+ * @return 0 if OK (found or not found), -ve on error
  */
 int uclass_find_next_device(struct udevice **devp);
 
index 392c1f1a43a74168b383941c8e9b56cfa1c6fc86..444ba61e591524e6ae8e021d145a708643ed558e 100644 (file)
 #define EC_LPC_CMDR_SCI                (1 << 5)  /* SCI event is pending */
 #define EC_LPC_CMDR_SMI                (1 << 6)  /* SMI event is pending */
 
+/* MEC uses 0x800/0x804 as register/index pair, thus an 8-byte resource */
+#define MEC_EMI_BASE           0x800
+#define MEC_EMI_SIZE           8
+
 #define EC_LPC_ADDR_MEMMAP       0x900
 #define EC_MEMMAP_SIZE         255 /* ACPI IO buffer max is 255 bytes */
 #define EC_MEMMAP_TEXT_MAX     8   /* Size of a string in the memory map */
index 37e56da46030883010d55ba524cf74c198ca9cc9..22396172e15f258155b6b3d9093985c10e85fd58 100644 (file)
@@ -422,6 +422,7 @@ struct efi_device_path_acpi_path {
 #  define DEVICE_PATH_SUB_TYPE_MSG_USB         0x05
 #  define DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR    0x0b
 #  define DEVICE_PATH_SUB_TYPE_MSG_USB_CLASS   0x0f
+#  define DEVICE_PATH_SUB_TYPE_MSG_NVME                0x17
 #  define DEVICE_PATH_SUB_TYPE_MSG_SD          0x1a
 #  define DEVICE_PATH_SUB_TYPE_MSG_MMC         0x1d
 
@@ -464,6 +465,12 @@ struct efi_device_path_sd_mmc_path {
        u8 slot_number;
 } __packed;
 
+struct efi_device_path_nvme {
+       struct efi_device_path dp;
+       u32 ns_id;
+       u8 eui64[8];
+} __packed;
+
 #define DEVICE_PATH_TYPE_MEDIA_DEVICE          0x04
 #  define DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH 0x01
 #  define DEVICE_PATH_SUB_TYPE_CDROM_PATH      0x02
index 53b369945b432ff0aa90c21f24ff0e3cb6642b52..381da80cdce0dbd7b74a037d23b76d986375f4a8 100644 (file)
 #include <part_efi.h>
 #include <efi_api.h>
 
+static inline int guidcmp(const void *g1, const void *g2)
+{
+       return memcmp(g1, g2, sizeof(efi_guid_t));
+}
+
 /* No need for efi loader support in SPL */
 #if CONFIG_IS_ENABLED(EFI_LOADER)
 
@@ -563,11 +568,6 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr,
        (((_dp)->type == DEVICE_PATH_TYPE_##_type) && \
         ((_dp)->sub_type == DEVICE_PATH_SUB_TYPE_##_subtype))
 
-static inline int guidcmp(const void *g1, const void *g2)
-{
-       return memcmp(g1, g2, sizeof(efi_guid_t));
-}
-
 /*
  * Use these to indicate that your code / data should go into the EFI runtime
  * section and thus still be available when the OS is running
index 635f53083b7bb85799ab96ae6c36b685879b5719..f1e58f9732dbf1de5957781b28bc8da97b92cfec 100644 (file)
@@ -416,23 +416,6 @@ fdt_addr_t fdtdec_get_addr(const void *blob, int node,
 fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
                const char *prop_name, fdt_size_t *sizep);
 
-/**
- * Look at an address property in a node and return the pci address which
- * corresponds to the given type in the form of fdt_pci_addr.
- * The property must hold one fdt_pci_addr with a lengh.
- *
- * @param blob         FDT blob
- * @param node         node to examine
- * @param type         pci address type (FDT_PCI_SPACE_xxx)
- * @param prop_name    name of property to find
- * @param addr         returns pci address in the form of fdt_pci_addr
- * @return 0 if ok, -ENOENT if the property did not exist, -EINVAL if the
- *             format of the property was invalid, -ENXIO if the requested
- *             address type was not found
- */
-int fdtdec_get_pci_addr(const void *blob, int node, enum fdt_pci_space type,
-               const char *prop_name, struct fdt_pci_addr *addr);
-
 /**
  * Look at the compatible property of a device node that represents a PCI
  * device and extract pci vendor id and device id from it.
index aacb0f5ebf2e6e8736fcfabce4d7f39ab5bf27de..75d19b1f6e511174cf48eac30b8d0dd8e6914c3d 100644 (file)
@@ -31,6 +31,19 @@ struct spl_handoff {
 void handoff_save_dram(struct spl_handoff *ho);
 void handoff_load_dram_size(struct spl_handoff *ho);
 void handoff_load_dram_banks(struct spl_handoff *ho);
+
+/**
+ * handoff_arch_save() - Save arch-specific info into the handoff area
+ *
+ * This is defined to an empty function by default, but arch-specific code can
+ * define it to write to spi_handoff->arch. It is called from
+ * write_spl_handoff().
+ *
+ * @ho: Handoff area to fill in
+ * @return 0 if OK, -ve on error
+ */
+int handoff_arch_save(struct spl_handoff *ho);
+
 #endif
 
 #endif
index 709b49d3936da1cc0858c1d42afd6736cf01e2f0..f9964a7664568615831b839d2f9e850c44b84ff8 100644 (file)
@@ -246,7 +246,13 @@ enum spi_nor_option_flags {
  */
 struct flash_info;
 
-/* TODO: Remove, once all users of spi_flash interface are moved to MTD */
+/*
+ * TODO: Remove, once all users of spi_flash interface are moved to MTD
+ *
+ * struct spi_flash {
+ *     Defined below (keep this text to enable searching for spi_flash decl)
+ * }
+ */
 #define spi_flash spi_nor
 
 /**
index 6d15e955d7e29b9c9fd99e3936032baf7ce644f4..d8f18a6afdf38b7fb7fe0782337880af61f699db 100644 (file)
@@ -76,6 +76,18 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
         int line, const char *func, const char *fmt, ...)
                __attribute__ ((format (__printf__, 6, 7)));
 
+static inline int _log_nop(enum log_category_t cat, enum log_level_t level,
+                          const char *file, int line, const char *func,
+                          const char *fmt, ...)
+               __attribute__ ((format (__printf__, 6, 7)));
+
+static inline int _log_nop(enum log_category_t cat, enum log_level_t level,
+                          const char *file, int line, const char *func,
+                          const char *fmt, ...)
+{
+       return 0;
+}
+
 /* Define this at the top of a file to add a prefix to debug messages */
 #ifndef pr_fmt
 #define pr_fmt(fmt) fmt
@@ -101,13 +113,14 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
 #define log_io(_fmt...)                log(LOG_CATEGORY, LOGL_DEBUG_IO, ##_fmt)
 #else
 #define _LOG_MAX_LEVEL LOGL_INFO
-#define log_err(_fmt...)
-#define log_warning(_fmt...)
-#define log_notice(_fmt...)
-#define log_info(_fmt...)
-#define log_debug(_fmt...)
-#define log_content(_fmt...)
-#define log_io(_fmt...)
+#define log_err(_fmt...)       log_nop(LOG_CATEGORY, LOGL_ERR, ##_fmt)
+#define log_warning(_fmt...)   log_nop(LOG_CATEGORY, LOGL_WARNING, ##_fmt)
+#define log_notice(_fmt...)    log_nop(LOG_CATEGORY, LOGL_NOTICE, ##_fmt)
+#define log_info(_fmt...)      log_nop(LOG_CATEGORY, LOGL_INFO, ##_fmt)
+#define log_debug(_fmt...)     log_nop(LOG_CATEGORY, LOGL_DEBUG, ##_fmt)
+#define log_content(_fmt...)   log_nop(LOG_CATEGORY, \
+                                       LOGL_DEBUG_CONTENT, ##_fmt)
+#define log_io(_fmt...)                log_nop(LOG_CATEGORY, LOGL_DEBUG_IO, ##_fmt)
 #endif
 
 #if CONFIG_IS_ENABLED(LOG)
@@ -129,6 +142,12 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
 #define log(_cat, _level, _fmt, _args...)
 #endif
 
+#define log_nop(_cat, _level, _fmt, _args...) ({ \
+       int _l = _level; \
+       _log_nop((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \
+                     __func__, pr_fmt(_fmt), ##_args); \
+})
+
 #ifdef DEBUG
 #define _DEBUG 1
 #else
index 22b89e4d6d19db7df724b1775d6aa386bc1c9818..701efeea855f3eb5d2564d85cf796bbb1279bfc0 100644 (file)
@@ -52,6 +52,7 @@
  * @reg_width:         IO accesses size of registers (in bytes)
  * @reg_shift:         Shift size of registers (0=byte, 1=16bit, 2=32bit...)
  * @clock:             UART base clock speed in Hz
+ * @bdf:               PCI slot/function (pci_dev_t)
  */
 struct ns16550_platdata {
        unsigned long base;
@@ -60,6 +61,9 @@ struct ns16550_platdata {
        int reg_offset;
        int clock;
        u32 fcr;
+#if defined(CONFIG_PCI) && defined(CONFIG_SPL)
+       int bdf;
+#endif
 };
 
 struct udevice;
index 2c3d14d2418aef2e2de1fda8b457aeaf33aa2fdd..2cdf8ce320c1d07e0d4b6938253dd4a0b3d38693 100644 (file)
@@ -78,4 +78,16 @@ int nvme_scan_namespace(void);
  */
 int nvme_print_info(struct udevice *udev);
 
+/**
+ * nvme_get_namespace_id - return namespace identifier
+ *
+ * This returns the namespace identifier.
+ *
+ * @udev:      NVMe controller device
+ * @ns_id:     Place where to put the name space identifier
+ * @eui64:     Place where to put the IEEE Extended Unique Identifier
+ * @return:    0 on success, -ve on error
+ */
+int nvme_get_namespace_id(struct udevice *udev, u32 *ns_id, u8 *eui64);
+
 #endif /* __NVME_H__ */
index 298d0d43559954c6d255b7476f98eadb3ed78437..8aa6636cfbf85441376dfc2c5ae2dbb1651cbc54 100644 (file)
 #define  PCI_BASE_ADDRESS_IO_MASK      (~0x03ULL)
 /* bit 1 is reserved if address_space = 1 */
 
+/* Convert a regsister address (e.g. PCI_BASE_ADDRESS_1) to a bar # (e.g. 1) */
+#define pci_offset_to_barnum(offset)   \
+               (((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32))
+
 /* Header type 0 (normal devices) */
 #define PCI_CARDBUS_CIS                0x28
 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c
@@ -1490,13 +1494,6 @@ int dm_pci_find_class(uint find_class, int index, struct udevice **devp);
  * struct dm_pci_emul_ops - PCI device emulator operations
  */
 struct dm_pci_emul_ops {
-       /**
-        * get_devfn(): Check which device and function this emulators
-        *
-        * @dev:        device to check
-        * @return the device and function this emulates, or -ve on error
-        */
-       int (*get_devfn)(struct udevice *dev);
        /**
         * read_config() - Read a PCI configuration value
         *
@@ -1598,7 +1595,7 @@ int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
 /**
  * pci_get_devfn() - Extract the devfn from fdt_pci_addr of the device
  *
- * Get devfn from fdt_pci_addr of the specifified device
+ * Get devfn from fdt_pci_addr of the specified device
  *
  * @dev:       PCI device
  * @return devfn in bits 15...8 if found, -ENODEV if not found
index f9c674d33bfd77240ed8005317cc6818b6188af1..4359636d87bc219465208a33b4ac2062f4be92ff 100644 (file)
@@ -49,6 +49,72 @@ static inline bool u_boot_first_phase(void)
        return false;
 }
 
+enum u_boot_phase {
+       PHASE_TPL,      /* Running in TPL */
+       PHASE_SPL,      /* Running in SPL */
+       PHASE_BOARD_F,  /* Running in U-Boot before relocation */
+       PHASE_BOARD_R,  /* Running in U-Boot after relocation */
+};
+
+/**
+ * spl_phase() - Find out the phase of U-Boot
+ *
+ * This can be used to avoid #ifdef logic and use if() instead.
+ *
+ * For example, to include code only in TPL, you might do:
+ *
+ *    #ifdef CONFIG_TPL_BUILD
+ *    ...
+ *    #endif
+ *
+ * but with this you can use:
+ *
+ *    if (spl_phase() == PHASE_TPL) {
+ *       ...
+ *    }
+ *
+ * To include code only in SPL, you might do:
+ *
+ *    #if defined(CONFIG_SPL_BUILD) && !defined(CONFIG_TPL_BUILD)
+ *    ...
+ *    #endif
+ *
+ * but with this you can use:
+ *
+ *    if (spl_phase() == PHASE_SPL) {
+ *       ...
+ *    }
+ *
+ * To include code only in U-Boot proper, you might do:
+ *
+ *    #ifndef CONFIG_SPL_BUILD
+ *    ...
+ *    #endif
+ *
+ * but with this you can use:
+ *
+ *    if (spl_phase() == PHASE_BOARD_F) {
+ *       ...
+ *    }
+ *
+ * @return U-Boot phase
+ */
+static inline enum u_boot_phase spl_phase(void)
+{
+#ifdef CONFIG_TPL_BUILD
+       return PHASE_TPL;
+#elif CONFIG_SPL_BUILD
+       return PHASE_SPL;
+#else
+       DECLARE_GLOBAL_DATA_PTR;
+
+       if (!(gd->flags & GD_FLG_RELOC))
+               return PHASE_BOARD_F;
+       else
+               return PHASE_BOARD_R;
+#endif
+}
+
 /* A string name for SPL or TPL */
 #ifdef CONFIG_SPL_BUILD
 # ifdef CONFIG_TPL_BUILD
index 3da45a5ec3221f8ea80091e06d21a9723b45cf02..135f0b372b074270beab7450a542edeeef94124f 100644 (file)
@@ -40,12 +40,12 @@ config PRINTF
 config SPL_PRINTF
        bool
        select SPL_SPRINTF
-       select SPL_STRTO if !USE_TINY_PRINTF
+       select SPL_STRTO if !SPL_USE_TINY_PRINTF
 
 config TPL_PRINTF
        bool
        select TPL_SPRINTF
-       select TPL_STRTO if !USE_TINY_PRINTF
+       select TPL_STRTO if !TPL_USE_TINY_PRINTF
 
 config SPRINTF
        bool
@@ -95,9 +95,9 @@ config SYS_HZ
          get_timer() must operate in milliseconds and this option must be
          set to 1000.
 
-config USE_TINY_PRINTF
+config SPL_USE_TINY_PRINTF
        bool "Enable tiny printf() version"
-       depends on SPL || TPL
+       depends on SPL
        default y
        help
          This option enables a tiny, stripped down printf version.
@@ -107,6 +107,18 @@ config USE_TINY_PRINTF
 
          The supported format specifiers are %c, %s, %u/%d and %x.
 
+config TPL_USE_TINY_PRINTF
+       bool "Enable tiny printf() version"
+       depends on TPL
+       default y if SPL_USE_TINY_PRINTF
+       help
+         This option enables a tiny, stripped down printf version.
+         This should only be used in space limited environments,
+         like SPL versions with hard memory limits. This version
+         reduces the code size by about 2.5KiB on armv7.
+
+         The supported format specifiers are %c, %s, %u/%d and %x.
+
 config PANIC_HANG
        bool "Do not reset the system on fatal error"
        help
index 2fffd68f943cb9e207bc4218e26cc98b5b039e96..d248d8626cea1ee2951c9380bd749b0754fe8418 100644 (file)
@@ -106,7 +106,7 @@ obj-y += panic.o
 
 ifeq ($(CONFIG_$(SPL_TPL_)BUILD),y)
 # SPL U-Boot may use full-printf, tiny-printf or none at all
-ifdef CONFIG_USE_TINY_PRINTF
+ifdef CONFIG_$(SPL_TPL_)USE_TINY_PRINTF
 obj-$(CONFIG_$(SPL_TPL_)SPRINTF) += tiny-printf.o
 else
 obj-$(CONFIG_$(SPL_TPL_)SPRINTF) += vsprintf.o strmhz.o
index cff20f375523be26aceaa88801eaa3723cc21b60..ec16d75e0e4cf2a2444d4a3f1e0836f18c937445 100644 (file)
@@ -205,8 +205,10 @@ int print_buffer(ulong addr, const void *data, uint width, uint count,
                addr += thislinelen * width;
                count -= thislinelen;
 
+#ifndef CONFIG_SPL_BUILD
                if (ctrlc())
                        return -1;
+#endif
        }
 
        return 0;
index 86297bb7c1166537a9625adbbfd32355013fc575..897fc1b2e8ac38f57819ab8157da4b968ea7428e 100644 (file)
@@ -10,6 +10,7 @@
 #include <dm.h>
 #include <usb.h>
 #include <mmc.h>
+#include <nvme.h>
 #include <efi_loader.h>
 #include <part.h>
 #include <sandboxblockdev.h>
@@ -451,6 +452,11 @@ static unsigned dp_size(struct udevice *dev)
                        return dp_size(dev->parent) +
                                sizeof(struct efi_device_path_sd_mmc_path);
 #endif
+#if defined(CONFIG_NVME)
+               case UCLASS_NVME:
+                       return dp_size(dev->parent) +
+                               sizeof(struct efi_device_path_nvme);
+#endif
 #ifdef CONFIG_SANDBOX
                case UCLASS_ROOT:
                         /*
@@ -583,6 +589,20 @@ static void *dp_fill(void *buf, struct udevice *dev)
                        sddp->slot_number = dev->seq;
                        return &sddp[1];
                        }
+#endif
+#if defined(CONFIG_NVME)
+               case UCLASS_NVME: {
+                       struct efi_device_path_nvme *dp =
+                               dp_fill(buf, dev->parent);
+                       u32 ns_id;
+
+                       dp->dp.type     = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+                       dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_NVME;
+                       dp->dp.length   = sizeof(*dp);
+                       nvme_get_namespace_id(dev, &ns_id, dp->eui64);
+                       memcpy(&dp->ns_id, &ns_id, sizeof(ns_id));
+                       return &dp[1];
+                       }
 #endif
                default:
                        debug("%s(%u) %s: unhandled parent class: %s (%u)\n",
index 0f3796b373bb8707902d90d13b9d75d75fcbaa3c..af1adbb71efebf40a2264ff3cf5e66087164a934 100644 (file)
@@ -148,6 +148,21 @@ static char *dp_msging(char *s, struct efi_device_path *dp)
 
                break;
        }
+       case DEVICE_PATH_SUB_TYPE_MSG_NVME: {
+               struct efi_device_path_nvme *ndp =
+                       (struct efi_device_path_nvme *)dp;
+               u32 ns_id;
+               int i;
+
+               memcpy(&ns_id, &ndp->ns_id, sizeof(ns_id));
+               s += sprintf(s, "NVMe(0x%x,", ns_id);
+               for (i = 0; i < sizeof(ndp->eui64); ++i)
+                       s += sprintf(s, "%s%02x", i ? "-" : "",
+                                    ndp->eui64[i]);
+               s += sprintf(s, ")");
+
+               break;
+       }
        case DEVICE_PATH_SUB_TYPE_MSG_SD:
        case DEVICE_PATH_SUB_TYPE_MSG_MMC: {
                const char *typename =
index 4c554c546b21ca80dd6c6e8cf14f03f2f552905f..d0daf7bdebbf25536350a6bbf46aa160a5800567 100644 (file)
@@ -478,10 +478,12 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
                        old_size = 0;
                }
        } else {
-               if ((data_size == 0 &&
-                    !(attributes & EFI_VARIABLE_APPEND_WRITE)) ||
-                   !attributes) {
-                       /* delete, but nothing to do */
+               if (data_size == 0 || !attributes ||
+                   (attributes & EFI_VARIABLE_APPEND_WRITE)) {
+                       /*
+                        * Trying to delete or to update a non-existent
+                        * variable.
+                        */
                        ret = EFI_NOT_FOUND;
                        goto out;
                }
index a6b41d1f008d84bf5f591af335b15abe7364e20f..5d98c029b8690daf29e4225d712368171969cf62 100644 (file)
@@ -21,9 +21,6 @@ static const efi_guid_t guid_vendor0 =
 static const efi_guid_t guid_vendor1 =
        EFI_GUID(0xff629290, 0x1fc1, 0xd73f,
                 0x8f, 0xb1, 0x32, 0xf9, 0x0c, 0xa0, 0x42, 0xea);
-static const efi_guid_t guid_global =
-       EFI_GUID(0x8be4df61, 0x93ca, 0x11d2,
-                0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c);
 
 /*
  * Setup unit test.
@@ -120,35 +117,29 @@ static int execute(void)
                                    7, v + 8);
        if (ret != EFI_SUCCESS) {
                efi_st_error("SetVariable(APPEND_WRITE) failed\n");
-       } else {
-               len = EFI_ST_MAX_DATA_SIZE;
-               ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
-                                           &attr, &len, data);
-               if (ret != EFI_SUCCESS) {
-                       efi_st_error("GetVariable failed\n");
-                       return EFI_ST_FAILURE;
-               }
-               if (len != 15)
-                       efi_st_todo("GetVariable returned wrong length %u\n",
-                                   (unsigned int)len);
-               if (memcmp(data, v, len))
-                       efi_st_todo("GetVariable returned wrong value\n");
+               return EFI_ST_FAILURE;
        }
+       len = EFI_ST_MAX_DATA_SIZE;
+       ret = runtime->get_variable(L"efi_st_var1", &guid_vendor1,
+                                   &attr, &len, data);
+       if (ret != EFI_SUCCESS) {
+               efi_st_error("GetVariable failed\n");
+               return EFI_ST_FAILURE;
+       }
+       if (len != 15)
+               efi_st_todo("GetVariable returned wrong length %u\n",
+                           (unsigned int)len);
+       if (memcmp(data, v, len))
+               efi_st_todo("GetVariable returned wrong value\n");
        /* Append variable 2 */
        ret = runtime->set_variable(L"efi_none", &guid_vendor1,
                                    EFI_VARIABLE_BOOTSERVICE_ACCESS |
                                    EFI_VARIABLE_APPEND_WRITE,
                                    15, v);
-       if (ret != EFI_NOT_FOUND)
+       if (ret != EFI_NOT_FOUND) {
                efi_st_error("SetVariable(APPEND_WRITE) with size 0 to non-existent variable returns wrong code\n");
-       /* Append variable 3 */
-       ret = runtime->set_variable(L"PlatformLangCodes", &guid_global,
-                                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                                   EFI_VARIABLE_RUNTIME_ACCESS |
-                                   EFI_VARIABLE_APPEND_WRITE,
-                                   15, v);
-       if (ret != EFI_WRITE_PROTECTED)
-               efi_st_todo("SetVariable(APPEND_WRITE) to read-only variable returns wrong code\n");
+               return EFI_ST_FAILURE;
+       }
        /* Enumerate variables */
        boottime->set_mem(&guid, 16, 0);
        *varname = 0;
index 74430c8b2ffd219fd0b1099177f4ca4503eecaac..17736ce66554e0ce56e317480e7b12b64e73b399 100644 (file)
@@ -186,60 +186,6 @@ fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name)
 }
 
 #if CONFIG_IS_ENABLED(PCI) && defined(CONFIG_DM_PCI)
-int fdtdec_get_pci_addr(const void *blob, int node, enum fdt_pci_space type,
-                       const char *prop_name, struct fdt_pci_addr *addr)
-{
-       const u32 *cell;
-       int len;
-       int ret = -ENOENT;
-
-       debug("%s: %s: ", __func__, prop_name);
-
-       /*
-        * If we follow the pci bus bindings strictly, we should check
-        * the value of the node's parent node's #address-cells and
-        * #size-cells. They need to be 3 and 2 accordingly. However,
-        * for simplicity we skip the check here.
-        */
-       cell = fdt_getprop(blob, node, prop_name, &len);
-       if (!cell)
-               goto fail;
-
-       if ((len % FDT_PCI_REG_SIZE) == 0) {
-               int num = len / FDT_PCI_REG_SIZE;
-               int i;
-
-               for (i = 0; i < num; i++) {
-                       debug("pci address #%d: %08lx %08lx %08lx\n", i,
-                             (ulong)fdt32_to_cpu(cell[0]),
-                             (ulong)fdt32_to_cpu(cell[1]),
-                             (ulong)fdt32_to_cpu(cell[2]));
-                       if ((fdt32_to_cpu(*cell) & type) == type) {
-                               addr->phys_hi = fdt32_to_cpu(cell[0]);
-                               addr->phys_mid = fdt32_to_cpu(cell[1]);
-                               addr->phys_lo = fdt32_to_cpu(cell[2]);
-                               break;
-                       }
-
-                       cell += (FDT_PCI_ADDR_CELLS +
-                                FDT_PCI_SIZE_CELLS);
-               }
-
-               if (i == num) {
-                       ret = -ENXIO;
-                       goto fail;
-               }
-
-               return 0;
-       }
-
-       ret = -EINVAL;
-
-fail:
-       debug("(not found)\n");
-       return ret;
-}
-
 int fdtdec_get_pci_vendev(const void *blob, int node, u16 *vendor, u16 *device)
 {
        const char *list, *end;
index c7b02451e0cb648b317cda8dfb196d303a307d4a..19209544a4aa7956dd8a5ed9429d18134cad2968 100644 (file)
@@ -1720,7 +1720,6 @@ CONFIG_SPL_NAND_MINIMAL
 CONFIG_SPL_NAND_RAW_ONLY
 CONFIG_SPL_NAND_SOFTECC
 CONFIG_SPL_NAND_WORKSPACE
-CONFIG_SPL_NO_CPU_SUPPORT_CODE
 CONFIG_SPL_PAD_TO
 CONFIG_SPL_PANIC_ON_RAW_IMAGE
 CONFIG_SPL_PBL_PAD
@@ -4339,7 +4338,6 @@ CONFIG_WATCHDOG_NOWAYOUT
 CONFIG_WATCHDOG_PRESC
 CONFIG_WATCHDOG_RC
 CONFIG_WATCHDOG_TIMEOUT
-CONFIG_WATCHDOG_TIMEOUT_MSECS
 CONFIG_WD_PERIOD
 CONFIG_X600
 CONFIG_X86EMU_DEBUG
index edd55b05d6e293ebe9856b0e7da3d894a778fe79..f74c4308439e6ba08d2d615bc5f5f38ffde1e560 100644 (file)
@@ -749,8 +749,7 @@ static int dm_test_uclass_devices_find(struct unit_test_state *uts)
                ut_assert(dev);
        }
 
-       ret = uclass_find_first_device(UCLASS_TEST_DUMMY, &dev);
-       ut_assert(ret == -ENODEV);
+       ut_assertok(uclass_find_first_device(UCLASS_TEST_DUMMY, &dev));
        ut_assert(!dev);
 
        return 0;
index c325f6600e70861002578b3862842a7b5015ba1a..fb93e4c78ae07077dc8473552c5ba39e16bcf3d4 100644 (file)
@@ -38,7 +38,7 @@ static int dm_test_pci_busdev(struct unit_test_state *uts)
        ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap));
        device = 0;
        ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
-       ut_asserteq(SANDBOX_PCI_DEVICE_ID, device);
+       ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
 
        /* Test bus#1 and its devices */
        ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus));
@@ -50,7 +50,7 @@ static int dm_test_pci_busdev(struct unit_test_state *uts)
        ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x0c, 0), &swap));
        device = 0;
        ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
-       ut_asserteq(SANDBOX_PCI_DEVICE_ID, device);
+       ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
 
        return 0;
 }
@@ -170,7 +170,7 @@ static int dm_test_pci_mixed(struct unit_test_state *uts)
        ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(2, 0x1f, 0), &swap));
        device = 0;
        ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
-       ut_asserteq(SANDBOX_PCI_DEVICE_ID, device);
+       ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
 
        /* First test I/O */
        io_addr = dm_pci_read_bar32(swap, 0);
@@ -294,3 +294,48 @@ static int dm_test_pci_ea(struct unit_test_state *uts)
        return 0;
 }
 DM_TEST(dm_test_pci_ea, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test the dev_read_addr_pci() function */
+static int dm_test_pci_addr_flat(struct unit_test_state *uts)
+{
+       struct udevice *swap1f, *swap1;
+       ulong io_addr, mem_addr;
+
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
+       io_addr = dm_pci_read_bar32(swap1f, 0);
+       ut_asserteq(io_addr, dev_read_addr_pci(swap1f));
+
+       /*
+        * This device has both I/O and MEM spaces but the MEM space appears
+        * first
+        */
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
+       mem_addr = dm_pci_read_bar32(swap1, 1);
+       ut_asserteq(mem_addr, dev_read_addr_pci(swap1));
+
+       return 0;
+}
+DM_TEST(dm_test_pci_addr_flat, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT |
+               DM_TESTF_FLAT_TREE);
+
+/*
+ * Test the dev_read_addr_pci() function with livetree. That function is
+ * not currently fully implemented, in that it fails to return the BAR address.
+ * Once that is implemented this test can be removed and dm_test_pci_addr_flat()
+ * can be used for both flattree and livetree by removing the DM_TESTF_FLAT_TREE
+ * flag above.
+ */
+static int dm_test_pci_addr_live(struct unit_test_state *uts)
+{
+       struct udevice *swap1f, *swap1;
+
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
+       ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f));
+
+       ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
+       ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1));
+
+       return 0;
+}
+DM_TEST(dm_test_pci_addr_live, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT |
+               DM_TESTF_LIVE_TREE);
index 5d79ce641d7b6c4198f08f9d1f78b327ea80e341..72648162a917a4be80d8d76b547683ab5ed31e81 100644 (file)
@@ -64,7 +64,7 @@ static int dm_test_destroy(struct unit_test_state *uts)
 
                /*
                 * If the uclass doesn't exist we don't want to create it. So
-                * check that here before we call uclass_find_device()/
+                * check that here before we call uclass_find_device().
                 */
                uc = uclass_find(id);
                if (!uc)
@@ -130,7 +130,7 @@ static int dm_test_main(const char *test_name)
        const int n_ents = ll_entry_count(struct unit_test, dm_test);
        struct unit_test_state *uts = &global_dm_test_state;
        struct unit_test *test;
-       int run_count;
+       int found;
 
        uts->priv = &_global_priv_dm_test_state;
        uts->fail_count = 0;
@@ -148,7 +148,7 @@ static int dm_test_main(const char *test_name)
        if (!test_name)
                printf("Running %d driver model tests\n", n_ents);
 
-       run_count = 0;
+       found = 0;
 #ifdef CONFIG_OF_LIVE
        uts->of_root = gd->of_root;
 #endif
@@ -180,16 +180,20 @@ static int dm_test_main(const char *test_name)
                        ut_assertok(dm_do_test(uts, test, false));
                        runs++;
                }
-               run_count += runs;
+               found++;
        }
 
-       if (test_name && !run_count)
+       if (test_name && !found)
                printf("Test '%s' not found\n", test_name);
        else
                printf("Failures: %d\n", uts->fail_count);
 
+       /* Put everything back to normal so that sandbox works as expected */
+#ifdef CONFIG_OF_LIVE
+       gd->of_root = uts->of_root;
+#endif
        gd->dm_root = NULL;
-       ut_assertok(dm_init(false));
+       ut_assertok(dm_init(IS_ENABLED(CONFIG_OF_LIVE)));
        dm_scan_platdata(false);
        dm_scan_fdt(gd->fdt_blob, false);
 
index b4f6392ab7455d8409d672bd42df0484b237cdd4..8e0f0a8c55b7d54a48f50aa0e712125dfa3e14ae 100644 (file)
@@ -934,6 +934,12 @@ BINMAN_DEBUG=1 to your build:
    make sandbox_defconfig
    make BINMAN_DEBUG=1
 
+To enable verbose logging from binman, base BINMAN_VERBOSE to your build, which
+adds a -v<level> option to the call to binman:
+
+   make sandbox_defconfig
+   make BINMAN_VERBOSE=5
+
 
 History / Credits
 -----------------
index 9e7587864ce0d1a8b30f3a50adeac0232fc3cfc6..cb51bc2dd4663683d9df1915aab2cb263a09c808 100644 (file)
@@ -468,29 +468,23 @@ def Binman(args):
         command.Run(pager, fname)
         return 0
 
-    if args.cmd == 'ls':
+    if args.cmd in ['ls', 'extract', 'replace']:
         try:
+            tout.Init(args.verbosity)
             tools.PrepareOutputDir(None)
-            ListEntries(args.image, args.paths)
-        finally:
-            tools.FinaliseOutputDir()
-        return 0
-
-    if args.cmd == 'extract':
-        try:
-            tools.PrepareOutputDir(None)
-            ExtractEntries(args.image, args.filename, args.outdir, args.paths,
-                           not args.uncompressed)
-        finally:
-            tools.FinaliseOutputDir()
-        return 0
-
-    if args.cmd == 'replace':
-        try:
-            tools.PrepareOutputDir(None)
-            ReplaceEntries(args.image, args.filename, args.indir, args.paths,
-                           do_compress=not args.compressed,
-                           allow_resize=not args.fix_size, write_map=args.map)
+            if args.cmd == 'ls':
+                ListEntries(args.image, args.paths)
+
+            if args.cmd == 'extract':
+                ExtractEntries(args.image, args.filename, args.outdir, args.paths,
+                               not args.uncompressed)
+
+            if args.cmd == 'replace':
+                ReplaceEntries(args.image, args.filename, args.indir, args.paths,
+                               do_compress=not args.compressed,
+                               allow_resize=not args.fix_size, write_map=args.map)
+        except:
+            raise
         finally:
             tools.FinaliseOutputDir()
         return 0
index 6a2c6e0d92e5b0cb0fff2dcce436490f4e2cbc9f..fe8e1dd8a579c09db40e3bcb53b27403add97d5f 100644 (file)
@@ -714,9 +714,27 @@ features to produce new behaviours.
         """
         # Use True here so that we get an uncompressed section to work from,
         # although compressed sections are currently not supported
+        tout.Debug("ReadChildData section '%s', entry '%s'" %
+                   (self.section.GetPath(), self.GetPath()))
         data = self.section.ReadChildData(self, decomp)
         return data
 
+    def ReadChildData(self, child, decomp=True):
+        """Read the data for a particular child entry
+
+        This reads data from the parent and extracts the piece that relates to
+        the given child.
+
+        Args:
+            child: Child entry to read data for (must be valid)
+            decomp: True to decompress any compressed data before returning it;
+                False to return the raw, uncompressed data
+
+        Returns:
+            Data for the child (bytes)
+        """
+        pass
+
     def LoadData(self, decomp=True):
         data = self.ReadData(decomp)
         self.contents_size = len(data)
index 4b69eda1a22bbf8c9d622bcfafc49b1d9c8581de..b9327dd799b7513cd914e2d50b06127ab7b23aa2 100644 (file)
@@ -100,6 +100,7 @@ class Entry_image_header(Entry):
                     offset = offset
                 else:
                     offset = image_size - IMAGE_HEADER_LEN
+        offset += self.section.GetStartOffset()
         return Entry.Pack(self, offset)
 
     def ProcessContents(self):
index 5d34fc546afd7ea9140e530b2c4a25564a7b9285..8179daf5628da928346c853e375617c88ed0db5f 100644 (file)
@@ -500,18 +500,12 @@ class Entry_section(Entry):
         return data
 
     def ReadChildData(self, child, decomp=True):
-        """Read the data for a particular child entry
-
-        Args:
-            child: Child entry to read data for
-            decomp: True to return uncompressed data, False to leave the data
-                compressed if it is compressed
-
-        Returns:
-            Data contents of entry
-        """
+        tout.Debug("ReadChildData for child '%s'" % child.GetPath())
         parent_data = self.ReadData(True)
-        data = parent_data[child.offset:child.offset + child.size]
+        offset = child.offset - self._skip_at_start
+        tout.Debug("Extract for child '%s': offset %#x, skip_at_start %#x, result %#x" %
+                   (child.GetPath(), child.offset, self._skip_at_start, offset))
+        data = parent_data[offset:offset + child.size]
         if decomp:
             indata = data
             data = tools.Decompress(indata, child.compress)
index 7b39a1ddcecc47396d313cf58519ffd227a07684..2beab7fd4d2d50ebdf9b2627005a1944cea78631 100644 (file)
@@ -201,6 +201,8 @@ class Image(section.Entry_section):
         return entry
 
     def ReadData(self, decomp=True):
+        tout.Debug("Image '%s' ReadData(), size=%#x" %
+                   (self.GetPath(), len(self._data)))
         return self._data
 
     def GetListEntries(self, entry_paths):
index 119d02cbb2b64a69c0c06cc1556d318acaefb2b2..f90b8ea7f5da467fe77f3aa811fa2642ce580617 100644 (file)
@@ -175,6 +175,7 @@ class TestFunctional(unittest.TestCase):
     """
     def setUp(self):
         self._base_dir = tempfile.mkdtemp()
+        self._output_dir = tempfile.mkdtemp()
         self._git_dir = os.path.join(self._base_dir, 'src')
         self._buildman_pathname = sys.argv[0]
         self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
@@ -207,6 +208,7 @@ class TestFunctional(unittest.TestCase):
 
     def tearDown(self):
         shutil.rmtree(self._base_dir)
+        shutil.rmtree(self._output_dir)
 
     def setupToolchains(self):
         self._toolchains = toolchain.Toolchains()
@@ -421,7 +423,7 @@ class TestFunctional(unittest.TestCase):
     def testCurrentSource(self):
         """Very simple test to invoke buildman on the current source"""
         self.setupToolchains();
-        self._RunControl()
+        self._RunControl('-o', self._output_dir)
         lines = terminal.GetPrintTestLines()
         self.assertIn('Building current source for %d boards' % len(boards),
                       lines[0].text)
@@ -434,7 +436,7 @@ class TestFunctional(unittest.TestCase):
     def testBadToolchain(self):
         """Test that missing toolchains are detected"""
         self.setupToolchains();
-        ret_code = self._RunControl('-b', TEST_BRANCH)
+        ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
         lines = terminal.GetPrintTestLines()
 
         # Buildman always builds the upstream commit as well
@@ -458,13 +460,13 @@ class TestFunctional(unittest.TestCase):
 
     def testBranch(self):
         """Test building a branch with all toolchains present"""
-        self._RunControl('-b', TEST_BRANCH)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
         self.assertEqual(self._builder.count, self._total_builds)
         self.assertEqual(self._builder.fail, 0)
 
     def testCount(self):
         """Test building a specific number of commitst"""
-        self._RunControl('-b', TEST_BRANCH, '-c2')
+        self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
         self.assertEqual(self._builder.count, 2 * len(boards))
         self.assertEqual(self._builder.fail, 0)
         # Each board has a mrproper, config, and then one make per commit
@@ -472,34 +474,34 @@ class TestFunctional(unittest.TestCase):
 
     def testIncremental(self):
         """Test building a branch twice - the second time should do nothing"""
-        self._RunControl('-b', TEST_BRANCH)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
 
         # Each board has a mrproper, config, and then one make per commit
         self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
         self._make_calls = 0
-        self._RunControl('-b', TEST_BRANCH, clean_dir=False)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
         self.assertEqual(self._make_calls, 0)
         self.assertEqual(self._builder.count, self._total_builds)
         self.assertEqual(self._builder.fail, 0)
 
     def testForceBuild(self):
         """The -f flag should force a rebuild"""
-        self._RunControl('-b', TEST_BRANCH)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
         self._make_calls = 0
-        self._RunControl('-b', TEST_BRANCH, '-f', clean_dir=False)
+        self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
         # Each board has a mrproper, config, and then one make per commit
         self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
 
     def testForceReconfigure(self):
         """The -f flag should force a rebuild"""
-        self._RunControl('-b', TEST_BRANCH, '-C')
+        self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
         # Each commit has a mrproper, config and make
         self.assertEqual(self._make_calls, len(boards) * self._commits * 3)
 
     def testErrors(self):
         """Test handling of build errors"""
         self._error['board2', 1] = 'fred\n'
-        self._RunControl('-b', TEST_BRANCH)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
         self.assertEqual(self._builder.count, self._total_builds)
         self.assertEqual(self._builder.fail, 1)
 
@@ -507,13 +509,13 @@ class TestFunctional(unittest.TestCase):
         # not be rebuilt
         del self._error['board2', 1]
         self._make_calls = 0
-        self._RunControl('-b', TEST_BRANCH, clean_dir=False)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
         self.assertEqual(self._builder.count, self._total_builds)
         self.assertEqual(self._make_calls, 0)
         self.assertEqual(self._builder.fail, 1)
 
         # Now use the -F flag to force rebuild of the bad commit
-        self._RunControl('-b', TEST_BRANCH, '-F', clean_dir=False)
+        self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
         self.assertEqual(self._builder.count, self._total_builds)
         self.assertEqual(self._builder.fail, 0)
         self.assertEqual(self._make_calls, 3)
index d68af056b6bd4948e7d239bf1a946a9875a95d3a..3908985c7b294a6db9d48cbc736382e0e1b188d3 100644 (file)
+# Copyright (c) 2011-2019, Ulf Magnusson
 # SPDX-License-Identifier: ISC
-#
-# Author: Ulf Magnusson
-#   https://github.com/ulfalizer/Kconfiglib
-
-# This is Kconfiglib, a Python library for scripting, debugging, and extracting
-# information from Kconfig-based configuration systems. To view the
-# documentation, run
-#
-#  $ pydoc kconfiglib
-#
-# or, if you prefer HTML,
-#
-#  $ pydoc -w kconfiglib
-#
-# The examples/ subdirectory contains examples, to be run with e.g.
-#
-#  $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
-#
-# Look in testsuite.py for the test suite.
 
 """
-Kconfiglib is a Python library for scripting and extracting information from
-Kconfig-based configuration systems. Features include the following:
+Overview
+========
 
- - Symbol values and properties can be looked up and values assigned
-   programmatically.
- - .config files can be read and written.
- - Expressions can be evaluated in the context of a Kconfig configuration.
- - Relations between symbols can be quickly determined, such as finding all
-   symbols that reference a particular symbol.
- - Highly compatible with the scripts/kconfig/*conf utilities. The test suite
-   automatically compares outputs between Kconfiglib and the C implementation
-   for a large number of cases.
+Kconfiglib is a Python 2/3 library for scripting and extracting information
+from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
+configuration systems.
 
-For the Linux kernel, scripts are run using
+See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer
+overview.
 
- $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>]
+Since Kconfiglib 12.0.0, the library version is available in
+kconfiglib.VERSION, which is a (<major>, <minor>, <patch>) tuple, e.g.
+(12, 0, 0).
 
-Using the 'scriptconfig' target ensures that required environment variables
-(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly.
 
-Scripts receive the name of the Kconfig file to load in sys.argv[1]. As of
-Linux 4.1.0-rc5, this is always "Kconfig" from the kernel top-level directory.
-If an argument is provided with SCRIPT_ARG, it appears as sys.argv[2].
+Using Kconfiglib on the Linux kernel with the Makefile targets
+==============================================================
 
-To get an interactive Python prompt with Kconfiglib preloaded and a Config
-object 'c' created, run
+For the Linux kernel, a handy interface is provided by the
+scripts/kconfig/Makefile patch, which can be applied with either 'git am' or
+the 'patch' utility:
 
- $ make iscriptconfig [ARCH=<arch>]
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1
 
-Kconfiglib supports both Python 2 and Python 3. For (i)scriptconfig, the Python
-interpreter to use can be passed in PYTHONCMD, which defaults to 'python'. PyPy
-works well too, and might give a nice speedup for long-running jobs.
+Warning: Not passing -p1 to patch will cause the wrong file to be patched.
 
-The examples/ directory contains short example scripts, which can be run with
-e.g.
+Please tell me if the patch does not apply. It should be trivial to apply
+manually, as it's just a block of text that needs to be inserted near the other
+*conf: targets in scripts/kconfig/Makefile.
 
- $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
+Look further down for a motivation for the Makefile patch and for instructions
+on how you can use Kconfiglib without it.
 
-or
+If you do not wish to install Kconfiglib via pip, the Makefile patch is set up
+so that you can also just clone Kconfiglib into the kernel root:
 
- $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel
+  $ git clone git://github.com/ulfalizer/Kconfiglib.git
+  $ git am Kconfiglib/makefile.patch  (or 'patch -p1 < Kconfiglib/makefile.patch')
 
-testsuite.py contains the test suite. See the top of the script for how to run
-it.
+Warning: The directory name Kconfiglib/ is significant in this case, because
+it's added to PYTHONPATH by the new targets in makefile.patch.
 
-Credits: Written by Ulf "Ulfalizer" Magnusson
+The targets added by the Makefile patch are described in the following
+sections.
 
-Send bug reports, suggestions and other feedback to ulfalizer a.t Google's
-email service. Don't wrestle with internal APIs. Tell me what you need and I
-might add it in a safe way as a client API instead."""
 
-import os
-import platform
-import re
-import sys
+make kmenuconfig
+----------------
 
-# File layout:
-#
-# Public classes
-# Public functions
-# Internal classes
-# Internal functions
-# Internal global constants
+This target runs the curses menuconfig interface with Python 3. As of
+Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only
+Python 3 was supported, so this was a backport).
 
-# Line length: 79 columns
 
-#
-# Public classes
-#
+make guiconfig
+--------------
 
-class Config(object):
+This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3
+are supported. To change the Python interpreter used, pass
+PYTHONCMD=<executable> to 'make'. The default is 'python'.
 
-    """Represents a Kconfig configuration, e.g. for i386 or ARM. This is the
-    set of symbols and other items appearing in the configuration together with
-    their values. Creating any number of Config objects -- including for
-    different architectures -- is safe; Kconfiglib has no global state."""
 
-    #
-    # Public interface
-    #
+make [ARCH=<arch>] iscriptconfig
+--------------------------------
 
-    def __init__(self, filename="Kconfig", base_dir=None, print_warnings=True,
-                 print_undef_assign=False):
-        """Creates a new Config object, representing a Kconfig configuration.
-        Raises Kconfig_Syntax_Error on syntax errors.
-
-        filename (default: "Kconfig"): The base Kconfig file of the
-           configuration. For the Linux kernel, you'll probably want "Kconfig"
-           from the top-level directory, as environment variables will make
-           sure the right Kconfig is included from there
-           (arch/<architecture>/Kconfig). If you are using Kconfiglib via 'make
-           scriptconfig', the filename of the base base Kconfig file will be in
-           sys.argv[1].
-
-        base_dir (default: None): The base directory relative to which 'source'
-           statements within Kconfig files will work. For the Linux kernel this
-           should be the top-level directory of the kernel tree. $-references
-           to existing environment variables will be expanded.
-
-           If None (the default), the environment variable 'srctree' will be
-           used if set, and the current directory otherwise. 'srctree' is set
-           by the Linux makefiles to the top-level kernel directory. A default
-           of "." would not work with an alternative build directory.
-
-        print_warnings (default: True): Set to True if warnings related to this
-           configuration should be printed to stderr. This can be changed later
-           with Config.set_print_warnings(). It is provided as a constructor
-           argument since warnings might be generated during parsing.
-
-        print_undef_assign (default: False): Set to True if informational
-           messages related to assignments to undefined symbols should be
-           printed to stderr for this configuration. Can be changed later with
-           Config.set_print_undef_assign()."""
-
-        # The set of all symbols, indexed by name (a string)
-        self.syms = {}
-        # Python 2/3 compatibility hack. This is the only one needed.
-        self.syms_iter = self.syms.values if sys.version_info[0] >= 3 else \
-                         self.syms.itervalues
+This target gives an interactive Python prompt where a Kconfig instance has
+been preloaded and is available in 'kconf'. To change the Python interpreter
+used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
 
-        # The set of all defined symbols in the configuration in the order they
-        # appear in the Kconfig files. This excludes the special symbols n, m,
-        # and y as well as symbols that are referenced but never defined.
-        self.kconfig_syms = []
+To get a feel for the API, try evaluating and printing the symbols in
+kconf.defined_syms, and explore the MenuNode menu tree starting at
+kconf.top_node by following 'next' and 'list' pointers.
 
-        # The set of all named choices (yes, choices can have names), indexed
-        # by name (a string)
-        self.named_choices = {}
+The item contained in a menu node is found in MenuNode.item (note that this can
+be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all
+symbols and choices have a 'nodes' attribute containing their menu nodes
+(usually only one). Printing a menu node will print its item, in Kconfig
+format.
 
-        # Lists containing all choices, menus and comments in the configuration
-        self.choices = []
-        self.menus = []
-        self.comments = []
+If you want to look up a symbol by name, use the kconf.syms dictionary.
 
-        def register_special_symbol(type_, name, val):
-            sym = Symbol()
-            sym.is_special_ = True
-            sym.is_defined_ = True
-            sym.config = self
-            sym.name = name
-            sym.type = type_
-            sym.cached_val = val
-            self.syms[name] = sym
-            return sym
-
-        # The special symbols n, m and y, used as shorthand for "n", "m" and
-        # "y"
-        self.n = register_special_symbol(TRISTATE, "n", "n")
-        self.m = register_special_symbol(TRISTATE, "m", "m")
-        self.y = register_special_symbol(TRISTATE, "y", "y")
-        # DEFCONFIG_LIST uses this
-        register_special_symbol(STRING, "UNAME_RELEASE", platform.uname()[2])
-
-        # The symbol with "option defconfig_list" set, containing a list of
-        # default .config files
-        self.defconfig_sym = None
-
-        # See Symbol.get_(src)arch()
-        self.arch = os.environ.get("ARCH")
-        self.srcarch = os.environ.get("SRCARCH")
-
-        # If you set CONFIG_ in the environment, Kconfig will prefix all symbols
-        # with its value when saving the configuration, instead of using the default, "CONFIG_".
-        self.config_prefix = os.environ.get("CONFIG_")
-        if self.config_prefix is None:
-            self.config_prefix = "CONFIG_"
-
-        # See Config.__init__(). We need this for get_defconfig_filename().
-        self.srctree = os.environ.get("srctree")
-        if self.srctree is None:
-            self.srctree = "."
 
-        self.filename = filename
-        self.base_dir = self.srctree if base_dir is None else \
-                        os.path.expandvars(base_dir)
+make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>]
+----------------------------------------------------
 
-        # The 'mainmenu' text
-        self.mainmenu_text = None
+This target runs the Python script given by the SCRIPT parameter on the
+configuration. sys.argv[1] holds the name of the top-level Kconfig file
+(currently always "Kconfig" in practice), and sys.argv[2] holds the SCRIPT_ARG
+argument, if given.
 
-        # The filename of the most recently loaded .config file
-        self.config_filename = None
-        # The textual header of the most recently loaded .config, uncommented
-        self.config_header = None
+See the examples/ subdirectory for example scripts.
 
-        self.print_warnings = print_warnings
-        self.print_undef_assign = print_undef_assign
-        self._warnings = []
 
-        # For parsing routines that stop when finding a line belonging to a
-        # different construct, these holds that line and the tokenized version
-        # of that line. The purpose is to avoid having to re-tokenize the line,
-        # which is inefficient and causes problems when recording references to
-        # symbols.
-        self.end_line = None
-        self.end_line_tokens = None
+make dumpvarsconfig
+-------------------
 
-        # See the comment in _parse_expr().
-        self._cur_item = None
-        self._line = None
-        self._filename = None
-        self._linenr = None
-        self._transform_m = None
+This target prints a list of all environment variables referenced from the
+Kconfig files, together with their values. See the
+Kconfiglib/examples/dumpvars.py script.
 
-        # Parse the Kconfig files
-        self.top_block = []
-        self._parse_file(filename, None, None, None, self.top_block)
+Only environment variables that are referenced via the Kconfig preprocessor
+$(FOO) syntax are included. The preprocessor was added in Linux 4.18.
 
-        # Build Symbol.dep for all symbols
-        self._build_dep()
 
-    def get_arch(self):
-        """Returns the value the environment variable ARCH had at the time the
-        Config instance was created, or None if ARCH was not set. For the
-        kernel, this corresponds to the architecture being built for, with
-        values such as "i386" or "mips"."""
-        return self.arch
-
-    def get_srcarch(self):
-        """Returns the value the environment variable SRCARCH had at the time
-        the Config instance was created, or None if SRCARCH was not set. For
-        the kernel, this corresponds to the particular arch/ subdirectory
-        containing architecture-specific code."""
-        return self.srcarch
-
-    def get_srctree(self):
-        """Returns the value the environment variable srctree had at the time
-        the Config instance was created, or None if srctree was not defined.
-        This variable points to the source directory and is used when building
-        in a separate directory."""
-        return self.srctree
-
-    def get_base_dir(self):
-        """Returns the base directory relative to which 'source' statements
-        will work, passed as an argument to Config.__init__()."""
-        return self.base_dir
-
-    def get_kconfig_filename(self):
-        """Returns the name of the (base) kconfig file this configuration was
-        loaded from."""
-        return self.filename
-
-    def get_config_filename(self):
-        """Returns the filename of the most recently loaded configuration file,
-        or None if no configuration has been loaded."""
-        return self.config_filename
-
-    def get_config_header(self):
-        """Returns the (uncommented) textual header of the .config file most
-        recently loaded with load_config(). Returns None if no .config file has
-        been loaded or if the most recently loaded .config file has no header.
-        The header consists of all lines up to but not including the first line
-        that either
-
-        1. Does not start with "#"
-        2. Has the form "# CONFIG_FOO is not set."
-        """
-        return self.config_header
-
-    def get_mainmenu_text(self):
-        """Returns the text of the 'mainmenu' statement (with $-references to
-        symbols replaced by symbol values), or None if the configuration has no
-        'mainmenu' statement."""
-        return None if self.mainmenu_text is None else \
-          self._expand_sym_refs(self.mainmenu_text)
-
-    def get_defconfig_filename(self):
-        """Returns the name of the defconfig file, which is the first existing
-        file in the list given in a symbol having 'option defconfig_list' set.
-        $-references to symbols will be expanded ("$FOO bar" -> "foo bar" if
-        FOO has the value "foo"). Returns None in case of no defconfig file.
-        Setting 'option defconfig_list' on multiple symbols currently results
-        in undefined behavior.
-
-        If the environment variable 'srctree' was set when the Config was
-        created, get_defconfig_filename() will first look relative to that
-        directory before looking in the current directory; see
-        Config.__init__().
-
-        WARNING: A wart here is that scripts/kconfig/Makefile sometimes uses
-        the --defconfig=<defconfig> option when calling the C implementation of
-        e.g. 'make defconfig'. This option overrides the 'option
-        defconfig_list' symbol, meaning the result from
-        get_defconfig_filename() might not match what 'make defconfig' would
-        use. That probably ought to be worked around somehow, so that this
-        function always gives the "expected" result."""
-        if self.defconfig_sym is None:
-            return None
-        for filename, cond_expr in self.defconfig_sym.def_exprs:
-            if self._eval_expr(cond_expr) == "y":
-                filename = self._expand_sym_refs(filename)
-                # We first look in $srctree. os.path.join() won't work here as
-                # an absolute path in filename would override $srctree.
-                srctree_filename = os.path.normpath(self.srctree + "/" +
-                                                    filename)
-                if os.path.exists(srctree_filename):
-                    return srctree_filename
-                if os.path.exists(filename):
-                    return filename
-        return None
+Using Kconfiglib without the Makefile targets
+=============================================
 
-    def get_symbol(self, name):
-        """Returns the symbol with name 'name', or None if no such symbol
-        appears in the configuration. An alternative shorthand is conf[name],
-        where conf is a Config instance, though that will instead raise
-        KeyError if the symbol does not exist."""
-        return self.syms.get(name)
+The make targets are only needed to pick up environment variables exported from
+the Kbuild makefiles and referenced inside Kconfig files, via e.g.
+'source "arch/$(SRCARCH)/Kconfig" and commands run via '$(shell,...)'.
 
-    def __getitem__(self, name):
-        """Returns the symbol with name 'name'. Raises KeyError if the symbol
-        does not appear in the configuration."""
-        return self.syms[name]
+These variables are referenced as of writing (Linux 4.18), together with sample
+values:
 
-    def get_symbols(self, all_symbols=True):
-        """Returns a list of symbols from the configuration. An alternative for
-        iterating over all defined symbols (in the order of definition) is
+  srctree          (.)
+  ARCH             (x86)
+  SRCARCH          (x86)
+  KERNELVERSION    (4.18.0)
+  CC               (gcc)
+  HOSTCC           (gcc)
+  HOSTCXX          (g++)
+  CC_VERSION_TEXT  (gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0)
 
-        for sym in config:
-            ...
+Older kernels only reference ARCH, SRCARCH, and KERNELVERSION.
 
-        which relies on Config implementing __iter__() and is equivalent to
+If your kernel is recent enough (4.18+), you can get a list of referenced
+environment variables via 'make dumpvarsconfig' (see above). Note that this
+command is added by the Makefile patch.
 
-        for sym in config.get_symbols(False):
-            ...
+To run Kconfiglib without the Makefile patch, set the environment variables
+manually:
 
-        all_symbols (default: True): If True, all symbols -- including special
-           and undefined symbols -- will be included in the result, in an
-           undefined order. If False, only symbols actually defined and not
-           merely referred to in the configuration will be included in the
-           result, and will appear in the order that they are defined within
-           the Kconfig configuration files."""
-        return list(self.syms.values()) if all_symbols else self.kconfig_syms
+  $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... python(3)
+  >>> import kconfiglib
+  >>> kconf = kconfiglib.Kconfig()  # filename defaults to "Kconfig"
 
-    def __iter__(self):
-        """Convenience function for iterating over the set of all defined
-        symbols in the configuration, used like
+Search the top-level Makefile for "Additional ARCH settings" to see other
+possibilities for ARCH and SRCARCH.
 
-        for sym in conf:
-            ...
 
-        The iteration happens in the order of definition within the Kconfig
-        configuration files. Symbols only referred to but not defined will not
-        be included, nor will the special symbols n, m, and y. If you want to
-        include such symbols as well, see config.get_symbols()."""
-        return iter(self.kconfig_syms)
+Intro to symbol values
+======================
 
-    def get_choices(self):
-        """Returns a list containing all choice statements in the
-        configuration, in the order they appear in the Kconfig files."""
-        return self.choices
+Kconfiglib has the same assignment semantics as the C implementation.
 
-    def get_menus(self):
-        """Returns a list containing all menus in the configuration, in the
-        order they appear in the Kconfig files."""
-        return self.menus
+Any symbol can be assigned a value by the user (via Kconfig.load_config() or
+Symbol.set_value()), but this user value is only respected if the symbol is
+visible, which corresponds to it (currently) being visible in the menuconfig
+interface.
 
-    def get_comments(self):
-        """Returns a list containing all comments in the configuration, in the
-        order they appear in the Kconfig files."""
-        return self.comments
+For symbols with prompts, the visibility of the symbol is determined by the
+condition on the prompt. Symbols without prompts are never visible, so setting
+a user value on them is pointless. A warning will be printed by default if
+Symbol.set_value() is called on a promptless symbol. Assignments to promptless
+symbols are normal within a .config file, so no similar warning will be printed
+by load_config().
 
-    def get_top_level_items(self):
-        """Returns a list containing the items (symbols, menus, choices, and
-        comments) at the top level of the configuration -- that is, all items
-        that do not appear within a menu or choice. The items appear in the
-        same order as within the configuration."""
-        return self.top_block
+Dependencies from parents and 'if'/'depends on' are propagated to properties,
+including prompts, so these two configurations are logically equivalent:
 
-    def load_config(self, filename, replace=True):
-        """Loads symbol values from a file in the familiar .config format.
-        Equivalent to calling Symbol.set_user_value() to set each of the
-        values.
+(1)
 
-        "# CONFIG_FOO is not set" within a .config file is treated specially
-        and sets the user value of FOO to 'n'. The C implementation works the
-        same way.
+  menu "menu"
+      depends on A
 
-        filename: The .config file to load. $-references to existing
-          environment variables will be expanded. For scripts to work even when
-          an alternative build directory is used with the Linux kernel, you
-          need to refer to the top-level kernel directory with "$srctree".
+  if B
 
-        replace (default: True): True if the configuration should replace the
-           old configuration; False if it should add to it.
+  config FOO
+      tristate "foo" if D
+      default y
+      depends on C
 
-        Returns a list or warnings (hopefully empty)
-        """
+  endif
 
-        self._warnings = []
-        # Regular expressions for parsing .config files
-        _set_re_match = re.compile(r"{}(\w+)=(.*)".format(self.config_prefix)).match
-        _unset_re_match = re.compile(r"# {}(\w+) is not set".format(self.config_prefix)).match
+  endmenu
 
-        # Put this first so that a missing file doesn't screw up our state
-        filename = os.path.expandvars(filename)
-        line_feeder = _FileFeed(filename)
+(2)
 
-        self.config_filename = filename
+  menu "menu"
+      depends on A
 
-        #
-        # Read header
-        #
+  config FOO
+      tristate "foo" if A && B && C && D
+      default y if A && B && C
 
-        def is_header_line(line):
-            return line is not None and line.startswith("#") and \
-                   not _unset_re_match(line)
+  endmenu
 
-        self.config_header = None
+In this example, A && B && C && D (the prompt condition) needs to be non-n for
+FOO to be visible (assignable). If its value is m, the symbol can only be
+assigned the value m: The visibility sets an upper bound on the value that can
+be assigned by the user, and any higher user value will be truncated down.
 
-        line = line_feeder.peek_next()
-        if is_header_line(line):
-            self.config_header = ""
-            while is_header_line(line_feeder.peek_next()):
-                self.config_header += line_feeder.get_next()[1:]
-            # Remove trailing newline
-            if self.config_header.endswith("\n"):
-                self.config_header = self.config_header[:-1]
+'default' properties are independent of the visibility, though a 'default' will
+often get the same condition as the prompt due to dependency propagation.
+'default' properties are used if the symbol is not visible or has no user
+value.
 
-        #
-        # Read assignments. Hotspot for some workloads.
-        #
+Symbols with no user value (or that have a user value but are not visible) and
+no (active) 'default' default to n for bool/tristate symbols, and to the empty
+string for other symbol types.
 
-        def warn_override(filename, linenr, name, old_user_val, new_user_val):
-            self._warn('overriding the value of {0}. '
-                       'Old value: "{1}", new value: "{2}".'
-                       .format(name, old_user_val, new_user_val),
-                       filename, linenr)
-
-        # Invalidate everything to keep things simple. It might be possible to
-        # improve performance for the case where multiple configurations are
-        # loaded by only invalidating a symbol (and its dependent symbols) if
-        # the new user value differs from the old. One complication would be
-        # that symbols not mentioned in the .config must lose their user value
-        # when replace = True, which is the usual case.
-        if replace:
-            self.unset_user_values()
-        else:
-            self._invalidate_all()
+'select' works similarly to symbol visibility, but sets a lower bound on the
+value of the symbol. The lower bound is determined by the value of the
+select*ing* symbol. 'select' does not respect visibility, so non-visible
+symbols can be forced to a particular (minimum) value by a select as well.
 
-        while 1:
-            line = line_feeder.get_next()
-            if line is None:
-                return self._warnings
+For non-bool/tristate symbols, it only matters whether the visibility is n or
+non-n: m visibility acts the same as y visibility.
 
-            line = line.rstrip()
+Conditions on 'default' and 'select' work in mostly intuitive ways. If the
+condition is n, the 'default' or 'select' is disabled. If it is m, the
+'default' or 'select' value (the value of the selecting symbol) is truncated
+down to m.
 
-            set_match = _set_re_match(line)
-            if set_match:
-                name, val = set_match.groups()
+When writing a configuration with Kconfig.write_config(), only symbols that are
+visible, have an (active) default, or are selected will get written out (note
+that this includes all symbols that would accept user values). Kconfiglib
+matches the .config format produced by the C implementations down to the
+character. This eases testing.
 
-                if val.startswith('"'):
-                    if len(val) < 2 or val[-1] != '"':
-                        _parse_error(line, "malformed string literal",
-                                     line_feeder.filename, line_feeder.linenr)
-                    # Strip quotes and remove escapings. The unescaping
-                    # procedure should be safe since " can only appear as \"
-                    # inside the string.
-                    val = val[1:-1].replace('\\"', '"').replace("\\\\", "\\")
+For a visible bool/tristate symbol FOO with value n, this line is written to
+.config:
 
-                if name in self.syms:
-                    sym = self.syms[name]
-                    if sym.user_val is not None:
-                        warn_override(line_feeder.filename, line_feeder.linenr,
-                                      name, sym.user_val, val)
-
-                    if sym.is_choice_sym:
-                        user_mode = sym.parent.user_mode
-                        if user_mode is not None and user_mode != val:
-                            self._warn("assignment to {0} changes mode of "
-                                       'containing choice from "{1}" to "{2}".'
-                                       .format(name, val, user_mode),
-                                       line_feeder.filename,
-                                       line_feeder.linenr)
-
-                    sym._set_user_value_no_invalidate(val, True)
-                else:
-                    if self.print_undef_assign:
-                        _stderr_msg('note: attempt to assign the value "{0}" '
-                                    "to the undefined symbol {1}."
-                                    .format(val, name),
-                                    line_feeder.filename, line_feeder.linenr)
-            else:
-                unset_match = _unset_re_match(line)
-                if unset_match:
-                    name = unset_match.group(1)
-                    if name in self.syms:
-                        sym = self.syms[name]
-                        if sym.user_val is not None:
-                            warn_override(line_feeder.filename,
-                                          line_feeder.linenr,
-                                          name, sym.user_val, "n")
-
-                        sym._set_user_value_no_invalidate("n", True)
-
-    def write_config(self, filename, header=None):
-        """Writes out symbol values in the familiar .config format.
-
-        Kconfiglib makes sure the format matches what the C implementation
-        would generate, down to whitespace. This eases testing.
-
-        filename: The filename under which to save the configuration.
-
-        header (default: None): A textual header that will appear at the
-           beginning of the file, with each line commented out automatically.
-           None means no header."""
-
-        for sym in self.syms_iter():
-            sym.already_written = False
-
-        with open(filename, "w") as f:
-            # Write header
-            if header is not None:
-                f.write(_comment(header) + "\n")
-
-            # Build and write configuration
-            conf_strings = []
-            _make_block_conf(self.top_block, conf_strings.append)
-            f.write("\n".join(conf_strings) + "\n")
-
-    def eval(self, s):
-        """Returns the value of the expression 's' -- where 's' is represented
-        as a string -- in the context of the configuration. Raises
-        Kconfig_Syntax_Error if syntax errors are detected in 's'.
-
-        For example, if FOO and BAR are tristate symbols at least one of which
-        has the value "y", then config.eval("y && (FOO || BAR)") => "y"
-
-        This function always yields a tristate value. To get the value of
-        non-bool, non-tristate symbols, use Symbol.get_value().
-
-        The result of this function is consistent with how evaluation works for
-        conditional expressions in the configuration as well as in the C
-        implementation. "m" and m are rewritten as '"m" && MODULES' and 'm &&
-        MODULES', respectively, and a result of "m" will get promoted to "y" if
-        we're running without modules.
-
-        Syntax checking is somewhat lax, partly to be compatible with lax
-        parsing in the C implementation."""
-        return self._eval_expr(self._parse_expr(self._tokenize(s, True), # Feed
-                                                None, # Current symbol/choice
-                                                s))   # line
-
-    def unset_user_values(self):
-        """Resets the values of all symbols, as if Config.load_config() or
-        Symbol.set_user_value() had never been called."""
-        for sym in self.syms_iter():
-            sym._unset_user_value_no_recursive_invalidate()
-
-    def set_print_warnings(self, print_warnings):
-        """Determines whether warnings related to this configuration (for
-        things like attempting to assign illegal values to symbols with
-        Symbol.set_user_value()) should be printed to stderr.
-
-        print_warnings: True if warnings should be printed."""
-        self.print_warnings = print_warnings
-
-    def set_print_undef_assign(self, print_undef_assign):
-        """Determines whether informational messages related to assignments to
-        undefined symbols should be printed to stderr for this configuration.
-
-        print_undef_assign: If True, such messages will be printed."""
-        self.print_undef_assign = print_undef_assign
+    # CONFIG_FOO is not set
 
-    def __str__(self):
-        """Returns a string containing various information about the Config."""
-        return _lines("Configuration",
-                      "File                                   : " +
-                        self.filename,
-                      "Base directory                         : " +
-                        self.base_dir,
-                      "Value of $ARCH at creation time        : " +
-                        ("(not set)" if self.arch is None else self.arch),
-                      "Value of $SRCARCH at creation time     : " +
-                        ("(not set)" if self.srcarch is None else
-                                        self.srcarch),
-                      "Source tree (derived from $srctree;",
-                      "defaults to '.' if $srctree isn't set) : " +
-                        self.srctree,
-                      "Most recently loaded .config           : " +
-                        ("(no .config loaded)"
-                          if self.config_filename is None else
-                             self.config_filename),
-                      "Print warnings                         : " +
-                        BOOL_STR[self.print_warnings],
-                      "Print assignments to undefined symbols : " +
-                        BOOL_STR[self.print_undef_assign])
+The point is to remember the user n selection (which might differ from the
+default value the symbol would get), while at the same sticking to the rule
+that undefined corresponds to n (.config uses Makefile format, making the line
+above a comment). When the .config file is read back in, this line will be
+treated the same as the following assignment:
 
-    #
-    # Private methods
-    #
+    CONFIG_FOO=n
 
-    #
-    # Kconfig parsing
-    #
+In Kconfiglib, the set of (currently) assignable values for a bool/tristate
+symbol appear in Symbol.assignable. For other symbol types, just check if
+sym.visibility is non-0 (non-n) to see whether the user value will have an
+effect.
 
-    def _parse_file(self, filename, parent, deps, visible_if_deps, block):
-        """Parses the Kconfig file 'filename'. Appends the Items in the file
-        (and any file it sources) to the list passed in the 'block' parameter.
-        See _parse_block() for the meaning of the parameters."""
-        self._parse_block(_FileFeed(filename), None, parent, deps,
-                          visible_if_deps, block)
 
-    def _parse_block(self, line_feeder, end_marker, parent, deps,
-                     visible_if_deps, block):
-        """Parses a block, which is the contents of either a file or an if,
-        menu, or choice statement. Appends the Items to the list passed in the
-        'block' parameter.
+Intro to the menu tree
+======================
 
-        line_feeder: A _FileFeed instance feeding lines from a file. The
-          Kconfig language is line-based in practice.
+The menu structure, as seen in e.g. menuconfig, is represented by a tree of
+MenuNode objects. The top node of the configuration corresponds to an implicit
+top-level menu, the title of which is shown at the top in the standard
+menuconfig interface. (The title is also available in Kconfig.mainmenu_text in
+Kconfiglib.)
 
-        end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for
-           ifs. None for files.
+The top node is found in Kconfig.top_node. From there, you can visit child menu
+nodes by following the 'list' pointer, and any following menu nodes by
+following the 'next' pointer. Usually, a non-None 'list' pointer indicates a
+menu or Choice, but menu nodes for symbols can sometimes have a non-None 'list'
+pointer too due to submenus created implicitly from dependencies.
 
-        parent: The enclosing menu or choice, or None if we're at the top
-           level.
+MenuNode.item is either a Symbol or a Choice object, or one of the constants
+MENU and COMMENT. The prompt of the menu node can be found in MenuNode.prompt,
+which also holds the title for menus and comments. For Symbol and Choice,
+MenuNode.help holds the help text (if any, otherwise None).
 
-        deps: Dependencies from enclosing menus, choices and ifs.
+Most symbols will only have a single menu node. A symbol defined in multiple
+locations will have one menu node for each location. The list of menu nodes for
+a Symbol or Choice can be found in the Symbol/Choice.nodes attribute.
 
-        visible_if_deps (default: None): 'visible if' dependencies from
-           enclosing menus.
+Note that prompts and help texts for symbols and choices are stored in their
+menu node(s) rather than in the Symbol or Choice objects themselves. This makes
+it possible to define a symbol in multiple locations with a different prompt or
+help text in each location. To get the help text or prompt for a symbol with a
+single menu node, do sym.nodes[0].help and sym.nodes[0].prompt, respectively.
+The prompt is a (text, condition) tuple, where condition determines the
+visibility (see 'Intro to expressions' below).
 
-        block: The list to add items to."""
+This organization mirrors the C implementation. MenuNode is called
+'struct menu' there, but I thought "menu" was a confusing name.
 
-        while 1:
-            # Do we already have a tokenized line that we determined wasn't
-            # part of whatever we were parsing earlier? See comment in
-            # Config.__init__().
-            if self.end_line is not None:
-                line = self.end_line
-                tokens = self.end_line_tokens
-                tokens.unget_all()
-
-                self.end_line = None
-                self.end_line_tokens = None
-            else:
-                line = line_feeder.get_next()
-                if line is None:
-                    if end_marker is not None:
-                        raise Kconfig_Syntax_Error("Unexpected end of file {0}"
-                                                 .format(line_feeder.filename))
-                    return
+It is possible to give a Choice a name and define it in multiple locations,
+hence why Choice.nodes is also a list.
 
-                tokens = self._tokenize(line, False, line_feeder.filename,
-                                        line_feeder.linenr)
+As a convenience, the properties added at a particular definition location are
+available on the MenuNode itself, in e.g. MenuNode.defaults. This is helpful
+when generating documentation, so that symbols/choices defined in multiple
+locations can be shown with the correct properties at each location.
 
-            t0 = tokens.get_next()
-            if t0 is None:
-                continue
 
-            # Cases are ordered roughly by frequency, which speeds things up a
-            # bit
-
-            if t0 == T_CONFIG or t0 == T_MENUCONFIG:
-                # The tokenizer will automatically allocate a new Symbol object
-                # for any new names it encounters, so we don't need to worry
-                # about that here.
-                sym = tokens.get_next()
-
-                # Symbols defined in multiple places get the parent of their
-                # first definition. However, for symbols whose parents are
-                # choice statements, the choice statement takes precedence.
-                if not sym.is_defined_ or isinstance(parent, Choice):
-                    sym.parent = parent
-                sym.is_defined_ = True
-
-                self._parse_properties(line_feeder, sym, deps, visible_if_deps)
-
-                self.kconfig_syms.append(sym)
-                block.append(sym)
-
-            elif t0 == T_SOURCE:
-                kconfig_file = tokens.get_next()
-                exp_kconfig_file = self._expand_sym_refs(kconfig_file)
-                f = os.path.join(self.base_dir, exp_kconfig_file)
-                if not os.path.exists(f):
-                    raise IOError('{0}:{1}: sourced file "{2}" (expands to '
-                                  '"{3}") not found. Perhaps base_dir '
-                                  '(argument to Config.__init__(), currently '
-                                  '"{4}") is set to the wrong value.'
-                                  .format(line_feeder.filename,
-                                          line_feeder.linenr,
-                                          kconfig_file, exp_kconfig_file,
-                                          self.base_dir))
-                # Add items to the same block
-                self._parse_file(f, parent, deps, visible_if_deps, block)
-
-            elif t0 == end_marker:
-                # We have reached the end of the block
-                return
+Intro to expressions
+====================
 
-            elif t0 == T_IF:
-                # If statements are treated as syntactic sugar for adding
-                # dependencies to enclosed items and do not have an explicit
-                # object representation.
-
-                dep_expr = self._parse_expr(tokens, None, line,
-                                            line_feeder.filename,
-                                            line_feeder.linenr)
-                # Add items to the same block
-                self._parse_block(line_feeder, T_ENDIF, parent,
-                                  _make_and(dep_expr, deps),
-                                  visible_if_deps, block)
-
-            elif t0 == T_COMMENT:
-                comment = Comment()
-                comment.config = self
-                comment.parent = parent
-                comment.filename = line_feeder.filename
-                comment.linenr = line_feeder.linenr
-                comment.text = tokens.get_next()
-
-                self._parse_properties(line_feeder, comment, deps,
-                                       visible_if_deps)
-
-                self.comments.append(comment)
-                block.append(comment)
-
-            elif t0 == T_MENU:
-                menu = Menu()
-                menu.config = self
-                menu.parent = parent
-                menu.filename = line_feeder.filename
-                menu.linenr = line_feeder.linenr
-                menu.title = tokens.get_next()
-
-                self._parse_properties(line_feeder, menu, deps,
-                                       visible_if_deps)
-
-                # This needs to go before _parse_block() so that we get the
-                # proper menu ordering in the case of nested functions
-                self.menus.append(menu)
-                # Parse contents and put Items in menu.block
-                self._parse_block(line_feeder, T_ENDMENU, menu, menu.dep_expr,
-                                  _make_and(visible_if_deps,
-                                            menu.visible_if_expr),
-                                  menu.block)
-
-                block.append(menu)
-
-            elif t0 == T_CHOICE:
-                name = tokens.get_next()
-                if name is None:
-                    choice = Choice()
-                    self.choices.append(choice)
-                else:
-                    # Named choice
-                    choice = self.named_choices.get(name)
-                    if choice is None:
-                        choice = Choice()
-                        choice.name = name
-                        self.named_choices[name] = choice
-                        self.choices.append(choice)
+Expressions can be evaluated with the expr_value() function and printed with
+the expr_str() function (these are used internally as well). Evaluating an
+expression always yields a tristate value, where n, m, and y are represented as
+0, 1, and 2, respectively.
 
-                choice.config = self
-                choice.parent = parent
+The following table should help you figure out how expressions are represented.
+A, B, C, ... are symbols (Symbol instances), NOT is the kconfiglib.NOT
+constant, etc.
 
-                choice.def_locations.append((line_feeder.filename,
-                                             line_feeder.linenr))
+Expression            Representation
+----------            --------------
+A                     A
+"A"                   A (constant symbol)
+!A                    (NOT, A)
+A && B                (AND, A, B)
+A && B && C           (AND, A, (AND, B, C))
+A || B                (OR, A, B)
+A || (B && C && D)    (OR, A, (AND, B, (AND, C, D)))
+A = B                 (EQUAL, A, B)
+A != "foo"            (UNEQUAL, A, foo (constant symbol))
+A && B = C && D       (AND, A, (AND, (EQUAL, B, C), D))
+n                     Kconfig.n (constant symbol)
+m                     Kconfig.m (constant symbol)
+y                     Kconfig.y (constant symbol)
+"y"                   Kconfig.y (constant symbol)
 
-                self._parse_properties(line_feeder, choice, deps,
-                                       visible_if_deps)
+Strings like "foo" in 'default "foo"' or 'depends on SYM = "foo"' are
+represented as constant symbols, so the only values that appear in expressions
+are symbols***. This mirrors the C implementation.
 
-                # Parse contents and put Items in choice.block
-                self._parse_block(line_feeder, T_ENDCHOICE, choice, deps,
-                                  visible_if_deps, choice.block)
+***For choice symbols, the parent Choice will appear in expressions as well,
+but it's usually invisible as the value interfaces of Symbol and Choice are
+identical. This mirrors the C implementation and makes different choice modes
+"just work".
 
-                choice._determine_actual_symbols()
+Manual evaluation examples:
 
-                # If no type is specified for the choice, its type is that of
-                # the first choice item with a specified type
-                if choice.type == UNKNOWN:
-                    for item in choice.actual_symbols:
-                        if item.type != UNKNOWN:
-                            choice.type = item.type
-                            break
+  - The value of A && B is min(A.tri_value, B.tri_value)
 
-                # Each choice item of UNKNOWN type gets the type of the choice
-                for item in choice.actual_symbols:
-                    if item.type == UNKNOWN:
-                        item.type = choice.type
+  - The value of A || B is max(A.tri_value, B.tri_value)
 
-                block.append(choice)
+  - The value of !A is 2 - A.tri_value
 
-            elif t0 == T_MAINMENU:
-                text = tokens.get_next()
-                if self.mainmenu_text is not None:
-                    self._warn("overriding 'mainmenu' text. "
-                               'Old value: "{0}", new value: "{1}".'
-                               .format(self.mainmenu_text, text),
-                               line_feeder.filename, line_feeder.linenr)
-                self.mainmenu_text = text
+  - The value of A = B is 2 (y) if A.str_value == B.str_value, and 0 (n)
+    otherwise. Note that str_value is used here instead of tri_value.
 
-            else:
-                _parse_error(line, "unrecognized construct",
-                             line_feeder.filename, line_feeder.linenr)
-
-    def _parse_properties(self, line_feeder, stmt, deps, visible_if_deps):
-        """Parsing of properties for symbols, menus, choices, and comments.
-        Takes care of propagating dependencies from enclosing menus and ifs."""
-
-        def parse_val_and_cond(tokens, line, filename, linenr):
-            """Parses '<expr1> if <expr2>' constructs, where the 'if' part is
-            optional. Returns a tuple containing the parsed expressions, with
-            None as the second element if the 'if' part is missing."""
-            return (self._parse_expr(tokens, stmt, line, filename, linenr,
-                                     False),
-                    self._parse_expr(tokens, stmt, line, filename, linenr)
-                    if tokens.check(T_IF) else None)
-
-        # In case the symbol is defined in multiple locations, we need to
-        # remember what prompts, defaults, selects, and implies are new for
-        # this definition, as "depends on" should only apply to the local
-        # definition.
-        new_prompt = None
-        new_def_exprs = []
-        new_selects = []
-        new_implies = []
-
-        # Dependencies from 'depends on' statements
-        depends_on_expr = None
+    For constant (as well as undefined) symbols, str_value matches the name of
+    the symbol. This mirrors the C implementation and explains why
+    'depends on SYM = "foo"' above works as expected.
 
-        while 1:
-            line = line_feeder.get_next()
-            if line is None:
-                break
+n/m/y are automatically converted to the corresponding constant symbols
+"n"/"m"/"y" (Kconfig.n/m/y) during parsing.
 
-            filename = line_feeder.filename
-            linenr = line_feeder.linenr
+Kconfig.const_syms is a dictionary like Kconfig.syms but for constant symbols.
 
-            tokens = self._tokenize(line, False, filename, linenr)
+If a condition is missing (e.g., <cond> when the 'if <cond>' is removed from
+'default A if <cond>'), it is actually Kconfig.y. The standard __str__()
+functions just avoid printing 'if y' conditions to give cleaner output.
 
-            t0 = tokens.get_next()
-            if t0 is None:
-                continue
 
-            # Cases are ordered roughly by frequency, which speeds things up a
-            # bit
+Kconfig extensions
+==================
 
-            if t0 == T_DEPENDS:
-                if not tokens.check(T_ON):
-                    _parse_error(line, 'expected "on" after "depends"',
-                                 filename, linenr)
+Kconfiglib includes a couple of Kconfig extensions:
 
-                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
-                                               linenr)
+'source' with relative path
+---------------------------
 
-                if isinstance(stmt, (Menu, Comment)):
-                    stmt.orig_deps = _make_and(stmt.orig_deps, parsed_deps)
-                else:
-                    depends_on_expr = _make_and(depends_on_expr, parsed_deps)
-
-            elif t0 == T_HELP:
-                # Find first non-blank (not all-space) line and get its
-                # indentation
-                line = line_feeder.next_nonblank()
-                if line is None:
-                    stmt.help = ""
-                    break
-                indent = _indentation(line)
-                if indent == 0:
-                    # If the first non-empty lines has zero indent, there is no
-                    # help text
-                    stmt.help = ""
-                    line_feeder.unget()
-                    break
+The 'rsource' statement sources Kconfig files with a path relative to directory
+of the Kconfig file containing the 'rsource' statement, instead of relative to
+the project root.
 
-                # The help text goes on till the first non-empty line with less
-                # indent
-                help_lines = [_deindent(line, indent)]
-                while 1:
-                    line = line_feeder.get_next()
-                    if line is None or \
-                       (not line.isspace() and _indentation(line) < indent):
-                        stmt.help = "".join(help_lines)
-                        break
-                    help_lines.append(_deindent(line, indent))
+Consider following directory tree:
 
-                if line is None:
-                    break
+  Project
+  +--Kconfig
+  |
+  +--src
+     +--Kconfig
+     |
+     +--SubSystem1
+        +--Kconfig
+        |
+        +--ModuleA
+           +--Kconfig
 
-                line_feeder.unget()
-
-            elif t0 == T_SELECT:
-                target = tokens.get_next()
-
-                stmt.referenced_syms.add(target)
-                stmt.selected_syms.add(target)
-
-                new_selects.append(
-                    (target,
-                     self._parse_expr(tokens, stmt, line, filename, linenr)
-                     if tokens.check(T_IF) else None))
-
-            elif t0 == T_IMPLY:
-                target = tokens.get_next()
-
-                stmt.referenced_syms.add(target)
-                stmt.implied_syms.add(target)
-
-                new_implies.append(
-                    (target,
-                     self._parse_expr(tokens, stmt, line, filename, linenr)
-                     if tokens.check(T_IF) else None))
-
-            elif t0 in (T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING):
-                stmt.type = TOKEN_TO_TYPE[t0]
-                if tokens.peek_next() is not None:
-                    new_prompt = parse_val_and_cond(tokens, line, filename,
-                                                    linenr)
-
-            elif t0 == T_DEFAULT:
-                new_def_exprs.append(parse_val_and_cond(tokens, line, filename,
-                                                        linenr))
-
-            elif t0 == T_DEF_BOOL:
-                stmt.type = BOOL
-                if tokens.peek_next() is not None:
-                    new_def_exprs.append(parse_val_and_cond(tokens, line,
-                                                            filename, linenr))
-
-            elif t0 == T_PROMPT:
-                # 'prompt' properties override each other within a single
-                # definition of a symbol, but additional prompts can be added
-                # by defining the symbol multiple times; hence 'new_prompt'
-                # instead of 'prompt'.
-                new_prompt = parse_val_and_cond(tokens, line, filename, linenr)
-
-            elif t0 == T_RANGE:
-                low = tokens.get_next()
-                high = tokens.get_next()
-                stmt.referenced_syms.add(low)
-                stmt.referenced_syms.add(high)
-
-                stmt.ranges.append(
-                    (low, high,
-                     self._parse_expr(tokens, stmt, line, filename, linenr)
-                     if tokens.check(T_IF) else None))
-
-            elif t0 == T_DEF_TRISTATE:
-                stmt.type = TRISTATE
-                if tokens.peek_next() is not None:
-                    new_def_exprs.append(parse_val_and_cond(tokens, line,
-                                                            filename, linenr))
-
-            elif t0 == T_OPTION:
-                if tokens.check(T_ENV) and tokens.check(T_EQUAL):
-                    env_var = tokens.get_next()
-
-                    stmt.is_special_ = True
-                    stmt.is_from_env = True
-
-                    if env_var not in os.environ:
-                        self._warn("The symbol {0} references the "
-                                   "non-existent environment variable {1} and "
-                                   "will get the empty string as its value. "
-                                   "If you're using Kconfiglib via "
-                                   "'make (i)scriptconfig', it should have "
-                                   "set up the environment correctly for you. "
-                                   "If you still got this message, that "
-                                   "might be an error, and you should email "
-                                   "ulfalizer a.t Google's email service."""
-                                   .format(stmt.name, env_var),
-                                   filename, linenr)
-
-                        stmt.cached_val = ""
-                    else:
-                        stmt.cached_val = os.environ[env_var]
+In this example, assume that src/SubSystem1/Kconfig wants to source
+src/SubSystem1/ModuleA/Kconfig.
 
-                elif tokens.check(T_DEFCONFIG_LIST):
-                    self.defconfig_sym = stmt
+With 'source', this statement would be used:
 
-                elif tokens.check(T_MODULES):
-                    # To reduce warning spam, only warn if 'option modules' is
-                    # set on some symbol that isn't MODULES, which should be
-                    # safe. I haven't run into any projects that make use
-                    # modules besides the kernel yet, and there it's likely to
-                    # keep being called "MODULES".
-                    if stmt.name != "MODULES":
-                        self._warn("the 'modules' option is not supported. "
-                                   "Let me know if this is a problem for you; "
-                                   "it shouldn't be that hard to implement. "
-                                   "(Note that modules are still supported -- "
-                                   "Kconfiglib just assumes the symbol name "
-                                   "MODULES, like older versions of the C "
-                                   "implementation did when 'option modules' "
-                                   "wasn't used.)",
-                                   filename, linenr)
+  source "src/SubSystem1/ModuleA/Kconfig"
 
-                elif tokens.check(T_ALLNOCONFIG_Y):
-                    if not isinstance(stmt, Symbol):
-                        _parse_error(line,
-                                     "the 'allnoconfig_y' option is only "
-                                     "valid for symbols",
-                                     filename, linenr)
-                    stmt.allnoconfig_y = True
+With 'rsource', this turns into
 
-                else:
-                    _parse_error(line, "unrecognized option", filename, linenr)
-
-            elif t0 == T_VISIBLE:
-                if not tokens.check(T_IF):
-                    _parse_error(line, 'expected "if" after "visible"',
-                                 filename, linenr)
-                if not isinstance(stmt, Menu):
-                    _parse_error(line,
-                                 "'visible if' is only valid for menus",
-                                 filename, linenr)
-
-                parsed_deps = self._parse_expr(tokens, stmt, line, filename,
-                                               linenr)
-                stmt.visible_if_expr = _make_and(stmt.visible_if_expr,
-                                                 parsed_deps)
-
-            elif t0 == T_OPTIONAL:
-                if not isinstance(stmt, Choice):
-                    _parse_error(line,
-                                 '"optional" is only valid for choices',
-                                 filename,
-                                 linenr)
-                stmt.optional = True
+  rsource "ModuleA/Kconfig"
 
-            else:
-                # See comment in Config.__init__()
-                self.end_line = line
-                self.end_line_tokens = tokens
-                break
+If an absolute path is given to 'rsource', it acts the same as 'source'.
 
-        # Done parsing properties. Now propagate 'depends on' and enclosing
-        # menu/if dependencies to expressions.
+'rsource' can be used to create "position-independent" Kconfig trees that can
+be moved around freely.
 
-        # The set of symbols referenced directly by the statement plus all
-        # symbols referenced by enclosing menus and ifs
-        stmt.all_referenced_syms = stmt.referenced_syms | _get_expr_syms(deps)
 
-        # Save original dependencies from enclosing menus and ifs
-        stmt.deps_from_containing = deps
+Globbing 'source'
+-----------------
 
-        if isinstance(stmt, (Menu, Comment)):
-            stmt.dep_expr = _make_and(stmt.orig_deps, deps)
-        else:
-            # Symbol or Choice
-
-            # See comment for 'menu_dep'
-            stmt.menu_dep = _make_and(deps, depends_on_expr)
-
-            # Propagate dependencies to prompts
-
-            if new_prompt is not None:
-                prompt, cond_expr = new_prompt
-                # Propagate 'visible if' dependencies from menus and local
-                # 'depends on' dependencies
-                cond_expr = _make_and(_make_and(cond_expr, visible_if_deps),
-                                      depends_on_expr)
-                # Save original
-                stmt.orig_prompts.append((prompt, cond_expr))
-                # Finalize with dependencies from enclosing menus and ifs
-                stmt.prompts.append((prompt, _make_and(cond_expr, deps)))
-
-            # Propagate dependencies to defaults
-
-            # Propagate 'depends on' dependencies
-            new_def_exprs = [(val_expr, _make_and(cond_expr, depends_on_expr))
-                             for val_expr, cond_expr in new_def_exprs]
-            # Save original
-            stmt.orig_def_exprs.extend(new_def_exprs)
-            # Finalize with dependencies from enclosing menus and ifs
-            stmt.def_exprs.extend([(val_expr, _make_and(cond_expr, deps))
-                                   for val_expr, cond_expr in new_def_exprs])
-
-            # Propagate dependencies to selects and implies
-
-            # Only symbols can select and imply
-            if isinstance(stmt, Symbol):
-                # Propagate 'depends on' dependencies
-                new_selects = [(target, _make_and(cond_expr, depends_on_expr))
-                               for target, cond_expr in new_selects]
-                new_implies = [(target, _make_and(cond_expr, depends_on_expr))
-                               for target, cond_expr in new_implies]
-                # Save original
-                stmt.orig_selects.extend(new_selects)
-                stmt.orig_implies.extend(new_implies)
-                # Finalize with dependencies from enclosing menus and ifs
-                for target, cond in new_selects:
-                    target.rev_dep = \
-                        _make_or(target.rev_dep,
-                                 _make_and(stmt, _make_and(cond, deps)))
-                for target, cond in new_implies:
-                    target.weak_rev_dep = \
-                        _make_or(target.weak_rev_dep,
-                                 _make_and(stmt, _make_and(cond, deps)))
-
-    def _parse_expr(self, feed, cur_item, line, filename=None, linenr=None,
-                    transform_m=True):
-        """Parses an expression from the tokens in 'feed' using a simple
-        top-down approach. The result has the form
-        '(<operator>, [<parsed operands>])', where <operator> is e.g.
-        kconfiglib.AND. If there is only one operand (i.e., no && or ||), then
-        the operand is returned directly. This also goes for subexpressions.
-
-        feed: _Feed instance containing the tokens for the expression.
-
-        cur_item: The item (Symbol, Choice, Menu, or Comment) currently being
-           parsed, or None if we're not parsing an item. Used for recording
-           references to symbols.
-
-        line: The line containing the expression being parsed.
-
-        filename (default: None): The file containing the expression.
-
-        linenr (default: None): The line number containing the expression.
-
-        transform_m (default: False): Determines if 'm' should be rewritten to
-           'm && MODULES' -- see parse_val_and_cond().
-
-        Expression grammar, in decreasing order of precedence:
-
-        <expr> -> <symbol>
-                  <symbol> '=' <symbol>
-                  <symbol> '!=' <symbol>
-                  '(' <expr> ')'
-                  '!' <expr>
-                  <expr> '&&' <expr>
-                  <expr> '||' <expr>"""
-
-        # Use instance variables to avoid having to pass these as arguments
-        # through the top-down parser in _parse_expr_rec(), which is tedious
-        # and obfuscates the code. A profiler run shows no noticeable
-        # performance difference.
-        self._cur_item = cur_item
-        self._transform_m = transform_m
-        self._line = line
-        self._filename = filename
-        self._linenr = linenr
-
-        return self._parse_expr_rec(feed)
-
-    def _parse_expr_rec(self, feed):
-        or_term = self._parse_or_term(feed)
-        if not feed.check(T_OR):
-            # Common case -- no need for an OR node since it's just a single
-            # operand
-            return or_term
-        or_terms = [or_term, self._parse_or_term(feed)]
-        while feed.check(T_OR):
-            or_terms.append(self._parse_or_term(feed))
-        return (OR, or_terms)
-
-    def _parse_or_term(self, feed):
-        and_term = self._parse_factor(feed)
-        if not feed.check(T_AND):
-            # Common case -- no need for an AND node since it's just a single
-            # operand
-            return and_term
-        and_terms = [and_term, self._parse_factor(feed)]
-        while feed.check(T_AND):
-            and_terms.append(self._parse_factor(feed))
-        return (AND, and_terms)
-
-    def _parse_factor(self, feed):
-        token = feed.get_next()
-
-        if isinstance(token, (Symbol, str)):
-            if self._cur_item is not None and isinstance(token, Symbol):
-                self._cur_item.referenced_syms.add(token)
-
-            next_token = feed.peek_next()
-            # For conditional expressions ('depends on <expr>',
-            # '... if <expr>', # etc.), "m" and m are rewritten to
-            # "m" && MODULES.
-            if next_token != T_EQUAL and next_token != T_UNEQUAL:
-                if self._transform_m and (token is self.m or token == "m"):
-                    return (AND, ["m", self._sym_lookup("MODULES")])
-                return token
+'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
+files. They require at least one matching file, raising a KconfigError
+otherwise.
 
-            relation = EQUAL if (feed.get_next() == T_EQUAL) else UNEQUAL
-            token_2 = feed.get_next()
-            if self._cur_item is not None and isinstance(token_2, Symbol):
-                self._cur_item.referenced_syms.add(token_2)
-            return (relation, token, token_2)
+For example, the following statement might source sub1/foofoofoo and
+sub2/foobarfoo:
 
-        if token == T_NOT:
-            return (NOT, self._parse_factor(feed))
+  source "sub[12]/foo*foo"
 
-        if token == T_OPEN_PAREN:
-            expr_parse = self._parse_expr_rec(feed)
-            if not feed.check(T_CLOSE_PAREN):
-                _parse_error(self._line, "missing end parenthesis",
-                             self._filename, self._linenr)
-            return expr_parse
+The glob patterns accepted are the same as for the standard glob.glob()
+function.
 
-        _parse_error(self._line, "malformed expression", self._filename,
-                     self._linenr)
+Two additional statements are provided for cases where it's acceptable for a
+pattern to match no files: 'osource' and 'orsource' (the o is for "optional").
 
-    def _tokenize(self, s, for_eval, filename=None, linenr=None):
-        """Returns a _Feed instance containing tokens derived from the string
-        's'. Registers any new symbols encountered (via _sym_lookup()).
+For example, the following statements will be no-ops if neither "foo" nor any
+files matching "bar*" exist:
 
-        (I experimented with a pure regular expression implementation, but it
-        came out slower, less readable, and wouldn't have been as flexible.)
+  osource "foo"
+  osource "bar*"
 
-        for_eval: True when parsing an expression for a call to Config.eval(),
-           in which case we should not treat the first token specially nor
-           register new symbols."""
+'orsource' does a relative optional source.
 
-        s = s.strip()
-        if s == "" or s[0] == "#":
-            return _Feed([])
+'source' and 'osource' are analogous to 'include' and '-include' in Make.
 
-        if for_eval:
-            previous = None # The previous token seen
-            tokens = []
-            i = 0 # The current index in the string being tokenized
 
-        else:
-            # The initial word on a line is parsed specially. Let
-            # command_chars = [A-Za-z0-9_]. Then
-            #  - leading non-command_chars characters are ignored, and
-            #  - the first token consists the following one or more
-            #    command_chars characters.
-            # This is why things like "----help--" are accepted.
-            initial_token_match = _initial_token_re_match(s)
-            if initial_token_match is None:
-                return _Feed([])
-            keyword = _get_keyword(initial_token_match.group(1))
-            if keyword == T_HELP:
-                # Avoid junk after "help", e.g. "---", being registered as a
-                # symbol
-                return _Feed([T_HELP])
-            if keyword is None:
-                # We expect a keyword as the first token
-                _tokenization_error(s, filename, linenr)
-
-            previous = keyword
-            tokens = [keyword]
-            # The current index in the string being tokenized
-            i = initial_token_match.end()
-
-        # _tokenize() is a hotspot during parsing, and this speeds things up a
-        # bit
-        strlen = len(s)
-        append = tokens.append
-
-        # Main tokenization loop. (Handles tokens past the first one.)
-        while i < strlen:
-            # Test for an identifier/keyword preceded by whitespace first; this
-            # is the most common case.
-            id_keyword_match = _id_keyword_re_match(s, i)
-            if id_keyword_match:
-                # We have an identifier or keyword. The above also stripped any
-                # whitespace for us.
-                name = id_keyword_match.group(1)
-                # Jump past it
-                i = id_keyword_match.end()
+Generalized def_* keywords
+--------------------------
 
-                keyword = _get_keyword(name)
-                if keyword is not None:
-                    # It's a keyword
-                    append(keyword)
-                elif previous in STRING_LEX:
-                    # What would ordinarily be considered an identifier is
-                    # treated as a string after certain tokens
-                    append(name)
-                else:
-                    # It's a symbol name. _sym_lookup() will take care of
-                    # allocating a new Symbol instance if it's the first time
-                    # we see it.
-                    sym = self._sym_lookup(name, for_eval)
-
-                    if previous == T_CONFIG or previous == T_MENUCONFIG:
-                        # If the previous token is T_(MENU)CONFIG
-                        # ("(menu)config"), we're tokenizing the first line of
-                        # a symbol definition, and should remember this as a
-                        # location where the symbol is defined
-                        sym.def_locations.append((filename, linenr))
-                    else:
-                        # Otherwise, it's a reference to the symbol
-                        sym.ref_locations.append((filename, linenr))
+def_int, def_hex, and def_string are available in addition to def_bool and
+def_tristate, allowing int, hex, and string symbols to be given a type and a
+default at the same time.
 
-                    append(sym)
 
-            else:
-                # Not an identifier/keyword
+Extra optional warnings
+-----------------------
 
-                while i < strlen and s[i].isspace():
-                    i += 1
-                if i == strlen:
-                    break
-                c = s[i]
-                i += 1
+Some optional warnings can be controlled via environment variables:
 
-                # String literal (constant symbol)
-                if c == '"' or c == "'":
-                    if "\\" in s:
-                        # Slow path: This could probably be sped up, but it's a
-                        # very unusual case anyway.
-                        quote = c
-                        val = ""
-                        while 1:
-                            if i >= len(s):
-                                _tokenization_error(s, filename, linenr)
-                            c = s[i]
-                            if c == quote:
-                                break
-                            if c == "\\":
-                                if i + 1 >= len(s):
-                                    _tokenization_error(s, filename, linenr)
-                                val += s[i + 1]
-                                i += 2
-                            else:
-                                val += c
-                                i += 1
-                        i += 1
-                        append(val)
-                    else:
-                        # Fast path: If the string contains no backslashes
-                        # (almost always) we can simply look for the matching
-                        # quote.
-                        end = s.find(c, i)
-                        if end == -1:
-                            _tokenization_error(s, filename, linenr)
-                        append(s[i:end])
-                        i = end + 1
-
-                elif c == "&":
-                    # Invalid characters are ignored
-                    if i >= len(s) or s[i] != "&": continue
-                    append(T_AND)
-                    i += 1
+  - KCONFIG_WARN_UNDEF: If set to 'y', warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with "0x" or "0X", to make it
+    possible to distinguish them from symbol references.
 
-                elif c == "|":
-                    # Invalid characters are ignored
-                    if i >= len(s) or s[i] != "|": continue
-                    append(T_OR)
-                    i += 1
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    KCONFIG_WARN_UNDEF is useful in projects that only have a single Kconfig
+    tree though.
 
-                elif c == "!":
-                    if i < len(s) and s[i] == "=":
-                        append(T_UNEQUAL)
-                        i += 1
-                    else:
-                        append(T_NOT)
+    KCONFIG_STRICT is an older alias for this environment variable, supported
+    for backwards compatibility.
 
-                elif c == "=": append(T_EQUAL)
-                elif c == "(": append(T_OPEN_PAREN)
-                elif c == ")": append(T_CLOSE_PAREN)
-                elif c == "#": break # Comment
+  - KCONFIG_WARN_UNDEF_ASSIGN: If set to 'y', warnings will be generated for
+    all assignments to undefined symbols within .config files. By default, no
+    such warnings are generated.
 
-                else: continue # Invalid characters are ignored
+    This warning can also be enabled/disabled via the Kconfig.warn_assign_undef
+    variable.
 
-            previous = tokens[-1]
 
-        return _Feed(tokens)
+Preprocessor user functions defined in Python
+---------------------------------------------
 
-    def _sym_lookup(self, name, for_eval=False):
-        """Fetches the symbol 'name' from the symbol table, creating and
-        registering it if it does not exist. If 'for_eval' is True, the symbol
-        won't be added to the symbol table if it does not exist -- this is for
-        Config.eval()."""
-        if name in self.syms:
-            return self.syms[name]
+Preprocessor functions can be defined in Python, which makes it simple to
+integrate information from existing Python tools into Kconfig (e.g. to have
+Kconfig symbols depend on hardware information stored in some other format).
 
-        new_sym = Symbol()
-        new_sym.config = self
-        new_sym.name = name
-        if for_eval:
-            self._warn("no symbol {0} in configuration".format(name))
-        else:
-            self.syms[name] = new_sym
-        return new_sym
+Putting a Python module named kconfigfunctions(.py) anywhere in sys.path will
+cause it to be imported by Kconfiglib (in Kconfig.__init__()). Note that
+sys.path can be customized via PYTHONPATH, and includes the directory of the
+module being run by default, as well as installation directories.
 
-    #
-    # Expression evaluation
-    #
+If the KCONFIG_FUNCTIONS environment variable is set, it gives a different
+module name to use instead of 'kconfigfunctions'.
 
-    def _eval_expr(self, expr):
-        """Evaluates an expression to "n", "m", or "y"."""
+The imported module is expected to define a global dictionary named 'functions'
+that maps function names to Python functions, as follows:
 
-        # Handles e.g. an "x if y" condition where the "if y" part is missing.
-        if expr is None:
-            return "y"
+  def my_fn(kconf, name, arg_1, arg_2, ...):
+      # kconf:
+      #   Kconfig instance
+      #
+      # name:
+      #   Name of the user-defined function ("my-fn"). Think argv[0].
+      #
+      # arg_1, arg_2, ...:
+      #   Arguments passed to the function from Kconfig (strings)
+      #
+      # Returns a string to be substituted as the result of calling the
+      # function
+      ...
 
-        res = self._eval_expr_rec(expr)
-        if res == "m":
-            # Promote "m" to "y" if we're running without modules.
-            #
-            # Internally, "m" is often rewritten to "m" && MODULES by both the
-            # C implementation and Kconfiglib, which takes care of cases where
-            # "m" should be demoted to "n" instead.
-            modules_sym = self.syms.get("MODULES")
-            if modules_sym is None or modules_sym.get_value() != "y":
-                return "y"
-        return res
+  def my_other_fn(kconf, name, arg_1, arg_2, ...):
+      ...
 
-    def _eval_expr_rec(self, expr):
-        if isinstance(expr, Symbol):
-            # Non-bool/tristate symbols are always "n" in a tristate sense,
-            # regardless of their value
-            if expr.type != BOOL and expr.type != TRISTATE:
-                return "n"
-            return expr.get_value()
-
-        if isinstance(expr, str):
-            return expr if (expr == "y" or expr == "m") else "n"
-
-        # Ordered by frequency
-
-        if expr[0] == AND:
-            res = "y"
-            for subexpr in expr[1]:
-                ev = self._eval_expr_rec(subexpr)
-                # Return immediately upon discovering an "n" term
-                if ev == "n":
-                    return "n"
-                if ev == "m":
-                    res = "m"
-            # 'res' is either "m" or "y" here; we already handled the
-            # short-circuiting "n" case in the loop.
-            return res
+  functions = {
+      "my-fn":       (my_fn,       <min.args>, <max.args>/None),
+      "my-other-fn": (my_other_fn, <min.args>, <max.args>/None),
+      ...
+  }
 
-        if expr[0] == NOT:
-            ev = self._eval_expr_rec(expr[1])
-            if ev == "y":
-                return "n"
-            return "y" if (ev == "n") else "m"
-
-        if expr[0] == OR:
-            res = "n"
-            for subexpr in expr[1]:
-                ev = self._eval_expr_rec(subexpr)
-                # Return immediately upon discovering a "y" term
-                if ev == "y":
-                    return "y"
-                if ev == "m":
-                    res = "m"
-            # 'res' is either "n" or "m" here; we already handled the
-            # short-circuiting "y" case in the loop.
-            return res
+  ...
 
-        if expr[0] == EQUAL:
-            return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n"
+<min.args> and <max.args> are the minimum and maximum number of arguments
+expected by the function (excluding the implicit 'name' argument). If
+<max.args> is None, there is no upper limit to the number of arguments. Passing
+an invalid number of arguments will generate a KconfigError exception.
 
-        if expr[0] == UNEQUAL:
-            return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n"
+Functions can access the current parsing location as kconf.filename/linenr.
+Accessing other fields of the Kconfig object is not safe. See the warning
+below.
 
-        _internal_error("Internal error while evaluating expression: "
-                        "unknown operation {0}.".format(expr[0]))
+Keep in mind that for a variable defined like 'foo = $(fn)', 'fn' will be
+called only when 'foo' is expanded. If 'fn' uses the parsing location and the
+intent is to use the location of the assignment, you want 'foo := $(fn)'
+instead, which calls the function immediately.
 
-    def _eval_min(self, e1, e2):
-        """Returns the minimum value of the two expressions. Equates None with
-        'y'."""
-        e1_eval = self._eval_expr(e1)
-        e2_eval = self._eval_expr(e2)
-        return e1_eval if tri_less(e1_eval, e2_eval) else e2_eval
+Once defined, user functions can be called from Kconfig in the same way as
+other preprocessor functions:
 
-    def _eval_max(self, e1, e2):
-        """Returns the maximum value of the two expressions. Equates None with
-        'y'."""
-        e1_eval = self._eval_expr(e1)
-        e2_eval = self._eval_expr(e2)
-        return e1_eval if tri_greater(e1_eval, e2_eval) else e2_eval
+    config FOO
+        ...
+        depends on $(my-fn,arg1,arg2)
 
-    #
-    # Dependency tracking (for caching and invalidation)
-    #
+If my_fn() returns "n", this will result in
 
-    def _build_dep(self):
-        """Populates the Symbol.dep sets, linking the symbol to the symbols
-        that immediately depend on it in the sense that changing the value of
-        the symbol might affect the values of those other symbols. This is used
-        for caching/invalidation purposes. The calculated sets might be larger
-        than necessary as we don't do any complicated analysis of the
-        expressions."""
-
-        # Adds 'sym' as a directly dependent symbol to all symbols that appear
-        # in the expression 'e'
-        def add_expr_deps(e, sym):
-            for s in _get_expr_syms(e):
-                s.dep.add(sym)
-
-        # The directly dependent symbols of a symbol are:
-        #  - Any symbols whose prompts, default values, rev_dep (select
-        #    condition), weak_rev_dep (imply condition) or ranges depend on the
-        #    symbol
-        #  - Any symbols that belong to the same choice statement as the symbol
-        #    (these won't be included in 'dep' as that makes the dependency
-        #    graph unwieldy, but Symbol._get_dependent() will include them)
-        #  - Any symbols in a choice statement that depends on the symbol
-        for sym in self.syms_iter():
-            for _, e in sym.prompts:
-                add_expr_deps(e, sym)
-
-            for v, e in sym.def_exprs:
-                add_expr_deps(v, sym)
-                add_expr_deps(e, sym)
-
-            add_expr_deps(sym.rev_dep, sym)
-            add_expr_deps(sym.weak_rev_dep, sym)
-
-            for l, u, e in sym.ranges:
-                add_expr_deps(l, sym)
-                add_expr_deps(u, sym)
-                add_expr_deps(e, sym)
-
-            if sym.is_choice_sym:
-                choice = sym.parent
-                for _, e in choice.prompts:
-                    add_expr_deps(e, sym)
-                for _, e in choice.def_exprs:
-                    add_expr_deps(e, sym)
-
-    def _eq_to_sym(self, eq):
-        """_expr_depends_on() helper. For (in)equalities of the form sym = y/m
-        or sym != n, returns sym. For other (in)equalities, returns None."""
-        relation, left, right = eq
-
-        def transform_y_m_n(item):
-            if item is self.y: return "y"
-            if item is self.m: return "m"
-            if item is self.n: return "n"
-            return item
-
-        left = transform_y_m_n(left)
-        right = transform_y_m_n(right)
-
-        # Make sure the symbol (if any) appears to the left
-        if not isinstance(left, Symbol):
-            left, right = right, left
-        if not isinstance(left, Symbol):
-            return None
-        if (relation == EQUAL and (right == "y" or right == "m")) or \
-           (relation == UNEQUAL and right == "n"):
-            return left
-        return None
+    config FOO
+        ...
+        depends on n
 
-    def _expr_depends_on(self, expr, sym):
-        """Reimplementation of expr_depends_symbol() from mconf.c. Used to
-        determine if a submenu should be implicitly created, which influences
-        what items inside choice statements are considered choice items."""
-        if expr is None:
-            return False
+Warning
+*******
 
-        def rec(expr):
-            if isinstance(expr, str):
-                return False
-            if isinstance(expr, Symbol):
-                return expr is sym
-
-            if expr[0] in (EQUAL, UNEQUAL):
-                return self._eq_to_sym(expr) is sym
-            if expr[0] == AND:
-                for and_expr in expr[1]:
-                    if rec(and_expr):
-                        return True
-            return False
+User-defined preprocessor functions are called as they're encountered at parse
+time, before all Kconfig files have been processed, and before the menu tree
+has been finalized. There are no guarantees that accessing Kconfig symbols or
+the menu tree via the 'kconf' parameter will work, and it could potentially
+lead to a crash.
 
-        return rec(expr)
+Preferably, user-defined functions should be stateless.
 
-    def _invalidate_all(self):
-        for sym in self.syms_iter():
-            sym._invalidate()
 
-    #
-    # Printing and misc.
-    #
+Feedback
+========
 
-    def _expand_sym_refs(self, s):
-        """Expands $-references to symbols in 's' to symbol values, or to the
-        empty string for undefined symbols."""
+Send bug reports, suggestions, and questions to ulfalizer a.t Google's email
+service, or open a ticket on the GitHub page.
+"""
+import errno
+import importlib
+import os
+import re
+import sys
 
-        while 1:
-            sym_ref_match = _sym_ref_re_search(s)
-            if sym_ref_match is None:
-                return s
+# Get rid of some attribute lookups. These are obvious in context.
+from glob import iglob
+from os.path import dirname, exists, expandvars, islink, join, realpath
 
-            sym_name = sym_ref_match.group(0)[1:]
-            sym = self.syms.get(sym_name)
-            expansion = "" if sym is None else sym.get_value()
 
-            s = s[:sym_ref_match.start()] + \
-                expansion + \
-                s[sym_ref_match.end():]
+VERSION = (12, 14, 0)
 
-    def _expr_val_str(self, expr, no_value_str="(none)",
-                      get_val_instead_of_eval=False):
-        """Printing helper. Returns a string with 'expr' and its value.
 
-        no_value_str: String to return when 'expr' is missing (None).
+# File layout:
+#
+# Public classes
+# Public functions
+# Internal functions
+# Global constants
 
-        get_val_instead_of_eval: Assume 'expr' is a symbol or string (constant
-          symbol) and get its value directly instead of evaluating it to a
-          tristate value."""
+# Line length: 79 columns
 
-        if expr is None:
-            return no_value_str
 
-        if get_val_instead_of_eval:
-            if isinstance(expr, str):
-                return _expr_to_str(expr)
-            val = expr.get_value()
-        else:
-            val = self._eval_expr(expr)
+#
+# Public classes
+#
 
-        return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val))
 
-    def _get_sym_or_choice_str(self, sc):
-        """Symbols and choices have many properties in common, so we factor out
-        common __str__() stuff here. "sc" is short for "symbol or choice"."""
+class Kconfig(object):
+    """
+    Represents a Kconfig configuration, e.g. for x86 or ARM. This is the set of
+    symbols, choices, and menu nodes appearing in the configuration. Creating
+    any number of Kconfig objects (including for different architectures) is
+    safe. Kconfiglib doesn't keep any global state.
 
-        # As we deal a lot with string representations here, use some
-        # convenient shorthand:
-        s = _expr_to_str
+    The following attributes are available. They should be treated as
+    read-only, and some are implemented through @property magic.
 
-        #
-        # Common symbol/choice properties
-        #
+    syms:
+      A dictionary with all symbols in the configuration, indexed by name. Also
+      includes all symbols that are referenced in expressions but never
+      defined, except for constant (quoted) symbols.
 
-        user_val_str = "(no user value)" if sc.user_val is None else \
-                       s(sc.user_val)
+      Undefined symbols can be recognized by Symbol.nodes being empty -- see
+      the 'Intro to the menu tree' section in the module docstring.
 
-        # Build prompts string
-        if not sc.prompts:
-            prompts_str = " (no prompts)"
-        else:
-            prompts_str_rows = []
-            for prompt, cond_expr in sc.orig_prompts:
-                prompts_str_rows.append(
-                    ' "{0}"'.format(prompt) if cond_expr is None else
-                    ' "{0}" if {1}'.format(prompt,
-                                           self._expr_val_str(cond_expr)))
-            prompts_str = "\n".join(prompts_str_rows)
-
-        # Build locations string
-        locations_str = "(no locations)" if not sc.def_locations else \
-                        " ".join(["{0}:{1}".format(filename, linenr) for
-                                  filename, linenr in sc.def_locations])
-
-        # Build additional-dependencies-from-menus-and-ifs string
-        additional_deps_str = " " + \
-          self._expr_val_str(sc.deps_from_containing,
-                             "(no additional dependencies)")
+    const_syms:
+      A dictionary like 'syms' for constant (quoted) symbols
 
-        #
-        # Symbol-specific stuff
-        #
+    named_choices:
+      A dictionary like 'syms' for named choices (choice FOO)
 
-        if isinstance(sc, Symbol):
-            # Build ranges string
-            if isinstance(sc, Symbol):
-                if not sc.ranges:
-                    ranges_str = " (no ranges)"
-                else:
-                    ranges_str_rows = []
-                    for l, u, cond_expr in sc.ranges:
-                        ranges_str_rows.append(
-                            " [{0}, {1}]".format(s(l), s(u))
-                            if cond_expr is None else
-                            " [{0}, {1}] if {2}"
-                            .format(s(l), s(u), self._expr_val_str(cond_expr)))
-                    ranges_str = "\n".join(ranges_str_rows)
-
-            # Build default values string
-            if not sc.def_exprs:
-                defaults_str = " (no default values)"
-            else:
-                defaults_str_rows = []
-                for val_expr, cond_expr in sc.orig_def_exprs:
-                    row_str = " " + self._expr_val_str(val_expr, "(none)",
-                                                       sc.type == STRING)
-                    defaults_str_rows.append(row_str)
-                    defaults_str_rows.append("  Condition: " +
-                                               self._expr_val_str(cond_expr))
-                defaults_str = "\n".join(defaults_str_rows)
-
-            # Build selects string
-            if not sc.orig_selects:
-                selects_str = " (no selects)"
-            else:
-                selects_str_rows = []
-                for target, cond_expr in sc.orig_selects:
-                    selects_str_rows.append(
-                        " {0}".format(target.name) if cond_expr is None else
-                        " {0} if {1}".format(target.name,
-                                             self._expr_val_str(cond_expr)))
-                selects_str = "\n".join(selects_str_rows)
-
-            # Build implies string
-            if not sc.orig_implies:
-                implies_str = " (no implies)"
-            else:
-                implies_str_rows = []
-                for target, cond_expr in sc.orig_implies:
-                    implies_str_rows.append(
-                        " {0}".format(target.name) if cond_expr is None else
-                        " {0} if {1}".format(target.name,
-                                             self._expr_val_str(cond_expr)))
-                implies_str = "\n".join(implies_str_rows)
-
-            res = _lines("Symbol " +
-                           ("(no name)" if sc.name is None else sc.name),
-                         "Type           : " + TYPENAME[sc.type],
-                         "Value          : " + s(sc.get_value()),
-                         "User value     : " + user_val_str,
-                         "Visibility     : " + s(_get_visibility(sc)),
-                         "Is choice item : " + BOOL_STR[sc.is_choice_sym],
-                         "Is defined     : " + BOOL_STR[sc.is_defined_],
-                         "Is from env.   : " + BOOL_STR[sc.is_from_env],
-                         "Is special     : " + BOOL_STR[sc.is_special_] + "\n")
-            if sc.ranges:
-                res += _lines("Ranges:", ranges_str + "\n")
-            res += _lines("Prompts:",
-                          prompts_str,
-                          "Default values:",
-                          defaults_str,
-                          "Selects:",
-                          selects_str,
-                          "Implies:",
-                          implies_str,
-                          "Reverse (select-related) dependencies:",
-                          " (no reverse dependencies)"
-                          if sc.rev_dep == "n"
-                          else " " + self._expr_val_str(sc.rev_dep),
-                          "Weak reverse (imply-related) dependencies:",
-                          " (no weak reverse dependencies)"
-                          if sc.weak_rev_dep == "n"
-                          else " " + self._expr_val_str(sc.weak_rev_dep),
-                          "Additional dependencies from enclosing menus "
-                            "and ifs:",
-                          additional_deps_str,
-                          "Locations: " + locations_str)
+    defined_syms:
+      A list with all defined symbols, in the same order as they appear in the
+      Kconfig files. Symbols defined in multiple locations appear multiple
+      times.
 
-            return res
+      Note: You probably want to use 'unique_defined_syms' instead. This
+      attribute is mostly maintained for backwards compatibility.
 
-        #
-        # Choice-specific stuff
-        #
+    unique_defined_syms:
+      A list like 'defined_syms', but with duplicates removed. Just the first
+      instance is kept for symbols defined in multiple locations. Kconfig order
+      is preserved otherwise.
 
-        # Build selected symbol string
-        sel = sc.get_selection()
-        sel_str = "(no selection)" if sel is None else sel.name
+      Using this attribute instead of 'defined_syms' can save work, and
+      automatically gives reasonable behavior when writing configuration output
+      (symbols defined in multiple locations only generate output once, while
+      still preserving Kconfig order for readability).
 
-        # Build default values string
-        if not sc.def_exprs:
-            defaults_str = " (no default values)"
-        else:
-            defaults_str_rows = []
-            for sym, cond_expr in sc.orig_def_exprs:
-                defaults_str_rows.append(
-                    " {0}".format(sym.name) if cond_expr is None else
-                    " {0} if {1}".format(sym.name,
-                                         self._expr_val_str(cond_expr)))
-            defaults_str = "\n".join(defaults_str_rows)
-
-        # Build contained symbols string
-        names = [sym.name for sym in sc.actual_symbols]
-        syms_string = " ".join(names) if names else "(empty)"
-
-        return _lines("Choice",
-                      "Name (for named choices): " +
-                        ("(no name)" if sc.name is None else sc.name),
-                      "Type            : " + TYPENAME[sc.type],
-                      "Selected symbol : " + sel_str,
-                      "User value      : " + user_val_str,
-                      "Mode            : " + s(sc.get_mode()),
-                      "Visibility      : " + s(_get_visibility(sc)),
-                      "Optional        : " + BOOL_STR[sc.optional],
-                      "Prompts:",
-                      prompts_str,
-                      "Defaults:",
-                      defaults_str,
-                      "Choice symbols:",
-                      " " + syms_string,
-                      "Additional dependencies from enclosing menus and "
-                        "ifs:",
-                      additional_deps_str,
-                      "Locations: " + locations_str)
+    choices:
+      A list with all choices, in the same order as they appear in the Kconfig
+      files.
 
-    def _warn(self, msg, filename=None, linenr=None):
-        """For printing warnings to stderr."""
-        msg = _build_msg("warning: " + msg, filename, linenr)
-        if self.print_warnings:
-            sys.stderr.write(msg + "\n")
-        self._warnings.append(msg)
+      Note: You probably want to use 'unique_choices' instead. This attribute
+      is mostly maintained for backwards compatibility.
 
-class Item(object):
+    unique_choices:
+      Analogous to 'unique_defined_syms', for choices. Named choices can have
+      multiple definition locations.
 
-    """Base class for symbols and other Kconfig constructs. Subclasses are
-    Symbol, Choice, Menu, and Comment."""
+    menus:
+      A list with all menus, in the same order as they appear in the Kconfig
+      files
 
-    def is_symbol(self):
-        """Returns True if the item is a symbol. Short for
-        isinstance(item, kconfiglib.Symbol)."""
-        return isinstance(self, Symbol)
+    comments:
+      A list with all comments, in the same order as they appear in the Kconfig
+      files
 
-    def is_choice(self):
-        """Returns True if the item is a choice. Short for
-        isinstance(item, kconfiglib.Choice)."""
-        return isinstance(self, Choice)
+    kconfig_filenames:
+      A list with the filenames of all Kconfig files included in the
+      configuration, relative to $srctree (or relative to the current directory
+      if $srctree isn't set), except absolute paths (e.g.
+      'source "/foo/Kconfig"') are kept as-is.
 
-    def is_menu(self):
-        """Returns True if the item is a menu. Short for
-        isinstance(item, kconfiglib.Menu)."""
-        return isinstance(self, Menu)
+      The files are listed in the order they are source'd, starting with the
+      top-level Kconfig file. If a file is source'd multiple times, it will
+      appear multiple times. Use set() to get unique filenames.
 
-    def is_comment(self):
-        """Returns True if the item is a comment. Short for
-        isinstance(item, kconfiglib.Comment)."""
-        return isinstance(self, Comment)
+      Note that Kconfig.sync_deps() already indirectly catches any file
+      modifications that change configuration output.
 
-class Symbol(Item):
+    env_vars:
+      A set() with the names of all environment variables referenced in the
+      Kconfig files.
 
-    """Represents a configuration symbol - e.g. FOO for
+      Only environment variables referenced with the preprocessor $(FOO) syntax
+      will be registered. The older $FOO syntax is only supported for backwards
+      compatibility.
 
-    config FOO
-        ..."""
+      Also note that $(FOO) won't be registered unless the environment variable
+      $FOO is actually set. If it isn't, $(FOO) is an expansion of an unset
+      preprocessor variable (which gives the empty string).
 
-    #
-    # Public interface
-    #
+      Another gotcha is that environment variables referenced in the values of
+      recursively expanded preprocessor variables (those defined with =) will
+      only be registered if the variable is actually used (expanded) somewhere.
 
-    def get_config(self):
-        """Returns the Config instance this symbol is from."""
-        return self.config
-
-    def get_name(self):
-        """Returns the name of the symbol."""
-        return self.name
-
-    def get_type(self):
-        """Returns the type of the symbol: one of UNKNOWN, BOOL, TRISTATE,
-        STRING, HEX, or INT. These are defined at the top level of the module,
-        so you'd do something like
-
-        if sym.get_type() == kconfiglib.STRING:
-            ..."""
-        return self.type
-
-    def get_prompts(self):
-        """Returns a list of prompts defined for the symbol, in the order they
-        appear in the configuration files. Returns the empty list for symbols
-        with no prompt.
-
-        This list will have a single entry for the vast majority of symbols
-        having prompts, but having multiple prompts for a single symbol is
-        possible through having multiple 'config' entries for it."""
-        return [prompt for prompt, _ in self.orig_prompts]
-
-    def get_help(self):
-        """Returns the help text of the symbol, or None if the symbol has no
-        help text."""
-        return self.help
-
-    def get_parent(self):
-        """Returns the menu or choice statement that contains the symbol, or
-        None if the symbol is at the top level. Note that if statements are
-        treated as syntactic and do not have an explicit class
-        representation."""
-        return self.parent
-
-    def get_def_locations(self):
-        """Returns a list of (filename, linenr) tuples, where filename (string)
-        and linenr (int) represent a location where the symbol is defined. For
-        the vast majority of symbols this list will only contain one element.
-        For the following Kconfig, FOO would get two entries: the lines marked
-        with *.
-
-        config FOO *
-            bool "foo prompt 1"
-
-        config FOO *
-            bool "foo prompt 2"
-        """
-        return self.def_locations
-
-    def get_ref_locations(self):
-        """Returns a list of (filename, linenr) tuples, where filename (string)
-        and linenr (int) represent a location where the symbol is referenced in
-        the configuration. For example, the lines marked by * would be included
-        for FOO below:
+      The note from the 'kconfig_filenames' documentation applies here too.
 
-        config A
-            bool
-            default BAR || FOO *
+    n/m/y:
+      The predefined constant symbols n/m/y. Also available in const_syms.
 
-        config B
-            tristate
-            depends on FOO *
-            default m if FOO *
+    modules:
+      The Symbol instance for the modules symbol. Currently hardcoded to
+      MODULES, which is backwards compatible. Kconfiglib will warn if
+      'option modules' is set on some other symbol. Tell me if you need proper
+      'option modules' support.
+
+      'modules' is never None. If the MODULES symbol is not explicitly defined,
+      its tri_value will be 0 (n), as expected.
+
+      A simple way to enable modules is to do 'kconf.modules.set_value(2)'
+      (provided the MODULES symbol is defined and visible). Modules are
+      disabled by default in the kernel Kconfig files as of writing, though
+      nearly all defconfig files enable them (with 'CONFIG_MODULES=y').
+
+    defconfig_list:
+      The Symbol instance for the 'option defconfig_list' symbol, or None if no
+      defconfig_list symbol exists. The defconfig filename derived from this
+      symbol can be found in Kconfig.defconfig_filename.
+
+    defconfig_filename:
+      The filename given by the defconfig_list symbol. This is taken from the
+      first 'default' with a satisfied condition where the specified file
+      exists (can be opened for reading). If a defconfig file foo/defconfig is
+      not found and $srctree was set when the Kconfig was created,
+      $srctree/foo/defconfig is looked up as well.
+
+      'defconfig_filename' is None if either no defconfig_list symbol exists,
+      or if the defconfig_list symbol has no 'default' with a satisfied
+      condition that specifies a file that exists.
+
+      Gotcha: scripts/kconfig/Makefile might pass --defconfig=<defconfig> to
+      scripts/kconfig/conf when running e.g. 'make defconfig'. This option
+      overrides the defconfig_list symbol, meaning defconfig_filename might not
+      always match what 'make defconfig' would use.
+
+    top_node:
+      The menu node (see the MenuNode class) of the implicit top-level menu.
+      Acts as the root of the menu tree.
+
+    mainmenu_text:
+      The prompt (title) of the top menu (top_node). Defaults to "Main menu".
+      Can be changed with the 'mainmenu' statement (see kconfig-language.txt).
+
+    variables:
+      A dictionary with all preprocessor variables, indexed by name. See the
+      Variable class.
+
+    warn:
+      Set this variable to True/False to enable/disable warnings. See
+      Kconfig.__init__().
+
+      When 'warn' is False, the values of the other warning-related variables
+      are ignored.
+
+      This variable as well as the other warn* variables can be read to check
+      the current warning settings.
+
+    warn_to_stderr:
+      Set this variable to True/False to enable/disable warnings on stderr. See
+      Kconfig.__init__().
+
+    warn_assign_undef:
+      Set this variable to True to generate warnings for assignments to
+      undefined symbols in configuration files.
+
+      This variable is False by default unless the KCONFIG_WARN_UNDEF_ASSIGN
+      environment variable was set to 'y' when the Kconfig instance was
+      created.
+
+    warn_assign_override:
+      Set this variable to True to generate warnings for multiple assignments
+      to the same symbol in configuration files, where the assignments set
+      different values (e.g. CONFIG_FOO=m followed by CONFIG_FOO=y, where the
+      last value would get used).
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warn_assign_redun:
+      Like warn_assign_override, but for multiple assignments setting a symbol
+      to the same value.
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warnings:
+      A list of strings containing all warnings that have been generated, for
+      cases where more flexibility is needed.
+
+      See the 'warn_to_stderr' parameter to Kconfig.__init__() and the
+      Kconfig.warn_to_stderr variable as well. Note that warnings still get
+      added to Kconfig.warnings when 'warn_to_stderr' is True.
+
+      Just as for warnings printed to stderr, only warnings that are enabled
+      will get added to Kconfig.warnings. See the various Kconfig.warn*
+      variables.
+
+    missing_syms:
+      A list with (name, value) tuples for all assignments to undefined symbols
+      within the most recently loaded .config file(s). 'name' is the symbol
+      name without the 'CONFIG_' prefix. 'value' is a string that gives the
+      right-hand side of the assignment verbatim.
+
+      See Kconfig.load_config() as well.
+
+    srctree:
+      The value of the $srctree environment variable when the configuration was
+      loaded, or the empty string if $srctree wasn't set. This gives nice
+      behavior with os.path.join(), which treats "" as the current directory,
+      without adding "./".
+
+      Kconfig files are looked up relative to $srctree (unless absolute paths
+      are used), and .config files are looked up relative to $srctree if they
+      are not found in the current directory. This is used to support
+      out-of-tree builds. The C tools use this environment variable in the same
+      way.
+
+      Changing $srctree after creating the Kconfig instance has no effect. Only
+      the value when the configuration is loaded matters. This avoids surprises
+      if multiple configurations are loaded with different values for $srctree.
+
+    config_prefix:
+      The value of the $CONFIG_ environment variable when the configuration was
+      loaded. This is the prefix used (and expected) on symbol names in .config
+      files and C headers. Defaults to "CONFIG_". Used in the same way in the C
+      tools.
+
+      Like for srctree, only the value of $CONFIG_ when the configuration is
+      loaded matters.
+
+    filename/linenr:
+      The current parsing location, for use in Python preprocessor functions.
+      See the module docstring.
+    """
+    __slots__ = (
+        "_encoding",
+        "_functions",
+        "_set_match",
+        "_srctree_prefix",
+        "_unset_match",
+        "_warn_assign_no_prompt",
+        "choices",
+        "comments",
+        "config_prefix",
+        "const_syms",
+        "defconfig_list",
+        "defined_syms",
+        "env_vars",
+        "kconfig_filenames",
+        "m",
+        "menus",
+        "missing_syms",
+        "modules",
+        "n",
+        "named_choices",
+        "srctree",
+        "syms",
+        "top_node",
+        "unique_choices",
+        "unique_defined_syms",
+        "variables",
+        "warn",
+        "warn_assign_override",
+        "warn_assign_redun",
+        "warn_assign_undef",
+        "warn_to_stderr",
+        "warnings",
+        "y",
+
+        # Parsing-related
+        "_parsing_kconfigs",
+        "_readline",
+        "filename",
+        "linenr",
+        "_include_path",
+        "_filestack",
+        "_line",
+        "_tokens",
+        "_tokens_i",
+        "_reuse_tokens",
+    )
 
-        if FOO *
-            config A
-                bool "A"
-        endif
+    #
+    # Public interface
+    #
 
-        config FOO (definition not included)
-            bool
+    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
+                 encoding="utf-8"):
         """
-        return self.ref_locations
-
-    def get_value(self):
-        """Calculate and return the value of the symbol. See also
-        Symbol.set_user_value()."""
-
-        if self.cached_val is not None:
-            return self.cached_val
+        Creates a new Kconfig object by parsing Kconfig files.
+        Note that Kconfig files are not the same as .config files (which store
+        configuration symbol values).
+
+        See the module docstring for some environment variables that influence
+        default warning settings (KCONFIG_WARN_UNDEF and
+        KCONFIG_WARN_UNDEF_ASSIGN).
+
+        Raises KconfigError on syntax/semantic errors, and OSError or (possibly
+        a subclass of) IOError on IO errors ('errno', 'strerror', and
+        'filename' are available). Note that IOError is an alias for OSError on
+        Python 3, so it's enough to catch OSError there. If you need Python 2/3
+        compatibility, it's easiest to catch EnvironmentError, which is a
+        common base class of OSError/IOError on Python 2 and an alias for
+        OSError on Python 3.
+
+        filename (default: "Kconfig"):
+          The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
+          from the top-level directory, as environment variables will make sure
+          the right Kconfig is included from there (arch/$SRCARCH/Kconfig as of
+          writing).
+
+          If $srctree is set, 'filename' will be looked up relative to it.
+          $srctree is also used to look up source'd files within Kconfig files.
+          See the class documentation.
+
+          If you are using Kconfiglib via 'make scriptconfig', the filename of
+          the base base Kconfig file will be in sys.argv[1]. It's currently
+          always "Kconfig" in practice.
+
+        warn (default: True):
+          True if warnings related to this configuration should be generated.
+          This can be changed later by setting Kconfig.warn to True/False. It
+          is provided as a constructor argument since warnings might be
+          generated during parsing.
+
+          See the other Kconfig.warn_* variables as well, which enable or
+          suppress certain warnings when warnings are enabled.
+
+          All generated warnings are added to the Kconfig.warnings list. See
+          the class documentation.
+
+        warn_to_stderr (default: True):
+          True if warnings should be printed to stderr in addition to being
+          added to Kconfig.warnings.
+
+          This can be changed later by setting Kconfig.warn_to_stderr to
+          True/False.
+
+        encoding (default: "utf-8"):
+          The encoding to use when reading and writing files, and when decoding
+          output from commands run via $(shell). If None, the encoding
+          specified in the current locale will be used.
+
+          The "utf-8" default avoids exceptions on systems that are configured
+          to use the C locale, which implies an ASCII encoding.
+
+          This parameter has no effect on Python 2, due to implementation
+          issues (regular strings turning into Unicode strings, which are
+          distinct in Python 2). Python 2 doesn't decode regular strings
+          anyway.
+
+          Related PEP: https://www.python.org/dev/peps/pep-0538/
+        """
+        self._encoding = encoding
 
-        # As a quirk of Kconfig, undefined symbols get their name as their
-        # value. This is why things like "FOO = bar" work for seeing if FOO has
-        # the value "bar".
-        if self.type == UNKNOWN:
-            self.cached_val = self.name
-            return self.name
+        self.srctree = os.getenv("srctree", "")
+        # A prefix we can reliably strip from glob() results to get a filename
+        # relative to $srctree. relpath() can cause issues for symlinks,
+        # because it assumes symlink/../foo is the same as foo/.
+        self._srctree_prefix = realpath(self.srctree) + os.sep
 
-        new_val = DEFAULT_VALUE[self.type]
-        vis = _get_visibility(self)
+        self.warn = warn
+        self.warn_to_stderr = warn_to_stderr
+        self.warn_assign_undef = os.getenv("KCONFIG_WARN_UNDEF_ASSIGN") == "y"
+        self.warn_assign_override = True
+        self.warn_assign_redun = True
+        self._warn_assign_no_prompt = True
 
-        # This is easiest to calculate together with the value
-        self.write_to_conf = False
+        self.warnings = []
 
-        if self.type == BOOL or self.type == TRISTATE:
-            # The visibility and mode (modules-only or single-selection) of
-            # choice items will be taken into account in _get_visibility()
-            if self.is_choice_sym:
-                if vis != "n":
-                    choice = self.parent
-                    mode = choice.get_mode()
+        self.config_prefix = os.getenv("CONFIG_", "CONFIG_")
+        # Regular expressions for parsing .config files
+        self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)")
+        self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
+            self.config_prefix))
 
-                    self.write_to_conf = (mode != "n")
+        self.syms = {}
+        self.const_syms = {}
+        self.defined_syms = []
+        self.missing_syms = []
+        self.named_choices = {}
+        self.choices = []
+        self.menus = []
+        self.comments = []
 
-                    if mode == "y":
-                        new_val = "y" if choice.get_selection() is self \
-                                  else "n"
-                    elif mode == "m":
-                        if self.user_val == "m" or self.user_val == "y":
-                            new_val = "m"
+        for nmy in "n", "m", "y":
+            sym = Symbol()
+            sym.kconfig = self
+            sym.name = nmy
+            sym.is_constant = True
+            sym.orig_type = TRISTATE
+            sym._cached_tri_val = STR_TO_TRI[nmy]
+
+            self.const_syms[nmy] = sym
+
+        self.n = self.const_syms["n"]
+        self.m = self.const_syms["m"]
+        self.y = self.const_syms["y"]
+
+        # Make n/m/y well-formed symbols
+        for nmy in "n", "m", "y":
+            sym = self.const_syms[nmy]
+            sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        # Maps preprocessor variables names to Variable instances
+        self.variables = {}
+
+        # Predefined preprocessor functions, with min/max number of arguments
+        self._functions = {
+            "info":       (_info_fn,       1, 1),
+            "error-if":   (_error_if_fn,   2, 2),
+            "filename":   (_filename_fn,   0, 0),
+            "lineno":     (_lineno_fn,     0, 0),
+            "shell":      (_shell_fn,      1, 1),
+            "warning-if": (_warning_if_fn, 2, 2),
+        }
+
+        # Add any user-defined preprocessor functions
+        try:
+            self._functions.update(
+                importlib.import_module(
+                    os.getenv("KCONFIG_FUNCTIONS", "kconfigfunctions")
+                ).functions)
+        except ImportError:
+            pass
+
+        # This determines whether previously unseen symbols are registered.
+        # They shouldn't be if we parse expressions after parsing, as part of
+        # Kconfig.eval_string().
+        self._parsing_kconfigs = True
+
+        self.modules = self._lookup_sym("MODULES")
+        self.defconfig_list = None
+
+        self.top_node = MenuNode()
+        self.top_node.kconfig = self
+        self.top_node.item = MENU
+        self.top_node.is_menuconfig = True
+        self.top_node.visibility = self.y
+        self.top_node.prompt = ("Main menu", self.y)
+        self.top_node.parent = None
+        self.top_node.dep = self.y
+        self.top_node.filename = filename
+        self.top_node.linenr = 1
+        self.top_node.include_path = ()
 
-            else:
-                # If the symbol is visible and has a user value, use that.
-                # Otherwise, look at defaults and weak reverse dependencies
-                # (implies).
-                use_defaults_and_weak_rev_deps = True
-
-                if vis != "n":
-                    self.write_to_conf = True
-                    if self.user_val is not None:
-                        new_val = self.config._eval_min(self.user_val, vis)
-                        use_defaults_and_weak_rev_deps = False
-
-                if use_defaults_and_weak_rev_deps:
-                    for val_expr, cond_expr in self.def_exprs:
-                        cond_eval = self.config._eval_expr(cond_expr)
-                        if cond_eval != "n":
-                            self.write_to_conf = True
-                            new_val = self.config._eval_min(val_expr,
-                                                            cond_eval)
-                            break
-
-                    weak_rev_dep_val = \
-                        self.config._eval_expr(self.weak_rev_dep)
-                    if weak_rev_dep_val != "n":
-                        self.write_to_conf = True
-                        new_val = self.config._eval_max(new_val,
-                                                        weak_rev_dep_val)
-
-                # Reverse (select-related) dependencies take precedence
-                rev_dep_val = self.config._eval_expr(self.rev_dep)
-                if rev_dep_val != "n":
-                    self.write_to_conf = True
-                    new_val = self.config._eval_max(new_val, rev_dep_val)
-
-            # We need to promote "m" to "y" in two circumstances:
-            #  1) If our type is boolean
-            #  2) If our weak_rev_dep (from IMPLY) is "y"
-            if new_val == "m" and \
-               (self.type == BOOL or
-                self.config._eval_expr(self.weak_rev_dep) == "y"):
-                new_val = "y"
-
-        elif self.type == INT or self.type == HEX:
-            has_active_range = False
-            low = None
-            high = None
-            use_defaults = True
+        # Parse the Kconfig files
 
-            base = 16 if self.type == HEX else 10
+        # Not used internally. Provided as a convenience.
+        self.kconfig_filenames = [filename]
+        self.env_vars = set()
 
-            for l, h, cond_expr in self.ranges:
-                if self.config._eval_expr(cond_expr) != "n":
-                    has_active_range = True
+        # Keeps track of the location in the parent Kconfig files. Kconfig
+        # files usually source other Kconfig files. See _enter_file().
+        self._filestack = []
+        self._include_path = ()
 
-                    low_str = _str_val(l)
-                    high_str = _str_val(h)
-                    low = int(low_str, base) if \
-                      _is_base_n(low_str, base) else 0
-                    high = int(high_str, base) if \
-                      _is_base_n(high_str, base) else 0
+        # The current parsing location
+        self.filename = filename
+        self.linenr = 0
 
-                    break
+        # Used to avoid retokenizing lines when we discover that they're not
+        # part of the construct currently being parsed. This is kinda like an
+        # unget operation.
+        self._reuse_tokens = False
 
-            if vis != "n":
-                self.write_to_conf = True
+        # Open the top-level Kconfig file. Store the readline() method directly
+        # as a small optimization.
+        self._readline = self._open(join(self.srctree, filename), "r").readline
 
-                if self.user_val is not None and \
-                   _is_base_n(self.user_val, base) and \
-                   (not has_active_range or
-                    low <= int(self.user_val, base) <= high):
+        try:
+            # Parse the Kconfig files
+            self._parse_block(None, self.top_node, self.top_node)
+            self.top_node.list = self.top_node.next
+            self.top_node.next = None
+        except UnicodeDecodeError as e:
+            _decoding_error(e, self.filename)
 
-                    # If the user value is OK, it is stored in exactly the same
-                    # form as specified in the assignment (with or without
-                    # "0x", etc).
+        # Close the top-level Kconfig file. __self__ fetches the 'file' object
+        # for the method.
+        self._readline.__self__.close()
 
-                    use_defaults = False
-                    new_val = self.user_val
+        self._parsing_kconfigs = False
 
-            if use_defaults:
-                for val_expr, cond_expr in self.def_exprs:
-                    if self.config._eval_expr(cond_expr) != "n":
-                        self.write_to_conf = True
-
-                        # If the default value is OK, it is stored in exactly
-                        # the same form as specified. Otherwise, it is clamped
-                        # to the range, and the output has "0x" as appropriate
-                        # for the type.
-
-                        new_val = _str_val(val_expr)
-
-                        if _is_base_n(new_val, base):
-                            new_val_num = int(new_val, base)
-                            if has_active_range:
-                                clamped_val = None
-
-                                if new_val_num < low:
-                                    clamped_val = low
-                                elif new_val_num > high:
-                                    clamped_val = high
-
-                                if clamped_val is not None:
-                                    new_val = (hex(clamped_val) if \
-                                      self.type == HEX else str(clamped_val))
-
-                            break
-                else: # For the for loop
-                    # If no user value or default kicks in but the hex/int has
-                    # an active range, then the low end of the range is used,
-                    # provided it's > 0, with "0x" prepended as appropriate.
-                    if has_active_range and low > 0:
-                        new_val = (hex(low) if self.type == HEX else str(low))
-
-        elif self.type == STRING:
-            use_defaults = True
+        # Do various menu tree post-processing
+        self._finalize_node(self.top_node, self.y)
 
-            if vis != "n":
-                self.write_to_conf = True
-                if self.user_val is not None:
-                    new_val = self.user_val
-                    use_defaults = False
+        self.unique_defined_syms = _ordered_unique(self.defined_syms)
+        self.unique_choices = _ordered_unique(self.choices)
 
-            if use_defaults:
-                for val_expr, cond_expr in self.def_exprs:
-                    if self.config._eval_expr(cond_expr) != "n":
-                        self.write_to_conf = True
-                        new_val = _str_val(val_expr)
-                        break
+        # Do sanity checks. Some of these depend on everything being finalized.
+        self._check_sym_sanity()
+        self._check_choice_sanity()
 
-        self.cached_val = new_val
-        return new_val
+        # KCONFIG_STRICT is an older alias for KCONFIG_WARN_UNDEF, supported
+        # for backwards compatibility
+        if os.getenv("KCONFIG_WARN_UNDEF") == "y" or \
+           os.getenv("KCONFIG_STRICT") == "y":
 
-    def get_user_value(self):
-        """Returns the value assigned to the symbol in a .config or via
-        Symbol.set_user_value() (provided the value was valid for the type of
-        the symbol). Returns None in case of no user value."""
-        return self.user_val
+            self._check_undef_syms()
 
-    def get_upper_bound(self):
-        """For string/hex/int symbols and for bool and tristate symbols that
-        cannot be modified (see is_modifiable()), returns None.
+        # Build Symbol._dependents for all symbols and choices
+        self._build_dep()
 
-        Otherwise, returns the highest value the symbol can be set to with
-        Symbol.set_user_value() (that will not be truncated): one of "m" or
-        "y", arranged from lowest to highest. This corresponds to the highest
-        value the symbol could be given in e.g. the 'make menuconfig'
-        interface.
+        # Check for dependency loops
+        check_dep_loop_sym = _check_dep_loop_sym  # Micro-optimization
+        for sym in self.unique_defined_syms:
+            check_dep_loop_sym(sym, False)
 
-        See also the tri_less*() and tri_greater*() functions, which could come
-        in handy."""
-        if self.type != BOOL and self.type != TRISTATE:
-            return None
-        rev_dep = self.config._eval_expr(self.rev_dep)
-        # A bool selected to "m" gets promoted to "y", pinning it
-        if rev_dep == "m" and self.type == BOOL:
-            return None
-        vis = _get_visibility(self)
-        if TRI_TO_INT[vis] > TRI_TO_INT[rev_dep]:
-            return vis
-        return None
+        # Add extra dependencies from choices to choice symbols that get
+        # awkward during dependency loop detection
+        self._add_choice_deps()
 
-    def get_lower_bound(self):
-        """For string/hex/int symbols and for bool and tristate symbols that
-        cannot be modified (see is_modifiable()), returns None.
+    @property
+    def mainmenu_text(self):
+        """
+        See the class documentation.
+        """
+        return self.top_node.prompt[0]
 
-        Otherwise, returns the lowest value the symbol can be set to with
-        Symbol.set_user_value() (that will not be truncated): one of "n" or
-        "m", arranged from lowest to highest. This corresponds to the lowest
-        value the symbol could be given in e.g. the 'make menuconfig'
-        interface.
+    @property
+    def defconfig_filename(self):
+        """
+        See the class documentation.
+        """
+        if self.defconfig_list:
+            for filename, cond in self.defconfig_list.defaults:
+                if expr_value(cond):
+                    try:
+                        with self._open_config(filename.str_value) as f:
+                            return f.name
+                    except EnvironmentError:
+                        continue
 
-        See also the tri_less*() and tri_greater*() functions, which could come
-        in handy."""
-        if self.type != BOOL and self.type != TRISTATE:
-            return None
-        rev_dep = self.config._eval_expr(self.rev_dep)
-        # A bool selected to "m" gets promoted to "y", pinning it
-        if rev_dep == "m" and self.type == BOOL:
-            return None
-        if TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]:
-            return rev_dep
         return None
 
-    def get_assignable_values(self):
-        """For string/hex/int symbols and for bool and tristate symbols that
-        cannot be modified (see is_modifiable()), returns the empty list.
-
-        Otherwise, returns a list containing the user values that can be
-        assigned to the symbol (that won't be truncated). Usage example:
-
-        if "m" in sym.get_assignable_values():
-            sym.set_user_value("m")
-
-        This is basically a more convenient interface to
-        get_lower/upper_bound() when wanting to test if a particular tristate
-        value can be assigned."""
-        if self.type != BOOL and self.type != TRISTATE:
-            return []
-        rev_dep = self.config._eval_expr(self.rev_dep)
-        # A bool selected to "m" gets promoted to "y", pinning it
-        if rev_dep == "m" and self.type == BOOL:
-            return []
-        res = ["n", "m", "y"][TRI_TO_INT[rev_dep] :
-                              TRI_TO_INT[_get_visibility(self)] + 1]
-        return res if len(res) > 1 else []
-
-    def get_visibility(self):
-        """Returns the visibility of the symbol: one of "n", "m" or "y". For
-        bool and tristate symbols, this is an upper bound on the value users
-        can set for the symbol. For other types of symbols, a visibility of "n"
-        means the user value will be ignored. A visibility of "n" corresponds
-        to not being visible in the 'make *config' interfaces.
-
-        Example (assuming we're running with modules enabled -- i.e., MODULES
-        set to 'y'):
-
-        # Assume this has been assigned 'n'
-        config N_SYM
-            tristate "N_SYM"
-
-        # Assume this has been assigned 'm'
-        config M_SYM
-            tristate "M_SYM"
-
-        # Has visibility 'n'
-        config A
-            tristate "A"
-            depends on N_SYM
+    def load_config(self, filename=None, replace=True, verbose=None):
+        """
+        Loads symbol values from a file in the .config format. Equivalent to
+        calling Symbol.set_value() to set each of the values.
 
-        # Has visibility 'm'
-        config B
-            tristate "B"
-            depends on M_SYM
+        "# CONFIG_FOO is not set" within a .config file sets the user value of
+        FOO to n. The C tools work the same way.
 
-        # Has visibility 'y'
-        config C
-            tristate "C"
-
-        # Has no prompt, and hence visibility 'n'
-        config D
-            tristate
-
-        Having visibility be tri-valued ensures that e.g. a symbol cannot be
-        set to "y" by the user if it depends on a symbol with value "m", which
-        wouldn't be safe.
-
-        You should probably look at get_lower/upper_bound(),
-        get_assignable_values() and is_modifiable() before using this."""
-        return _get_visibility(self)
-
-    def get_referenced_symbols(self, refs_from_enclosing=False):
-        """Returns the set() of all symbols referenced by this symbol. For
-        example, the symbol defined by
-
-        config FOO
-            bool
-            prompt "foo" if A && B
-            default C if D
-            depends on E
-            select F if G
-
-        references the symbols A through G.
-
-        refs_from_enclosing (default: False): If True, the symbols referenced
-           by enclosing menus and ifs will be included in the result."""
-        return self.all_referenced_syms if refs_from_enclosing else \
-               self.referenced_syms
-
-    def get_selected_symbols(self):
-        """Returns the set() of all symbols X for which this symbol has a
-        'select X' or 'select X if Y' (regardless of whether Y is satisfied or
-        not). This is a subset of the symbols returned by
-        get_referenced_symbols()."""
-        return self.selected_syms
-
-    def get_implied_symbols(self):
-        """Returns the set() of all symbols X for which this symbol has an
-        'imply X' or 'imply X if Y' (regardless of whether Y is satisfied or
-        not). This is a subset of the symbols returned by
-        get_referenced_symbols()."""
-        return self.implied_syms
-
-    def set_user_value(self, v):
-        """Sets the user value of the symbol.
+        For each symbol, the Symbol.user_value attribute holds the value the
+        symbol was assigned in the .config file (if any). The user value might
+        differ from Symbol.str/tri_value if there are unsatisfied dependencies.
 
-        Equal in effect to assigning the value to the symbol within a .config
-        file. Use get_lower/upper_bound() or get_assignable_values() to find
-        the range of currently assignable values for bool and tristate symbols;
-        setting values outside this range will cause the user value to differ
-        from the result of Symbol.get_value() (be truncated). Values that are
-        invalid for the type (such as a_bool.set_user_value("foo")) are
-        ignored, and a warning is emitted if an attempt is made to assign such
-        a value.
-
-        For any type of symbol, is_modifiable() can be used to check if a user
-        value will currently have any effect on the symbol, as determined by
-        its visibility and range of assignable values. Any value that is valid
-        for the type (bool, tristate, etc.) will end up being reflected in
-        get_user_value() though, and might have an effect later if conditions
-        change. To get rid of the user value, use unset_user_value().
-
-        Any symbols dependent on the symbol are (recursively) invalidated, so
-        things will just work with regards to dependencies.
-
-        v: The user value to give to the symbol."""
-        self._set_user_value_no_invalidate(v, False)
-
-        # There might be something more efficient you could do here, but play
-        # it safe.
-        if self.name == "MODULES":
-            self.config._invalidate_all()
-            return
+        Calling this function also updates the Kconfig.missing_syms attribute
+        with a list of all assignments to undefined symbols within the
+        configuration file. Kconfig.missing_syms is cleared if 'replace' is
+        True, and appended to otherwise. See the documentation for
+        Kconfig.missing_syms as well.
 
-        self._invalidate()
-        self._invalidate_dependent()
-
-    def unset_user_value(self):
-        """Resets the user value of the symbol, as if the symbol had never
-        gotten a user value via Config.load_config() or
-        Symbol.set_user_value()."""
-        self._unset_user_value_no_recursive_invalidate()
-        self._invalidate_dependent()
-
-    def is_modifiable(self):
-        """Returns True if the value of the symbol could be modified by calling
-        Symbol.set_user_value().
-
-        For bools and tristates, this corresponds to the symbol being visible
-        in the 'make menuconfig' interface and not already being pinned to a
-        specific value (e.g. because it is selected by another symbol).
-
-        For strings and numbers, this corresponds to just being visible. (See
-        Symbol.get_visibility().)"""
-        if self.is_special_:
-            return False
-        if self.type == BOOL or self.type == TRISTATE:
-            rev_dep = self.config._eval_expr(self.rev_dep)
-            # A bool selected to "m" gets promoted to "y", pinning it
-            if rev_dep == "m" and self.type == BOOL:
-                return False
-            return TRI_TO_INT[_get_visibility(self)] > TRI_TO_INT[rev_dep]
-        return _get_visibility(self) != "n"
-
-    def is_defined(self):
-        """Returns False if the symbol is referred to in the Kconfig but never
-        actually defined."""
-        return self.is_defined_
-
-    def is_special(self):
-        """Returns True if the symbol is one of the special symbols n, m, y, or
-        UNAME_RELEASE, or gets its value from the environment."""
-        return self.is_special_
-
-    def is_from_environment(self):
-        """Returns True if the symbol gets its value from the environment."""
-        return self.is_from_env
-
-    def has_ranges(self):
-        """Returns True if the symbol is of type INT or HEX and has ranges that
-        limit what values it can take on."""
-        return bool(self.ranges)
-
-    def is_choice_symbol(self):
-        """Returns True if the symbol is in a choice statement and is an actual
-        choice symbol (see Choice.get_symbols())."""
-        return self.is_choice_sym
-
-    def is_choice_selection(self):
-        """Returns True if the symbol is contained in a choice statement and is
-        the selected item. Equivalent to
-
-        sym.is_choice_symbol() and sym.get_parent().get_selection() is sym"""
-        return self.is_choice_sym and self.parent.get_selection() is self
-
-    def is_allnoconfig_y(self):
-        """Returns True if the symbol has the 'allnoconfig_y' option set."""
-        return self.allnoconfig_y
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
 
-    def __str__(self):
-        """Returns a string containing various information about the symbol."""
-        return self.config._get_sym_or_choice_str(self)
+        filename (default: None):
+          Path to load configuration from (a string). Respects $srctree if set
+          (see the class documentation).
 
-    #
-    # Private methods
-    #
+          If 'filename' is None (the default), the configuration file to load
+          (if any) is calculated automatically, giving the behavior you'd
+          usually want:
 
-    def __init__(self):
-        """Symbol constructor -- not intended to be called directly by
-        Kconfiglib clients."""
-
-        self.name = None
-        self.type = UNKNOWN
-        self.prompts = []
-        self.def_exprs = [] # 'default' properties
-        self.ranges = [] # 'range' properties (for int and hex)
-        self.help = None # Help text
-        self.rev_dep = "n" # Reverse (select-related) dependencies
-        self.weak_rev_dep = "n" # Weak reverse (imply-related) dependencies
-        self.config = None
-        self.parent = None
-
-        self.user_val = None # Value set by user
-
-        # The prompt, default value, select, and imply conditions without any
-        # dependencies from menus and ifs propagated to them
-        self.orig_prompts = []
-        self.orig_def_exprs = []
-        self.orig_selects = []
-        self.orig_implies = []
-
-        # Dependencies inherited from containing menus and ifs
-        self.deps_from_containing = None
-        # The set of symbols referenced by this symbol (see
-        # get_referenced_symbols())
-        self.referenced_syms = set()
-        # The set of symbols selected by this symbol (see
-        # get_selected_symbols())
-        self.selected_syms = set()
-        # The set of symbols implied by this symbol (see get_implied_symbols())
-        self.implied_syms = set()
-        # Like 'referenced_syms', but includes symbols from
-        # dependencies inherited from enclosing menus and ifs
-        self.all_referenced_syms = set()
-
-        # This records only dependencies from enclosing ifs and menus together
-        # with local 'depends on' dependencies. Needed when determining actual
-        # choice items (hrrrr...). See Choice._determine_actual_symbols().
-        self.menu_dep = None
-
-        # See Symbol.get_ref/def_locations().
-        self.def_locations = []
-        self.ref_locations = []
-
-        # Populated in Config._build_dep() after parsing. Links the symbol to
-        # the symbols that immediately depend on it (in a caching/invalidation
-        # sense). The total set of dependent symbols for the symbol (the
-        # transitive closure) is calculated on an as-needed basis in
-        # _get_dependent().
-        self.dep = set()
-
-        # Cached values
-
-        # Caches the calculated value
-        self.cached_val = None
-        # Caches the visibility, which acts as an upper bound on the value
-        self.cached_visibility = None
-        # Caches the total list of dependent symbols. Calculated in
-        # _get_dependent().
-        self.cached_deps = None
-
-        # Flags
-
-        # Does the symbol have an entry in the Kconfig file? The trailing
-        # underscore avoids a collision with is_defined().
-        self.is_defined_ = False
-        # Should the symbol get an entry in .config?
-        self.write_to_conf = False
-        # Set to true when _make_conf() is called on a symbol, so that symbols
-        # defined in multiple locations only get one .config entry. We need to
-        # reset it prior to writing out a new .config.
-        self.already_written = False
-        # This is set to True for "actual" choice symbols; see
-        # Choice._determine_actual_symbols().
-        self.is_choice_sym = False
-        # Does the symbol get its value in some special way, e.g. from the
-        # environment or by being one of the special symbols n, m, and y? If
-        # so, the value is stored in self.cached_val, which is never
-        # invalidated. The trailing underscore avoids a collision with
-        # is_special().
-        self.is_special_ = False
-        # Does the symbol get its value from the environment?
-        self.is_from_env = False
-        # Does the symbol have the 'allnoconfig_y' option set?
-        self.allnoconfig_y = False
+            1. If the KCONFIG_CONFIG environment variable is set, it gives the
+               path to the configuration file to load. Otherwise, ".config" is
+               used. See standard_config_filename().
 
-    def _invalidate(self):
-        if self.is_special_:
-            return
+            2. If the path from (1.) doesn't exist, the configuration file
+               given by kconf.defconfig_filename is loaded instead, which is
+               derived from the 'option defconfig_list' symbol.
 
-        if self.is_choice_sym:
-            self.parent._invalidate()
+            3. If (1.) and (2.) fail to find a configuration file to load, no
+               configuration file is loaded, and symbols retain their current
+               values (e.g., their default values). This is not an error.
 
-        self.cached_val = None
-        self.cached_visibility = None
+           See the return value as well.
 
-    def _invalidate_dependent(self):
-        for sym in self._get_dependent():
-            sym._invalidate()
+        replace (default: True):
+          If True, all existing user values will be cleared before loading the
+          .config. Pass False to merge configurations.
 
-    def _set_user_value_no_invalidate(self, v, suppress_load_warnings):
-        """Like set_user_value(), but does not invalidate any symbols.
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
 
-        suppress_load_warnings: some warnings are annoying when loading a
-           .config that can be helpful when manually invoking set_user_value().
-           This flag is set to True to suppress such warnings.
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
 
-           Perhaps this could be made optional for load_config() instead."""
+          Will probably be removed in some future version.
 
-        if self.is_special_:
-            if self.is_from_env:
-                self.config._warn('attempt to assign the value "{0}" to the '
-                                  'symbol {1}, which gets its value from the '
-                                  'environment. Assignment ignored.'
-                                  .format(v, self.name))
-            else:
-                self.config._warn('attempt to assign the value "{0}" to the '
-                                  'special symbol {1}. Assignment ignored.'
-                                  .format(v, self.name))
-            return
+        Returns a string with a message saying which file got loaded (or
+        possibly that no file got loaded, when 'filename' is None). This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.load_config()). The returned message distinguishes between
+        loading (replace == True) and merging (replace == False).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("load_config")
+
+        msg = None
+        if filename is None:
+            filename = standard_config_filename()
+            if not exists(filename) and \
+               not exists(join(self.srctree, filename)):
+                defconfig = self.defconfig_filename
+                if defconfig is None:
+                    return "Using default symbol values (no '{}')" \
+                           .format(filename)
+
+                msg = " default configuration '{}' (no '{}')" \
+                      .format(defconfig, filename)
+                filename = defconfig
+
+        if not msg:
+            msg = " configuration '{}'".format(filename)
+
+        # Disable the warning about assigning to symbols without prompts. This
+        # is normal and expected within a .config file.
+        self._warn_assign_no_prompt = False
+
+        # This stub only exists to make sure _warn_assign_no_prompt gets
+        # reenabled
+        try:
+            self._load_config(filename, replace)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, filename)
+        finally:
+            self._warn_assign_no_prompt = True
+
+        return ("Loaded" if replace else "Merged") + msg
+
+    def _load_config(self, filename, replace):
+        with self._open_config(filename) as f:
+            if replace:
+                self.missing_syms = []
+
+                # If we're replacing the configuration, keep track of which
+                # symbols and choices got set so that we can unset the rest
+                # later. This avoids invalidating everything and is faster.
+                # Another benefit is that invalidation must be rock solid for
+                # it to work, making it a good test.
+
+                for sym in self.unique_defined_syms:
+                    sym._was_set = False
+
+                for choice in self.unique_choices:
+                    choice._was_set = False
+
+            # Small optimizations
+            set_match = self._set_match
+            unset_match = self._unset_match
+            get_sym = self.syms.get
+
+            for linenr, line in enumerate(f, 1):
+                # The C tools ignore trailing whitespace
+                line = line.rstrip()
+
+                match = set_match(line)
+                if match:
+                    name, val = match.groups()
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, val, filename, linenr)
+                        continue
+
+                    if sym.orig_type in _BOOL_TRISTATE:
+                        # The C implementation only checks the first character
+                        # to the right of '=', for whatever reason
+                        if not (sym.orig_type is BOOL
+                                and val.startswith(("y", "n")) or
+                                sym.orig_type is TRISTATE
+                                and val.startswith(("y", "m", "n"))):
+                            self._warn("'{}' is not a valid value for the {} "
+                                       "symbol {}. Assignment ignored."
+                                       .format(val, TYPE_TO_STR[sym.orig_type],
+                                               _name_and_loc(sym)),
+                                       filename, linenr)
+                            continue
+
+                        val = val[0]
+
+                        if sym.choice and val != "n":
+                            # During .config loading, we infer the mode of the
+                            # choice from the kind of values that are assigned
+                            # to the choice symbols
+
+                            prev_mode = sym.choice.user_value
+                            if prev_mode is not None and \
+                               TRI_TO_STR[prev_mode] != val:
+
+                                self._warn("both m and y assigned to symbols "
+                                           "within the same choice",
+                                           filename, linenr)
+
+                            # Set the choice's mode
+                            sym.choice.set_value(val)
+
+                    elif sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            self._warn("malformed string literal in "
+                                       "assignment to {}. Assignment ignored."
+                                       .format(_name_and_loc(sym)),
+                                       filename, linenr)
+                            continue
+
+                        val = unescape(match.group(1))
 
-        if not self.is_defined_:
-            filename, linenr = self.ref_locations[0]
-            if self.config.print_undef_assign:
-                _stderr_msg('note: attempt to assign the value "{0}" to {1}, '
-                            "which is referenced at {2}:{3} but never "
-                            "defined. Assignment ignored."
-                            .format(v, self.name, filename, linenr))
-            return
+                else:
+                    match = unset_match(line)
+                    if not match:
+                        # Print a warning for lines that match neither
+                        # set_match() nor unset_match() and that are not blank
+                        # lines or comments. 'line' has already been
+                        # rstrip()'d, so blank lines show up as "" here.
+                        if line and not line.lstrip().startswith("#"):
+                            self._warn("ignoring malformed line '{}'"
+                                       .format(line),
+                                       filename, linenr)
 
-        # Check if the value is valid for our type
-        if not ((self.type == BOOL     and (v == "y" or v == "n")   ) or
-                (self.type == TRISTATE and (v == "y" or v == "m" or
-                                            v == "n")               ) or
-                (self.type == STRING                                ) or
-                (self.type == INT      and _is_base_n(v, 10)        ) or
-                (self.type == HEX      and _is_base_n(v, 16)        )):
-            self.config._warn('the value "{0}" is invalid for {1}, which has '
-                              "type {2}. Assignment ignored."
-                              .format(v, self.name, TYPENAME[self.type]))
-            return
+                        continue
 
-        if not self.prompts and not suppress_load_warnings:
-            self.config._warn('assigning "{0}" to the symbol {1} which '
-                              'lacks prompts and thus has visibility "n". '
-                              'The assignment will have no effect.'
-                              .format(v, self.name))
+                    name = match.group(1)
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, "n", filename, linenr)
+                        continue
 
-        self.user_val = v
+                    if sym.orig_type not in _BOOL_TRISTATE:
+                        continue
 
-        if self.is_choice_sym and (self.type == BOOL or self.type == TRISTATE):
-            choice = self.parent
-            if v == "y":
-                choice.user_val = self
-                choice.user_mode = "y"
-            elif v == "m":
-                choice.user_val = None
-                choice.user_mode = "m"
+                    val = "n"
 
-    def _unset_user_value_no_recursive_invalidate(self):
-        self._invalidate()
-        self.user_val = None
+                # Done parsing the assignment. Set the value.
 
-        if self.is_choice_sym:
-            self.parent._unset_user_value()
+                if sym._was_set:
+                    self._assigned_twice(sym, val, filename, linenr)
 
-    def _make_conf(self, append_fn):
-        if self.already_written:
-            return
+                sym.set_value(val)
 
-        self.already_written = True
+        if replace:
+            # If we're replacing the configuration, unset the symbols that
+            # didn't get set
 
-        # Note: write_to_conf is determined in get_value()
-        val = self.get_value()
-        if not self.write_to_conf:
-            return
+            for sym in self.unique_defined_syms:
+                if not sym._was_set:
+                    sym.unset_value()
 
-        if self.type == BOOL or self.type == TRISTATE:
-            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)
-                      if val == "y" or val == "m" else
-                      "# {0}{1} is not set".format(self.config.config_prefix, self.name))
+            for choice in self.unique_choices:
+                if not choice._was_set:
+                    choice.unset_value()
 
-        elif self.type == INT or self.type == HEX:
-            append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val))
+    def _undef_assign(self, name, val, filename, linenr):
+        # Called for assignments to undefined symbols during .config loading
 
-        elif self.type == STRING:
-            # Escape \ and "
-            append_fn('{0}{1}="{2}"'
-                      .format(self.config.config_prefix, self.name,
-                              val.replace("\\", "\\\\").replace('"', '\\"')))
+        self.missing_syms.append((name, val))
+        if self.warn_assign_undef:
+            self._warn(
+                "attempt to assign the value '{}' to the undefined symbol {}"
+                .format(val, name), filename, linenr)
 
+    def _assigned_twice(self, sym, new_val, filename, linenr):
+        # Called when a symbol is assigned more than once in a .config file
+
+        # Use strings for bool/tristate user values in the warning
+        if sym.orig_type in _BOOL_TRISTATE:
+            user_val = TRI_TO_STR[sym.user_value]
         else:
-            _internal_error("Internal error while creating .config: unknown "
-                            'type "{0}".'.format(self.type))
-
-    def _get_dependent(self):
-        """Returns the set of symbols that should be invalidated if the value
-        of the symbol changes, because they might be affected by the change.
-        Note that this is an internal API -- it's probably of limited
-        usefulness to clients."""
-        if self.cached_deps is not None:
-            return self.cached_deps
-
-        res = set(self.dep)
-        for s in self.dep:
-            res |= s._get_dependent()
-
-        if self.is_choice_sym:
-            # Choice symbols also depend (recursively) on their siblings. The
-            # siblings are not included in 'dep' to avoid dependency loops.
-            for sibling in self.parent.actual_symbols:
-                if sibling is not self:
-                    res.add(sibling)
-                    res |= sibling.dep
-                    for s in sibling.dep:
-                        res |= s._get_dependent()
-
-        self.cached_deps = res
-        return res
+            user_val = sym.user_value
+
+        msg = '{} set more than once. Old value "{}", new value "{}".'.format(
+            _name_and_loc(sym), user_val, new_val)
+
+        if user_val == new_val:
+            if self.warn_assign_redun:
+                self._warn(msg, filename, linenr)
+        elif self.warn_assign_override:
+            self._warn(msg, filename, linenr)
+
+    def write_autoconf(self, filename,
+                       header="/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
+        r"""
+        Writes out symbol values as a C header file, matching the format used
+        by include/generated/autoconf.h in the kernel.
+
+        The ordering of the #defines matches the one generated by
+        write_config(). The order in the C implementation depends on the hash
+        table implementation as of writing, and so won't match.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        filename:
+          Self-explanatory.
+
+        header (default: "/* Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) */\n"):
+          Text that will be inserted verbatim at the beginning of the file. You
+          would usually want it enclosed in '/* */' to make it a C comment,
+          and include a final terminating newline.
+        """
+        self._write_if_changed(filename, self._autoconf_contents(header))
 
-    def _has_auto_menu_dep_on(self, on):
-        """See Choice._determine_actual_symbols()."""
-        if not isinstance(self.parent, Choice):
-            _internal_error("Attempt to determine auto menu dependency for "
-                            "symbol ouside of choice.")
+    def _autoconf_contents(self, header):
+        # write_autoconf() helper. Returns the contents to write as a string,
+        # with 'header' at the beginning.
 
-        if not self.prompts:
-            # If we have no prompt, use the menu dependencies instead (what was
-            # specified with 'depends on')
-            return self.menu_dep is not None and \
-                   self.config._expr_depends_on(self.menu_dep, on)
+        # "".join()ed later
+        chunks = [header]
+        add = chunks.append
 
-        for _, cond_expr in self.prompts:
-            if self.config._expr_depends_on(cond_expr, on):
-                return True
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+            if not sym._write_to_conf:
+                continue
 
-        return False
+            if sym.orig_type in _BOOL_TRISTATE:
+                if val == "y":
+                    add("#define {}{} 1\n"
+                        .format(self.config_prefix, sym.name))
+                elif val == "m":
+                    add("#define {}{}_MODULE 1\n"
+                        .format(self.config_prefix, sym.name))
+
+            elif sym.orig_type is STRING:
+                add('#define {}{} "{}"\n'
+                    .format(self.config_prefix, sym.name, escape(val)))
+
+            else:  # sym.orig_type in _INT_HEX:
+                if sym.orig_type is HEX and \
+                   not val.startswith(("0x", "0X")):
+                    val = "0x" + val
+
+                add("#define {}{} {}\n"
+                    .format(self.config_prefix, sym.name, val))
+
+        return "".join(chunks)
+
+    def write_config(self, filename=None,
+                     header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n",
+                     save_old=True, verbose=None):
+        r"""
+        Writes out symbol values in the .config format. The format matches the
+        C implementation, including ordering.
+
+        Symbols appear in the same order in generated .config files as they do
+        in the Kconfig files. For symbols defined in multiple locations, a
+        single assignment is written out corresponding to the first location
+        where the symbol is defined.
+
+        See the 'Intro to symbol values' section in the module docstring to
+        understand which symbols get written out.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Filename to save configuration to (a string).
+
+          If None (the default), the filename in the environment variable
+          KCONFIG_CONFIG is used if set, and ".config" otherwise. See
+          standard_config_filename().
+
+        header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
+          Text that will be inserted verbatim at the beginning of the file. You
+          would usually want each line to start with '#' to make it a comment,
+          and include a final terminating newline.
+
+        save_old (default: True):
+          If True and <filename> already exists, a copy of it will be saved to
+          <filename>.old in the same directory before the new configuration is
+          written.
+
+          Errors are silently ignored if <filename>.old cannot be written (e.g.
+          due to being a directory, or <filename> being something like
+          /dev/null).
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got saved. This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.write_config()).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("write_config")
 
-class Menu(Item):
+        if filename is None:
+            filename = standard_config_filename()
 
-    """Represents a menu statement."""
+        contents = self._config_contents(header)
+        if self._contents_eq(filename, contents):
+            return "No change to '{}'".format(filename)
 
-    #
-    # Public interface
-    #
+        if save_old:
+            _save_old(filename)
 
-    def get_config(self):
-        """Return the Config instance this menu is from."""
-        return self.config
-
-    def get_title(self):
-        """Returns the title text of the menu."""
-        return self.title
-
-    def get_parent(self):
-        """Returns the menu or choice statement that contains the menu, or
-        None if the menu is at the top level. Note that if statements are
-        treated as syntactic sugar and do not have an explicit class
-        representation."""
-        return self.parent
-
-    def get_location(self):
-        """Returns the location of the menu as a (filename, linenr) tuple,
-        where filename is a string and linenr an int."""
-        return (self.filename, self.linenr)
-
-    def get_items(self, recursive=False):
-        """Returns a list containing the items (symbols, menus, choice
-        statements and comments) in in the menu, in the same order that the
-        items appear within the menu.
-
-        recursive (default: False): True if items contained in items within the
-           menu should be included recursively (preorder)."""
-
-        if not recursive:
-            return self.block
-
-        res = []
-        for item in self.block:
-            res.append(item)
-            if isinstance(item, Menu):
-                res.extend(item.get_items(True))
-            elif isinstance(item, Choice):
-                res.extend(item.get_items())
-        return res
+        with self._open(filename, "w") as f:
+            f.write(contents)
 
-    def get_symbols(self, recursive=False):
-        """Returns a list containing the symbols in the menu, in the same order
-        that they appear within the menu.
+        return "Configuration saved to '{}'".format(filename)
 
-        recursive (default: False): True if symbols contained in items within
-           the menu should be included recursively."""
+    def _config_contents(self, header):
+        # write_config() helper. Returns the contents to write as a string,
+        # with 'header' at the beginning.
+        #
+        # More memory friendly would be to 'yield' the strings and
+        # "".join(_config_contents()), but it was a bit slower on my system.
 
-        return [item for item in self.get_items(recursive) if
-                isinstance(item, Symbol)]
+        # node_iter() was used here before commit 3aea9f7 ("Add '# end of
+        # <menu>' after menus in .config"). Those comments get tricky to
+        # implement with it.
 
-    def get_visibility(self):
-        """Returns the visibility of the menu. This also affects the visibility
-        of subitems. See also Symbol.get_visibility()."""
-        return self.config._eval_expr(self.dep_expr)
+        for sym in self.unique_defined_syms:
+            sym._visited = False
 
-    def get_visible_if_visibility(self):
-        """Returns the visibility the menu gets from its 'visible if'
-        condition. "y" if the menu has no 'visible if' condition."""
-        return self.config._eval_expr(self.visible_if_expr)
+        # Did we just print an '# end of ...' comment?
+        after_end_comment = False
 
-    def get_referenced_symbols(self, refs_from_enclosing=False):
-        """See Symbol.get_referenced_symbols()."""
-        return self.all_referenced_syms if refs_from_enclosing else \
-               self.referenced_syms
+        # "".join()ed later
+        chunks = [header]
+        add = chunks.append
 
-    def __str__(self):
-        """Returns a string containing various information about the menu."""
-        depends_on_str = self.config._expr_val_str(self.orig_deps,
-                                                   "(no dependencies)")
-        visible_if_str = self.config._expr_val_str(self.visible_if_expr,
-                                                   "(no dependencies)")
-
-        additional_deps_str = " " + \
-          self.config._expr_val_str(self.deps_from_containing,
-                                    "(no additional dependencies)")
-
-        return _lines("Menu",
-                      "Title                     : " + self.title,
-                      "'depends on' dependencies : " + depends_on_str,
-                      "'visible if' dependencies : " + visible_if_str,
-                      "Additional dependencies from enclosing menus and "
-                        "ifs:",
-                      additional_deps_str,
-                      "Location: {0}:{1}".format(self.filename, self.linenr))
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+
+                    # Add a comment when leaving visible menus
+                    if node.item is MENU and expr_value(node.dep) and \
+                       expr_value(node.visibility) and \
+                       node is not self.top_node:
+                        add("# end of {}\n".format(node.prompt[0]))
+                        after_end_comment = True
+
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return "".join(chunks)
 
-    #
-    # Private methods
-    #
+            # Generate configuration output for the node
 
-    def __init__(self):
-        """Menu constructor -- not intended to be called directly by
-        Kconfiglib clients."""
-
-        self.title = None
-        self.dep_expr = None
-        self.visible_if_expr = None
-        self.block = [] # List of contained items
-        self.config = None
-        self.parent = None
-
-        # Dependency expression without dependencies from enclosing menus and
-        # ifs propagated
-        self.orig_deps = None
-
-        # Dependencies inherited from containing menus and ifs
-        self.deps_from_containing = None
-        # The set of symbols referenced by this menu (see
-        # get_referenced_symbols())
-        self.referenced_syms = set()
-        # Like 'referenced_syms', but includes symbols from
-        # dependencies inherited from enclosing menus and ifs
-        self.all_referenced_syms = None
+            item = node.item
 
-        self.filename = None
-        self.linenr = None
+            if item.__class__ is Symbol:
+                if item._visited:
+                    continue
+                item._visited = True
 
-    def _make_conf(self, append_fn):
-        if self.config._eval_expr(self.dep_expr) != "n" and \
-           self.config._eval_expr(self.visible_if_expr) != "n":
-            append_fn("\n#\n# {0}\n#".format(self.title))
-        _make_block_conf(self.block, append_fn)
+                conf_string = item.config_string
+                if not conf_string:
+                    continue
 
-class Choice(Item):
+                if after_end_comment:
+                    # Add a blank line before the first symbol printed after an
+                    # '# end of ...' comment
+                    after_end_comment = False
+                    add("\n")
+                add(conf_string)
 
-    """Represents a choice statement. A choice can be in one of three modes:
+            elif expr_value(node.dep) and \
+                 ((item is MENU and expr_value(node.visibility)) or
+                  item is COMMENT):
 
-    "n" - The choice is not visible and no symbols can be selected.
+                add("\n#\n# {}\n#\n".format(node.prompt[0]))
+                after_end_comment = False
 
-    "m" - Any number of symbols can be set to "m". The rest will be "n". This
-          is safe since potentially conflicting options don't actually get
-          compiled into the kernel simultaneously with "m".
+    def write_min_config(self, filename,
+                         header="# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
+        """
+        Writes out a "minimal" configuration file, omitting symbols whose value
+        matches their default value. The format matches the one produced by
+        'make savedefconfig'.
+
+        The resulting configuration file is incomplete, but a complete
+        configuration can be derived from it by loading it. Minimal
+        configuration files can serve as a more manageable configuration format
+        compared to a "full" .config file, especially when configurations files
+        are merged or edited by hand.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename:
+          Self-explanatory.
+
+        header (default: "# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)\n"):
+          Text that will be inserted verbatim at the beginning of the file. You
+          would usually want each line to start with '#' to make it a comment,
+          and include a final terminating newline.
+
+        Returns a string with a message saying which file got saved. This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.write_min_config()).
+        """
+        contents = self._min_config_contents(header)
+        if self._contents_eq(filename, contents):
+            return "No change to '{}'".format(filename)
+
+        with self._open(filename, "w") as f:
+            f.write(contents)
+
+        return "Minimal configuration saved to '{}'".format(filename)
+
+    def _min_config_contents(self, header):
+        # write_min_config() helper. Returns the contents to write as a string,
+        # with 'header' at the beginning.
+
+        chunks = [header]
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # Skip symbols that cannot be changed. Only check
+            # non-choice symbols, as selects don't affect choice
+            # symbols.
+            if not sym.choice and \
+               sym.visibility <= expr_value(sym.rev_dep):
+                continue
+
+            # Skip symbols whose value matches their default
+            if sym.str_value == sym._str_default():
+                continue
+
+            # Skip symbols that would be selected by default in a
+            # choice, unless the choice is optional or the symbol type
+            # isn't bool (it might be possible to set the choice mode
+            # to n or the symbol to m in those cases).
+            if sym.choice and \
+               not sym.choice.is_optional and \
+               sym.choice._selection_from_defaults() is sym and \
+               sym.orig_type is BOOL and \
+               sym.tri_value == 2:
+                continue
+
+            add(sym.config_string)
+
+        return "".join(chunks)
+
+    def sync_deps(self, path):
+        """
+        Creates or updates a directory structure that can be used to avoid
+        doing a full rebuild whenever the configuration is changed, mirroring
+        include/config/ in the kernel.
+
+        This function is intended to be called during each build, before
+        compiling source files that depend on configuration symbols.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        path:
+          Path to directory
+
+        sync_deps(path) does the following:
+
+          1. If the directory <path> does not exist, it is created.
+
+          2. If <path>/auto.conf exists, old symbol values are loaded from it,
+             which are then compared against the current symbol values. If a
+             symbol has changed value (would generate different output in
+             autoconf.h compared to before), the change is signaled by
+             touch'ing a file corresponding to the symbol.
+
+             The first time sync_deps() is run on a directory, <path>/auto.conf
+             won't exist, and no old symbol values will be available. This
+             logically has the same effect as updating the entire
+             configuration.
+
+             The path to a symbol's file is calculated from the symbol's name
+             by replacing all '_' with '/' and appending '.h'. For example, the
+             symbol FOO_BAR_BAZ gets the file <path>/foo/bar/baz.h, and FOO
+             gets the file <path>/foo.h.
+
+             This scheme matches the C tools. The point is to avoid having a
+             single directory with a huge number of files, which the underlying
+             filesystem might not handle well.
+
+          3. A new auto.conf with the current symbol values is written, to keep
+             track of them for the next build.
+
+             If auto.conf exists and its contents is identical to what would
+             get written out, it is left untouched. This avoids updating file
+             metadata like the modification time and possibly triggering
+             redundant work in build tools.
+
+
+        The last piece of the puzzle is knowing what symbols each source file
+        depends on. Knowing that, dependencies can be added from source files
+        to the files corresponding to the symbols they depends on. The source
+        file will then get recompiled (only) when the symbol value changes
+        (provided sync_deps() is run first during each build).
+
+        The tool in the kernel that extracts symbol dependencies from source
+        files is scripts/basic/fixdep.c. Missing symbol files also correspond
+        to "not changed", which fixdep deals with by using the $(wildcard) Make
+        function when adding symbol prerequisites to source files.
+
+        In case you need a different scheme for your project, the sync_deps()
+        implementation can be used as a template.
+        """
+        if not exists(path):
+            os.mkdir(path, 0o755)
+
+        # Load old values from auto.conf, if any
+        self._load_old_vals(path)
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+
+            # n tristate values do not get written to auto.conf and autoconf.h,
+            # making a missing symbol logically equivalent to n
+
+            if sym._write_to_conf:
+                if sym._old_val is None and \
+                   sym.orig_type in _BOOL_TRISTATE and \
+                   val == "n":
+                    # No old value (the symbol was missing or n), new value n.
+                    # No change.
+                    continue
+
+                if val == sym._old_val:
+                    # New value matches old. No change.
+                    continue
+
+            elif sym._old_val is None:
+                # The symbol wouldn't appear in autoconf.h (because
+                # _write_to_conf is false), and it wouldn't have appeared in
+                # autoconf.h previously either (because it didn't appear in
+                # auto.conf). No change.
+                continue
+
+            # 'sym' has a new value. Flag it.
+            _touch_dep_file(path, sym.name)
+
+        # Remember the current values as the "new old" values.
+        #
+        # This call could go anywhere after the call to _load_old_vals(), but
+        # putting it last means _sync_deps() can be safely rerun if it fails
+        # before this point.
+        self._write_old_vals(path)
+
+    def _load_old_vals(self, path):
+        # Loads old symbol values from auto.conf into a dedicated
+        # Symbol._old_val field. Mirrors load_config().
+        #
+        # The extra field could be avoided with some trickery involving dumping
+        # symbol values and restoring them later, but this is simpler and
+        # faster. The C tools also use a dedicated field for this purpose.
+
+        for sym in self.unique_defined_syms:
+            sym._old_val = None
+
+        try:
+            auto_conf = self._open(join(path, "auto.conf"), "r")
+        except EnvironmentError as e:
+            if e.errno == errno.ENOENT:
+                # No old values
+                return
+            raise
+
+        with auto_conf as f:
+            for line in f:
+                match = self._set_match(line)
+                if not match:
+                    # We only expect CONFIG_FOO=... (and possibly a header
+                    # comment) in auto.conf
+                    continue
+
+                name, val = match.groups()
+                if name in self.syms:
+                    sym = self.syms[name]
+
+                    if sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            continue
+                        val = unescape(match.group(1))
+
+                    self.syms[name]._old_val = val
+                else:
+                    # Flag that the symbol no longer exists, in
+                    # case something still depends on it
+                    _touch_dep_file(path, name)
+
+    def _write_old_vals(self, path):
+        # Helper for writing auto.conf. Basically just a simplified
+        # write_config() that doesn't write any comments (including
+        # '# CONFIG_FOO is not set' comments). The format matches the C
+        # implementation, though the ordering is arbitrary there (depends on
+        # the hash table implementation).
+        #
+        # A separate helper function is neater than complicating write_config()
+        # by passing a flag to it, plus we only need to look at symbols here.
+
+        self._write_if_changed(
+            os.path.join(path, "auto.conf"),
+            self._old_vals_contents())
+
+    def _old_vals_contents(self):
+        # _write_old_vals() helper. Returns the contents to write as a string.
+
+        # Temporary list instead of generator makes this a bit faster
+        return "".join([
+            sym.config_string for sym in self.unique_defined_syms
+                if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value)
+        ])
+
+    def node_iter(self, unique_syms=False):
+        """
+        Returns a generator for iterating through all MenuNode's in the Kconfig
+        tree. The iteration is done in Kconfig definition order (each node is
+        visited before its children, and the children of a node are visited
+        before the next node).
+
+        The Kconfig.top_node menu node is skipped. It contains an implicit menu
+        that holds the top-level items.
+
+        As an example, the following code will produce a list equal to
+        Kconfig.defined_syms:
+
+          defined_syms = [node.item for node in kconf.node_iter()
+                          if isinstance(node.item, Symbol)]
+
+        unique_syms (default: False):
+          If True, only the first MenuNode will be included for symbols defined
+          in multiple locations.
+
+          Using kconf.node_iter(True) in the example above would give a list
+          equal to unique_defined_syms.
+        """
+        if unique_syms:
+            for sym in self.unique_defined_syms:
+                sym._visited = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return
+
+            if unique_syms and node.item.__class__ is Symbol:
+                if node.item._visited:
+                    continue
+                node.item._visited = True
+
+            yield node
+
+    def eval_string(self, s):
+        """
+        Returns the tristate value of the expression 's', represented as 0, 1,
+        and 2 for n, m, and y, respectively. Raises KconfigError on syntax
+        errors. Warns if undefined symbols are referenced.
+
+        As an example, if FOO and BAR are tristate symbols at least one of
+        which has the value y, then eval_string("y && (FOO || BAR)") returns
+        2 (y).
+
+        To get the string value of non-bool/tristate symbols, use
+        Symbol.str_value. eval_string() always returns a tristate value, and
+        all non-bool/tristate symbols have the tristate value 0 (n).
+
+        The expression parsing is consistent with how parsing works for
+        conditional ('if ...') expressions in the configuration, and matches
+        the C implementation. m is rewritten to 'm && MODULES', so
+        eval_string("m") will return 0 (n) unless modules are enabled.
+        """
+        # The parser is optimized to be fast when parsing Kconfig files (where
+        # an expression can never appear at the beginning of a line). We have
+        # to monkey-patch things a bit here to reuse it.
+
+        self.filename = None
+
+        self._tokens = self._tokenize("if " + s)
+        # Strip "if " to avoid giving confusing error messages
+        self._line = s
+        self._tokens_i = 1  # Skip the 'if' token
+
+        return expr_value(self._expect_expr_and_eol())
+
+    def unset_values(self):
+        """
+        Removes any user values from all symbols, as if Kconfig.load_config()
+        or Symbol.set_value() had never been called.
+        """
+        self._warn_assign_no_prompt = False
+        try:
+            # set_value() already rejects undefined symbols, and they don't
+            # need to be invalidated (because their value never changes), so we
+            # can just iterate over defined symbols
+            for sym in self.unique_defined_syms:
+                sym.unset_value()
+
+            for choice in self.unique_choices:
+                choice.unset_value()
+        finally:
+            self._warn_assign_no_prompt = True
+
+    def enable_warnings(self):
+        """
+        Do 'Kconfig.warn = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = True
+
+    def disable_warnings(self):
+        """
+        Do 'Kconfig.warn = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = False
+
+    def enable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = True
+
+    def disable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = False
+
+    def enable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_undef = True
+
+    def disable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_undef = False
+
+    def enable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = True' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = True
+
+    def disable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = False
+
+    def enable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_redun = True
+
+    def disable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_redun = False
+
+    def __repr__(self):
+        """
+        Returns a string with information about the Kconfig object when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        def status(flag):
+            return "enabled" if flag else "disabled"
+
+        return "<{}>".format(", ".join((
+            "configuration with {} symbols".format(len(self.syms)),
+            'main menu prompt "{}"'.format(self.mainmenu_text),
+            "srctree is current directory" if not self.srctree else
+                'srctree "{}"'.format(self.srctree),
+            'config symbol prefix "{}"'.format(self.config_prefix),
+            "warnings " + status(self.warn),
+            "printing of warnings to stderr " + status(self.warn_to_stderr),
+            "undef. symbol assignment warnings " +
+                status(self.warn_assign_undef),
+            "overriding symbol assignment warnings " +
+                status(self.warn_assign_override),
+            "redundant symbol assignment warnings " +
+                status(self.warn_assign_redun)
+        )))
+
+    #
+    # Private methods
+    #
+
+
+    #
+    # File reading
+    #
+
+    def _open_config(self, filename):
+        # Opens a .config file. First tries to open 'filename', then
+        # '$srctree/filename' if $srctree was set when the configuration was
+        # loaded.
+
+        try:
+            return self._open(filename, "r")
+        except EnvironmentError as e:
+            # This will try opening the same file twice if $srctree is unset,
+            # but it's not a big deal
+            try:
+                return self._open(join(self.srctree, filename), "r")
+            except EnvironmentError as e2:
+                # This is needed for Python 3, because e2 is deleted after
+                # the try block:
+                #
+                # https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
+                e = e2
+
+            raise _KconfigIOError(
+                e, "Could not open '{}' ({}: {}). Check that the $srctree "
+                   "environment variable ({}) is set correctly."
+                   .format(filename, errno.errorcode[e.errno], e.strerror,
+                           "set to '{}'".format(self.srctree) if self.srctree
+                               else "unset or blank"))
+
+    def _enter_file(self, filename):
+        # Jumps to the beginning of a sourced Kconfig file, saving the previous
+        # position and file object.
+        #
+        # filename:
+        #   Absolute path to file
+
+        # Path relative to $srctree, stored in e.g. self.filename (which makes
+        # it indirectly show up in MenuNode.filename). Equals 'filename' for
+        # absolute paths passed to 'source'.
+        if filename.startswith(self._srctree_prefix):
+            # Relative path (or a redundant absolute path to within $srctree,
+            # but it's probably fine to reduce those too)
+            rel_filename = filename[len(self._srctree_prefix):]
+        else:
+            # Absolute path
+            rel_filename = filename
+
+        self.kconfig_filenames.append(rel_filename)
+
+        # The parent Kconfig files are represented as a list of
+        # (<include path>, <Python 'file' object for Kconfig file>) tuples.
+        #
+        # <include path> is immutable and holds a *tuple* of
+        # (<filename>, <linenr>) tuples, giving the locations of the 'source'
+        # statements in the parent Kconfig files. The current include path is
+        # also available in Kconfig._include_path.
+        #
+        # The point of this redundant setup is to allow Kconfig._include_path
+        # to be assigned directly to MenuNode.include_path without having to
+        # copy it, sharing it wherever possible.
+
+        # Save include path and 'file' object (via its 'readline' function)
+        # before entering the file
+        self._filestack.append((self._include_path, self._readline))
+
+        # _include_path is a tuple, so this rebinds the variable instead of
+        # doing in-place modification
+        self._include_path += ((self.filename, self.linenr),)
+
+        # Check for recursive 'source'
+        for name, _ in self._include_path:
+            if name == rel_filename:
+                raise KconfigError(
+                    "\n{}:{}: recursive 'source' of '{}' detected. Check that "
+                    "environment variables are set correctly.\n"
+                    "Include path:\n{}"
+                    .format(self.filename, self.linenr, rel_filename,
+                            "\n".join("{}:{}".format(name, linenr)
+                                      for name, linenr in self._include_path)))
+
+        try:
+            self._readline = self._open(filename, "r").readline
+        except EnvironmentError as e:
+            # We already know that the file exists
+            raise _KconfigIOError(
+                e, "{}:{}: Could not open '{}' (in '{}') ({}: {})"
+                   .format(self.filename, self.linenr, filename,
+                           self._line.strip(),
+                           errno.errorcode[e.errno], e.strerror))
+
+        self.filename = rel_filename
+        self.linenr = 0
+
+    def _leave_file(self):
+        # Returns from a Kconfig file to the file that sourced it. See
+        # _enter_file().
+
+        # Restore location from parent Kconfig file
+        self.filename, self.linenr = self._include_path[-1]
+        # Restore include path and 'file' object
+        self._readline.__self__.close()  # __self__ fetches the 'file' object
+        self._include_path, self._readline = self._filestack.pop()
+
+    def _next_line(self):
+        # Fetches and tokenizes the next line from the current Kconfig file.
+        # Returns False at EOF and True otherwise.
+
+        # We might already have tokens from parsing a line and discovering that
+        # it's part of a different construct
+        if self._reuse_tokens:
+            self._reuse_tokens = False
+            # self._tokens_i is known to be 1 here, because _parse_properties()
+            # leaves it like that when it can't recognize a line (or parses
+            # a help text)
+            return True
+
+        # readline() returns '' over and over at EOF, which we rely on for help
+        # texts at the end of files (see _line_after_help())
+        line = self._readline()
+        if not line:
+            return False
+        self.linenr += 1
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        # Initialize to 1 instead of 0 to factor out code from _parse_block()
+        # and _parse_properties(). They immediately fetch self._tokens[0].
+        self._tokens_i = 1
+
+        return True
+
+    def _line_after_help(self, line):
+        # Tokenizes a line after a help text. This case is special in that the
+        # line has already been fetched (to discover that it isn't part of the
+        # help text).
+        #
+        # An earlier version used a _saved_line variable instead that was
+        # checked in _next_line(). This special-casing gets rid of it and makes
+        # _reuse_tokens alone sufficient to handle unget.
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        self._reuse_tokens = True
+
+    def _write_if_changed(self, filename, contents):
+        # Writes 'contents' into 'filename', but only if it differs from the
+        # current contents of the file.
+        #
+        # Another variant would be write a temporary file on the same
+        # filesystem, compare the files, and rename() the temporary file if it
+        # differs, but it breaks stuff like write_config("/dev/null"), which is
+        # used out there to force evaluation-related warnings to be generated.
+        # This simple version is pretty failsafe and portable.
+
+        if not self._contents_eq(filename, contents):
+            with self._open(filename, "w") as f:
+                f.write(contents)
+
+    def _contents_eq(self, filename, contents):
+        # Returns True if the contents of 'filename' is 'contents' (a string),
+        # and False otherwise (including if 'filename' can't be opened/read)
+
+        try:
+            with self._open(filename, "r") as f:
+                # Robust re. things like encoding and line endings (mmap()
+                # trickery isn't)
+                return f.read(len(contents) + 1) == contents
+        except EnvironmentError:
+            # If the error here would prevent writing the file as well, we'll
+            # notice it later
+            return False
+
+    #
+    # Tokenization
+    #
+
+    def _lookup_sym(self, name):
+        # Fetches the symbol 'name' from the symbol table, creating and
+        # registering it if it does not exist. If '_parsing_kconfigs' is False,
+        # it means we're in eval_string(), and new symbols won't be registered.
+
+        if name in self.syms:
+            return self.syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = False
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.syms[name] = sym
+        else:
+            self._warn("no symbol {} in configuration".format(name))
+
+        return sym
+
+    def _lookup_const_sym(self, name):
+        # Like _lookup_sym(), for constant (quoted) symbols
+
+        if name in self.const_syms:
+            return self.const_syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = True
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.const_syms[name] = sym
+
+        return sym
+
+    def _tokenize(self, s):
+        # Parses 's', returning a None-terminated list of tokens. Registers any
+        # new symbols encountered with _lookup(_const)_sym().
+        #
+        # Tries to be reasonably speedy by processing chunks of text via
+        # regexes and string operations where possible. This is the biggest
+        # hotspot during parsing.
+        #
+        # It might be possible to rewrite this to 'yield' tokens instead,
+        # working across multiple lines. Lookback and compatibility with old
+        # janky versions of the C tools complicate things though.
+
+        self._line = s  # Used for error reporting
+
+        # Initial token on the line
+        match = _command_match(s)
+        if not match:
+            if s.isspace() or s.lstrip().startswith("#"):
+                return (None,)
+            self._parse_error("unknown token at start of line")
+
+        # Tricky implementation detail: While parsing a token, 'token' refers
+        # to the previous token. See _STRING_LEX for why this is needed.
+        token = _get_keyword(match.group(1))
+        if not token:
+            # Backwards compatibility with old versions of the C tools, which
+            # (accidentally) accepted stuff like "--help--" and "-help---".
+            # This was fixed in the C tools by commit c2264564 ("kconfig: warn
+            # of unhandled characters in Kconfig commands"), committed in July
+            # 2015, but it seems people still run Kconfiglib on older kernels.
+            if s.strip(" \t\n-") == "help":
+                return (_T_HELP, None)
+
+            # If the first token is not a keyword (and not a weird help token),
+            # we have a preprocessor variable assignment (or a bare macro on a
+            # line)
+            self._parse_assignment(s)
+            return (None,)
+
+        tokens = [token]
+        # The current index in the string being tokenized
+        i = match.end()
+
+        # Main tokenization loop (for tokens past the first one)
+        while i < len(s):
+            # Test for an identifier/keyword first. This is the most common
+            # case.
+            match = _id_keyword_match(s, i)
+            if match:
+                # We have an identifier or keyword
+
+                # Check what it is. lookup_sym() will take care of allocating
+                # new symbols for us the first time we see them. Note that
+                # 'token' still refers to the previous token.
+
+                name = match.group(1)
+                keyword = _get_keyword(name)
+                if keyword:
+                    # It's a keyword
+                    token = keyword
+                    # Jump past it
+                    i = match.end()
+
+                elif token not in _STRING_LEX:
+                    # It's a non-const symbol, except we translate n, m, and y
+                    # into the corresponding constant symbols, like the C
+                    # implementation
+
+                    if "$" in name:
+                        # Macro expansion within symbol name
+                        name, s, i = self._expand_name(s, i)
+                    else:
+                        i = match.end()
+
+                    token = self.const_syms[name] if name in STR_TO_TRI else \
+                        self._lookup_sym(name)
+
+                else:
+                    # It's a case of missing quotes. For example, the
+                    # following is accepted:
+                    #
+                    #   menu unquoted_title
+                    #
+                    #   config A
+                    #       tristate unquoted_prompt
+                    #
+                    #   endmenu
+                    #
+                    # Named choices ('choice FOO') also end up here.
+
+                    if token is not _T_CHOICE:
+                        self._warn("style: quotes recommended around '{}' in '{}'"
+                                   .format(name, self._line.strip()),
+                                   self.filename, self.linenr)
+
+                    token = name
+                    i = match.end()
+
+            else:
+                # Neither a keyword nor a non-const symbol
+
+                # We always strip whitespace after tokens, so it is safe to
+                # assume that s[i] is the start of a token here.
+                c = s[i]
+
+                if c in "\"'":
+                    if "$" not in s and "\\" not in s:
+                        # Fast path for lines without $ and \. Find the
+                        # matching quote.
+                        end_i = s.find(c, i + 1) + 1
+                        if not end_i:
+                            self._parse_error("unterminated string")
+                        val = s[i + 1:end_i - 1]
+                        i = end_i
+                    else:
+                        # Slow path
+                        s, end_i = self._expand_str(s, i)
+
+                        # os.path.expandvars() and the $UNAME_RELEASE replace()
+                        # is a backwards compatibility hack, which should be
+                        # reasonably safe as expandvars() leaves references to
+                        # undefined env. vars. as is.
+                        #
+                        # The preprocessor functionality changed how
+                        # environment variables are referenced, to $(FOO).
+                        val = expandvars(s[i + 1:end_i - 1]
+                                         .replace("$UNAME_RELEASE",
+                                                  _UNAME_RELEASE))
+
+                        i = end_i
+
+                    # This is the only place where we don't survive with a
+                    # single token of lookback: 'option env="FOO"' does not
+                    # refer to a constant symbol named "FOO".
+                    token = \
+                        val if token in _STRING_LEX or tokens[0] is _T_OPTION \
+                        else self._lookup_const_sym(val)
+
+                elif s.startswith("&&", i):
+                    token = _T_AND
+                    i += 2
+
+                elif s.startswith("||", i):
+                    token = _T_OR
+                    i += 2
+
+                elif c == "=":
+                    token = _T_EQUAL
+                    i += 1
+
+                elif s.startswith("!=", i):
+                    token = _T_UNEQUAL
+                    i += 2
+
+                elif c == "!":
+                    token = _T_NOT
+                    i += 1
+
+                elif c == "(":
+                    token = _T_OPEN_PAREN
+                    i += 1
+
+                elif c == ")":
+                    token = _T_CLOSE_PAREN
+                    i += 1
+
+                elif c == "#":
+                    break
+
+
+                # Very rare
+
+                elif s.startswith("<=", i):
+                    token = _T_LESS_EQUAL
+                    i += 2
+
+                elif c == "<":
+                    token = _T_LESS
+                    i += 1
+
+                elif s.startswith(">=", i):
+                    token = _T_GREATER_EQUAL
+                    i += 2
+
+                elif c == ">":
+                    token = _T_GREATER
+                    i += 1
+
+
+                else:
+                    self._parse_error("unknown tokens in line")
+
+
+                # Skip trailing whitespace
+                while i < len(s) and s[i].isspace():
+                    i += 1
+
+
+            # Add the token
+            tokens.append(token)
+
+        # None-terminating the token list makes token fetching simpler/faster
+        tokens.append(None)
+
+        return tokens
+
+    # Helpers for syntax checking and token fetching. See the
+    # 'Intro to expressions' section for what a constant symbol is.
+    #
+    # More of these could be added, but the single-use cases are inlined as an
+    # optimization.
+
+    def _expect_sym(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not Symbol:
+            self._parse_error("expected symbol")
+
+        return token
+
+    def _expect_nonconst_sym(self):
+        # Used for 'select' and 'imply' only. We know the token indices.
+
+        token = self._tokens[1]
+        self._tokens_i = 2
+
+        if token.__class__ is not Symbol or token.is_constant:
+            self._parse_error("expected nonconstant symbol")
+
+        return token
+
+    def _expect_str_and_eol(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not str:
+            self._parse_error("expected string")
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return token
+
+    def _expect_expr_and_eol(self):
+        expr = self._parse_expr(True)
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _check_token(self, token):
+        # If the next token is 'token', removes it and returns True
+
+        if self._tokens[self._tokens_i] is token:
+            self._tokens_i += 1
+            return True
+        return False
+
+    #
+    # Preprocessor logic
+    #
+
+    def _parse_assignment(self, s):
+        # Parses a preprocessor variable assignment, registering the variable
+        # if it doesn't already exist. Also takes care of bare macros on lines
+        # (which are allowed, and can be useful for their side effects).
+
+        # Expand any macros in the left-hand side of the assignment (the
+        # variable name)
+        s = s.lstrip()
+        i = 0
+        while 1:
+            i = _assignment_lhs_fragment_match(s, i).end()
+            if s.startswith("$(", i):
+                s, i = self._expand_macro(s, i, ())
+            else:
+                break
+
+        if s.isspace():
+            # We also accept a bare macro on a line (e.g.
+            # $(warning-if,$(foo),ops)), provided it expands to a blank string
+            return
+
+        # Assigned variable
+        name = s[:i]
+
+
+        # Extract assignment operator (=, :=, or +=) and value
+        rhs_match = _assignment_rhs_match(s, i)
+        if not rhs_match:
+            self._parse_error("syntax error")
+
+        op, val = rhs_match.groups()
+
+
+        if name in self.variables:
+            # Already seen variable
+            var = self.variables[name]
+        else:
+            # New variable
+            var = Variable()
+            var.kconfig = self
+            var.name = name
+            var._n_expansions = 0
+            self.variables[name] = var
+
+            # += acts like = on undefined variables (defines a recursive
+            # variable)
+            if op == "+=":
+                op = "="
+
+        if op == "=":
+            var.is_recursive = True
+            var.value = val
+        elif op == ":=":
+            var.is_recursive = False
+            var.value = self._expand_whole(val, ())
+        else:  # op == "+="
+            # += does immediate expansion if the variable was last set
+            # with :=
+            var.value += " " + (val if var.is_recursive else
+                                self._expand_whole(val, ()))
+
+    def _expand_whole(self, s, args):
+        # Expands preprocessor macros in all of 's'. Used whenever we don't
+        # have to worry about delimiters. See _expand_macro() re. the 'args'
+        # parameter.
+        #
+        # Returns the expanded string.
+
+        i = 0
+        while 1:
+            i = s.find("$(", i)
+            if i == -1:
+                break
+            s, i = self._expand_macro(s, i, args)
+        return s
+
+    def _expand_name(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded name, the expanded 's' (including the part
+        # before the name), and the index of the first character in the next
+        # token after the name.
+
+        s, end_i = self._expand_name_iter(s, i)
+        name = s[i:end_i]
+        # isspace() is False for empty strings
+        if not name.strip():
+            # Avoid creating a Kconfig symbol with a blank name. It's almost
+            # guaranteed to be an error.
+            self._parse_error("macro expanded to blank string")
+
+        # Skip trailing whitespace
+        while end_i < len(s) and s[end_i].isspace():
+            end_i += 1
+
+        return name, s, end_i
+
+    def _expand_name_iter(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded 's' (including the part before the name) and the
+        # index of the first character after the expanded name in 's'.
+
+        while 1:
+            match = _name_special_search(s, i)
+
+            if match.group() == "$(":
+                s, i = self._expand_macro(s, match.start(), ())
+            else:
+                return (s, match.start())
+
+    def _expand_str(self, s, i):
+        # Expands a quoted string starting at index 'i' in 's'. Handles both
+        # backslash escapes and macro expansion.
+        #
+        # Returns the expanded 's' (including the part before the string) and
+        # the index of the first character after the expanded string in 's'.
+
+        quote = s[i]
+        i += 1  # Skip over initial "/'
+        while 1:
+            match = _string_special_search(s, i)
+            if not match:
+                self._parse_error("unterminated string")
+
+
+            if match.group() == quote:
+                # Found the end of the string
+                return (s, match.end())
+
+            elif match.group() == "\\":
+                # Replace '\x' with 'x'. 'i' ends up pointing to the character
+                # after 'x', which allows macros to be canceled with '\$(foo)'.
+                i = match.end()
+                s = s[:match.start()] + s[i:]
+
+            elif match.group() == "$(":
+                # A macro call within the string
+                s, i = self._expand_macro(s, match.start(), ())
+
+            else:
+                # A ' quote within " quotes or vice versa
+                i += 1
+
+    def _expand_macro(self, s, i, args):
+        # Expands a macro starting at index 'i' in 's'. If this macro resulted
+        # from the expansion of another macro, 'args' holds the arguments
+        # passed to that macro.
+        #
+        # Returns the expanded 's' (including the part before the macro) and
+        # the index of the first character after the expanded macro in 's'.
+
+        start = i
+        i += 2  # Skip over "$("
+
+        # Start of current macro argument
+        arg_start = i
+
+        # Arguments of this macro call
+        new_args = []
+
+        while 1:
+            match = _macro_special_search(s, i)
+            if not match:
+                self._parse_error("missing end parenthesis in macro expansion")
+
+
+            if match.group() == ")":
+                # Found the end of the macro
+
+                new_args.append(s[arg_start:match.start()])
+
+                prefix = s[:start]
+
+                # $(1) is replaced by the first argument to the function, etc.,
+                # provided at least that many arguments were passed
+
+                try:
+                    # Does the macro look like an integer, with a corresponding
+                    # argument? If so, expand it to the value of the argument.
+                    prefix += args[int(new_args[0])]
+                except (ValueError, IndexError):
+                    # Regular variables are just functions without arguments,
+                    # and also go through the function value path
+                    prefix += self._fn_val(new_args)
+
+                return (prefix + s[match.end():],
+                        len(prefix))
+
+            elif match.group() == ",":
+                # Found the end of a macro argument
+                new_args.append(s[arg_start:match.start()])
+                arg_start = i = match.end()
+
+            else:  # match.group() == "$("
+                # A nested macro call within the macro
+                s, i = self._expand_macro(s, match.start(), args)
+
+    def _fn_val(self, args):
+        # Returns the result of calling the function args[0] with the arguments
+        # args[1..len(args)-1]. Plain variables are treated as functions
+        # without arguments.
+
+        fn = args[0]
+
+        if fn in self.variables:
+            var = self.variables[fn]
+
+            if len(args) == 1:
+                # Plain variable
+                if var._n_expansions:
+                    self._parse_error("Preprocessor variable {} recursively "
+                                      "references itself".format(var.name))
+            elif var._n_expansions > 100:
+                # Allow functions to call themselves, but guess that functions
+                # that are overly recursive are stuck
+                self._parse_error("Preprocessor function {} seems stuck "
+                                  "in infinite recursion".format(var.name))
+
+            var._n_expansions += 1
+            res = self._expand_whole(self.variables[fn].value, args)
+            var._n_expansions -= 1
+            return res
+
+        if fn in self._functions:
+            # Built-in or user-defined function
+
+            py_fn, min_arg, max_arg = self._functions[fn]
+
+            if len(args) - 1 < min_arg or \
+               (max_arg is not None and len(args) - 1 > max_arg):
+
+                if min_arg == max_arg:
+                    expected_args = min_arg
+                elif max_arg is None:
+                    expected_args = "{} or more".format(min_arg)
+                else:
+                    expected_args = "{}-{}".format(min_arg, max_arg)
+
+                raise KconfigError("{}:{}: bad number of arguments in call "
+                                   "to {}, expected {}, got {}"
+                                   .format(self.filename, self.linenr, fn,
+                                           expected_args, len(args) - 1))
+
+            return py_fn(self, *args)
+
+        # Environment variables are tried last
+        if fn in os.environ:
+            self.env_vars.add(fn)
+            return os.environ[fn]
+
+        return ""
+
+    #
+    # Parsing
+    #
+
+    def _make_and(self, e1, e2):
+        # Constructs an AND (&&) expression. Performs trivial simplification.
+
+        if e1 is self.y:
+            return e2
+
+        if e2 is self.y:
+            return e1
+
+        if e1 is self.n or e2 is self.n:
+            return self.n
+
+        return (AND, e1, e2)
+
+    def _make_or(self, e1, e2):
+        # Constructs an OR (||) expression. Performs trivial simplification.
+
+        if e1 is self.n:
+            return e2
+
+        if e2 is self.n:
+            return e1
+
+        if e1 is self.y or e2 is self.y:
+            return self.y
+
+        return (OR, e1, e2)
+
+    def _parse_block(self, end_token, parent, prev):
+        # Parses a block, which is the contents of either a file or an if,
+        # menu, or choice statement.
+        #
+        # end_token:
+        #   The token that ends the block, e.g. _T_ENDIF ("endif") for ifs.
+        #   None for files.
+        #
+        # parent:
+        #   The parent menu node, corresponding to a menu, Choice, or 'if'.
+        #   'if's are flattened after parsing.
+        #
+        # prev:
+        #   The previous menu node. New nodes will be added after this one (by
+        #   modifying their 'next' pointer).
+        #
+        #   'prev' is reused to parse a list of child menu nodes (for a menu or
+        #   Choice): After parsing the children, the 'next' pointer is assigned
+        #   to the 'list' pointer to "tilt up" the children above the node.
+        #
+        # Returns the final menu node in the block (or 'prev' if the block is
+        # empty). This allows chaining.
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
+                # The tokenizer allocates Symbol objects for us
+                sym = self._tokens[1]
+
+                if sym.__class__ is not Symbol or sym.is_constant:
+                    self._parse_error("missing or bad symbol name")
+
+                if self._tokens[2] is not None:
+                    self._trailing_tokens_error()
+
+                self.defined_syms.append(sym)
+
+                node = MenuNode()
+                node.kconfig = self
+                node.item = sym
+                node.is_menuconfig = (t0 is _T_MENUCONFIG)
+                node.prompt = node.help = node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                sym.nodes.append(node)
+
+                self._parse_properties(node)
+
+                if node.is_menuconfig and not node.prompt:
+                    self._warn("the menuconfig symbol {} has no prompt"
+                               .format(_name_and_loc(sym)))
+
+                # Equivalent to
+                #
+                #   prev.next = node
+                #   prev = node
+                #
+                # due to tricky Python semantics. The order matters.
+                prev.next = prev = node
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 in _SOURCE_TOKENS:
+                pattern = self._expect_str_and_eol()
+
+                if t0 in _REL_SOURCE_TOKENS:
+                    # Relative source
+                    pattern = join(dirname(self.filename), pattern)
+
+                # - glob() doesn't support globbing relative to a directory, so
+                #   we need to prepend $srctree to 'pattern'. Use join()
+                #   instead of '+' so that an absolute path in 'pattern' is
+                #   preserved.
+                #
+                # - Sort the glob results to ensure a consistent ordering of
+                #   Kconfig symbols, which indirectly ensures a consistent
+                #   ordering in e.g. .config files
+                filenames = sorted(iglob(join(self._srctree_prefix, pattern)))
+
+                if not filenames and t0 in _OBL_SOURCE_TOKENS:
+                    raise KconfigError(
+                        "{}:{}: '{}' not found (in '{}'). Check that "
+                        "environment variables are set correctly (e.g. "
+                        "$srctree, which is {}). Also note that unset "
+                        "environment variables expand to the empty string."
+                        .format(self.filename, self.linenr, pattern,
+                                self._line.strip(),
+                                "set to '{}'".format(self.srctree)
+                                    if self.srctree else "unset or blank"))
+
+                for filename in filenames:
+                    self._enter_file(filename)
+                    prev = self._parse_block(None, parent, prev)
+                    self._leave_file()
+
+            elif t0 is end_token:
+                # Reached the end of the block. Terminate the final node and
+                # return it.
+
+                if self._tokens[1] is not None:
+                    self._trailing_tokens_error()
+
+                prev.next = None
+                return prev
+
+            elif t0 is _T_IF:
+                node = MenuNode()
+                node.item = node.prompt = None
+                node.parent = parent
+                node.dep = self._expect_expr_and_eol()
+
+                self._parse_block(_T_ENDIF, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MENU:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_MENU == MENU
+                node.is_menuconfig = True
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.visibility = self.y
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.menus.append(node)
+
+                self._parse_properties(node)
+                self._parse_block(_T_ENDMENU, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_COMMENT:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_COMMENT == COMMENT
+                node.is_menuconfig = False
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.comments.append(node)
+
+                self._parse_properties(node)
+
+                prev.next = prev = node
+
+            elif t0 is _T_CHOICE:
+                if self._tokens[1] is None:
+                    choice = Choice()
+                    choice.direct_dep = self.n
+                else:
+                    # Named choice
+                    name = self._expect_str_and_eol()
+                    choice = self.named_choices.get(name)
+                    if not choice:
+                        choice = Choice()
+                        choice.name = name
+                        choice.direct_dep = self.n
+                        self.named_choices[name] = choice
+
+                self.choices.append(choice)
+
+                node = MenuNode()
+                node.kconfig = choice.kconfig = self
+                node.item = choice
+                node.is_menuconfig = True
+                node.prompt = node.help = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                choice.nodes.append(node)
+
+                self._parse_properties(node)
+                self._parse_block(_T_ENDCHOICE, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MAINMENU:
+                self.top_node.prompt = (self._expect_str_and_eol(), self.y)
+
+            else:
+                # A valid endchoice/endif/endmenu is caught by the 'end_token'
+                # check above
+                self._parse_error(
+                    "no corresponding 'choice'" if t0 is _T_ENDCHOICE else
+                    "no corresponding 'if'"     if t0 is _T_ENDIF else
+                    "no corresponding 'menu'"   if t0 is _T_ENDMENU else
+                    "unrecognized construct")
+
+        # End of file reached. Terminate the final node and return it.
+
+        if end_token:
+            raise KconfigError(
+                "expected '{}' at end of '{}'"
+                .format("endchoice" if end_token is _T_ENDCHOICE else
+                        "endif"     if end_token is _T_ENDIF else
+                        "endmenu",
+                        self.filename))
+
+        prev.next = None
+        return prev
+
+    def _parse_cond(self):
+        # Parses an optional 'if <expr>' construct and returns the parsed
+        # <expr>, or self.y if the next token is not _T_IF
+
+        expr = self._parse_expr(True) if self._check_token(_T_IF) else self.y
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _parse_properties(self, node):
+        # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
+        # 'default's, etc.) Properties are later copied up to symbols and
+        # choices in a separate pass after parsing, in e.g.
+        # _add_props_to_sym().
+        #
+        # An older version of this code added properties directly to symbols
+        # and choices instead of to their menu nodes (and handled dependency
+        # propagation simultaneously), but that loses information on where a
+        # property is added when a symbol or choice is defined in multiple
+        # locations. Some Kconfig configuration systems rely heavily on such
+        # symbols, and better docs can be generated by keeping track of where
+        # properties are added.
+        #
+        # node:
+        #   The menu node we're parsing properties on
+
+        # Dependencies from 'depends on'. Will get propagated to the properties
+        # below.
+        node.dep = self.y
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 in _TYPE_TOKENS:
+                # Relies on '_T_BOOL is BOOL', etc., to save a conversion
+                self._set_type(node, t0)
+                if self._tokens[1] is not None:
+                    self._parse_prompt(node)
+
+            elif t0 is _T_DEPENDS:
+                if not self._check_token(_T_ON):
+                    self._parse_error("expected 'on' after 'depends'")
+
+                node.dep = self._make_and(node.dep,
+                                          self._expect_expr_and_eol())
+
+            elif t0 is _T_HELP:
+                self._parse_help(node)
+
+            elif t0 is _T_SELECT:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can select")
+
+                node.selects.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 is _T_DEFAULT:
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 in _DEF_TOKEN_TO_TYPE:
+                self._set_type(node, _DEF_TOKEN_TO_TYPE[t0])
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 is _T_PROMPT:
+                self._parse_prompt(node)
+
+            elif t0 is _T_RANGE:
+                node.ranges.append((self._expect_sym(), self._expect_sym(),
+                                    self._parse_cond()))
+
+            elif t0 is _T_IMPLY:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can imply")
+
+                node.implies.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is _T_VISIBLE:
+                if not self._check_token(_T_IF):
+                    self._parse_error("expected 'if' after 'visible'")
+
+                node.visibility = self._make_and(node.visibility,
+                                                 self._expect_expr_and_eol())
+
+            elif t0 is _T_OPTION:
+                if self._check_token(_T_ENV):
+                    if not self._check_token(_T_EQUAL):
+                        self._parse_error("expected '=' after 'env'")
+
+                    env_var = self._expect_str_and_eol()
+                    node.item.env_var = env_var
+
+                    if env_var in os.environ:
+                        node.defaults.append(
+                            (self._lookup_const_sym(os.environ[env_var]),
+                             self.y))
+                    else:
+                        self._warn("{1} has 'option env=\"{0}\"', "
+                                   "but the environment variable {0} is not "
+                                   "set".format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                    if env_var != node.item.name:
+                        self._warn("Kconfiglib expands environment variables "
+                                   "in strings directly, meaning you do not "
+                                   "need 'option env=...' \"bounce\" symbols. "
+                                   "For compatibility with the C tools, "
+                                   "rename {} to {} (so that the symbol name "
+                                   "matches the environment variable name)."
+                                   .format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_DEFCONFIG_LIST):
+                    if not self.defconfig_list:
+                        self.defconfig_list = node.item
+                    else:
+                        self._warn("'option defconfig_list' set on multiple "
+                                   "symbols ({0} and {1}). Only {0} will be "
+                                   "used.".format(self.defconfig_list.name,
+                                                  node.item.name),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_MODULES):
+                    # To reduce warning spam, only warn if 'option modules' is
+                    # set on some symbol that isn't MODULES, which should be
+                    # safe. I haven't run into any projects that make use
+                    # modules besides the kernel yet, and there it's likely to
+                    # keep being called "MODULES".
+                    if node.item is not self.modules:
+                        self._warn("the 'modules' option is not supported. "
+                                   "Let me know if this is a problem for you, "
+                                   "as it wouldn't be that hard to implement. "
+                                   "Note that modules are supported -- "
+                                   "Kconfiglib just assumes the symbol name "
+                                   "MODULES, like older versions of the C "
+                                   "implementation did when 'option modules' "
+                                   "wasn't used.",
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_ALLNOCONFIG_Y):
+                    if node.item.__class__ is not Symbol:
+                        self._parse_error("the 'allnoconfig_y' option is only "
+                                          "valid for symbols")
+
+                    node.item.is_allnoconfig_y = True
+
+                else:
+                    self._parse_error("unrecognized option")
+
+            elif t0 is _T_OPTIONAL:
+                if node.item.__class__ is not Choice:
+                    self._parse_error('"optional" is only valid for choices')
+
+                node.item.is_optional = True
+
+            else:
+                # Reuse the tokens for the non-property line later
+                self._reuse_tokens = True
+                return
+
+    def _set_type(self, node, new_type):
+        # UNKNOWN is falsy
+        if node.item.orig_type and node.item.orig_type is not new_type:
+            self._warn("{} defined with multiple types, {} will be used"
+                       .format(_name_and_loc(node.item),
+                               TYPE_TO_STR[new_type]))
+
+        node.item.orig_type = new_type
+
+    def _parse_prompt(self, node):
+        # 'prompt' properties override each other within a single definition of
+        # a symbol, but additional prompts can be added by defining the symbol
+        # multiple times
+
+        if node.prompt:
+            self._warn(_name_and_loc(node.item) +
+                       " defined with multiple prompts in single location")
+
+        prompt = self._tokens[1]
+        self._tokens_i = 2
+
+        if prompt.__class__ is not str:
+            self._parse_error("expected prompt string")
+
+        if prompt != prompt.strip():
+            self._warn(_name_and_loc(node.item) +
+                       " has leading or trailing whitespace in its prompt")
+
+            # This avoid issues for e.g. reStructuredText documentation, where
+            # '*prompt *' is invalid
+            prompt = prompt.strip()
+
+        node.prompt = (prompt, self._parse_cond())
+
+    def _parse_help(self, node):
+        if node.help is not None:
+            self._warn(_name_and_loc(node.item) + " defined with more than "
+                       "one help text -- only the last one will be used")
+
+        # Micro-optimization. This code is pretty hot.
+        readline = self._readline
+
+        # Find first non-blank (not all-space) line and get its
+        # indentation
+
+        while 1:
+            line = readline()
+            self.linenr += 1
+            if not line:
+                self._empty_help(node, line)
+                return
+            if not line.isspace():
+                break
+
+        len_ = len  # Micro-optimization
+
+        # Use a separate 'expline' variable here and below to avoid stomping on
+        # any tabs people might've put deliberately into the first line after
+        # the help text
+        expline = line.expandtabs()
+        indent = len_(expline) - len_(expline.lstrip())
+        if not indent:
+            self._empty_help(node, line)
+            return
+
+        # The help text goes on till the first non-blank line with less indent
+        # than the first line
+
+        # Add the first line
+        lines = [expline[indent:]]
+        add_line = lines.append  # Micro-optimization
+
+        while 1:
+            line = readline()
+            if line.isspace():
+                # No need to preserve the exact whitespace in these
+                add_line("\n")
+            elif not line:
+                # End of file
+                break
+            else:
+                expline = line.expandtabs()
+                if len_(expline) - len_(expline.lstrip()) < indent:
+                    break
+                add_line(expline[indent:])
+
+        self.linenr += len_(lines)
+        node.help = "".join(lines).rstrip()
+        if line:
+            self._line_after_help(line)
+
+    def _empty_help(self, node, line):
+        self._warn(_name_and_loc(node.item) +
+                   " has 'help' but empty help text")
+        node.help = ""
+        if line:
+            self._line_after_help(line)
+
+    def _parse_expr(self, transform_m):
+        # Parses an expression from the tokens in Kconfig._tokens using a
+        # simple top-down approach. See the module docstring for the expression
+        # format.
+        #
+        # transform_m:
+        #   True if m should be rewritten to m && MODULES. See the
+        #   Kconfig.eval_string() documentation.
+
+        # Grammar:
+        #
+        #   expr:     and_expr ['||' expr]
+        #   and_expr: factor ['&&' and_expr]
+        #   factor:   <symbol> ['='/'!='/'<'/... <symbol>]
+        #             '!' factor
+        #             '(' expr ')'
+        #
+        # It helps to think of the 'expr: and_expr' case as a single-operand OR
+        # (no ||), and of the 'and_expr: factor' case as a single-operand AND
+        # (no &&). Parsing code is always a bit tricky.
+
+        # Mind dump: parse_factor() and two nested loops for OR and AND would
+        # work as well. The straightforward implementation there gives a
+        # (op, (op, (op, A, B), C), D) parse for A op B op C op D. Representing
+        # expressions as (op, [list of operands]) instead goes nicely with that
+        # version, but is wasteful for short expressions and complicates
+        # expression evaluation and other code that works on expressions (more
+        # complicated code likely offsets any performance gain from less
+        # recursion too). If we also try to optimize the list representation by
+        # merging lists when possible (e.g. when ANDing two AND expressions),
+        # we end up allocating a ton of lists instead of reusing expressions,
+        # which is bad.
+
+        and_expr = self._parse_and_expr(transform_m)
+
+        # Return 'and_expr' directly if we have a "single-operand" OR.
+        # Otherwise, parse the expression on the right and make an OR node.
+        # This turns A || B || C || D into (OR, A, (OR, B, (OR, C, D))).
+        return and_expr if not self._check_token(_T_OR) else \
+            (OR, and_expr, self._parse_expr(transform_m))
+
+    def _parse_and_expr(self, transform_m):
+        factor = self._parse_factor(transform_m)
+
+        # Return 'factor' directly if we have a "single-operand" AND.
+        # Otherwise, parse the right operand and make an AND node. This turns
+        # A && B && C && D into (AND, A, (AND, B, (AND, C, D))).
+        return factor if not self._check_token(_T_AND) else \
+            (AND, factor, self._parse_and_expr(transform_m))
+
+    def _parse_factor(self, transform_m):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is Symbol:
+            # Plain symbol or relation
+
+            if self._tokens[self._tokens_i] not in _RELATIONS:
+                # Plain symbol
+
+                # For conditional expressions ('depends on <expr>',
+                # '... if <expr>', etc.), m is rewritten to m && MODULES.
+                if transform_m and token is self.m:
+                    return (AND, self.m, self.modules)
+
+                return token
+
+            # Relation
+            #
+            # _T_EQUAL, _T_UNEQUAL, etc., deliberately have the same values as
+            # EQUAL, UNEQUAL, etc., so we can just use the token directly
+            self._tokens_i += 1
+            return (self._tokens[self._tokens_i - 1], token,
+                    self._expect_sym())
+
+        if token is _T_NOT:
+            # token == _T_NOT == NOT
+            return (token, self._parse_factor(transform_m))
+
+        if token is _T_OPEN_PAREN:
+            expr_parse = self._parse_expr(transform_m)
+            if self._check_token(_T_CLOSE_PAREN):
+                return expr_parse
+
+        self._parse_error("malformed expression")
+
+    #
+    # Caching and invalidation
+    #
+
+    def _build_dep(self):
+        # Populates the Symbol/Choice._dependents sets, which contain all other
+        # items (symbols and choices) that immediately depend on the item in
+        # the sense that changing the value of the item might affect the value
+        # of the dependent items. This is used for caching/invalidation.
+        #
+        # The calculated sets might be larger than necessary as we don't do any
+        # complex analysis of the expressions.
+
+        make_depend_on = _make_depend_on  # Micro-optimization
+
+        # Only calculate _dependents for defined symbols. Constant and
+        # undefined symbols could theoretically be selected/implied, but it
+        # wouldn't change their value, so it's not a true dependency.
+        for sym in self.unique_defined_syms:
+            # Symbols depend on the following:
+
+            # The prompt conditions
+            for node in sym.nodes:
+                if node.prompt:
+                    make_depend_on(sym, node.prompt[1])
+
+            # The default values and their conditions
+            for value, cond in sym.defaults:
+                make_depend_on(sym, value)
+                make_depend_on(sym, cond)
+
+            # The reverse and weak reverse dependencies
+            make_depend_on(sym, sym.rev_dep)
+            make_depend_on(sym, sym.weak_rev_dep)
+
+            # The ranges along with their conditions
+            for low, high, cond in sym.ranges:
+                make_depend_on(sym, low)
+                make_depend_on(sym, high)
+                make_depend_on(sym, cond)
+
+            # The direct dependencies. This is usually redundant, as the direct
+            # dependencies get propagated to properties, but it's needed to get
+            # invalidation solid for 'imply', which only checks the direct
+            # dependencies (even if there are no properties to propagate it
+            # to).
+            make_depend_on(sym, sym.direct_dep)
+
+            # In addition to the above, choice symbols depend on the choice
+            # they're in, but that's handled automatically since the Choice is
+            # propagated to the conditions of the properties before
+            # _build_dep() runs.
+
+        for choice in self.unique_choices:
+            # Choices depend on the following:
+
+            # The prompt conditions
+            for node in choice.nodes:
+                if node.prompt:
+                    make_depend_on(choice, node.prompt[1])
+
+            # The default symbol conditions
+            for _, cond in choice.defaults:
+                make_depend_on(choice, cond)
+
+    def _add_choice_deps(self):
+        # Choices also depend on the choice symbols themselves, because the
+        # y-mode selection of the choice might change if a choice symbol's
+        # visibility changes.
+        #
+        # We add these dependencies separately after dependency loop detection.
+        # The invalidation algorithm can handle the resulting
+        # <choice symbol> <-> <choice> dependency loops, but they make loop
+        # detection awkward.
+
+        for choice in self.unique_choices:
+            for sym in choice.syms:
+                sym._dependents.add(choice)
+
+    def _invalidate_all(self):
+        # Undefined symbols never change value and don't need to be
+        # invalidated, so we can just iterate over defined symbols.
+        # Invalidating constant symbols would break things horribly.
+        for sym in self.unique_defined_syms:
+            sym._invalidate()
+
+        for choice in self.unique_choices:
+            choice._invalidate()
+
+    #
+    # Post-parsing menu tree processing, including dependency propagation and
+    # implicit submenu creation
+    #
+
+    def _finalize_node(self, node, visible_if):
+        # Finalizes a menu node and its children:
+        #
+        #  - Copies properties from menu nodes up to their contained
+        #    symbols/choices
+        #
+        #  - Propagates dependencies from parent to child nodes
+        #
+        #  - Creates implicit menus (see kconfig-language.txt)
+        #
+        #  - Removes 'if' nodes
+        #
+        #  - Sets 'choice' types and registers choice symbols
+        #
+        # menu_finalize() in the C implementation is similar.
+        #
+        # node:
+        #   The menu node to finalize. This node and its children will have
+        #   been finalized when the function returns, and any implicit menus
+        #   will have been created.
+        #
+        # visible_if:
+        #   Dependencies from 'visible if' on parent menus. These are added to
+        #   the prompts of symbols and choices.
+
+        if node.item.__class__ is Symbol:
+            # Copy defaults, ranges, selects, and implies to the Symbol
+            self._add_props_to_sym(node)
+
+            # Find any items that should go in an implicit menu rooted at the
+            # symbol
+            cur = node
+            while cur.next and _auto_menu_dep(node, cur.next):
+                # This makes implicit submenu creation work recursively, with
+                # implicit menus inside implicit menus
+                self._finalize_node(cur.next, visible_if)
+                cur = cur.next
+                cur.parent = node
+
+            if cur is not node:
+                # Found symbols that should go in an implicit submenu. Tilt
+                # them up above us.
+                node.list = node.next
+                node.next = cur.next
+                cur.next = None
+
+        elif node.list:
+            # The menu node is a choice, menu, or if. Finalize each child node.
+
+            if node.item is MENU:
+                visible_if = self._make_and(visible_if, node.visibility)
+
+            # Propagate the menu node's dependencies to each child menu node.
+            #
+            # This needs to go before the recursive _finalize_node() call so
+            # that implicit submenu creation can look ahead at dependencies.
+            self._propagate_deps(node, visible_if)
+
+            # Finalize the children
+            cur = node.list
+            while cur:
+                self._finalize_node(cur, visible_if)
+                cur = cur.next
+
+        if node.list:
+            # node's children have been individually finalized. Do final steps
+            # to finalize this "level" in the menu tree.
+            _flatten(node.list)
+            _remove_ifs(node)
+
+        # Empty choices (node.list None) are possible, so this needs to go
+        # outside
+        if node.item.__class__ is Choice:
+            # Add the node's non-node-specific properties to the choice, like
+            # _add_props_to_sym() does
+            choice = node.item
+            choice.direct_dep = self._make_or(choice.direct_dep, node.dep)
+            choice.defaults += node.defaults
+
+            _finalize_choice(node)
+
+    def _propagate_deps(self, node, visible_if):
+        # Propagates 'node's dependencies to its child menu nodes
+
+        # If the parent node holds a Choice, we use the Choice itself as the
+        # parent dependency. This makes sense as the value (mode) of the choice
+        # limits the visibility of the contained choice symbols. The C
+        # implementation works the same way.
+        #
+        # Due to the similar interface, Choice works as a drop-in replacement
+        # for Symbol here.
+        basedep = node.item if node.item.__class__ is Choice else node.dep
+
+        cur = node.list
+        while cur:
+            dep = cur.dep = self._make_and(cur.dep, basedep)
+
+            if cur.item.__class__ in _SYMBOL_CHOICE:
+                # Propagate 'visible if' and dependencies to the prompt
+                if cur.prompt:
+                    cur.prompt = (cur.prompt[0],
+                                  self._make_and(
+                                      cur.prompt[1],
+                                      self._make_and(visible_if, dep)))
+
+                # Propagate dependencies to defaults
+                if cur.defaults:
+                    cur.defaults = [(default, self._make_and(cond, dep))
+                                    for default, cond in cur.defaults]
+
+                # Propagate dependencies to ranges
+                if cur.ranges:
+                    cur.ranges = [(low, high, self._make_and(cond, dep))
+                                  for low, high, cond in cur.ranges]
+
+                # Propagate dependencies to selects
+                if cur.selects:
+                    cur.selects = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.selects]
+
+                # Propagate dependencies to implies
+                if cur.implies:
+                    cur.implies = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.implies]
+
+            elif cur.prompt:  # Not a symbol/choice
+                # Propagate dependencies to the prompt. 'visible if' is only
+                # propagated to symbols/choices.
+                cur.prompt = (cur.prompt[0],
+                              self._make_and(cur.prompt[1], dep))
+
+            cur = cur.next
+
+    def _add_props_to_sym(self, node):
+        # Copies properties from the menu node 'node' up to its contained
+        # symbol, and adds (weak) reverse dependencies to selected/implied
+        # symbols.
+        #
+        # This can't be rolled into _propagate_deps(), because that function
+        # traverses the menu tree roughly breadth-first, meaning properties on
+        # symbols defined in multiple locations could end up in the wrong
+        # order.
+
+        sym = node.item
+
+        # See the Symbol class docstring
+        sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
+
+        sym.defaults += node.defaults
+        sym.ranges += node.ranges
+        sym.selects += node.selects
+        sym.implies += node.implies
+
+        # Modify the reverse dependencies of the selected symbol
+        for target, cond in node.selects:
+            target.rev_dep = self._make_or(
+                target.rev_dep,
+                self._make_and(sym, cond))
+
+        # Modify the weak reverse dependencies of the implied
+        # symbol
+        for target, cond in node.implies:
+            target.weak_rev_dep = self._make_or(
+                target.weak_rev_dep,
+                self._make_and(sym, cond))
+
+    #
+    # Misc.
+    #
+
+    def _check_sym_sanity(self):
+        # Checks various symbol properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def num_ok(sym, type_):
+            # Returns True if the (possibly constant) symbol 'sym' is valid as a value
+            # for a symbol of type type_ (INT or HEX)
+
+            # 'not sym.nodes' implies a constant or undefined symbol, e.g. a plain
+            # "123"
+            if not sym.nodes:
+                return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
+
+            return sym.orig_type is type_
+
+        for sym in self.unique_defined_syms:
+            if sym.orig_type in _BOOL_TRISTATE:
+                # A helper function could be factored out here, but keep it
+                # speedy/straightforward
+
+                for target_sym, _ in sym.selects:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} selects the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(_name_and_loc(sym),
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           _name_and_loc(target_sym)))
+
+                for target_sym, _ in sym.implies:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} implies the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(_name_and_loc(sym),
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           _name_and_loc(target_sym)))
+
+            elif sym.orig_type:  # STRING/INT/HEX
+                for default, _ in sym.defaults:
+                    if default.__class__ is not Symbol:
+                        raise KconfigError(
+                            "the {} symbol {} has a malformed default {} -- expected "
+                            "a single symbol"
+                            .format(TYPE_TO_STR[sym.orig_type], _name_and_loc(sym),
+                                    expr_str(default)))
+
+                    if sym.orig_type is STRING:
+                        if not default.is_constant and not default.nodes and \
+                           not default.name.isupper():
+                            # 'default foo' on a string symbol could be either a symbol
+                            # reference or someone leaving out the quotes. Guess that
+                            # the quotes were left out if 'foo' isn't all-uppercase
+                            # (and no symbol named 'foo' exists).
+                            self._warn("style: quotes recommended around "
+                                       "default value for string symbol "
+                                       + _name_and_loc(sym))
+
+                    elif not num_ok(default, sym.orig_type):  # INT/HEX
+                        self._warn("the {0} symbol {1} has a non-{0} default {2}"
+                                   .format(TYPE_TO_STR[sym.orig_type],
+                                           _name_and_loc(sym),
+                                           _name_and_loc(default)))
+
+                if sym.selects or sym.implies:
+                    self._warn("the {} symbol {} has selects or implies"
+                               .format(TYPE_TO_STR[sym.orig_type],
+                                       _name_and_loc(sym)))
+
+            else:  # UNKNOWN
+                self._warn("{} defined without a type"
+                           .format(_name_and_loc(sym)))
+
+
+            if sym.ranges:
+                if sym.orig_type not in _INT_HEX:
+                    self._warn(
+                        "the {} symbol {} has ranges, but is not int or hex"
+                        .format(TYPE_TO_STR[sym.orig_type],
+                                _name_and_loc(sym)))
+                else:
+                    for low, high, _ in sym.ranges:
+                        if not num_ok(low, sym.orig_type) or \
+                           not num_ok(high, sym.orig_type):
+
+                            self._warn("the {0} symbol {1} has a non-{0} "
+                                       "range [{2}, {3}]"
+                                       .format(TYPE_TO_STR[sym.orig_type],
+                                               _name_and_loc(sym),
+                                               _name_and_loc(low),
+                                               _name_and_loc(high)))
+
+    def _check_choice_sanity(self):
+        # Checks various choice properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def warn_select_imply(sym, expr, expr_type):
+            msg = "the choice symbol {} is {} by the following symbols, but " \
+                  "select/imply has no effect on choice symbols" \
+                  .format(_name_and_loc(sym), expr_type)
+
+            # si = select/imply
+            for si in split_expr(expr, OR):
+                msg += "\n - " + _name_and_loc(split_expr(si, AND)[0])
+
+            self._warn(msg)
+
+        for choice in self.unique_choices:
+            if choice.orig_type not in _BOOL_TRISTATE:
+                self._warn("{} defined with type {}"
+                           .format(_name_and_loc(choice),
+                                   TYPE_TO_STR[choice.orig_type]))
+
+            for node in choice.nodes:
+                if node.prompt:
+                    break
+            else:
+                self._warn(_name_and_loc(choice) + " defined without a prompt")
+
+            for default, _ in choice.defaults:
+                if default.__class__ is not Symbol:
+                    raise KconfigError(
+                        "{} has a malformed default {}"
+                        .format(_name_and_loc(choice), expr_str(default)))
+
+                if default.choice is not choice:
+                    self._warn("the default selection {} of {} is not "
+                               "contained in the choice"
+                               .format(_name_and_loc(default),
+                                       _name_and_loc(choice)))
+
+            for sym in choice.syms:
+                if sym.defaults:
+                    self._warn("default on the choice symbol {} will have "
+                               "no effect, as defaults do not affect choice "
+                               "symbols".format(_name_and_loc(sym)))
+
+                if sym.rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.rev_dep, "selected")
+
+                if sym.weak_rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.weak_rev_dep, "implied")
+
+                for node in sym.nodes:
+                    if node.parent.item is choice:
+                        if not node.prompt:
+                            self._warn("the choice symbol {} has no prompt"
+                                       .format(_name_and_loc(sym)))
+
+                    elif node.prompt:
+                        self._warn("the choice symbol {} is defined with a "
+                                   "prompt outside the choice"
+                                   .format(_name_and_loc(sym)))
+
+    def _parse_error(self, msg):
+        raise KconfigError("{}couldn't parse '{}': {}".format(
+            "" if self.filename is None else
+                "{}:{}: ".format(self.filename, self.linenr),
+            self._line.strip(), msg))
+
+    def _trailing_tokens_error(self):
+        self._parse_error("extra tokens at end of line")
+
+    def _open(self, filename, mode):
+        # open() wrapper:
+        #
+        # - Enable universal newlines mode on Python 2 to ease
+        #   interoperability between Linux and Windows. It's already the
+        #   default on Python 3.
+        #
+        #   The "U" flag would currently work for both Python 2 and 3, but it's
+        #   deprecated on Python 3, so play it future-safe.
+        #
+        #   io.open() defaults to universal newlines on Python 2 (and is an
+        #   alias for open() on Python 3), but it returns 'unicode' strings and
+        #   slows things down:
+        #
+        #     Parsing x86 Kconfigs on Python 2
+        #
+        #     with open(..., "rU"):
+        #
+        #       real  0m0.930s
+        #       user  0m0.905s
+        #       sys   0m0.025s
+        #
+        #     with io.open():
+        #
+        #       real  0m1.069s
+        #       user  0m1.040s
+        #       sys   0m0.029s
+        #
+        #   There's no appreciable performance difference between "r" and
+        #   "rU" for parsing performance on Python 2.
+        #
+        # - For Python 3, force the encoding. Forcing the encoding on Python 2
+        #   turns strings into Unicode strings, which gets messy. Python 2
+        #   doesn't decode regular strings anyway.
+        return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \
+               open(filename, mode, encoding=self._encoding)
+
+    def _check_undef_syms(self):
+        # Prints warnings for all references to undefined symbols within the
+        # Kconfig files
+
+        def is_num(s):
+            # Returns True if the string 's' looks like a number.
+            #
+            # Internally, all operands in Kconfig are symbols, only undefined symbols
+            # (which numbers usually are) get their name as their value.
+            #
+            # Only hex numbers that start with 0x/0X are classified as numbers.
+            # Otherwise, symbols whose names happen to contain only the letters A-F
+            # would trigger false positives.
+
+            try:
+                int(s)
+            except ValueError:
+                if not s.startswith(("0x", "0X")):
+                    return False
+
+                try:
+                    int(s, 16)
+                except ValueError:
+                    return False
+
+            return True
+
+        for sym in (self.syms.viewvalues if _IS_PY2 else self.syms.values)():
+            # - sym.nodes empty means the symbol is undefined (has no
+            #   definition locations)
+            #
+            # - Due to Kconfig internals, numbers show up as undefined Kconfig
+            #   symbols, but shouldn't be flagged
+            #
+            # - The MODULES symbol always exists
+            if not sym.nodes and not is_num(sym.name) and \
+               sym.name != "MODULES":
+
+                msg = "undefined symbol {}:".format(sym.name)
+                for node in self.node_iter():
+                    if sym in node.referenced:
+                        msg += "\n\n- Referenced at {}:{}:\n\n{}" \
+                               .format(node.filename, node.linenr, node)
+                self._warn(msg)
+
+    def _warn(self, msg, filename=None, linenr=None):
+        # For printing general warnings
+
+        if not self.warn:
+            return
+
+        msg = "warning: " + msg
+        if filename is not None:
+            msg = "{}:{}: {}".format(filename, linenr, msg)
+
+        self.warnings.append(msg)
+        if self.warn_to_stderr:
+            sys.stderr.write(msg + "\n")
+
+
+class Symbol(object):
+    """
+    Represents a configuration symbol:
+
+      (menu)config FOO
+          ...
+
+    The following attributes are available. They should be viewed as read-only,
+    and some are implemented through @property magic (but are still efficient
+    to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Symbol's
+    MenuNode(s) rather than in the Symbol itself. Check the MenuNode class and
+    the Symbol.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the symbol, e.g. "FOO" for 'config FOO'.
+
+    type:
+      The type of the symbol. One of BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN.
+      UNKNOWN is for undefined symbols, (non-special) constant symbols, and
+      symbols defined without a type.
+
+      When running without modules (MODULES having the value n), TRISTATE
+      symbols magically change type to BOOL. This also happens for symbols
+      within choices in "y" mode. This matches the C tools, and makes sense for
+      menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the symbol.
+
+    str_value:
+      The value of the symbol as a string. Gives the value for string/int/hex
+      symbols. For bool/tristate symbols, gives "n", "m", or "y".
+
+      This is the symbol value that's used in relational expressions
+      (A = B, A != B, etc.)
+
+      Gotcha: For int/hex symbols, the exact format of the value must often be
+      preserved (e.g., when writing a .config file), hence why you can't get it
+      directly as an int. Do int(int_sym.str_value) or
+      int(hex_sym.str_value, 16) to get the integer value.
+
+    tri_value:
+      The tristate value of the symbol as an integer. One of 0, 1, 2,
+      representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
+
+      This is the symbol value that's used outside of relation expressions
+      (A, !A, A && B, A || B).
+
+    assignable:
+      A tuple containing the tristate user values that can currently be
+      assigned to the symbol (that would be respected), ordered from lowest (0,
+      representing n) to highest (2, representing y). This corresponds to the
+      selections available in the menuconfig interface. The set of assignable
+      values is calculated from the symbol's visibility and selects/implies.
+
+      Returns the empty set for non-bool/tristate symbols and for symbols with
+      visibility n. The other possible values are (0, 2), (0, 1, 2), (1, 2),
+      (1,), and (2,). A (1,) or (2,) result means the symbol is visible but
+      "locked" to m or y through a select, perhaps in combination with the
+      visibility. menuconfig represents this as -M- and -*-, respectively.
+
+      For string/hex/int symbols, check if Symbol.visibility is non-0 (non-n)
+      instead to determine if the value can be changed.
+
+      Some handy 'assignable' idioms:
+
+        # Is 'sym' an assignable (visible) bool/tristate symbol?
+        if sym.assignable:
+            # What's the highest value it can be assigned? [-1] in Python
+            # gives the last element.
+            sym_high = sym.assignable[-1]
+
+            # The lowest?
+            sym_low = sym.assignable[0]
+
+            # Can the symbol be set to at least m?
+            if sym.assignable[-1] >= 1:
+                ...
+
+        # Can the symbol be set to m?
+        if 1 in sym.assignable:
+            ...
+
+    visibility:
+      The visibility of the symbol. One of 0, 1, 2, representing n, m, y. See
+      the module documentation for an overview of symbol values and visibility.
+
+    user_value:
+      The user value of the symbol. None if no user value has been assigned
+      (via Kconfig.load_config() or Symbol.set_value()).
+
+      Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
+      symbol types.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Symbol.set_value().
+
+    config_string:
+      The .config assignment string that would get written out for the symbol
+      by Kconfig.write_config(). Returns the empty string if no .config
+      assignment would get written out.
+
+      In general, visible symbols, symbols with (active) defaults, and selected
+      symbols get written out. This includes all non-n-valued bool/tristate
+      symbols, and all visible string/int/hex symbols.
+
+      Symbols with the (no longer needed) 'option env=...' option generate no
+      configuration output, and neither does the special
+      'option defconfig_list' symbol.
+
+      Tip: This field is useful when generating custom configuration output,
+      even for non-.config-like formats. To write just the symbols that would
+      get written out to .config files, do this:
+
+        if sym.config_string:
+            *Write symbol, e.g. by looking sym.str_value*
+
+      This is a superset of the symbols written out by write_autoconf().
+      That function skips all n-valued symbols.
+
+      There usually won't be any great harm in just writing all symbols either,
+      though you might get some special symbols and possibly some "redundant"
+      n-valued symbol entries in there.
+
+    nodes:
+      A list of MenuNodes for this symbol. Will contain a single MenuNode for
+      most symbols. Undefined and constant symbols have an empty nodes list.
+      Symbols defined in multiple locations get one node for each location.
+
+    choice:
+      Holds the parent Choice for choice symbols, and None for non-choice
+      symbols. Doubles as a flag for whether a symbol is a choice symbol.
+
+    defaults:
+      List of (default, cond) tuples for the symbol's 'default' properties. For
+      example, 'default A && B if C || D' is represented as
+      ((AND, A, B), (OR, C, D)). If no condition was given, 'cond' is
+      self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    selects:
+      List of (symbol, cond) tuples for the symbol's 'select' properties. For
+      example, 'select A if B && C' is represented as (A, (AND, B, C)). If no
+      condition was given, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'select'
+      conditions.
+
+    implies:
+      Like 'selects', for imply.
+
+    ranges:
+      List of (low, high, cond) tuples for the symbol's 'range' properties. For
+      example, 'range 1 2 if A' is represented as (1, 2, A). If there is no
+      condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'range'
+      conditions.
+
+      Gotcha: 1 and 2 above will be represented as (undefined) Symbols rather
+      than plain integers. Undefined symbols get their name as their string
+      value, so this works out. The C tools work the same way.
+
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      See the corresponding attributes on the MenuNode class.
+
+    rev_dep:
+      Reverse dependency expression from other symbols selecting this symbol.
+      Multiple selections get ORed together. A condition on a select is ANDed
+      with the selecting symbol.
+
+      For example, if A has 'select FOO' and B has 'select FOO if C', then
+      FOO's rev_dep will be (OR, A, (AND, B, C)).
+
+    weak_rev_dep:
+      Like rev_dep, for imply.
+
+    direct_dep:
+      The direct ('depends on') dependencies for the symbol, or self.kconfig.y
+      if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If the symbol is defined in multiple locations, the dependencies from the
+      different locations get ORed together.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the symbol.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the symbol (see the 'Intro to symbol values' section in
+      the module docstring).
+
+      Choices appear in the dependencies of choice symbols.
+
+      For the following definitions, only B and not C appears in A's
+      'referenced'. To get transitive references, you'll have to recursively
+      expand 'references' until no new items appear.
+
+        config A
+                bool
+                depends on B
+
+        config B
+                bool
+                depends on C
+
+        config C
+                bool
+
+      See the Symbol.direct_dep attribute if you're only interested in the
+      direct dependencies of the symbol (its 'depends on'). You can extract the
+      symbols in it with the global expr_items() function.
+
+    env_var:
+      If the Symbol has an 'option env="FOO"' option, this contains the name
+      ("FOO") of the environment variable. None for symbols without no
+      'option env'.
+
+      'option env="FOO"' acts like a 'default' property whose value is the
+      value of $FOO.
+
+      Symbols with 'option env' are never written out to .config files, even if
+      they are visible. env_var corresponds to a flag called SYMBOL_AUTO in the
+      C implementation.
+
+    is_allnoconfig_y:
+      True if the symbol has 'option allnoconfig_y' set on it. This has no
+      effect internally (except when printing symbols), but can be checked by
+      scripts.
+
+    is_constant:
+      True if the symbol is a constant (quoted) symbol.
+
+    kconfig:
+      The Kconfig instance this symbol is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_str_val",
+        "_cached_tri_val",
+        "_cached_vis",
+        "_dependents",
+        "_old_val",
+        "_visited",
+        "_was_set",
+        "_write_to_conf",
+        "choice",
+        "defaults",
+        "direct_dep",
+        "env_var",
+        "implies",
+        "is_allnoconfig_y",
+        "is_constant",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "ranges",
+        "rev_dep",
+        "selects",
+        "user_value",
+        "weak_rev_dep",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        See the class documentation.
+        """
+        if self.orig_type is TRISTATE and \
+           (self.choice and self.choice.tri_value == 2 or
+            not self.kconfig.modules.tri_value):
+
+            return BOOL
+
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_str_val is not None:
+            return self._cached_str_val
+
+        if self.orig_type in _BOOL_TRISTATE:
+            # Also calculates the visibility, so invalidation safe
+            self._cached_str_val = TRI_TO_STR[self.tri_value]
+            return self._cached_str_val
+
+        # As a quirk of Kconfig, undefined symbols get their name as their
+        # string value. This is why things like "FOO = bar" work for seeing if
+        # FOO has the value "bar".
+        if not self.orig_type:  # UNKNOWN
+            self._cached_str_val = self.name
+            return self.name
+
+        val = ""
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        self._write_to_conf = (vis != 0)
+
+        if self.orig_type in _INT_HEX:
+            # The C implementation checks the user value against the range in a
+            # separate code path (post-processing after loading a .config).
+            # Checking all values here instead makes more sense for us. It
+            # requires that we check for a range first.
+
+            base = _TYPE_TO_BASE[self.orig_type]
+
+            # Check if a range is in effect
+            for low_expr, high_expr, cond in self.ranges:
+                if expr_value(cond):
+                    has_active_range = True
+
+                    # The zeros are from the C implementation running strtoll()
+                    # on empty strings
+                    low = int(low_expr.str_value, base) if \
+                      _is_base_n(low_expr.str_value, base) else 0
+                    high = int(high_expr.str_value, base) if \
+                      _is_base_n(high_expr.str_value, base) else 0
+
+                    break
+            else:
+                has_active_range = False
+
+            # Defaults are used if the symbol is invisible, lacks a user value,
+            # or has an out-of-range user value
+            use_defaults = True
+
+            if vis and self.user_value:
+                user_val = int(self.user_value, base)
+                if has_active_range and not low <= user_val <= high:
+                    num2str = str if base == 10 else hex
+                    self.kconfig._warn(
+                        "user value {} on the {} symbol {} ignored due to "
+                        "being outside the active range ([{}, {}]) -- falling "
+                        "back on defaults"
+                        .format(num2str(user_val), TYPE_TO_STR[self.orig_type],
+                                _name_and_loc(self),
+                                num2str(low), num2str(high)))
+                else:
+                    # If the user value is well-formed and satisfies range
+                    # contraints, it is stored in exactly the same form as
+                    # specified in the assignment (with or without "0x", etc.)
+                    val = self.user_value
+                    use_defaults = False
+
+            if use_defaults:
+                # No user value or invalid user value. Look at defaults.
+
+                # Used to implement the warning below
+                has_default = False
+
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        has_default = self._write_to_conf = True
+
+                        val = sym.str_value
+
+                        if _is_base_n(val, base):
+                            val_num = int(val, base)
+                        else:
+                            val_num = 0  # strtoll() on empty string
+
+                        break
+                else:
+                    val_num = 0  # strtoll() on empty string
+
+                # This clamping procedure runs even if there's no default
+                if has_active_range:
+                    clamp = None
+                    if val_num < low:
+                        clamp = low
+                    elif val_num > high:
+                        clamp = high
+
+                    if clamp is not None:
+                        # The value is rewritten to a standard form if it is
+                        # clamped
+                        val = str(clamp) \
+                              if self.orig_type is INT else \
+                              hex(clamp)
+
+                        if has_default:
+                            num2str = str if base == 10 else hex
+                            self.kconfig._warn(
+                                "default value {} on {} clamped to {} due to "
+                                "being outside the active range ([{}, {}])"
+                                .format(val_num, _name_and_loc(self),
+                                        num2str(clamp), num2str(low),
+                                        num2str(high)))
+
+        elif self.orig_type is STRING:
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = self.user_value
+            else:
+                # Otherwise, look at defaults
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        val = sym.str_value
+                        self._write_to_conf = True
+                        break
+
+        # env_var corresponds to SYMBOL_AUTO in the C implementation, and is
+        # also set on the defconfig_list symbol there. Test for the
+        # defconfig_list symbol explicitly instead here, to avoid a nonsensical
+        # env_var setting and the defconfig_list symbol being printed
+        # incorrectly. This code is pretty cold anyway.
+        if self.env_var is not None or self is self.kconfig.defconfig_list:
+            self._write_to_conf = False
+
+        self._cached_str_val = val
+        return val
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_tri_val is not None:
+            return self._cached_tri_val
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            if self.orig_type:  # != UNKNOWN
+                # Would take some work to give the location here
+                self.kconfig._warn(
+                    "The {} symbol {} is being evaluated in a logical context "
+                    "somewhere. It will always evaluate to n."
+                    .format(TYPE_TO_STR[self.orig_type], _name_and_loc(self)))
+
+            self._cached_tri_val = 0
+            return 0
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        self._write_to_conf = (vis != 0)
+
+        val = 0
+
+        if not self.choice:
+            # Non-choice symbol
+
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = min(self.user_value, vis)
+
+            else:
+                # Otherwise, look at defaults and weak reverse dependencies
+                # (implies)
+
+                for default, cond in self.defaults:
+                    dep_val = expr_value(cond)
+                    if dep_val:
+                        val = min(expr_value(default), dep_val)
+                        if val:
+                            self._write_to_conf = True
+                        break
+
+                # Weak reverse dependencies are only considered if our
+                # direct dependencies are met
+                dep_val = expr_value(self.weak_rev_dep)
+                if dep_val and expr_value(self.direct_dep):
+                    val = max(dep_val, val)
+                    self._write_to_conf = True
+
+            # Reverse (select-related) dependencies take precedence
+            dep_val = expr_value(self.rev_dep)
+            if dep_val:
+                if expr_value(self.direct_dep) < dep_val:
+                    self._warn_select_unsatisfied_deps()
+
+                val = max(dep_val, val)
+                self._write_to_conf = True
+
+            # m is promoted to y for (1) bool symbols and (2) symbols with a
+            # weak_rev_dep (from imply) of y
+            if val == 1 and \
+               (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
+                val = 2
+
+        elif vis == 2:
+            # Visible choice symbol in y-mode choice. The choice mode limits
+            # the visibility of choice symbols, so it's sufficient to just
+            # check the visibility of the choice symbols themselves.
+            val = 2 if self.choice.selection is self else 0
+
+        elif vis and self.user_value:
+            # Visible choice symbol in m-mode choice, with set non-0 user value
+            val = 1
+
+        self._cached_tri_val = val
+        return val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def config_string(self):
+        """
+        See the class documentation.
+        """
+        # _write_to_conf is determined when the value is calculated. This is a
+        # hidden function call due to property magic.
+        val = self.str_value
+        if not self._write_to_conf:
+            return ""
+
+        if self.orig_type in _BOOL_TRISTATE:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val) \
+                   if val != "n" else \
+                   "# {}{} is not set\n" \
+                   .format(self.kconfig.config_prefix, self.name)
+
+        if self.orig_type in _INT_HEX:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val)
+
+        # sym.orig_type is STRING
+        return '{}{}="{}"\n' \
+               .format(self.kconfig.config_prefix, self.name, escape(val))
+
+    def set_value(self, value):
+        """
+        Sets the user value of the symbol.
+
+        Equal in effect to assigning the value to the symbol within a .config
+        file. For bool and tristate symbols, use the 'assignable' attribute to
+        check which values can currently be assigned. Setting values outside
+        'assignable' will cause Symbol.user_value to differ from
+        Symbol.str/tri_value (be truncated down or up).
+
+        Setting a choice symbol to 2 (y) sets Choice.user_selection to the
+        choice symbol in addition to setting Symbol.user_value.
+        Choice.user_selection is considered when the choice is in y mode (the
+        "normal" mode).
+
+        Other symbols that depend (possibly indirectly) on this symbol are
+        automatically recalculated to reflect the assigned value.
+
+        value:
+          The user value to give to the symbol. For bool and tristate symbols,
+          n/m/y can be specified either as 0/1/2 (the usual format for tristate
+          values in Kconfiglib) or as one of the strings "n"/"m"/"y". For other
+          symbol types, pass a string.
+
+          Note that the value for an int/hex symbol is passed as a string, e.g.
+          "123" or "0x0123". The format of this string is preserved in the
+          output.
+
+          Values that are invalid for the type (such as "foo" or 1 (m) for a
+          BOOL or "0x123" for an INT) are ignored and won't be stored in
+          Symbol.user_value. Kconfiglib will print a warning by default for
+          invalid assignments, and set_value() will return False.
+
+        Returns True if the value is valid for the type of the symbol, and
+        False otherwise. This only looks at the form of the value. For BOOL and
+        TRISTATE symbols, check the Symbol.assignable attribute to see what
+        values are currently in range and would actually be reflected in the
+        value of the symbol. For other symbol types, check whether the
+        visibility is non-n.
+        """
+        if self.orig_type in _BOOL_TRISTATE and value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        # If the new user value matches the old, nothing changes, and we can
+        # avoid invalidating cached values.
+        #
+        # This optimization is skipped for choice symbols: Setting a choice
+        # symbol's user value to y might change the state of the choice, so it
+        # wouldn't be safe (symbol user values always match the values set in a
+        # .config file or via set_value(), and are never implicitly updated).
+        if value == self.user_value and not self.choice:
+            self._was_set = True
+            return True
+
+        # Check if the value is valid for our type
+        if not (self.orig_type is BOOL     and value in (2, 0)     or
+                self.orig_type is TRISTATE and value in TRI_TO_STR or
+                value.__class__ is str and
+                (self.orig_type is STRING                        or
+                 self.orig_type is INT and _is_base_n(value, 10) or
+                 self.orig_type is HEX and _is_base_n(value, 16)
+                                       and int(value, 16) >= 0)):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        _name_and_loc(self), TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+
+        if self.choice and value == 2:
+            # Setting a choice symbol to y makes it the user selection of the
+            # choice. Like for symbol user values, the user selection is not
+            # guaranteed to match the actual selection of the choice, as
+            # dependencies come into play.
+            self.choice.user_selection = self
+            self.choice._was_set = True
+            self.choice._rec_invalidate()
+        else:
+            self._rec_invalidate_if_has_prompt()
+
+        return True
+
+    def unset_value(self):
+        """
+        Removes any user value from the symbol, as if the symbol had never
+        gotten a user value via Kconfig.load_config() or Symbol.set_value().
+        """
+        if self.user_value is not None:
+            self.user_value = None
+            self._rec_invalidate_if_has_prompt()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [s for node in self.nodes for s in node.orig_selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [i for node in self.nodes for i in node.orig_implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [r for node in self.nodes for r in node.orig_ranges]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the symbol (including its name,
+        value, visibility, and location(s)) when it is evaluated on e.g. the
+        interactive Python prompt.
+        """
+        fields = ["symbol " + self.name, TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        # Only add quotes for non-bool/tristate symbols
+        add("value " + (self.str_value if self.orig_type in _BOOL_TRISTATE
+                        else '"{}"'.format(self.str_value)))
+
+        if not self.is_constant:
+            # These aren't helpful to show for constant symbols
+
+            if self.user_value is not None:
+                # Only add quotes for non-bool/tristate symbols
+                add("user value " + (TRI_TO_STR[self.user_value]
+                                     if self.orig_type in _BOOL_TRISTATE
+                                     else '"{}"'.format(self.user_value)))
+
+            add("visibility " + TRI_TO_STR[self.visibility])
+
+            if self.choice:
+                add("choice symbol")
+
+            if self.is_allnoconfig_y:
+                add("allnoconfig_y")
+
+            if self is self.kconfig.defconfig_list:
+                add("is the defconfig_list symbol")
+
+            if self.env_var is not None:
+                add("from environment variable " + self.env_var)
+
+            if self is self.kconfig.modules:
+                add("is the modules symbol")
+
+            add("direct deps " + TRI_TO_STR[expr_value(self.direct_dep)])
+
+        if self.nodes:
+            for node in self.nodes:
+                add("{}:{}".format(node.filename, node.linenr))
+        else:
+            add("constant" if self.is_constant else "undefined")
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the symbol when it is printed.
+        Matches the Kconfig format, with any parent dependencies propagated to
+        the 'depends on' condition.
+
+        The string is constructed by joining the strings returned by
+        MenuNode.__str__() for each of the symbol's menu nodes, so symbols
+        defined in multiple locations will return a string with all
+        definitions.
+
+        The returned string does not end in a newline. An empty string is
+        returned for undefined and constant symbols.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Symbol.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Symbol constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   kconfig
+        #   direct_dep
+        #   is_constant
+        #   name
+        #   rev_dep
+        #   weak_rev_dep
+
+        # - UNKNOWN == 0
+        # - _visited is used during tree iteration and dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+        self.user_value = \
+        self.choice = \
+        self.env_var = \
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+        # _write_to_conf is calculated along with the value. If True, the
+        # Symbol gets a .config entry.
+
+        self.is_allnoconfig_y = \
+        self._was_set = \
+        self._write_to_conf = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            return ()
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        if not vis:
+            return ()
+
+        rev_dep_val = expr_value(self.rev_dep)
+
+        if vis == 2:
+            if self.choice:
+                return (2,)
+
+            if not rev_dep_val:
+                if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                    return (0, 2)
+                return (0, 1, 2)
+
+            if rev_dep_val == 2:
+                return (2,)
+
+            # rev_dep_val == 1
+
+            if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                return (2,)
+            return (1, 2)
+
+        # vis == 1
+
+        # Must be a tristate here, because bool m visibility gets promoted to y
+
+        if not rev_dep_val:
+            return (0, 1) if expr_value(self.weak_rev_dep) != 2 else (0, 2)
+
+        if rev_dep_val == 2:
+            return (2,)
+
+        # vis == rev_dep_val == 1
+
+        return (1,)
+
+    def _invalidate(self):
+        # Marks the symbol as needing to be recalculated
+
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+    def _rec_invalidate(self):
+        # Invalidates the symbol and all items that (possibly) depend on it
+
+        if self is self.kconfig.modules:
+            # Invalidating MODULES has wide-ranging effects
+            self.kconfig._invalidate_all()
+        else:
+            self._invalidate()
+
+            for item in self._dependents:
+                # _cached_vis doubles as a flag that tells us whether 'item'
+                # has cached values, because it's calculated as a side effect
+                # of calculating all other (non-constant) cached values.
+                #
+                # If item._cached_vis is None, it means there can't be cached
+                # values on other items that depend on 'item', because if there
+                # were, some value on 'item' would have been calculated and
+                # item._cached_vis set as a side effect. It's therefore safe to
+                # stop the invalidation at symbols with _cached_vis None.
+                #
+                # This approach massively speeds up scripts that set a lot of
+                # values, vs simply invalidating all possibly dependent symbols
+                # (even when you already have a list of all the dependent
+                # symbols, because some symbols get huge dependency trees).
+                #
+                # This gracefully handles dependency loops too, which is nice
+                # for choices, where the choice depends on the choice symbols
+                # and vice versa.
+                if item._cached_vis is not None:
+                    item._rec_invalidate()
+
+    def _rec_invalidate_if_has_prompt(self):
+        # Invalidates the symbol and its dependent symbols, but only if the
+        # symbol has a prompt. User values never have an effect on promptless
+        # symbols, so we skip invalidation for them as an optimization.
+        #
+        # This also prevents constant (quoted) symbols from being invalidated
+        # if set_value() is called on them, which would make them lose their
+        # value and break things.
+        #
+        # Prints a warning if the symbol has no prompt. In some contexts (e.g.
+        # when loading a .config files) assignments to promptless symbols are
+        # normal and expected, so the warning can be disabled.
+
+        for node in self.nodes:
+            if node.prompt:
+                self._rec_invalidate()
+                return
+
+        if self.kconfig._warn_assign_no_prompt:
+            self.kconfig._warn(_name_and_loc(self) + " has no prompt, meaning "
+                               "user values have no effect on it")
+
+    def _str_default(self):
+        # write_min_config() helper function. Returns the value the symbol
+        # would get from defaults if it didn't have a user value. Uses exactly
+        # the same algorithm as the C implementation (though a bit cleaned up),
+        # for compatibility.
+
+        if self.orig_type in _BOOL_TRISTATE:
+            val = 0
+
+            # Defaults, selects, and implies do not affect choice symbols
+            if not self.choice:
+                for default, cond in self.defaults:
+                    cond_val = expr_value(cond)
+                    if cond_val:
+                        val = min(expr_value(default), cond_val)
+                        break
+
+                val = max(expr_value(self.rev_dep),
+                          expr_value(self.weak_rev_dep),
+                          val)
+
+                # Transpose mod to yes if type is bool (possibly due to modules
+                # being disabled)
+                if val == 1 and self.type is BOOL:
+                    val = 2
+
+            return TRI_TO_STR[val]
+
+        if self.orig_type:  # STRING/INT/HEX
+            for default, cond in self.defaults:
+                if expr_value(cond):
+                    return default.str_value
+
+        return ""
+
+    def _warn_select_unsatisfied_deps(self):
+        # Helper for printing an informative warning when a symbol with
+        # unsatisfied direct dependencies (dependencies from 'depends on', ifs,
+        # and menus) is selected by some other symbol. Also warn if a symbol
+        # whose direct dependencies evaluate to m is selected to y.
+
+        msg = "{} has direct dependencies {} with value {}, but is " \
+              "currently being {}-selected by the following symbols:" \
+              .format(_name_and_loc(self), expr_str(self.direct_dep),
+                      TRI_TO_STR[expr_value(self.direct_dep)],
+                      TRI_TO_STR[expr_value(self.rev_dep)])
+
+        # The reverse dependencies from each select are ORed together
+        for select in split_expr(self.rev_dep, OR):
+            if expr_value(select) <= expr_value(self.direct_dep):
+                # Only include selects that exceed the direct dependencies
+                continue
+
+            # - 'select A if B' turns into A && B
+            # - 'select A' just turns into A
+            #
+            # In both cases, we can split on AND and pick the first operand
+            selecting_sym = split_expr(select, AND)[0]
+
+            msg += "\n - {}, with value {}, direct dependencies {} " \
+                   "(value: {})" \
+                   .format(_name_and_loc(selecting_sym),
+                           selecting_sym.str_value,
+                           expr_str(selecting_sym.direct_dep),
+                           TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
+
+            if select.__class__ is tuple:
+                msg += ", and select condition {} (value: {})" \
+                       .format(expr_str(select[2]),
+                               TRI_TO_STR[expr_value(select[2])])
+
+        self.kconfig._warn(msg)
+
+
+class Choice(object):
+    """
+    Represents a choice statement:
+
+      choice
+          ...
+      endchoice
+
+    The following attributes are available on Choice instances. They should be
+    treated as read-only, and some are implemented through @property magic (but
+    are still efficient to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Choice's
+    MenuNode(s) rather than in the Choice itself. Check the MenuNode class and
+    the Choice.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the choice, e.g. "FOO" for 'choice FOO', or None if the
+      Choice has no name.
+
+    type:
+      The type of the choice. One of BOOL, TRISTATE, UNKNOWN. UNKNOWN is for
+      choices defined without a type where none of the contained symbols have a
+      type either (otherwise the choice inherits the type of the first symbol
+      defined with a type).
+
+      When running without modules (CONFIG_MODULES=n), TRISTATE choices
+      magically change type to BOOL. This matches the C tools, and makes sense
+      for menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the choice.
+
+    tri_value:
+      The tristate value (mode) of the choice. A choice can be in one of three
+      modes:
+
+        0 (n) - The choice is disabled and no symbols can be selected. For
+                visible choices, this mode is only possible for choices with
+                the 'optional' flag set (see kconfig-language.txt).
+
+        1 (m) - Any number of choice symbols can be set to m, the rest will
+                be n.
+
+        2 (y) - One symbol will be y, the rest n.
+
+      Only tristate choices can be in m mode. The visibility of the choice is
+      an upper bound on the mode, and the mode in turn is an upper bound on the
+      visibility of the choice symbols.
+
+      To change the mode, use Choice.set_value().
+
+      Implementation note:
+        The C tools internally represent choices as a type of symbol, with
+        special-casing in many code paths. This is why there is a lot of
+        similarity to Symbol. The value (mode) of a choice is really just a
+        normal symbol value, and an implicit reverse dependency forces its
+        lower bound to m for visible non-optional choices (the reverse
+        dependency is 'm && <visibility>').
+
+        Symbols within choices get the choice propagated as a dependency to
+        their properties. This turns the mode of the choice into an upper bound
+        on e.g. the visibility of choice symbols, and explains the gotcha
+        related to printing choice symbols mentioned in the module docstring.
+
+        Kconfiglib uses a separate Choice class only because it makes the code
+        and interface less confusing (especially in a user-facing interface).
+        Corresponding attributes have the same name in the Symbol and Choice
+        classes, for consistency and compatibility.
+
+    assignable:
+      See the symbol class documentation. Gives the assignable values (modes).
+
+    visibility:
+      See the Symbol class documentation. Acts on the value (mode).
+
+    selection:
+      The Symbol instance of the currently selected symbol. None if the Choice
+      is not in y mode or has no selected symbol (due to unsatisfied
+      dependencies on choice symbols).
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol you want to select instead.
+
+    user_value:
+      The value (mode) selected by the user through Choice.set_value(). Either
+      0, 1, or 2, or None if the user hasn't selected a mode. See
+      Symbol.user_value.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Choice.set_value() instead.
+
+    user_selection:
+      The symbol selected by the user (by setting it to y). Ignored if the
+      choice is not in y mode, but still remembered so that the choice "snaps
+      back" to the user selection if the mode is changed back to y. This might
+      differ from 'selection' due to unsatisfied dependencies.
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol to be selected instead.
+
+    syms:
+      List of symbols contained in the choice.
+
+      Obscure gotcha: If a symbol depends on the previous symbol within a
+      choice so that an implicit menu is created, it won't be a choice symbol,
+      and won't be included in 'syms'.
+
+    nodes:
+      A list of MenuNodes for this choice. In practice, the list will probably
+      always contain a single MenuNode, but it is possible to give a choice a
+      name and define it in multiple locations.
+
+    defaults:
+      List of (symbol, cond) tuples for the choice's 'defaults' properties. For
+      example, 'default A if B && C' is represented as (A, (AND, B, C)). If
+      there is no condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    orig_defaults:
+      See the corresponding attribute on the MenuNode class.
+
+    direct_dep:
+      See Symbol.direct_dep.
+
+    referenced:
+      A set() with all symbols referenced in the properties and property
+      conditions of the choice.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the choice (see the 'Intro to symbol values' section in
+      the module docstring).
+
+    is_optional:
+      True if the choice has the 'optional' flag set on it and can be in
+      n mode.
+
+    kconfig:
+      The Kconfig instance this choice is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_selection",
+        "_cached_vis",
+        "_dependents",
+        "_visited",
+        "_was_set",
+        "defaults",
+        "direct_dep",
+        "is_constant",
+        "is_optional",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "syms",
+        "user_selection",
+        "user_value",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        Returns the type of the choice. See Symbol.type.
+        """
+        if self.orig_type is TRISTATE and not self.kconfig.modules.tri_value:
+            return BOOL
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        return TRI_TO_STR[self.tri_value]
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        # This emulates a reverse dependency of 'm && visibility' for
+        # non-optional choices, which is how the C implementation does it
+
+        val = 0 if self.is_optional else 1
+
+        if self.user_value is not None:
+            val = max(val, self.user_value)
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        val = min(val, self.visibility)
+
+        # Promote m to y for boolean choices
+        return 2 if val == 1 and self.type is BOOL else val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def selection(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_selection is _NO_CACHED_SELECTION:
+            self._cached_selection = self._selection()
+        return self._cached_selection
+
+    def set_value(self, value):
+        """
+        Sets the user value (mode) of the choice. Like for Symbol.set_value(),
+        the visibility might truncate the value. Choices without the 'optional'
+        attribute (is_optional) can never be in n mode, but 0/"n" is still
+        accepted since it's not a malformed value (though it will have no
+        effect).
+
+        Returns True if the value is valid for the type of the choice, and
+        False otherwise. This only looks at the form of the value. Check the
+        Choice.assignable attribute to see what values are currently in range
+        and would actually be reflected in the mode of the choice.
+        """
+        if value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        if value == self.user_value:
+            # We know the value must be valid if it was successfully set
+            # previously
+            self._was_set = True
+            return True
+
+        if not (self.orig_type is BOOL     and value in (2, 0) or
+                self.orig_type is TRISTATE and value in TRI_TO_STR):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        _name_and_loc(self), TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+        self._rec_invalidate()
+
+        return True
+
+    def unset_value(self):
+        """
+        Resets the user value (mode) and user selection of the Choice, as if
+        the user had never touched the mode or any of the choice symbols.
+        """
+        if self.user_value is not None or self.user_selection:
+            self.user_value = self.user_selection = None
+            self._rec_invalidate()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the choice when it is evaluated
+        on e.g. the interactive Python prompt.
+        """
+        fields = ["choice " + self.name if self.name else "choice",
+                  TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        add("mode " + self.str_value)
+
+        if self.user_value is not None:
+            add('user mode {}'.format(TRI_TO_STR[self.user_value]))
+
+        if self.selection:
+            add("{} selected".format(self.selection.name))
+
+        if self.user_selection:
+            user_sel_str = "{} selected by user" \
+                           .format(self.user_selection.name)
+
+            if self.selection is not self.user_selection:
+                user_sel_str += " (overridden)"
+
+            add(user_sel_str)
+
+        add("visibility " + TRI_TO_STR[self.visibility])
+
+        if self.is_optional:
+            add("optional")
+
+        for node in self.nodes:
+            add("{}:{}".format(node.filename, node.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the choice when it is printed.
+        Matches the Kconfig format (though without the contained choice
+        symbols), with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The returned string does not end in a newline.
+
+        See Symbol.__str__() as well.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Choice.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Choice constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   direct_dep
+        #   kconfig
+
+        # - UNKNOWN == 0
+        # - _visited is used during dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.syms = []
+        self.defaults = []
+
+        self.name = \
+        self.user_value = self.user_selection = \
+        self._cached_vis = self._cached_assignable = None
+
+        self._cached_selection = _NO_CACHED_SELECTION
+
+        # is_constant is checked by _make_depend_on(). Just set it to avoid
+        # having to special-case choices.
+        self.is_constant = self.is_optional = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        if not vis:
+            return ()
+
+        if vis == 2:
+            if not self.is_optional:
+                return (2,) if self.type is BOOL else (1, 2)
+            return (0, 2) if self.type is BOOL else (0, 1, 2)
+
+        # vis == 1
+
+        return (0, 1) if self.is_optional else (1,)
+
+    def _selection(self):
+        # Worker function for the 'selection' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        if self.tri_value != 2:
+            # Not in y mode, so no selection
+            return None
+
+        # Use the user selection if it's visible
+        if self.user_selection and self.user_selection.visibility:
+            return self.user_selection
+
+        # Otherwise, check if we have a default
+        return self._selection_from_defaults()
+
+    def _selection_from_defaults(self):
+        # Check if we have a default
+        for sym, cond in self.defaults:
+            # The default symbol must be visible too
+            if expr_value(cond) and sym.visibility:
+                return sym
+
+        # Otherwise, pick the first visible symbol, if any
+        for sym in self.syms:
+            if sym.visibility:
+                return sym
+
+        # Couldn't find a selection
+        return None
+
+    def _invalidate(self):
+        self._cached_vis = self._cached_assignable = None
+        self._cached_selection = _NO_CACHED_SELECTION
+
+    def _rec_invalidate(self):
+        # See Symbol._rec_invalidate()
+
+        self._invalidate()
+
+        for item in self._dependents:
+            if item._cached_vis is not None:
+                item._rec_invalidate()
+
+
+class MenuNode(object):
+    """
+    Represents a menu node in the configuration. This corresponds to an entry
+    in e.g. the 'make menuconfig' interface, though non-visible choices, menus,
+    and comments also get menu nodes. If a symbol or choice is defined in
+    multiple locations, it gets one menu node for each location.
+
+    The top-level menu node, corresponding to the implicit top-level menu, is
+    available in Kconfig.top_node.
+
+    The menu nodes for a Symbol or Choice can be found in the
+    Symbol/Choice.nodes attribute. Menus and comments are represented as plain
+    menu nodes, with their text stored in the prompt attribute (prompt[0]).
+    This mirrors the C implementation.
+
+    The following attributes are available on MenuNode instances. They should
+    be viewed as read-only.
+
+    item:
+      Either a Symbol, a Choice, or one of the constants MENU and COMMENT.
+      Menus and comments are represented as plain menu nodes. Ifs are collapsed
+      (matching the C implementation) and do not appear in the final menu tree.
+
+    next:
+      The following menu node. None if there is no following node.
+
+    list:
+      The first child menu node. None if there are no children.
+
+      Choices and menus naturally have children, but Symbols can also have
+      children because of menus created automatically from dependencies (see
+      kconfig-language.txt).
+
+    parent:
+      The parent menu node. None if there is no parent.
+
+    prompt:
+      A (string, cond) tuple with the prompt for the menu node and its
+      conditional expression (which is self.kconfig.y if there is no
+      condition). None if there is no prompt.
+
+      For symbols and choices, the prompt is stored in the MenuNode rather than
+      the Symbol or Choice instance. For menus and comments, the prompt holds
+      the text.
+
+    defaults:
+      The 'default' properties for this particular menu node. See
+      symbol.defaults.
+
+      When evaluating defaults, you should use Symbol/Choice.defaults instead,
+      as it include properties from all menu nodes (a symbol/choice can have
+      multiple definition locations/menu nodes). MenuNode.defaults is meant for
+      documentation generation.
+
+    selects:
+      Like MenuNode.defaults, for selects.
+
+    implies:
+      Like MenuNode.defaults, for implies.
+
+    ranges:
+      Like MenuNode.defaults, for ranges.
+
+    orig_prompt:
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      These work the like the corresponding attributes without orig_*, but omit
+      any dependencies propagated from 'depends on' and surrounding 'if's (the
+      direct dependencies, stored in MenuNode.dep).
+
+      One use for this is generating less cluttered documentation, by only
+      showing the direct dependencies in one place.
+
+    help:
+      The help text for the menu node for Symbols and Choices. None if there is
+      no help text. Always stored in the node rather than the Symbol or Choice.
+      It is possible to have a separate help text at each location if a symbol
+      is defined in multiple locations.
+
+      Trailing whitespace (including a final newline) is stripped from the help
+      text. This was not the case before Kconfiglib 10.21.0, where the format
+      was undocumented.
+
+    dep:
+      The direct ('depends on') dependencies for the menu node, or
+      self.kconfig.y if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If a symbol or choice is defined in multiple locations, only the
+      properties defined at a particular location get the corresponding
+      MenuNode.dep dependencies propagated to them.
+
+    visibility:
+      The 'visible if' dependencies for the menu node (which must represent a
+      menu), or self.kconfig.y if there are no 'visible if' dependencies.
+      'visible if' dependencies are recursively propagated to the prompts of
+      symbols and choices within the menu.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the menu node.
+
+      Also includes dependencies inherited from surrounding menus and ifs.
+      Choices appear in the dependencies of choice symbols.
+
+    is_menuconfig:
+      Set to True if the children of the menu node should be displayed in a
+      separate menu. This is the case for the following items:
+
+        - Menus (node.item == MENU)
+
+        - Choices
+
+        - Symbols defined with the 'menuconfig' keyword. The children come from
+          implicitly created submenus, and should be displayed in a separate
+          menu rather than being indented.
+
+      'is_menuconfig' is just a hint on how to display the menu node. It's
+      ignored internally by Kconfiglib, except when printing symbols.
+
+    filename/linenr:
+      The location where the menu node appears. The filename is relative to
+      $srctree (or to the current directory if $srctree isn't set), except
+      absolute paths are used for paths outside $srctree.
+
+    include_path:
+      A tuple of (filename, linenr) tuples, giving the locations of the
+      'source' statements via which the Kconfig file containing this menu node
+      was included. The first element is the location of the 'source' statement
+      in the top-level Kconfig file passed to Kconfig.__init__(), etc.
+
+      Note that the Kconfig file of the menu node itself isn't included. Check
+      'filename' and 'linenr' for that.
+
+    kconfig:
+      The Kconfig instance the menu node is from.
+    """
+    __slots__ = (
+        "dep",
+        "filename",
+        "help",
+        "include_path",
+        "is_menuconfig",
+        "item",
+        "kconfig",
+        "linenr",
+        "list",
+        "next",
+        "parent",
+        "prompt",
+        "visibility",
+
+        # Properties
+        "defaults",
+        "selects",
+        "implies",
+        "ranges",
+    )
+
+    def __init__(self):
+        # Properties defined on this particular menu node. A local 'depends on'
+        # only applies to these, in case a symbol is defined in multiple
+        # locations.
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+    @property
+    def orig_prompt(self):
+        """
+        See the class documentation.
+        """
+        if not self.prompt:
+            return None
+        return (self.prompt[0], self._strip_dep(self.prompt[1]))
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [(default, self._strip_dep(cond))
+                for default, cond in self.defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [(select, self._strip_dep(cond))
+                for select, cond in self.selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [(imply, self._strip_dep(cond))
+                for imply, cond in self.implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [(low, high, self._strip_dep(cond))
+                for low, high, cond in self.ranges]
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        # self.dep is included to catch dependencies from a lone 'depends on'
+        # when there are no properties to propagate it to
+        res = expr_items(self.dep)
+
+        if self.prompt:
+            res |= expr_items(self.prompt[1])
+
+        if self.item is MENU:
+            res |= expr_items(self.visibility)
+
+        for value, cond in self.defaults:
+            res |= expr_items(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.selects:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.implies:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for low, high, cond in self.ranges:
+            res.add(low)
+            res.add(high)
+            res |= expr_items(cond)
+
+        return res
+
+    def __repr__(self):
+        """
+        Returns a string with information about the menu node when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        fields = []
+        add = fields.append
+
+        if self.item.__class__ is Symbol:
+            add("menu node for symbol " + self.item.name)
+
+        elif self.item.__class__ is Choice:
+            s = "menu node for choice"
+            if self.item.name is not None:
+                s += " " + self.item.name
+            add(s)
+
+        elif self.item is MENU:
+            add("menu node for menu")
+
+        else:  # self.item is COMMENT
+            add("menu node for comment")
+
+        if self.prompt:
+            add('prompt "{}" (visibility {})'.format(
+                self.prompt[0], TRI_TO_STR[expr_value(self.prompt[1])]))
+
+        if self.item.__class__ is Symbol and self.is_menuconfig:
+            add("is menuconfig")
+
+        add("deps " + TRI_TO_STR[expr_value(self.dep)])
+
+        if self.item is MENU:
+            add("'visible if' deps " + TRI_TO_STR[expr_value(self.visibility)])
+
+        if self.item.__class__ in _SYMBOL_CHOICE and self.help is not None:
+            add("has help")
+
+        if self.list:
+            add("has child")
 
-    "y" - One symbol will be "y" while the rest are "n".
+        if self.next:
+            add("has next")
 
-    Only tristate choices can be in "m" mode, and the visibility of the choice
-    is an upper bound on the mode, so that e.g. a choice that depends on a
-    symbol with value "m" will be in "m" mode.
+        add("{}:{}".format(self.filename, self.linenr))
 
-    The mode changes automatically when a value is assigned to a symbol within
-    the choice.
+        return "<{}>".format(", ".join(fields))
 
-    See Symbol.get_visibility() too."""
+    def __str__(self):
+        """
+        Returns a string representation of the menu node. Matches the Kconfig
+        format, with any parent dependencies propagated to the 'depends on'
+        condition.
 
-    #
-    # Public interface
-    #
+        The output could (almost) be fed back into a Kconfig parser to redefine
+        the object associated with the menu node. See the module documentation
+        for a gotcha related to choice symbols.
 
-    def get_config(self):
-        """Returns the Config instance this choice is from."""
-        return self.config
-
-    def get_name(self):
-        """For named choices, returns the name. Returns None for unnamed
-        choices. No named choices appear anywhere in the kernel Kconfig files
-        as of Linux 3.7.0-rc8."""
-        return self.name
-
-    def get_type(self):
-        """Returns the type of the choice. See Symbol.get_type()."""
-        return self.type
-
-    def get_prompts(self):
-        """Returns a list of prompts defined for the choice, in the order they
-        appear in the configuration files. Returns the empty list for choices
-        with no prompt.
-
-        This list will have a single entry for the vast majority of choices
-        having prompts, but having multiple prompts for a single choice is
-        possible through having multiple 'choice' entries for it (though I'm
-        not sure if that ever happens in practice)."""
-        return [prompt for prompt, _ in self.orig_prompts]
-
-    def get_help(self):
-        """Returns the help text of the choice, or None if the choice has no
-        help text."""
-        return self.help
-
-    def get_parent(self):
-        """Returns the menu or choice statement that contains the choice, or
-        None if the choice is at the top level. Note that if statements are
-        treated as syntactic sugar and do not have an explicit class
-        representation."""
-        return self.parent
-
-    def get_def_locations(self):
-        """Returns a list of (filename, linenr) tuples, where filename (string)
-        and linenr (int) represent a location where the choice is defined. For
-        the vast majority of choices (all of them as of Linux 3.7.0-rc8) this
-        list will only contain one element, but its possible for named choices
-        to be defined in multiple locations."""
-        return self.def_locations
-
-    def get_selection(self):
-        """Returns the symbol selected (either by the user or through
-        defaults), or None if either no symbol is selected or the mode is not
-        "y"."""
-        if self.cached_selection is not None:
-            if self.cached_selection == NO_SELECTION:
-                return None
-            return self.cached_selection
-
-        if self.get_mode() != "y":
-            return self._cache_ret(None)
-
-        # User choice available?
-        if self.user_val is not None and _get_visibility(self.user_val) == "y":
-            return self._cache_ret(self.user_val)
-
-        if self.optional:
-            return self._cache_ret(None)
-
-        return self._cache_ret(self.get_selection_from_defaults())
-
-    def get_selection_from_defaults(self):
-        """Like Choice.get_selection(), but acts as if no symbol has been
-        selected by the user and no 'optional' flag is in effect."""
-
-        if not self.actual_symbols:
-            return None
+        For symbols and choices with multiple menu nodes (multiple definition
+        locations), properties that aren't associated with a particular menu
+        node are shown on all menu nodes ('option env=...', 'optional' for
+        choices, etc.).
 
-        for symbol, cond_expr in self.def_exprs:
-            if self.config._eval_expr(cond_expr) != "n":
-                chosen_symbol = symbol
-                break
-        else:
-            chosen_symbol = self.actual_symbols[0]
-
-        # Is the chosen symbol visible?
-        if _get_visibility(chosen_symbol) != "n":
-            return chosen_symbol
-        # Otherwise, pick the first visible symbol
-        for sym in self.actual_symbols:
-            if _get_visibility(sym) != "n":
-                return sym
-        return None
+        The returned string does not end in a newline.
+        """
+        return self.custom_str(standard_sc_expr_str)
 
-    def get_user_selection(self):
-        """If the choice is in "y" mode and has a user-selected symbol, returns
-        that symbol. Otherwise, returns None."""
-        return self.user_val
-
-    def get_items(self):
-        """Gets all items contained in the choice in the same order as within
-        the configuration ("items" instead of "symbols" since choices and
-        comments might appear within choices. This only happens in one place as
-        of Linux 3.7.0-rc8, in drivers/usb/gadget/Kconfig)."""
-        return self.block
-
-    def get_symbols(self):
-        """Returns a list containing the choice's symbols.
-
-        A quirk (perhaps a bug) of Kconfig is that you can put items within a
-        choice that will not be considered members of the choice insofar as
-        selection is concerned. This happens for example if one symbol within a
-        choice 'depends on' the symbol preceding it, or if you put non-symbol
-        items within choices.
-
-        As of Linux 3.7.0-rc8, this seems to be used intentionally in one
-        place: drivers/usb/gadget/Kconfig.
-
-        This function returns the "proper" symbols of the choice in the order
-        they appear in the choice, excluding such items. If you want all items
-        in the choice, use get_items()."""
-        return self.actual_symbols
-
-    def get_referenced_symbols(self, refs_from_enclosing=False):
-        """See Symbol.get_referenced_symbols()."""
-        return self.all_referenced_syms if refs_from_enclosing else \
-               self.referenced_syms
-
-    def get_visibility(self):
-        """Returns the visibility of the choice statement: one of "n", "m" or
-        "y". This acts as an upper limit on the mode of the choice (though bool
-        choices can only have the mode "y"). See the class documentation for an
-        explanation of modes."""
-        return _get_visibility(self)
-
-    def get_mode(self):
-        """Returns the mode of the choice. See the class documentation for
-        an explanation of modes."""
-        minimum_mode = "n" if self.optional else "m"
-        mode = self.user_mode if self.user_mode is not None else minimum_mode
-        mode = self.config._eval_min(mode, _get_visibility(self))
-
-        # Promote "m" to "y" for boolean choices
-        if mode == "m" and self.type == BOOL:
-            return "y"
-
-        return mode
-
-    def is_optional(self):
-        """Returns True if the choice has the 'optional' flag set (and so will
-        default to "n" mode)."""
-        return self.optional
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like MenuNode.__str__(), but allows a custom format to be used
+        for all symbol/choice references. See expr_str().
+        """
+        return self._menu_comment_node_str(sc_expr_str_fn) \
+               if self.item in _MENU_COMMENT else \
+               self._sym_choice_node_str(sc_expr_str_fn)
 
-    def __str__(self):
-        """Returns a string containing various information about the choice
-        statement."""
-        return self.config._get_sym_or_choice_str(self)
+    def _menu_comment_node_str(self, sc_expr_str_fn):
+        s = '{} "{}"'.format("menu" if self.item is MENU else "comment",
+                             self.prompt[0])
 
-    #
-    # Private methods
-    #
+        if self.dep is not self.kconfig.y:
+            s += "\n\tdepends on {}".format(expr_str(self.dep, sc_expr_str_fn))
 
-    def __init__(self):
-        """Choice constructor -- not intended to be called directly by
-        Kconfiglib clients."""
-
-        self.name = None # Yes, choices can be named
-        self.type = UNKNOWN
-        self.prompts = []
-        self.def_exprs = [] # 'default' properties
-        self.help = None # Help text
-        self.block = [] # List of contained items
-        self.config = None
-        self.parent = None
-
-        self.user_val = None
-        self.user_mode = None
-
-        # We need to filter out symbols that appear within the choice block but
-        # are not considered choice items (see
-        # Choice._determine_actual_symbols()) This list holds the "actual"
-        # choice items.
-        self.actual_symbols = []
-
-        # The prompts and default values without any dependencies from
-        # enclosing menus and ifs propagated
-        self.orig_prompts = []
-        self.orig_def_exprs = []
-
-        # Dependencies inherited from containing menus and ifs
-        self.deps_from_containing = None
-        # The set of symbols referenced by this choice (see
-        # get_referenced_symbols())
-        self.referenced_syms = set()
-        # Like 'referenced_syms', but includes symbols from
-        # dependencies inherited from enclosing menus and ifs
-        self.all_referenced_syms = set()
-
-        # See Choice.get_def_locations()
-        self.def_locations = []
-
-        # Cached values
-        self.cached_selection = None
-        self.cached_visibility = None
-
-        self.optional = False
-
-    def _determine_actual_symbols(self):
-        """If a symbol's visibility depends on the preceding symbol within a
-        choice, it is no longer viewed as a choice item. (This is quite
-        possibly a bug, but some things consciously use it... ugh. It stems
-        from automatic submenu creation.) In addition, it's possible to have
-        choices and comments within choices, and those shouldn't be considered
-        choice items either. Only drivers/usb/gadget/Kconfig seems to depend on
-        any of this. This method computes the "actual" items in the choice and
-        sets the is_choice_sym flag on them (retrieved via is_choice_symbol()).
-
-        Don't let this scare you: an earlier version simply checked for a
-        sequence of symbols where all symbols after the first appeared in the
-        'depends on' expression of the first, and that worked fine.  The added
-        complexity is to be future-proof in the event that
-        drivers/usb/gadget/Kconfig turns even more sinister. It might very well
-        be overkilling things (especially if that file is refactored ;)."""
-
-        # Items might depend on each other in a tree structure, so we need a
-        # stack to keep track of the current tentative parent
-        stack = []
-
-        for item in self.block:
-            if not isinstance(item, Symbol):
-                stack = []
-                continue
+        if self.item is MENU and self.visibility is not self.kconfig.y:
+            s += "\n\tvisible if {}".format(expr_str(self.visibility,
+                                                     sc_expr_str_fn))
 
-            while stack:
-                if item._has_auto_menu_dep_on(stack[-1]):
-                    # The item should not be viewed as a choice item, so don't
-                    # set item.is_choice_sym
-                    stack.append(item)
-                    break
-                else:
-                    stack.pop()
-            else:
-                item.is_choice_sym = True
-                self.actual_symbols.append(item)
-                stack.append(item)
-
-    def _cache_ret(self, selection):
-        # As None is used to indicate the lack of a cached value we can't use
-        # that to cache the fact that the choice has no selection. Instead, we
-        # use the symbolic constant NO_SELECTION.
-        if selection is None:
-            self.cached_selection = NO_SELECTION
+        return s
+
+    def _sym_choice_node_str(self, sc_expr_str_fn):
+        def indent_add(s):
+            lines.append("\t" + s)
+
+        def indent_add_cond(s, cond):
+            if cond is not self.kconfig.y:
+                s += " if " + expr_str(cond, sc_expr_str_fn)
+            indent_add(s)
+
+        sc = self.item
+
+        if sc.__class__ is Symbol:
+            lines = [("menuconfig " if self.is_menuconfig else "config ")
+                     + sc.name]
         else:
-            self.cached_selection = selection
+            lines = ["choice " + sc.name if sc.name else "choice"]
 
-        return selection
+        if sc.orig_type and not self.prompt:  # sc.orig_type != UNKNOWN
+            # If there's a prompt, we'll use the '<type> "prompt"' shorthand
+            # instead
+            indent_add(TYPE_TO_STR[sc.orig_type])
 
-    def _invalidate(self):
-        self.cached_selection = None
-        self.cached_visibility = None
+        if self.prompt:
+            if sc.orig_type:
+                prefix = TYPE_TO_STR[sc.orig_type]
+            else:
+                # Symbol defined without a type (which generates a warning)
+                prefix = "prompt"
 
-    def _unset_user_value(self):
-        self._invalidate()
-        self.user_val = None
-        self.user_mode = None
+            indent_add_cond(prefix + ' "{}"'.format(escape(self.prompt[0])),
+                            self.orig_prompt[1])
 
-    def _make_conf(self, append_fn):
-        _make_block_conf(self.block, append_fn)
+        if sc.__class__ is Symbol:
+            if sc.is_allnoconfig_y:
+                indent_add("option allnoconfig_y")
 
-class Comment(Item):
+            if sc is sc.kconfig.defconfig_list:
+                indent_add("option defconfig_list")
 
-    """Represents a comment statement."""
+            if sc.env_var is not None:
+                indent_add('option env="{}"'.format(sc.env_var))
 
-    #
-    # Public interface
-    #
+            if sc is sc.kconfig.modules:
+                indent_add("option modules")
 
-    def get_config(self):
-        """Returns the Config instance this comment is from."""
-        return self.config
+            for low, high, cond in self.orig_ranges:
+                indent_add_cond(
+                    "range {} {}".format(sc_expr_str_fn(low),
+                                         sc_expr_str_fn(high)),
+                    cond)
 
-    def get_text(self):
-        """Returns the text of the comment."""
-        return self.text
+        for default, cond in self.orig_defaults:
+            indent_add_cond("default " + expr_str(default, sc_expr_str_fn),
+                            cond)
 
-    def get_parent(self):
-        """Returns the menu or choice statement that contains the comment, or
-        None if the comment is at the top level. Note that if statements are
-        treated as syntactic sugar and do not have an explicit class
-        representation."""
-        return self.parent
+        if sc.__class__ is Choice and sc.is_optional:
+            indent_add("optional")
 
-    def get_location(self):
-        """Returns the location of the comment as a (filename, linenr) tuple,
-        where filename is a string and linenr an int."""
-        return (self.filename, self.linenr)
+        if sc.__class__ is Symbol:
+            for select, cond in self.orig_selects:
+                indent_add_cond("select " + sc_expr_str_fn(select), cond)
 
-    def get_visibility(self):
-        """Returns the visibility of the comment. See also
-        Symbol.get_visibility()."""
-        return self.config._eval_expr(self.dep_expr)
+            for imply, cond in self.orig_implies:
+                indent_add_cond("imply " + sc_expr_str_fn(imply), cond)
 
-    def get_referenced_symbols(self, refs_from_enclosing=False):
-        """See Symbol.get_referenced_symbols()."""
-        return self.all_referenced_syms if refs_from_enclosing else \
-               self.referenced_syms
+        if self.dep is not sc.kconfig.y:
+            indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn))
 
-    def __str__(self):
-        """Returns a string containing various information about the
-        comment."""
-        dep_str = self.config._expr_val_str(self.orig_deps,
-                                            "(no dependencies)")
-
-        additional_deps_str = " " + \
-          self.config._expr_val_str(self.deps_from_containing,
-                                    "(no additional dependencies)")
-
-        return _lines("Comment",
-                      "Text: "         + str(self.text),
-                      "Dependencies: " + dep_str,
-                      "Additional dependencies from enclosing menus and "
-                        "ifs:",
-                      additional_deps_str,
-                      "Location: {0}:{1}".format(self.filename, self.linenr))
+        if self.help is not None:
+            indent_add("help")
+            for line in self.help.splitlines():
+                indent_add("  " + line)
 
-    #
-    # Private methods
-    #
+        return "\n".join(lines)
 
-    def __init__(self):
-        """Comment constructor -- not intended to be called directly by
-        Kconfiglib clients."""
-
-        self.text = None
-        self.dep_expr = None
-        self.config = None
-        self.parent = None
-
-        # Dependency expression without dependencies from enclosing menus and
-        # ifs propagated
-        self.orig_deps = None
-
-        # Dependencies inherited from containing menus and ifs
-        self.deps_from_containing = None
-        # The set of symbols referenced by this comment (see
-        # get_referenced_symbols())
-        self.referenced_syms = set()
-        # Like 'referenced_syms', but includes symbols from
-        # dependencies inherited from enclosing menus and ifs
-        self.all_referenced_syms = None
+    def _strip_dep(self, expr):
+        # Helper function for removing MenuNode.dep from 'expr'. Uses two
+        # pieces of internal knowledge: (1) Expressions are reused rather than
+        # copied, and (2) the direct dependencies always appear at the end.
 
-        self.filename = None
-        self.linenr = None
+        # ... if dep -> ... if y
+        if self.dep is expr:
+            return self.kconfig.y
+
+        # (AND, X, dep) -> X
+        if expr.__class__ is tuple and expr[0] is AND and expr[2] is self.dep:
+            return expr[1]
+
+        return expr
+
+
+class Variable(object):
+    """
+    Represents a preprocessor variable/function.
+
+    The following attributes are available:
+
+    name:
+      The name of the variable.
+
+    value:
+      The unexpanded value of the variable.
+
+    expanded_value:
+      The expanded value of the variable. For simple variables (those defined
+      with :=), this will equal 'value'. Accessing this property will raise a
+      KconfigError if the expansion seems to be stuck in a loop.
+
+      Accessing this field is the same as calling expanded_value_w_args() with
+      no arguments. I hadn't considered function arguments when adding it. It
+      is retained for backwards compatibility though.
+
+    is_recursive:
+      True if the variable is recursive (defined with =).
+    """
+    __slots__ = (
+        "_n_expansions",
+        "is_recursive",
+        "kconfig",
+        "name",
+        "value",
+    )
 
-    def _make_conf(self, append_fn):
-        if self.config._eval_expr(self.dep_expr) != "n":
-            append_fn("\n#\n# {0}\n#".format(self.text))
+    @property
+    def expanded_value(self):
+        """
+        See the class documentation.
+        """
+        return self.expanded_value_w_args()
+
+    def expanded_value_w_args(self, *args):
+        """
+        Returns the expanded value of the variable/function. Any arguments
+        passed will be substituted for $(1), $(2), etc.
+
+        Raises a KconfigError if the expansion seems to be stuck in a loop.
+        """
+        return self.kconfig._fn_val((self.name,) + args)
+
+    def __repr__(self):
+        return "<variable {}, {}, value '{}'>" \
+               .format(self.name,
+                       "recursive" if self.is_recursive else "immediate",
+                       self.value)
+
+
+class KconfigError(Exception):
+    """
+    Exception raised for Kconfig-related errors.
+
+    KconfigError and KconfigSyntaxError are the same class. The
+    KconfigSyntaxError alias is only maintained for backwards compatibility.
+    """
+
+KconfigSyntaxError = KconfigError  # Backwards compatibility
+
+
+class InternalError(Exception):
+    "Never raised. Kept around for backwards compatibility."
+
+
+# Workaround:
+#
+# If 'errno' and 'strerror' are set on IOError, then __str__() always returns
+# "[Errno <errno>] <strerror>", ignoring any custom message passed to the
+# constructor. By defining our own subclass, we can use a custom message while
+# also providing 'errno', 'strerror', and 'filename' to scripts.
+class _KconfigIOError(IOError):
+    def __init__(self, ioerror, msg):
+        self.msg = msg
+        super(_KconfigIOError, self).__init__(
+            ioerror.errno, ioerror.strerror, ioerror.filename)
 
-class Kconfig_Syntax_Error(Exception):
-    """Exception raised for syntax errors."""
-    pass
+    def __str__(self):
+        return self.msg
 
-class Internal_Error(Exception):
-    """Exception raised for internal errors."""
-    pass
 
 #
 # Public functions
 #
 
-def tri_less(v1, v2):
-    """Returns True if the tristate v1 is less than the tristate v2, where "n",
-    "m" and "y" are ordered from lowest to highest."""
-    return TRI_TO_INT[v1] < TRI_TO_INT[v2]
 
-def tri_less_eq(v1, v2):
-    """Returns True if the tristate v1 is less than or equal to the tristate
-    v2, where "n", "m" and "y" are ordered from lowest to highest."""
-    return TRI_TO_INT[v1] <= TRI_TO_INT[v2]
+def expr_value(expr):
+    """
+    Evaluates the expression 'expr' to a tristate value. Returns 0 (n), 1 (m),
+    or 2 (y).
 
-def tri_greater(v1, v2):
-    """Returns True if the tristate v1 is greater than the tristate v2, where
-    "n", "m" and "y" are ordered from lowest to highest."""
-    return TRI_TO_INT[v1] > TRI_TO_INT[v2]
+    'expr' must be an already-parsed expression from a Symbol, Choice, or
+    MenuNode property. To evaluate an expression represented as a string, use
+    Kconfig.eval_string().
 
-def tri_greater_eq(v1, v2):
-    """Returns True if the tristate v1 is greater than or equal to the tristate
-    v2, where "n", "m" and "y" are ordered from lowest to highest."""
-    return TRI_TO_INT[v1] >= TRI_TO_INT[v2]
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    if expr.__class__ is not tuple:
+        return expr.tri_value
 
-#
-# Internal classes
-#
+    if expr[0] is AND:
+        v1 = expr_value(expr[1])
+        # Short-circuit the n case as an optimization (~5% faster
+        # allnoconfig.py and allyesconfig.py, as of writing)
+        return 0 if not v1 else min(v1, expr_value(expr[2]))
 
-class _Feed(object):
+    if expr[0] is OR:
+        v1 = expr_value(expr[1])
+        # Short-circuit the y case as an optimization
+        return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
 
-    """Class for working with sequences in a stream-like fashion; handy for
-    tokens."""
+    if expr[0] is NOT:
+        return 2 - expr_value(expr[1])
 
-    # This would be more helpful on the item classes, but would remove some
-    # flexibility
-    __slots__ = ['items', 'length', 'i']
+    # Relation
+    #
+    # Implements <, <=, >, >= comparisons as well. These were added to
+    # kconfig in 31847b67 (kconfig: allow use of relations other than
+    # (in)equality).
 
-    def __init__(self, items):
-        self.items = items
-        self.length = len(self.items)
-        self.i = 0
+    rel, v1, v2 = expr
 
-    def get_next(self):
-        if self.i >= self.length:
-            return None
-        item = self.items[self.i]
-        self.i += 1
-        return item
-
-    def peek_next(self):
-        return None if self.i >= self.length else self.items[self.i]
-
-    def check(self, token):
-        """Check if the next token is 'token'. If so, remove it from the token
-        feed and return True. Otherwise, leave it in and return False."""
-        if self.i < self.length and self.items[self.i] == token:
-            self.i += 1
-            return True
-        return False
+    # If both operands are strings...
+    if v1.orig_type is STRING and v2.orig_type is STRING:
+        # ...then compare them lexicographically
+        comp = _strcmp(v1.str_value, v2.str_value)
+    else:
+        # Otherwise, try to compare them as numbers
+        try:
+            comp = _sym_to_num(v1) - _sym_to_num(v2)
+        except ValueError:
+            # Fall back on a lexicographic comparison if the operands don't
+            # parse as numbers
+            comp = _strcmp(v1.str_value, v2.str_value)
+
+    return 2*(comp == 0 if rel is EQUAL else
+              comp != 0 if rel is UNEQUAL else
+              comp <  0 if rel is LESS else
+              comp <= 0 if rel is LESS_EQUAL else
+              comp >  0 if rel is GREATER else
+              comp >= 0)
+
+
+def standard_sc_expr_str(sc):
+    """
+    Standard symbol/choice printing function. Uses plain Kconfig syntax, and
+    displays choices as <choice> (or <choice NAME>, for named choices).
+
+    See expr_str().
+    """
+    if sc.__class__ is Symbol:
+        if sc.is_constant and sc.name not in STR_TO_TRI:
+            return '"{}"'.format(escape(sc.name))
+        return sc.name
+
+    return "<choice {}>".format(sc.name) if sc.name else "<choice>"
+
+
+def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
+    """
+    Returns the string representation of the expression 'expr', as in a Kconfig
+    file.
+
+    Passing subexpressions of expressions to this function works as expected.
+
+    sc_expr_str_fn (default: standard_sc_expr_str):
+      This function is called for every symbol/choice (hence "sc") appearing in
+      the expression, with the symbol/choice as the argument. It is expected to
+      return a string to be used for the symbol/choice.
+
+      This can be used e.g. to turn symbols/choices into links when generating
+      documentation, or for printing the value of each symbol/choice after it.
+
+      Note that quoted values are represented as constants symbols
+      (Symbol.is_constant == True).
+    """
+    if expr.__class__ is not tuple:
+        return sc_expr_str_fn(expr)
+
+    if expr[0] is AND:
+        return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
+                                 _parenthesize(expr[2], OR, sc_expr_str_fn))
+
+    if expr[0] is OR:
+        # This turns A && B || C && D into "(A && B) || (C && D)", which is
+        # redundant, but more readable
+        return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
+                                 _parenthesize(expr[2], AND, sc_expr_str_fn))
+
+    if expr[0] is NOT:
+        if expr[1].__class__ is tuple:
+            return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
+        return "!" + sc_expr_str_fn(expr[1])  # Symbol
+
+    # Relation
+    #
+    # Relation operands are always symbols (quoted strings are constant
+    # symbols)
+    return "{} {} {}".format(sc_expr_str_fn(expr[1]), REL_TO_STR[expr[0]],
+                             sc_expr_str_fn(expr[2]))
 
-    def unget_all(self):
-        self.i = 0
 
-class _FileFeed(object):
+def expr_items(expr):
+    """
+    Returns a set() of all items (symbols and choices) that appear in the
+    expression 'expr'.
 
-    """Feeds lines from a file. Keeps track of the filename and current line
-    number. Joins any line ending in \\ with the following line. We need to be
-    careful to get the line number right in the presence of continuation
-    lines."""
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    res = set()
 
-    __slots__ = ['filename', 'lines', 'length', 'linenr']
+    def rec(subexpr):
+        if subexpr.__class__ is tuple:
+            # AND, OR, NOT, or relation
 
-    def __init__(self, filename):
-        self.filename = _clean_up_path(filename)
-        with open(filename, "r") as f:
-            # No interleaving of I/O and processing yet. Don't know if it would
-            # help.
-            self.lines = f.readlines()
-        self.length = len(self.lines)
-        self.linenr = 0
+            rec(subexpr[1])
 
-    def get_next(self):
-        if self.linenr >= self.length:
-            return None
-        line = self.lines[self.linenr]
-        self.linenr += 1
-        while line.endswith("\\\n"):
-            line = line[:-2] + self.lines[self.linenr]
-            self.linenr += 1
-        return line
+            # NOTs only have a single operand
+            if subexpr[0] is not NOT:
+                rec(subexpr[2])
 
-    def peek_next(self):
-        linenr = self.linenr
-        if linenr >= self.length:
-            return None
-        line = self.lines[linenr]
-        while line.endswith("\\\n"):
-            linenr += 1
-            line = line[:-2] + self.lines[linenr]
-        return line
-
-    def unget(self):
-        self.linenr -= 1
-        while self.lines[self.linenr].endswith("\\\n"):
-            self.linenr -= 1
-
-    def next_nonblank(self):
-        """Removes lines up to and including the next non-blank (not all-space)
-        line and returns it. Returns None if there are no more non-blank
-        lines."""
-        while 1:
-            line = self.get_next()
-            if line is None or not line.isspace():
-                return line
+        else:
+            # Symbol or choice
+            res.add(subexpr)
 
-#
-# Internal functions
-#
+    rec(expr)
+    return res
 
-def _get_visibility(sc):
-    """Symbols and Choices have a "visibility" that acts as an upper bound on
-    the values a user can set for them, corresponding to the visibility in e.g.
-    'make menuconfig'. This function calculates the visibility for the Symbol
-    or Choice 'sc' -- the logic is nearly identical."""
-    if sc.cached_visibility is None:
-        vis = "n"
-        for _, cond_expr in sc.prompts:
-            vis = sc.config._eval_max(vis, cond_expr)
-
-        if isinstance(sc, Symbol) and sc.is_choice_sym:
-            if sc.type == TRISTATE and vis == "m" and \
-               sc.parent.get_mode() == "y":
-                # Choice symbols with visibility "m" are not visible if the
-                # choice has mode "y"
-                vis = "n"
-            else:
-                vis = sc.config._eval_min(vis, _get_visibility(sc.parent))
 
-        # Promote "m" to "y" if we're dealing with a non-tristate
-        if vis == "m" and sc.type != TRISTATE:
-            vis = "y"
+def split_expr(expr, op):
+    """
+    Returns a list containing the top-level AND or OR operands in the
+    expression 'expr', in the same (left-to-right) order as they appear in
+    the expression.
 
-        sc.cached_visibility = vis
+    This can be handy e.g. for splitting (weak) reverse dependencies
+    from 'select' and 'imply' into individual selects/implies.
 
-    return sc.cached_visibility
+    op:
+      Either AND to get AND operands, or OR to get OR operands.
 
-def _make_and(e1, e2):
-    """Constructs an AND (&&) expression. Performs trivial simplification.
-    Nones equate to 'y'.
+      (Having this as an operand might be more future-safe than having two
+      hardcoded functions.)
 
-    Note: returns None if e1 == e2 == None."""
-    if e1 is None or e1 == "y":
-        return e2
-    if e2 is None or e2 == "y":
-        return e1
 
-    # Prefer to merge argument lists if possible to reduce the number of nodes
+    Pseudo-code examples:
 
-    if isinstance(e1, tuple) and e1[0] == AND:
-        if isinstance(e2, tuple) and e2[0] == AND:
-            return (AND, e1[1] + e2[1])
-        return (AND, e1[1] + [e2])
+      split_expr( A                    , OR  )  ->  [A]
+      split_expr( A && B               , OR  )  ->  [A && B]
+      split_expr( A || B               , OR  )  ->  [A, B]
+      split_expr( A || B               , AND )  ->  [A || B]
+      split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
 
-    if isinstance(e2, tuple) and e2[0] == AND:
-        return (AND, e2[1] + [e1])
+      # Second || is not at the top level
+      split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
+
+      # Parentheses don't matter as long as we stay at the top level (don't
+      # encounter any non-'op' nodes)
+      split_expr( (A || B) || C        , OR )  ->  [A, B, C]
+      split_expr( A || (B || C)        , OR )  ->  [A, B, C]
+    """
+    res = []
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple and subexpr[0] is op:
+            rec(subexpr[1])
+            rec(subexpr[2])
+        else:
+            res.append(subexpr)
+
+    rec(expr)
+    return res
 
-    return (AND, [e1, e2])
 
-def _make_or(e1, e2):
-    """Constructs an OR (||) expression. Performs trivial simplification and
-    avoids Nones. Nones equate to 'y', which is usually what we want, but needs
-    to be kept in mind."""
+def escape(s):
+    r"""
+    Escapes the string 's' in the same fashion as is done for display in
+    Kconfig format and when writing strings to a .config file. " and \ are
+    replaced by \" and \\, respectively.
+    """
+    # \ must be escaped before " to avoid double escaping
+    return s.replace("\\", r"\\").replace('"', r'\"')
 
-    # Perform trivial simplification and avoid None's (which
-    # correspond to y's)
-    if e1 is None or e2 is None or e1 == "y" or e2 == "y":
-        return "y"
-    if e1 == "n":
-        return e2
 
-    # Prefer to merge argument lists if possible to reduce the number of nodes
+def unescape(s):
+    r"""
+    Unescapes the string 's'. \ followed by any character is replaced with just
+    that character. Used internally when reading .config files.
+    """
+    return _unescape_sub(r"\1", s)
 
-    if isinstance(e1, tuple) and e1[0] == OR:
-        if isinstance(e2, tuple) and e2[0] == OR:
-            return (OR, e1[1] + e2[1])
-        return (OR, e1[1] + [e2])
+# unescape() helper
+_unescape_sub = re.compile(r"\\(.)").sub
 
-    if isinstance(e2, tuple) and e2[0] == OR:
-        return (OR, e2[1] + [e1])
 
-    return (OR, [e1, e2])
+def standard_kconfig():
+    """
+    Helper for tools. Loads the top-level Kconfig specified as the first
+    command-line argument, or "Kconfig" if there are no command-line arguments.
+    Returns the Kconfig instance.
 
-def _get_expr_syms_rec(expr, res):
-    """_get_expr_syms() helper. Recurses through expressions."""
-    if isinstance(expr, Symbol):
-        res.add(expr)
-    elif isinstance(expr, str):
+    Exits with sys.exit() (which raises a SystemExit exception) and prints a
+    usage note to stderr if more than one command-line argument is passed.
+    """
+    if len(sys.argv) > 2:
+        sys.exit("usage: {} [Kconfig]".format(sys.argv[0]))
+
+    # Only show backtraces for unexpected exceptions
+    try:
+        return Kconfig("Kconfig" if len(sys.argv) < 2 else sys.argv[1])
+    except (EnvironmentError, KconfigError) as e:
+        # Some long exception messages have extra newlines for better
+        # formatting when reported as an unhandled exception. Strip them here.
+        sys.exit(str(e).strip())
+
+
+def standard_config_filename():
+    """
+    Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies the
+    .config file to load/save) if it is set, and ".config" otherwise.
+
+    Calling load_config() with filename=None might give the behavior you want,
+    without having to use this function.
+    """
+    return os.getenv("KCONFIG_CONFIG", ".config")
+
+
+def load_allconfig(kconf, filename):
+    """
+    Helper for all*config. Loads (merges) the configuration file specified by
+    KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in the
+    Linux kernel.
+
+    Disables warnings for duplicated assignments within configuration files for
+    the duration of the call (kconf.warn_assign_override/warn_assign_redun = False),
+    and restores the previous warning settings at the end. The
+    KCONFIG_ALLCONFIG configuration file is expected to override symbols.
+
+    Exits with sys.exit() (which raises a SystemExit exception) and prints an
+    error to stderr if KCONFIG_ALLCONFIG is set but the configuration file
+    can't be opened.
+
+    kconf:
+      Kconfig instance to load the configuration in.
+
+    filename:
+      Command-specific configuration filename - "allyes.config",
+      "allno.config", etc.
+    """
+    allconfig = os.getenv("KCONFIG_ALLCONFIG")
+    if allconfig is None:
         return
-    elif expr[0] == AND or expr[0] == OR:
-        for term in expr[1]:
-            _get_expr_syms_rec(term, res)
-    elif expr[0] == NOT:
-        _get_expr_syms_rec(expr[1], res)
-    elif expr[0] == EQUAL or expr[0] == UNEQUAL:
-        if isinstance(expr[1], Symbol):
-            res.add(expr[1])
-        if isinstance(expr[2], Symbol):
-            res.add(expr[2])
+
+    def std_msg(e):
+        # "Upcasts" a _KconfigIOError to an IOError, removing the custom
+        # __str__() message. The standard message is better here.
+        #
+        # This might also convert an OSError to an IOError in obscure cases,
+        # but it's probably not a big deal. The distinction is shaky (see
+        # PEP-3151).
+        return IOError(e.errno, e.strerror, e.filename)
+
+    old_warn_assign_override = kconf.warn_assign_override
+    old_warn_assign_redun = kconf.warn_assign_redun
+    kconf.warn_assign_override = kconf.warn_assign_redun = False
+
+    if allconfig in ("", "1"):
+        try:
+            print(kconf.load_config(filename, False))
+        except EnvironmentError as e1:
+            try:
+                print(kconf.load_config("all.config", False))
+            except EnvironmentError as e2:
+                sys.exit("error: KCONFIG_ALLCONFIG is set, but neither {} "
+                         "nor all.config could be opened: {}, {}"
+                         .format(filename, std_msg(e1), std_msg(e2)))
     else:
-        _internal_error("Internal error while fetching symbols from an "
-                        "expression with token stream {0}.".format(expr))
+        try:
+            print(kconf.load_config(allconfig, False))
+        except EnvironmentError as e:
+            sys.exit("error: KCONFIG_ALLCONFIG is set to '{}', which "
+                     "could not be opened: {}"
+                     .format(allconfig, std_msg(e)))
 
-def _get_expr_syms(expr):
-    """Returns the set() of symbols appearing in expr."""
-    res = set()
-    if expr is not None:
-        _get_expr_syms_rec(expr, res)
-    return res
+    kconf.warn_assign_override = old_warn_assign_override
+    kconf.warn_assign_redun = old_warn_assign_redun
 
-def _str_val(obj):
-    """Returns the value of obj as a string. If obj is not a string (constant
-    symbol), it must be a Symbol."""
-    return obj if isinstance(obj, str) else obj.get_value()
-
-def _make_block_conf(block, append_fn):
-    """Returns a list of .config strings for a block (list) of items."""
-
-    # Collect the substrings in a list and later use join() instead of += to
-    # build the final .config contents. With older Python versions, this yields
-    # linear instead of quadratic complexity.
-    for item in block:
-        item._make_conf(append_fn)
-
-def _sym_str_string(sym_or_str):
-    if isinstance(sym_or_str, str):
-        return '"' + sym_or_str + '"'
-    return sym_or_str.name
-
-def _intersperse(lst, op):
-    """_expr_to_str() helper. Gets the string representation of each expression
-    in lst and produces a list where op has been inserted between the
-    elements."""
-    if not lst:
-        return ""
 
-    res = []
+#
+# Internal functions
+#
 
-    def handle_sub_expr(expr):
-        no_parens = isinstance(expr, (str, Symbol)) or \
-                    expr[0] in (EQUAL, UNEQUAL) or \
-                    PRECEDENCE[op] <= PRECEDENCE[expr[0]]
-        if not no_parens:
-            res.append("(")
-        res.extend(_expr_to_str_rec(expr))
-        if not no_parens:
-            res.append(")")
 
-    op_str = OP_TO_STR[op]
+def _visibility(sc):
+    # Symbols and Choices have a "visibility" that acts as an upper bound on
+    # the values a user can set for them, corresponding to the visibility in
+    # e.g. 'make menuconfig'. This function calculates the visibility for the
+    # Symbol or Choice 'sc' -- the logic is nearly identical.
 
-    handle_sub_expr(lst[0])
-    for expr in lst[1:]:
-        res.append(op_str)
-        handle_sub_expr(expr)
+    vis = 0
 
-    return res
+    for node in sc.nodes:
+        if node.prompt:
+            vis = max(vis, expr_value(node.prompt[1]))
 
-def _expr_to_str_rec(expr):
-    if expr is None:
-        return [""]
+    if sc.__class__ is Symbol and sc.choice:
+        if sc.choice.orig_type is TRISTATE and \
+           sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
+            # Non-tristate choice symbols are only visible in y mode
+            return 0
 
-    if isinstance(expr, (Symbol, str)):
-        return [_sym_str_string(expr)]
+        if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value == 2:
+            # Choice symbols with m visibility are not visible in y mode
+            return 0
 
-    if expr[0] in (AND, OR):
-        return _intersperse(expr[1], expr[0])
+    # Promote m to y if we're dealing with a non-tristate (possibly due to
+    # modules being disabled)
+    if vis == 1 and sc.type is not TRISTATE:
+        return 2
 
-    if expr[0] == NOT:
-        need_parens = not isinstance(expr[1], (str, Symbol))
+    return vis
 
-        res = ["!"]
-        if need_parens:
-            res.append("(")
-        res.extend(_expr_to_str_rec(expr[1]))
-        if need_parens:
-            res.append(")")
-        return res
 
-    if expr[0] in (EQUAL, UNEQUAL):
-        return [_sym_str_string(expr[1]),
-                OP_TO_STR[expr[0]],
-                _sym_str_string(expr[2])]
+def _make_depend_on(sc, expr):
+    # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
+    # Constant symbols in 'expr' are skipped as they can never change value
+    # anyway.
+
+    if expr.__class__ is tuple:
+        # AND, OR, NOT, or relation
+
+        _make_depend_on(sc, expr[1])
+
+        # NOTs only have a single operand
+        if expr[0] is not NOT:
+            _make_depend_on(sc, expr[2])
+
+    elif not expr.is_constant:
+        # Non-constant symbol, or choice
+        expr._dependents.add(sc)
+
 
-def _expr_to_str(expr):
-    return "".join(_expr_to_str_rec(expr))
+def _parenthesize(expr, type_, sc_expr_str_fn):
+    # expr_str() helper. Adds parentheses around expressions of type 'type_'.
 
-def _indentation(line):
-    """Returns the length of the line's leading whitespace, treating tab stops
-    as being spaced 8 characters apart."""
-    line = line.expandtabs()
-    return len(line) - len(line.lstrip())
+    if expr.__class__ is tuple and expr[0] is type_:
+        return "({})".format(expr_str(expr, sc_expr_str_fn))
+    return expr_str(expr, sc_expr_str_fn)
+
+
+def _ordered_unique(lst):
+    # Returns 'lst' with any duplicates removed, preserving order. This hacky
+    # version seems to be a common idiom. It relies on short-circuit evaluation
+    # and set.add() returning None, which is falsy.
+
+    seen = set()
+    seen_add = seen.add
+    return [x for x in lst if x not in seen and not seen_add(x)]
 
-def _deindent(line, indent):
-    """Deindent 'line' by 'indent' spaces."""
-    line = line.expandtabs()
-    if len(line) <= indent:
-        return line
-    return line[indent:]
 
 def _is_base_n(s, n):
     try:
@@ -3412,133 +6222,809 @@ def _is_base_n(s, n):
     except ValueError:
         return False
 
-def _lines(*args):
-    """Returns a string consisting of all arguments, with newlines inserted
-    between them."""
-    return "\n".join(args)
-
-def _comment(s):
-    """Returns a new string with "#" inserted before each line in 's'."""
-    if not s:
-        return "#"
-    res = "".join(["#" + line for line in s.splitlines(True)])
-    if s.endswith("\n"):
-        return res + "#"
-    return res
 
-def _clean_up_path(path):
-    """Strips an initial "./" and any trailing slashes from 'path'."""
-    if path.startswith("./"):
-        path = path[2:]
-    return path.rstrip("/")
-
-def _build_msg(msg, filename, linenr):
-    if filename is not None:
-        msg = "{0}:{1}: ".format(_clean_up_path(filename), linenr) + msg
-    return msg
-
-def _stderr_msg(msg, filename, linenr):
-    sys.stderr.write(_build_msg(msg, filename, linenr) + "\n")
-
-def _tokenization_error(s, filename, linenr):
-    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
-    raise Kconfig_Syntax_Error("{0}Couldn't tokenize '{1}'"
-                               .format(loc, s.strip()))
-
-def _parse_error(s, msg, filename, linenr):
-    loc = "" if filename is None else "{0}:{1}: ".format(filename, linenr)
-    raise Kconfig_Syntax_Error("{0}Couldn't parse '{1}'{2}"
-                               .format(loc, s.strip(),
-                                       "." if msg is None else ": " + msg))
-
-def _internal_error(msg):
-    raise Internal_Error(msg +
-      "\nSorry! You may want to send an email to ulfalizer a.t Google's "
-      "email service to tell me about this. Include the message above and the "
-      "stack trace and describe what you were doing.")
+def _strcmp(s1, s2):
+    # strcmp()-alike that returns -1, 0, or 1
+
+    return (s1 > s2) - (s1 < s2)
+
+
+def _sym_to_num(sym):
+    # expr_value() helper for converting a symbol to a number. Raises
+    # ValueError for symbols that can't be converted.
+
+    # For BOOL and TRISTATE, n/m/y count as 0/1/2. This mirrors 9059a3493ef
+    # ("kconfig: fix relational operators for bool and tristate symbols") in
+    # the C implementation.
+    return sym.tri_value if sym.orig_type in _BOOL_TRISTATE else \
+           int(sym.str_value, _TYPE_TO_BASE[sym.orig_type])
+
+
+def _touch_dep_file(path, sym_name):
+    # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the sync_deps()
+    # docstring.
+
+    sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h"
+    sym_path_dir = dirname(sym_path)
+    if not exists(sym_path_dir):
+        os.makedirs(sym_path_dir, 0o755)
+
+    # A kind of truncating touch, mirroring the C tools
+    os.close(os.open(
+        sym_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644))
+
+
+def _save_old(path):
+    # See write_config()
+
+    def copy(src, dst):
+        # Import as needed, to save some startup time
+        import shutil
+        shutil.copyfile(src, dst)
+
+    if islink(path):
+        # Preserve symlinks
+        copy_fn = copy
+    elif hasattr(os, "replace"):
+        # Python 3 (3.3+) only. Best choice when available, because it
+        # removes <filename>.old on both *nix and Windows.
+        copy_fn = os.replace
+    elif os.name == "posix":
+        # Removes <filename>.old on POSIX systems
+        copy_fn = os.rename
+    else:
+        # Fall back on copying
+        copy_fn = copy
+
+    try:
+        copy_fn(path, path + ".old")
+    except Exception:
+        # Ignore errors from 'path' missing as well as other errors.
+        # <filename>.old file is usually more of a nice-to-have, and not worth
+        # erroring out over e.g. if <filename>.old happens to be a directory or
+        # <filename> is something like /dev/null.
+        pass
+
+
+def _name_and_loc(sc):
+    # Helper for giving the symbol/choice name and location(s) in e.g. warnings
+
+    # Reuse the expression format. That way choices show up as
+    # '<choice (name, if any)>'
+    name = standard_sc_expr_str(sc)
+
+    if not sc.nodes:
+        return name + " (undefined)"
+
+    return "{} (defined at {})".format(
+        name,
+        ", ".join("{}:{}".format(node.filename, node.linenr)
+                  for node in sc.nodes))
+
+
+# Menu manipulation
+
+
+def _expr_depends_on(expr, sym):
+    # Reimplementation of expr_depends_symbol() from mconf.c. Used to determine
+    # if a submenu should be implicitly created. This also influences which
+    # items inside choice statements are considered choice items.
+
+    if expr.__class__ is not tuple:
+        return expr is sym
+
+    if expr[0] in _EQUAL_UNEQUAL:
+        # Check for one of the following:
+        # sym = m/y, m/y = sym, sym != n, n != sym
+
+        left, right = expr[1:]
+
+        if right is sym:
+            left, right = right, left
+        elif left is not sym:
+            return False
+
+        return (expr[0] is EQUAL and right is sym.kconfig.m or
+                                     right is sym.kconfig.y) or \
+               (expr[0] is UNEQUAL and right is sym.kconfig.n)
+
+    return expr[0] is AND and \
+           (_expr_depends_on(expr[1], sym) or
+            _expr_depends_on(expr[2], sym))
+
+
+def _auto_menu_dep(node1, node2):
+    # Returns True if node2 has an "automatic menu dependency" on node1. If
+    # node2 has a prompt, we check its condition. Otherwise, we look directly
+    # at node2.dep.
+
+    return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep,
+                            node1.item)
+
+
+def _flatten(node):
+    # "Flattens" menu nodes without prompts (e.g. 'if' nodes and non-visible
+    # symbols with children from automatic menu creation) so that their
+    # children appear after them instead. This gives a clean menu structure
+    # with no unexpected "jumps" in the indentation.
+    #
+    # Do not flatten promptless choices (which can appear "legitimately" if a
+    # named choice is defined in multiple locations to add on symbols). It
+    # looks confusing, and the menuconfig already shows all choice symbols if
+    # you enter the choice at some location with a prompt.
+
+    while node:
+        if node.list and not node.prompt and \
+           node.item.__class__ is not Choice:
+
+            last_node = node.list
+            while 1:
+                last_node.parent = node.parent
+                if not last_node.next:
+                    break
+                last_node = last_node.next
+
+            last_node.next = node.next
+            node.next = node.list
+            node.list = None
+
+        node = node.next
+
+
+def _remove_ifs(node):
+    # Removes 'if' nodes (which can be recognized by MenuNode.item being None),
+    # which are assumed to already have been flattened. The C implementation
+    # doesn't bother to do this, but we expose the menu tree directly, and it
+    # makes it nicer to work with.
+
+    cur = node.list
+    while cur and not cur.item:
+        cur = cur.next
+
+    node.list = cur
+
+    while cur:
+        next = cur.next
+        while next and not next.item:
+            next = next.next
+
+        # Equivalent to
+        #
+        #   cur.next = next
+        #   cur = next
+        #
+        # due to tricky Python semantics. The order matters.
+        cur.next = cur = next
+
+
+def _finalize_choice(node):
+    # Finalizes a choice, marking each symbol whose menu node has the choice as
+    # the parent as a choice symbol, and automatically determining types if not
+    # specified.
+
+    choice = node.item
+
+    cur = node.list
+    while cur:
+        if cur.item.__class__ is Symbol:
+            cur.item.choice = choice
+            choice.syms.append(cur.item)
+        cur = cur.next
+
+    # If no type is specified for the choice, its type is that of
+    # the first choice item with a specified type
+    if not choice.orig_type:
+        for item in choice.syms:
+            if item.orig_type:
+                choice.orig_type = item.orig_type
+                break
+
+    # Each choice item of UNKNOWN type gets the type of the choice
+    for sym in choice.syms:
+        if not sym.orig_type:
+            sym.orig_type = choice.orig_type
+
+
+def _check_dep_loop_sym(sym, ignore_choice):
+    # Detects dependency loops using depth-first search on the dependency graph
+    # (which is calculated earlier in Kconfig._build_dep()).
+    #
+    # Algorithm:
+    #
+    #  1. Symbols/choices start out with _visited = 0, meaning unvisited.
+    #
+    #  2. When a symbol/choice is first visited, _visited is set to 1, meaning
+    #     "visited, potentially part of a dependency loop". The recursive
+    #     search then continues from the symbol/choice.
+    #
+    #  3. If we run into a symbol/choice X with _visited already set to 1,
+    #     there's a dependency loop. The loop is found on the call stack by
+    #     recording symbols while returning ("on the way back") until X is seen
+    #     again.
+    #
+    #  4. Once a symbol/choice and all its dependencies (or dependents in this
+    #     case) have been checked recursively without detecting any loops, its
+    #     _visited is set to 2, meaning "visited, not part of a dependency
+    #     loop".
+    #
+    #     This saves work if we run into the symbol/choice again in later calls
+    #     to _check_dep_loop_sym(). We just return immediately.
+    #
+    # Choices complicate things, as every choice symbol depends on every other
+    # choice symbol in a sense. When a choice is "entered" via a choice symbol
+    # X, we visit all choice symbols from the choice except X, and prevent
+    # immediately revisiting the choice with a flag (ignore_choice).
+    #
+    # Maybe there's a better way to handle this (different flags or the
+    # like...)
+
+    if not sym._visited:
+        # sym._visited == 0, unvisited
+
+        sym._visited = 1
+
+        for dep in sym._dependents:
+            # Choices show up in Symbol._dependents when the choice has the
+            # symbol in a 'prompt' or 'default' condition (e.g.
+            # 'default ... if SYM').
+            #
+            # Since we aren't entering the choice via a choice symbol, all
+            # choice symbols need to be checked, hence the None.
+            loop = _check_dep_loop_choice(dep, None) \
+                   if dep.__class__ is Choice \
+                   else _check_dep_loop_sym(dep, False)
+
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        if sym.choice and not ignore_choice:
+            loop = _check_dep_loop_choice(sym.choice, sym)
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        # The symbol is not part of a dependency loop
+        sym._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if sym._visited == 2:
+        # The symbol was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # sym._visited == 1, found a dependency loop. Return the symbol as the
+    # first element in it.
+    return (sym,)
+
+
+def _check_dep_loop_choice(choice, skip):
+    if not choice._visited:
+        # choice._visited == 0, unvisited
+
+        choice._visited = 1
+
+        # Check for loops involving choice symbols. If we came here via a
+        # choice symbol, skip that one, as we'd get a false positive
+        # '<sym FOO> -> <choice> -> <sym FOO>' loop otherwise.
+        for sym in choice.syms:
+            if sym is not skip:
+                # Prevent the choice from being immediately re-entered via the
+                # "is a choice symbol" path by passing True
+                loop = _check_dep_loop_sym(sym, True)
+                if loop:
+                    # Dependency loop found
+                    return _found_dep_loop(loop, choice)
+
+        # The choice is not part of a dependency loop
+        choice._visited = 2
+
+        # No dependency loop found
+        return None
 
+    if choice._visited == 2:
+        # The choice was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # choice._visited == 1, found a dependency loop. Return the choice as the
+    # first element in it.
+    return (choice,)
+
+
+def _found_dep_loop(loop, cur):
+    # Called "on the way back" when we know we have a loop
+
+    # Is the symbol/choice 'cur' where the loop started?
+    if cur is not loop[0]:
+        # Nope, it's just a part of the loop
+        return loop + (cur,)
+
+    # Yep, we have the entire loop. Throw an exception that shows it.
+
+    msg = "\nDependency loop\n" \
+            "===============\n\n"
+
+    for item in loop:
+        if item is not loop[0]:
+            msg += "...depends on "
+            if item.__class__ is Symbol and item.choice:
+                msg += "the choice symbol "
+
+        msg += "{}, with definition...\n\n{}\n\n" \
+               .format(_name_and_loc(item), item)
+
+        # Small wart: Since we reuse the already calculated
+        # Symbol/Choice._dependents sets for recursive dependency detection, we
+        # lose information on whether a dependency came from a 'select'/'imply'
+        # condition or e.g. a 'depends on'.
+        #
+        # This might cause selecting symbols to "disappear". For example,
+        # a symbol B having 'select A if C' gives a direct dependency from A to
+        # C, since it corresponds to a reverse dependency of B && C.
+        #
+        # Always print reverse dependencies for symbols that have them to make
+        # sure information isn't lost. I wonder if there's some neat way to
+        # improve this.
+
+        if item.__class__ is Symbol:
+            if item.rev_dep is not item.kconfig.n:
+                msg += "(select-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+            if item.weak_rev_dep is not item.kconfig.n:
+                msg += "(imply-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+    msg += "...depends again on {}".format(_name_and_loc(loop[0]))
+
+    raise KconfigError(msg)
+
+
+def _decoding_error(e, filename, macro_linenr=None):
+    # Gives the filename and context for UnicodeDecodeError's, which are a pain
+    # to debug otherwise. 'e' is the UnicodeDecodeError object.
+    #
+    # If the decoding error is for the output of a $(shell,...) command,
+    # macro_linenr holds the line number where it was run (the exact line
+    # number isn't available for decoding errors in files).
+
+    raise KconfigError(
+        "\n"
+        "Malformed {} in {}\n"
+        "Context: {}\n"
+        "Problematic data: {}\n"
+        "Reason: {}".format(
+            e.encoding,
+            "'{}'".format(filename) if macro_linenr is None else
+                "output from macro at {}:{}".format(filename, macro_linenr),
+            e.object[max(e.start - 40, 0):e.end + 40],
+            e.object[e.start:e.end],
+            e.reason))
+
+
+def _warn_verbose_deprecated(fn_name):
+    sys.stderr.write(
+        "Deprecation warning: {0}()'s 'verbose' argument has no effect. Since "
+        "Kconfiglib 12.0.0, the message is returned from {0}() instead, "
+        "and is always generated. Do e.g. print(kconf.{0}()) if you want to "
+        "want to show a message like \"Loaded configuration '.config'\" on "
+        "stdout. The old API required ugly hacks to reuse messages in "
+        "configuration interfaces.\n".format(fn_name))
+
+
+# Predefined preprocessor functions
+
+
+def _filename_fn(kconf, _):
+    return kconf.filename
+
+
+def _lineno_fn(kconf, _):
+    return str(kconf.linenr)
+
+
+def _info_fn(kconf, _, msg):
+    print("{}:{}: {}".format(kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _warning_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        kconf._warn(msg, kconf.filename, kconf.linenr)
+
+    return ""
+
+
+def _error_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        raise KconfigError("{}:{}: {}".format(
+            kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _shell_fn(kconf, _, command):
+    # Only import as needed, to save some startup time
+    import subprocess
+
+    stdout, stderr = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    ).communicate()
+
+    if not _IS_PY2:
+        try:
+            stdout = stdout.decode(kconf._encoding)
+            stderr = stderr.decode(kconf._encoding)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, kconf.filename, kconf.linenr)
+
+    if stderr:
+        kconf._warn("'{}' wrote to stderr: {}".format(
+                        command, "\n".join(stderr.splitlines())),
+                    kconf.filename, kconf.linenr)
+
+    # Universal newlines with splitlines() (to prevent e.g. stray \r's in
+    # command output on Windows), trailing newline removal, and
+    # newline-to-space conversion.
+    #
+    # On Python 3 versions before 3.6, it's not possible to specify the
+    # encoding when passing universal_newlines=True to Popen() (the 'encoding'
+    # parameter was added in 3.6), so we do this manual version instead.
+    return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
+
+#
+# Global constants
+#
+
+TRI_TO_STR = {
+    0: "n",
+    1: "m",
+    2: "y",
+}
+
+STR_TO_TRI = {
+    "n": 0,
+    "m": 1,
+    "y": 2,
+}
+
+# Constant representing that there's no cached choice selection. This is
+# distinct from a cached None (no selection). Any object that's not None or a
+# Symbol will do. We test this with 'is'.
+_NO_CACHED_SELECTION = 0
+
+# Are we running on Python 2?
+_IS_PY2 = sys.version_info[0] < 3
+
+try:
+    _UNAME_RELEASE = os.uname()[2]
+except AttributeError:
+    # Only import as needed, to save some startup time
+    import platform
+    _UNAME_RELEASE = platform.uname()[2]
+
+# The token and type constants below are safe to test with 'is', which is a bit
+# faster (~30% faster on my machine, and a few % faster for total parsing
+# time), even without assuming Python's small integer optimization (which
+# caches small integer objects). The constants end up pointing to unique
+# integer objects, and since we consistently refer to them via the names below,
+# we always get the same object.
 #
-# Internal global constants
+# Client code should use == though.
+
+# Tokens, with values 1, 2, ... . Avoiding 0 simplifies some checks by making
+# all tokens except empty strings truthy.
+(
+    _T_ALLNOCONFIG_Y,
+    _T_AND,
+    _T_BOOL,
+    _T_CHOICE,
+    _T_CLOSE_PAREN,
+    _T_COMMENT,
+    _T_CONFIG,
+    _T_DEFAULT,
+    _T_DEFCONFIG_LIST,
+    _T_DEF_BOOL,
+    _T_DEF_HEX,
+    _T_DEF_INT,
+    _T_DEF_STRING,
+    _T_DEF_TRISTATE,
+    _T_DEPENDS,
+    _T_ENDCHOICE,
+    _T_ENDIF,
+    _T_ENDMENU,
+    _T_ENV,
+    _T_EQUAL,
+    _T_GREATER,
+    _T_GREATER_EQUAL,
+    _T_HELP,
+    _T_HEX,
+    _T_IF,
+    _T_IMPLY,
+    _T_INT,
+    _T_LESS,
+    _T_LESS_EQUAL,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_MENUCONFIG,
+    _T_MODULES,
+    _T_NOT,
+    _T_ON,
+    _T_OPEN_PAREN,
+    _T_OPTION,
+    _T_OPTIONAL,
+    _T_OR,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RANGE,
+    _T_RSOURCE,
+    _T_SELECT,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+    _T_UNEQUAL,
+    _T_VISIBLE,
+) = range(1, 51)
+
+# Keyword to token map, with the get() method assigned directly as a small
+# optimization
+_get_keyword = {
+    "---help---":     _T_HELP,
+    "allnoconfig_y":  _T_ALLNOCONFIG_Y,
+    "bool":           _T_BOOL,
+    "boolean":        _T_BOOL,
+    "choice":         _T_CHOICE,
+    "comment":        _T_COMMENT,
+    "config":         _T_CONFIG,
+    "def_bool":       _T_DEF_BOOL,
+    "def_hex":        _T_DEF_HEX,
+    "def_int":        _T_DEF_INT,
+    "def_string":     _T_DEF_STRING,
+    "def_tristate":   _T_DEF_TRISTATE,
+    "default":        _T_DEFAULT,
+    "defconfig_list": _T_DEFCONFIG_LIST,
+    "depends":        _T_DEPENDS,
+    "endchoice":      _T_ENDCHOICE,
+    "endif":          _T_ENDIF,
+    "endmenu":        _T_ENDMENU,
+    "env":            _T_ENV,
+    "grsource":       _T_ORSOURCE,  # Backwards compatibility
+    "gsource":        _T_OSOURCE,   # Backwards compatibility
+    "help":           _T_HELP,
+    "hex":            _T_HEX,
+    "if":             _T_IF,
+    "imply":          _T_IMPLY,
+    "int":            _T_INT,
+    "mainmenu":       _T_MAINMENU,
+    "menu":           _T_MENU,
+    "menuconfig":     _T_MENUCONFIG,
+    "modules":        _T_MODULES,
+    "on":             _T_ON,
+    "option":         _T_OPTION,
+    "optional":       _T_OPTIONAL,
+    "orsource":       _T_ORSOURCE,
+    "osource":        _T_OSOURCE,
+    "prompt":         _T_PROMPT,
+    "range":          _T_RANGE,
+    "rsource":        _T_RSOURCE,
+    "select":         _T_SELECT,
+    "source":         _T_SOURCE,
+    "string":         _T_STRING,
+    "tristate":       _T_TRISTATE,
+    "visible":        _T_VISIBLE,
+}.get
+
+# The constants below match the value of the corresponding tokens to remove the
+# need for conversion
+
+# Node types
+MENU    = _T_MENU
+COMMENT = _T_COMMENT
+
+# Expression types
+AND           = _T_AND
+OR            = _T_OR
+NOT           = _T_NOT
+EQUAL         = _T_EQUAL
+UNEQUAL       = _T_UNEQUAL
+LESS          = _T_LESS
+LESS_EQUAL    = _T_LESS_EQUAL
+GREATER       = _T_GREATER
+GREATER_EQUAL = _T_GREATER_EQUAL
+
+REL_TO_STR = {
+    EQUAL:         "=",
+    UNEQUAL:       "!=",
+    LESS:          "<",
+    LESS_EQUAL:    "<=",
+    GREATER:       ">",
+    GREATER_EQUAL: ">=",
+}
+
+# Symbol/choice types. UNKNOWN is 0 (falsy) to simplify some checks.
+# Client code shouldn't rely on it though, as it was non-zero in
+# older versions.
+UNKNOWN  = 0
+BOOL     = _T_BOOL
+TRISTATE = _T_TRISTATE
+STRING   = _T_STRING
+INT      = _T_INT
+HEX      = _T_HEX
+
+TYPE_TO_STR = {
+    UNKNOWN:  "unknown",
+    BOOL:     "bool",
+    TRISTATE: "tristate",
+    STRING:   "string",
+    INT:      "int",
+    HEX:      "hex",
+}
+
+# Used in comparisons. 0 means the base is inferred from the format of the
+# string.
+_TYPE_TO_BASE = {
+    HEX:      16,
+    INT:      10,
+    STRING:   0,
+    UNKNOWN:  0,
+}
+
+# def_bool -> BOOL, etc.
+_DEF_TOKEN_TO_TYPE = {
+    _T_DEF_BOOL:     BOOL,
+    _T_DEF_HEX:      HEX,
+    _T_DEF_INT:      INT,
+    _T_DEF_STRING:   STRING,
+    _T_DEF_TRISTATE: TRISTATE,
+}
+
+# Tokens after which strings are expected. This is used to tell strings from
+# constant symbol references during tokenization, both of which are enclosed in
+# quotes.
 #
+# Identifier-like lexemes ("missing quotes") are also treated as strings after
+# these tokens. _T_CHOICE is included to avoid symbols being registered for
+# named choices.
+_STRING_LEX = frozenset({
+    _T_BOOL,
+    _T_CHOICE,
+    _T_COMMENT,
+    _T_HEX,
+    _T_INT,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RSOURCE,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+})
+
+# Various sets for quick membership tests. Gives a single global lookup and
+# avoids creating temporary dicts/tuples.
+
+_TYPE_TOKENS = frozenset({
+    _T_BOOL,
+    _T_TRISTATE,
+    _T_INT,
+    _T_HEX,
+    _T_STRING,
+})
+
+_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+    _T_OSOURCE,
+    _T_ORSOURCE,
+})
+
+_REL_SOURCE_TOKENS = frozenset({
+    _T_RSOURCE,
+    _T_ORSOURCE,
+})
+
+# Obligatory (non-optional) sources
+_OBL_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+})
+
+_BOOL_TRISTATE = frozenset({
+    BOOL,
+    TRISTATE,
+})
+
+_BOOL_TRISTATE_UNKNOWN = frozenset({
+    BOOL,
+    TRISTATE,
+    UNKNOWN,
+})
+
+_INT_HEX = frozenset({
+    INT,
+    HEX,
+})
+
+_SYMBOL_CHOICE = frozenset({
+    Symbol,
+    Choice,
+})
+
+_MENU_COMMENT = frozenset({
+    MENU,
+    COMMENT,
+})
+
+_EQUAL_UNEQUAL = frozenset({
+    EQUAL,
+    UNEQUAL,
+})
+
+_RELATIONS = frozenset({
+    EQUAL,
+    UNEQUAL,
+    LESS,
+    LESS_EQUAL,
+    GREATER,
+    GREATER_EQUAL,
+})
+
+# Helper functions for getting compiled regular expressions, with the needed
+# matching function returned directly as a small optimization.
+#
+# Use ASCII regex matching on Python 3. It's already the default on Python 2.
+
+
+def _re_match(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
+
+
+def _re_search(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
+
+
+# Various regular expressions used during parsing
+
+# The initial token on a line. Also eats leading and trailing whitespace, so
+# that we can jump straight to the next token (or to the end of the line if
+# there is only one token).
+#
+# This regex will also fail to match for empty lines and comment lines.
+#
+# '$' is included to detect preprocessor variable assignments with macro
+# expansions in the left-hand side.
+_command_match = _re_match(r"\s*([A-Za-z0-9_$-]+)\s*")
+
+# An identifier/keyword after the first token. Also eats trailing whitespace.
+# '$' is included to detect identifiers containing macro expansions.
+_id_keyword_match = _re_match(r"([A-Za-z0-9_$/.-]+)\s*")
+
+# A fragment in the left-hand side of a preprocessor variable assignment. These
+# are the portions between macro expansions ($(foo)). Macros are supported in
+# the LHS (variable name).
+_assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
+
+# The assignment operator and value (right-hand side) in a preprocessor
+# variable assignment
+_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
+
+# Special characters/strings while expanding a macro (')', ',', and '$(')
+_macro_special_search = _re_search(r"\)|,|\$\(")
+
+# Special characters/strings while expanding a string (quotes, '\', and '$(')
+_string_special_search = _re_search(r'"|\'|\\|\$\(')
+
+# Special characters/strings while expanding a symbol name. Also includes
+# end-of-line, in case the macro is the last thing on the line.
+_name_special_search = _re_search(r'[^A-Za-z0-9_$/.-]|\$\(|$')
 
-# Tokens
-(T_AND, T_OR, T_NOT,
- T_OPEN_PAREN, T_CLOSE_PAREN,
- T_EQUAL, T_UNEQUAL,
- T_MAINMENU, T_MENU, T_ENDMENU,
- T_SOURCE, T_CHOICE, T_ENDCHOICE,
- T_COMMENT, T_CONFIG, T_MENUCONFIG,
- T_HELP, T_IF, T_ENDIF, T_DEPENDS, T_ON,
- T_OPTIONAL, T_PROMPT, T_DEFAULT,
- T_BOOL, T_TRISTATE, T_HEX, T_INT, T_STRING,
- T_DEF_BOOL, T_DEF_TRISTATE,
- T_SELECT, T_IMPLY, T_RANGE, T_OPTION, T_ALLNOCONFIG_Y, T_ENV,
- T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(40)
-
-# The leading underscore before the function assignments below prevent pydoc
-# from listing them. The constants could be hidden too, but they're fairly
-# obviously internal anyway, so don't bother spamming the code.
-
-# Keyword to token map. Note that the get() method is assigned directly as a
-# small optimization.
-_get_keyword = \
-  {"mainmenu": T_MAINMENU, "menu": T_MENU, "endmenu": T_ENDMENU,
-   "endif": T_ENDIF, "endchoice": T_ENDCHOICE, "source": T_SOURCE,
-   "choice": T_CHOICE, "config": T_CONFIG, "comment": T_COMMENT,
-   "menuconfig": T_MENUCONFIG, "help": T_HELP, "if": T_IF,
-   "depends": T_DEPENDS, "on": T_ON, "optional": T_OPTIONAL,
-   "prompt": T_PROMPT, "default": T_DEFAULT, "bool": T_BOOL, "boolean": T_BOOL,
-   "tristate": T_TRISTATE, "int": T_INT, "hex": T_HEX, "def_bool": T_DEF_BOOL,
-   "def_tristate": T_DEF_TRISTATE, "string": T_STRING, "select": T_SELECT,
-   "imply" : T_IMPLY, "range": T_RANGE, "option": T_OPTION,
-   "allnoconfig_y": T_ALLNOCONFIG_Y, "env": T_ENV,
-   "defconfig_list": T_DEFCONFIG_LIST, "modules": T_MODULES,
-   "visible": T_VISIBLE}.get
-
-# Strings to use for True and False
-BOOL_STR = {False: "false", True: "true"}
-
-# Tokens after which identifier-like lexemes are treated as strings. T_CHOICE
-# is included to avoid symbols being registered for named choices.
-STRING_LEX = frozenset((T_BOOL, T_TRISTATE, T_INT, T_HEX, T_STRING, T_CHOICE,
-                        T_PROMPT, T_MENU, T_COMMENT, T_SOURCE, T_MAINMENU))
-
-# Matches the initial token on a line; see _tokenize(). Also eats trailing
-# whitespace as an optimization.
-_initial_token_re_match = re.compile(r"[^\w]*(\w+)\s*").match
-
-# Matches an identifier/keyword optionally preceded by whitespace. Also eats
-# trailing whitespace as an optimization.
-_id_keyword_re_match = re.compile(r"\s*([\w./-]+)\s*").match
-
-# Regular expression for finding $-references to symbols in strings
-_sym_ref_re_search = re.compile(r"\$[A-Za-z0-9_]+").search
-
-# Integers representing symbol types
-UNKNOWN, BOOL, TRISTATE, STRING, HEX, INT = range(6)
-
-# Strings to use for types
-TYPENAME = {UNKNOWN: "unknown", BOOL: "bool", TRISTATE: "tristate",
-            STRING: "string", HEX: "hex", INT: "int"}
-
-# Token to type mapping
-TOKEN_TO_TYPE = {T_BOOL: BOOL, T_TRISTATE: TRISTATE, T_STRING: STRING,
-                 T_INT: INT, T_HEX: HEX}
-
-# Default values for symbols of different types (the value the symbol gets if
-# it is not assigned a user value and none of its 'default' clauses kick in)
-DEFAULT_VALUE = {BOOL: "n", TRISTATE: "n", STRING: "", INT: "", HEX: ""}
-
-# Indicates that no item is selected in a choice statement
-NO_SELECTION = 0
-
-# Integers representing expression types
-AND, OR, NOT, EQUAL, UNEQUAL = range(5)
-
-# Map from tristate values to integers
-TRI_TO_INT = {"n": 0, "m": 1, "y": 2}
-
-# Printing-related stuff
-
-OP_TO_STR = {AND: " && ", OR: " || ", EQUAL: " = ", UNEQUAL: " != "}
-PRECEDENCE = {OR: 0, AND: 1, NOT: 2}
+# A valid right-hand side for an assignment to a string symbol in a .config
+# file, including escaped characters. Extracts the contents.
+_conf_string_match = _re_match(r'"((?:[^\\"]|\\.)*)"')
index e9bbd15e15a937686d19f4ba6eb385e37fb5369b..4ff0bffaefabcf244fb662d822d756cfbbd0e9ef 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0+
 #
 # Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
@@ -91,7 +91,7 @@ def output_is_new(output):
 
     # Detect a board that has been removed since the current board database
     # was generated
-    with open(output) as f:
+    with open(output, encoding="utf-8") as f:
         for line in f:
             if line[0] == '#' or line == '\n':
                 continue
@@ -118,12 +118,12 @@ class KconfigScanner:
     }
 
     def __init__(self):
-        """Scan all the Kconfig files and create a Config object."""
+        """Scan all the Kconfig files and create a Kconfig object."""
         # Define environment variables referenced from Kconfig
         os.environ['srctree'] = os.getcwd()
         os.environ['UBOOTVERSION'] = 'dummy'
         os.environ['KCONFIG_OBJDIR'] = ''
-        self._conf = kconfiglib.Config(print_warnings=False)
+        self._conf = kconfiglib.Kconfig(warn=False)
 
     def __del__(self):
         """Delete a leftover temporary file before exit.
@@ -165,11 +165,7 @@ class KconfigScanner:
                 else:
                     f.write(line[colon + 1:])
 
-        warnings = self._conf.load_config(self._tmpfile)
-        if warnings:
-            for warning in warnings:
-                print '%s: %s' % (defconfig, warning)
-
+        self._conf.load_config(self._tmpfile)
         try_remove(self._tmpfile)
         self._tmpfile = None
 
@@ -177,8 +173,8 @@ class KconfigScanner:
 
         # Get the value of CONFIG_SYS_ARCH, CONFIG_SYS_CPU, ... etc.
         # Set '-' if the value is empty.
-        for key, symbol in self._SYMBOL_TABLE.items():
-            value = self._conf.get_symbol(symbol).get_value()
+        for key, symbol in list(self._SYMBOL_TABLE.items()):
+            value = self._conf.syms.get(symbol).str_value
             if value:
                 params[key] = value
             else:
@@ -242,8 +238,8 @@ def scan_defconfigs(jobs=1):
     processes = []
     queues = []
     for i in range(jobs):
-        defconfigs = all_defconfigs[total_boards * i / jobs :
-                                    total_boards * (i + 1) / jobs]
+        defconfigs = all_defconfigs[total_boards * i // jobs :
+                                    total_boards * (i + 1) // jobs]
         q = multiprocessing.Queue(maxsize=-1)
         p = multiprocessing.Process(target=scan_defconfigs_for_multiprocess,
                                     args=(q, defconfigs))
@@ -290,7 +286,7 @@ class MaintainersDatabase:
           'Active', 'Orphan' or '-'.
         """
         if not target in self.database:
-            print >> sys.stderr, "WARNING: no status info for '%s'" % target
+            print("WARNING: no status info for '%s'" % target, file=sys.stderr)
             return '-'
 
         tmp = self.database[target][0]
@@ -301,8 +297,8 @@ class MaintainersDatabase:
         elif tmp.startswith('Orphan'):
             return 'Orphan'
         else:
-            print >> sys.stderr, ("WARNING: %s: unknown status for '%s'" %
-                                  (tmp, target))
+            print(("WARNING: %s: unknown status for '%s'" %
+                                  (tmp, target)), file=sys.stderr)
             return '-'
 
     def get_maintainers(self, target):
@@ -313,7 +309,7 @@ class MaintainersDatabase:
           they are separated with colons.
         """
         if not target in self.database:
-            print >> sys.stderr, "WARNING: no maintainers for '%s'" % target
+            print("WARNING: no maintainers for '%s'" % target, file=sys.stderr)
             return ''
 
         return ':'.join(self.database[target][1])
@@ -330,7 +326,7 @@ class MaintainersDatabase:
         targets = []
         maintainers = []
         status = '-'
-        for line in open(file):
+        for line in open(file, encoding="utf-8"):
             # Check also commented maintainers
             if line[:3] == '#M:':
                 line = line[1:]
@@ -404,7 +400,7 @@ def format_and_output(params_list, output):
     # ignore case when sorting
     output_lines.sort(key=str.lower)
 
-    with open(output, 'w') as f:
+    with open(output, 'w', encoding="utf-8") as f:
         f.write(COMMENT_BLOCK + '\n'.join(output_lines) + '\n')
 
 def gen_boards_cfg(output, jobs=1, force=False):
@@ -418,7 +414,7 @@ def gen_boards_cfg(output, jobs=1, force=False):
     check_top_directory()
 
     if not force and output_is_new(output):
-        print "%s is up to date. Nothing to do." % output
+        print("%s is up to date. Nothing to do." % output)
         sys.exit(0)
 
     params_list = scan_defconfigs(jobs)
index 0bbc7c1991131ed3a84bee058879781c3d06d937..b99417e9d6300dbd56cf6e8b72f4e9253ac1e0ab 100755 (executable)
@@ -851,7 +851,7 @@ class KconfigScanner:
         os.environ['srctree'] = os.getcwd()
         os.environ['UBOOTVERSION'] = 'dummy'
         os.environ['KCONFIG_OBJDIR'] = ''
-        self.conf = kconfiglib.Config()
+        self.conf = kconfiglib.Kconfig()
 
 
 class KconfigParser:
@@ -1525,7 +1525,7 @@ def find_kconfig_rules(kconf, config, imply_config):
     """Check whether a config has a 'select' or 'imply' keyword
 
     Args:
-        kconf: Kconfig.Config object
+        kconf: Kconfiglib.Kconfig object
         config: Name of config to check (without CONFIG_ prefix)
         imply_config: Implying config (without CONFIG_ prefix) which may or
             may not have an 'imply' for 'config')
@@ -1533,7 +1533,7 @@ def find_kconfig_rules(kconf, config, imply_config):
     Returns:
         Symbol object for 'config' if found, else None
     """
-    sym = kconf.get_symbol(imply_config)
+    sym = kconf.syms.get(imply_config)
     if sym:
         for sel in sym.get_selected_symbols() | sym.get_implied_symbols():
             if sel.get_name() == config:
@@ -1547,7 +1547,7 @@ def check_imply_rule(kconf, config, imply_config):
     to add an 'imply' for 'config' to that part of the Kconfig.
 
     Args:
-        kconf: Kconfig.Config object
+        kconf: Kconfiglib.Kconfig object
         config: Name of config to check (without CONFIG_ prefix)
         imply_config: Implying config (without CONFIG_ prefix) which may or
             may not have an 'imply' for 'config')
@@ -1558,7 +1558,7 @@ def check_imply_rule(kconf, config, imply_config):
             line number within the Kconfig file, or 0 if none
             message indicating the result
     """
-    sym = kconf.get_symbol(imply_config)
+    sym = kconf.syms.get(imply_config)
     if not sym:
         return 'cannot find sym'
     locs = sym.get_def_locations()
@@ -1784,7 +1784,7 @@ def do_imply_config(config_list, add_imply, imply_flags, skip_added,
                         if skip_added:
                             show = False
                 else:
-                    sym = kconf.get_symbol(iconfig[CONFIG_LEN:])
+                    sym = kconf.syms.get(iconfig[CONFIG_LEN:])
                     fname = ''
                     if sym:
                         locs = sym.get_def_locations()