diff options
212 files changed, 11136 insertions, 4216 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0c4343468d..967abed9f2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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; diff --git a/.travis.yml b/.travis.yml index 0ce09e35b7..c48b711659 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: @@ -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)) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3b0e315061..7086db368f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -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 diff --git a/arch/arm/cpu/arm926ejs/Makefile b/arch/arm/cpu/arm926ejs/Makefile index fdb0c926fb..b051025bb0 100644 --- a/arch/arm/cpu/arm926ejs/Makefile +++ b/arch/arm/cpu/arm926ejs/Makefile @@ -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 diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 62da168ef8..6ea09ffd3b 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -691,6 +691,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) += \ @@ -727,6 +729,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 index 0000000000..8c84dd08fd --- /dev/null +++ b/arch/arm/dts/at91-sama5d27_wlsom1_ek-u-boot.dtsi @@ -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 index 0000000000..ab23f5c209 --- /dev/null +++ b/arch/arm/dts/at91-sama5d27_wlsom1_ek.dts @@ -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 index 0000000000..e01539e5ce --- /dev/null +++ b/arch/arm/dts/sam9x60.dtsi @@ -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 index 0000000000..93cf1262f6 --- /dev/null +++ b/arch/arm/dts/sam9x60ek-u-boot.dtsi @@ -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 index 0000000000..bed59f3da2 --- /dev/null +++ b/arch/arm/dts/sam9x60ek.dts @@ -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"; +}; diff --git a/arch/arm/dts/sama5d2.dtsi b/arch/arm/dts/sama5d2.dtsi index 830251a539..5adc47b906 100644 --- a/arch/arm/dts/sama5d2.dtsi +++ b/arch/arm/dts/sama5d2.dtsi @@ -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 index 0000000000..889a0034d1 --- /dev/null +++ b/arch/arm/dts/sama5d27_wlsom1.dtsi @@ -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; + }; + }; + }; + }; + }; +}; diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index c3b21b7557..85524004f9 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -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" diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 045ac88806..cbd0ed68c2 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -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 diff --git a/arch/arm/mach-at91/arm926ejs/Makefile b/arch/arm/mach-at91/arm926ejs/Makefile index 6b0b28957a..8de6a2f966 100644 --- a/arch/arm/mach-at91/arm926ejs/Makefile +++ b/arch/arm/mach-at91/arm926ejs/Makefile @@ -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 index 0000000000..d463bbc788 --- /dev/null +++ b/arch/arm/mach-at91/arm926ejs/sam9x60_devices.c @@ -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 diff --git a/arch/arm/mach-at91/armv7/sama5d2_devices.c b/arch/arm/mach-at91/armv7/sama5d2_devices.c index 59a0c44913..9e9d026c3e 100644 --- a/arch/arm/mach-at91/armv7/sama5d2_devices.c +++ b/arch/arm/mach-at91/armv7/sama5d2_devices.c @@ -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"; } } diff --git a/arch/arm/mach-at91/armv7/sama5d4_devices.c b/arch/arm/mach-at91/armv7/sama5d4_devices.c index 5c693df2ec..e68ae99407 100644 --- a/arch/arm/mach-at91/armv7/sama5d4_devices.c +++ b/arch/arm/mach-at91/armv7/sama5d4_devices.c @@ -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() diff --git a/arch/arm/mach-at91/atmel_sfr.c b/arch/arm/mach-at91/atmel_sfr.c index 22251153a1..b14222460f 100644 --- a/arch/arm/mach-at91/atmel_sfr.c +++ b/arch/arm/mach-at91/atmel_sfr.c @@ -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); +} + diff --git a/arch/arm/mach-at91/include/mach/at91_common.h b/arch/arm/mach-at91/include/mach/at91_common.h index df7d0e7051..e929b5e1d2 100644 --- a/arch/arm/mach-at91/include/mach/at91_common.h +++ b/arch/arm/mach-at91/include/mach/at91_common.h @@ -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/sama5_sfr.h b/arch/arm/mach-at91/include/mach/at91_sfr.h index f9c412f998..0300c336dd 100644 --- a/arch/arm/mach-at91/include/mach/sama5_sfr.h +++ b/arch/arm/mach-at91/include/mach/at91_sfr.h @@ -6,12 +6,15 @@ * Bo Shen <voice.shen@atmel.com> */ -#ifndef __SAMA5_SFR_H -#define __SAMA5_SFR_H +#ifndef __AT91_SFR_H +#define __AT91_SFR_H struct atmel_sfr { u32 reserved1; /* 0x00 */ - u32 ddrcfg; /* 0x04: DDR Configuration Register */ + 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 */ @@ -28,7 +31,16 @@ struct atmel_sfr { }; /* 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 @@ -58,9 +70,39 @@ struct atmel_sfr { #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 +/* 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 diff --git a/arch/arm/mach-at91/include/mach/atmel_mpddrc.h b/arch/arm/mach-at91/include/mach/atmel_mpddrc.h index 45a76a60fa..40ec87e2ff 100644 --- a/arch/arm/mach-at91/include/mach/atmel_mpddrc.h +++ b/arch/arm/mach-at91/include/mach/atmel_mpddrc.h @@ -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 diff --git a/arch/arm/mach-at91/include/mach/hardware.h b/arch/arm/mach-at91/include/mach/hardware.h index 3a7752b999..88acca8549 100644 --- a/arch/arm/mach-at91/include/mach/hardware.h +++ b/arch/arm/mach-at91/include/mach/hardware.h @@ -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 index 0000000000..0f00a9ae87 --- /dev/null +++ b/arch/arm/mach-at91/include/mach/sam9x60.h @@ -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/sama5d2.h b/arch/arm/mach-at91/include/mach/sama5d2.h index c7d9bb5ad3..d1b2e01cdd 100644 --- a/arch/arm/mach-at91/include/mach/sama5d2.h +++ b/arch/arm/mach-at91/include/mach/sama5d2.h @@ -220,7 +220,11 @@ #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 diff --git a/arch/arm/mach-at91/mpddrc.c b/arch/arm/mach-at91/mpddrc.c index 81ccd6ab9a..3df0ea7c79 100644 --- a/arch/arm/mach-at91/mpddrc.c +++ b/arch/arm/mach-at91/mpddrc.c @@ -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; +} diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index ed8056e871..4c87cbc00f 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -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 diff --git a/arch/arm/mach-rmobile/Kconfig b/arch/arm/mach-rmobile/Kconfig index 52ab891425..8343000c8f 100644 --- a/arch/arm/mach-rmobile/Kconfig +++ b/arch/arm/mach-rmobile/Kconfig @@ -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)" diff --git a/arch/arm/mach-rmobile/Kconfig.32 b/arch/arm/mach-rmobile/Kconfig.32 index 1441c80692..d5e437f0d2 100644 --- a/arch/arm/mach-rmobile/Kconfig.32 +++ b/arch/arm/mach-rmobile/Kconfig.32 @@ -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 diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig index 1d914648e3..45de153aa5 100644 --- a/arch/arm/mach-socfpga/Kconfig +++ b/arch/arm/mach-socfpga/Kconfig @@ -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 diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c index f13bd256cc..07f54f0684 100644 --- a/arch/arm/mach-tegra/board2.c +++ b/arch/arm/mach-tegra/board2.c @@ -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"); diff --git a/arch/arm/mach-uniphier/pinctrl-glue.c b/arch/arm/mach-uniphier/pinctrl-glue.c index c4d3b17c39..b45f72f59a 100644 --- a/arch/arm/mach-uniphier/pinctrl-glue.c +++ b/arch/arm/mach-uniphier/pinctrl-glue.c @@ -13,14 +13,14 @@ 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; diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index fdfb209f77..2046cb53c4 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -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; diff --git a/arch/sandbox/cpu/spl.c b/arch/sandbox/cpu/spl.c index 4f415c71d6..44c68a39bc 100644 --- a/arch/sandbox/cpu/spl.c +++ b/arch/sandbox/cpu/spl.c @@ -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; +} diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 82828f0c1d..cfc542c806 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -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) { diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index c6d5650c20..f09bc70b0d 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -103,9 +103,14 @@ 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"; }; }; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 27b0baab27..25cac056bb 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -447,29 +447,39 @@ 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"; }; }; @@ -499,9 +509,14 @@ 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"; }; }; @@ -713,11 +728,13 @@ 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"; diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h index 2a350a826c..4a35c41972 100644 --- a/arch/sandbox/include/asm/io.h +++ b/arch/sandbox/include/asm/io.h @@ -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> diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index 2d773d3fa6..ad3e94beb9 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -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. diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index cbf209693d..cd2b9e3155 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -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 diff --git a/arch/sandbox/lib/pci_io.c b/arch/sandbox/lib/pci_io.c index 01822c6069..f22e47c7f6 100644 --- a/arch/sandbox/lib/pci_io.c +++ b/arch/sandbox/lib/pci_io.c @@ -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); } diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 218e817cf3..47bf28c434 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -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. diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 3f1f62da2b..6296b55ff8 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -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 \ diff --git a/arch/x86/cpu/baytrail/acpi.c b/arch/x86/cpu/baytrail/acpi.c index 445e4ba2d7..f44228e693 100644 --- a/arch/x86/cpu/baytrail/acpi.c +++ b/arch/x86/cpu/baytrail/acpi.c @@ -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. */ diff --git a/arch/x86/cpu/baytrail/cpu.c b/arch/x86/cpu/baytrail/cpu.c index 2eb917283b..9394eab956 100644 --- a/arch/x86/cpu/baytrail/cpu.c +++ b/arch/x86/cpu/baytrail/cpu.c @@ -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 diff --git a/arch/x86/cpu/baytrail/fsp_configs.c b/arch/x86/cpu/baytrail/fsp_configs.c index cefd26299a..1d1948c91a 100644 --- a/arch/x86/cpu/baytrail/fsp_configs.c +++ b/arch/x86/cpu/baytrail/fsp_configs.c @@ -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; diff --git a/arch/x86/cpu/braswell/fsp_configs.c b/arch/x86/cpu/braswell/fsp_configs.c index 7fe6fa7995..60101d742d 100644 --- a/arch/x86/cpu/braswell/fsp_configs.c +++ b/arch/x86/cpu/braswell/fsp_configs.c @@ -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; diff --git a/arch/x86/cpu/broadwell/cpu.c b/arch/x86/cpu/broadwell/cpu.c index bb7c361408..55a7439f1c 100644 --- a/arch/x86/cpu/broadwell/cpu.c +++ b/arch/x86/cpu/broadwell/cpu.c @@ -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); } diff --git a/arch/x86/cpu/broadwell/cpu_full.c b/arch/x86/cpu/broadwell/cpu_full.c index c1db184549..895edeb4bc 100644 --- a/arch/x86/cpu/broadwell/cpu_full.c +++ b/arch/x86/cpu/broadwell/cpu_full.c @@ -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) diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index 290ee084e5..9ee4b0294a 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -19,13 +19,13 @@ */ #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> diff --git a/arch/x86/cpu/intel_common/cpu.c b/arch/x86/cpu/intel_common/cpu.c index d0ac17808c..4d093a5391 100644 --- a/arch/x86/cpu/intel_common/cpu.c +++ b/arch/x86/cpu/intel_common/cpu.c @@ -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); +} diff --git a/arch/x86/cpu/intel_common/cpu_from_spl.c b/arch/x86/cpu/intel_common/cpu_from_spl.c index a6233c75ce..b7bb524162 100644 --- a/arch/x86/cpu/intel_common/cpu_from_spl.c +++ b/arch/x86/cpu/intel_common/cpu_from_spl.c @@ -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; diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c index c8b16e32c0..6db9da81b7 100644 --- a/arch/x86/cpu/ivybridge/cpu.c +++ b/arch/x86/cpu/ivybridge/cpu.c @@ -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); } diff --git a/arch/x86/cpu/ivybridge/fsp_configs.c b/arch/x86/cpu/ivybridge/fsp_configs.c index 2fd06b3bed..0e6453c847 100644 --- a/arch/x86/cpu/ivybridge/fsp_configs.c +++ b/arch/x86/cpu/ivybridge/fsp_configs.c @@ -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; diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c index 6edc3e233c..56ab6bf4ac 100644 --- a/arch/x86/cpu/ivybridge/model_206ax.c +++ b/arch/x86/cpu/ivybridge/model_206ax.c @@ -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; } diff --git a/arch/x86/cpu/ivybridge/northbridge.c b/arch/x86/cpu/ivybridge/northbridge.c index a809b823b3..0f427afcb8 100644 --- a/arch/x86/cpu/ivybridge/northbridge.c +++ b/arch/x86/cpu/ivybridge/northbridge.c @@ -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)); diff --git a/arch/x86/cpu/mtrr.c b/arch/x86/cpu/mtrr.c index 0939736164..a00db422e7 100644 --- a/arch/x86/cpu/mtrr.c +++ b/arch/x86/cpu/mtrr.c @@ -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; +} diff --git a/arch/x86/cpu/pci.c b/arch/x86/cpu/pci.c index c6218250e1..e1aae158ce 100644 --- a/arch/x86/cpu/pci.c +++ b/arch/x86/cpu/pci.c @@ -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; diff --git a/arch/x86/cpu/queensbay/fsp_configs.c b/arch/x86/cpu/queensbay/fsp_configs.c index c4d117783c..381edd0761 100644 --- a/arch/x86/cpu/queensbay/fsp_configs.c +++ b/arch/x86/cpu/queensbay/fsp_configs.c @@ -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() */ diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c index 76556fc7f7..66737e655b 100644 --- a/arch/x86/cpu/queensbay/tnc.c +++ b/arch/x86/cpu/queensbay/tnc.c @@ -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) diff --git a/arch/x86/cpu/start.S b/arch/x86/cpu/start.S index 3c9bdf2a9d..01524635e9 100644 --- a/arch/x86/cpu/start.S +++ b/arch/x86/cpu/start.S @@ -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 diff --git a/arch/x86/cpu/start_from_spl.S b/arch/x86/cpu/start_from_spl.S index 4d4e5d0758..a73b4d7c45 100644 --- a/arch/x86/cpu/start_from_spl.S +++ b/arch/x86/cpu/start_from_spl.S @@ -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> */ diff --git a/arch/x86/cpu/start_from_tpl.S b/arch/x86/cpu/start_from_tpl.S index 44b5363a68..9a4974a5f1 100644 --- a/arch/x86/cpu/start_from_tpl.S +++ b/arch/x86/cpu/start_from_tpl.S @@ -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> diff --git a/arch/x86/cpu/turbo.c b/arch/x86/cpu/turbo.c index a41d511238..be468d2b2c 100644 --- a/arch/x86/cpu/turbo.c +++ b/arch/x86/cpu/turbo.c @@ -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); diff --git a/arch/x86/cpu/u-boot-spl.lds b/arch/x86/cpu/u-boot-spl.lds index f20c0b810d..c1e9bfbf66 100644 --- a/arch/x86/cpu/u-boot-spl.lds +++ b/arch/x86/cpu/u-boot-spl.lds @@ -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 = .; diff --git a/arch/x86/cpu/wakeup.S b/arch/x86/cpu/wakeup.S index 663b02f27d..244ca1276a 100644 --- a/arch/x86/cpu/wakeup.S +++ b/arch/x86/cpu/wakeup.S @@ -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/arch-broadwell/cpu.h b/arch/x86/include/asm/arch-broadwell/cpu.h index ca22a79996..3bc3bd6609 100644 --- a/arch/x86/include/asm/arch-broadwell/cpu.h +++ b/arch/x86/include/asm/arch-broadwell/cpu.h @@ -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 diff --git a/arch/x86/include/asm/arch-broadwell/pch.h b/arch/x86/include/asm/arch-broadwell/pch.h index 23153a040f..ecdf6d16f9 100644 --- a/arch/x86/include/asm/arch-broadwell/pch.h +++ b/arch/x86/include/asm/arch-broadwell/pch.h @@ -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) diff --git a/arch/x86/include/asm/arch-ivybridge/model_206ax.h b/arch/x86/include/asm/arch-ivybridge/model_206ax.h index 850d96bdd9..4839ebc312 100644 --- a/arch/x86/include/asm/arch-ivybridge/model_206ax.h +++ b/arch/x86/include/asm/arch-ivybridge/model_206ax.h @@ -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 diff --git a/arch/x86/include/asm/cpu_common.h b/arch/x86/include/asm/cpu_common.h index 4c91a5dace..cdd99a90b7 100644 --- a/arch/x86/include/asm/cpu_common.h +++ b/arch/x86/include/asm/cpu_common.h @@ -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_hob.h b/arch/x86/include/asm/fsp/fsp_hob.h index 3bb79c4b67..d248520e97 100644 --- a/arch/x86/include/asm/fsp/fsp_hob.h +++ b/arch/x86/include/asm/fsp/fsp_hob.h @@ -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) diff --git a/arch/x86/include/asm/fsp/fsp_infoheader.h b/arch/x86/include/asm/fsp/fsp_infoheader.h index 86f78014b7..e72c052ed1 100644 --- a/arch/x86/include/asm/fsp/fsp_infoheader.h +++ b/arch/x86/include/asm/fsp/fsp_infoheader.h @@ -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 diff --git a/arch/x86/include/asm/fsp/fsp_support.h b/arch/x86/include/asm/fsp/fsp_support.h index 7b92392a27..4ac27d26f5 100644 --- a/arch/x86/include/asm/fsp/fsp_support.h +++ b/arch/x86/include/asm/fsp/fsp_support.h @@ -7,182 +7,157 @@ #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/fsp/fsp_api.h b/arch/x86/include/asm/fsp1/fsp_api.h index f2d70799f3..f2d70799f3 100644 --- a/arch/x86/include/asm/fsp/fsp_api.h +++ b/arch/x86/include/asm/fsp1/fsp_api.h diff --git a/arch/x86/include/asm/fsp/fsp_ffs.h b/arch/x86/include/asm/fsp1/fsp_ffs.h index b7558e5a17..b7558e5a17 100644 --- a/arch/x86/include/asm/fsp/fsp_ffs.h +++ b/arch/x86/include/asm/fsp1/fsp_ffs.h diff --git a/arch/x86/include/asm/fsp1/fsp_support.h b/arch/x86/include/asm/fsp1/fsp_support.h new file mode 100644 index 0000000000..a44a5504a4 --- /dev/null +++ b/arch/x86/include/asm/fsp1/fsp_support.h @@ -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 index 0000000000..3b2077b392 --- /dev/null +++ b/arch/x86/include/asm/fsp_arch.h @@ -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 diff --git a/arch/x86/include/asm/handoff.h b/arch/x86/include/asm/handoff.h index 4d18d59efe..aec49b9b81 100644 --- a/arch/x86/include/asm/handoff.h +++ b/arch/x86/include/asm/handoff.h @@ -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 diff --git a/arch/x86/include/asm/hob.h b/arch/x86/include/asm/hob.h index b4239821aa..56e11dbb28 100644 --- a/arch/x86/include/asm/hob.h +++ b/arch/x86/include/asm/hob.h @@ -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); diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 9c1dbe61d5..5bc8b6c22c 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -43,6 +43,12 @@ #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 @@ -67,6 +73,11 @@ #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 @@ -78,21 +89,67 @@ #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 @@ -404,68 +461,6 @@ #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 */ @@ -600,6 +595,12 @@ #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 diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h index 2d897f82ef..672617256e 100644 --- a/arch/x86/include/asm/mtrr.h +++ b/arch/x86/include/asm/mtrr.h @@ -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) diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 118ac937d9..2a72073572 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -17,11 +17,48 @@ #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 diff --git a/arch/x86/include/asm/spl.h b/arch/x86/include/asm/spl.h index 27432b2897..1bef4877eb 100644 --- a/arch/x86/include/asm/spl.h +++ b/arch/x86/include/asm/spl.h @@ -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, }; diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index c252192bf4..2466ad2ad3 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -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 diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 906be5eab9..ca0ca1066b 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -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 diff --git a/arch/x86/lib/acpi_s3.c b/arch/x86/lib/acpi_s3.c index 03917188a9..197636c4b5 100644 --- a/arch/x86/lib/acpi_s3.c +++ b/arch/x86/lib/acpi_s3.c @@ -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> diff --git a/arch/x86/lib/coreboot_table.c b/arch/x86/lib/coreboot_table.c index 2d08a2db0d..8685aa3046 100644 --- a/arch/x86/lib/coreboot_table.c +++ b/arch/x86/lib/coreboot_table.c @@ -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> diff --git a/arch/x86/lib/fsp/Makefile b/arch/x86/lib/fsp/Makefile index 870de71bd7..9e34856473 100644 --- a/arch/x86/lib/fsp/Makefile +++ b/arch/x86/lib/fsp/Makefile @@ -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_common.c b/arch/x86/lib/fsp/fsp_common.c index ed0827c6e9..40ba866d77 100644 --- a/arch/x86/lib/fsp/fsp_common.c +++ b/arch/x86/lib/fsp/fsp_common.c @@ -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; -} diff --git a/arch/x86/lib/fsp/fsp_dram.c b/arch/x86/lib/fsp/fsp_dram.c index 3a23b70410..2d1023068f 100644 --- a/arch/x86/lib/fsp/fsp_dram.c +++ b/arch/x86/lib/fsp/fsp_dram.c @@ -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_support.c b/arch/x86/lib/fsp/fsp_support.c index 0eaa9b232b..983888fd74 100644 --- a/arch/x86/lib/fsp/fsp_support.c +++ b/arch/x86/lib/fsp/fsp_support.c @@ -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(¶ms, 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 = ¶ms; - - 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(¶ms); -} - -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 = ¶ms; - - /* - * 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 index 0000000000..870de71bd7 --- /dev/null +++ b/arch/x86/lib/fsp1/Makefile @@ -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/fsp/fsp_car.S b/arch/x86/lib/fsp1/fsp_car.S index 8c54cea3db..a64a653435 100644 --- a/arch/x86/lib/fsp/fsp_car.S +++ b/arch/x86/lib/fsp1/fsp_car.S @@ -20,10 +20,10 @@ car_init: car_init_start: post_code(POST_CAR_START) - lea find_fsp_header_romstack, %esp - jmp find_fsp_header + lea fsp_find_header_romstack, %esp + jmp fsp_find_header -find_fsp_header_ret: +fsp_find_header_ret: /* EAX points to FSP_INFO_HEADER */ mov %eax, %ebp @@ -91,8 +91,8 @@ die: * contain the function return address as well as the parameters. */ .balign 4 -find_fsp_header_romstack: - .long find_fsp_header_ret +fsp_find_header_romstack: + .long fsp_find_header_ret .balign 4 temp_ram_init_romstack: diff --git a/arch/x86/lib/fsp1/fsp_common.c b/arch/x86/lib/fsp1/fsp_common.c new file mode 100644 index 0000000000..e8066d8de3 --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_common.c @@ -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 index 0000000000..6a3349b42a --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_dram.c @@ -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/fsp/fsp_graphics.c b/arch/x86/lib/fsp1/fsp_graphics.c index 91d2d08557..52e71334f9 100644 --- a/arch/x86/lib/fsp/fsp_graphics.c +++ b/arch/x86/lib/fsp1/fsp_graphics.c @@ -7,7 +7,7 @@ #include <dm.h> #include <vbe.h> #include <video.h> -#include <asm/fsp/fsp_support.h> +#include <asm/fsp1/fsp_support.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/arch/x86/lib/fsp1/fsp_support.c b/arch/x86/lib/fsp1/fsp_support.c new file mode 100644 index 0000000000..c7a2c73846 --- /dev/null +++ b/arch/x86/lib/fsp1/fsp_support.c @@ -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(¶ms, 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 = ¶ms; + + 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(¶ms); +} + +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 = ¶ms; + + /* + * 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; +} diff --git a/arch/x86/lib/hob.c b/arch/x86/lib/hob.c index dcee29b04c..f2c47240ee 100644 --- a/arch/x86/lib/hob.c +++ b/arch/x86/lib/hob.c @@ -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) diff --git a/arch/x86/lib/init_helpers.c b/arch/x86/lib/init_helpers.c index 4774a9bdb7..3e3a11ac2f 100644 --- a/arch/x86/lib/init_helpers.c +++ b/arch/x86/lib/init_helpers.c @@ -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(); } diff --git a/arch/x86/lib/lpc-uclass.c b/arch/x86/lib/lpc-uclass.c index 505d7a943d..1302a6e34a 100644 --- a/arch/x86/lib/lpc-uclass.c +++ b/arch/x86/lib/lpc-uclass.c @@ -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 }; diff --git a/arch/x86/lib/spl.c b/arch/x86/lib/spl.c index 5d5d1a9ca7..2baac91383 100644 --- a/arch/x86/lib/spl.c +++ b/arch/x86/lib/spl.c @@ -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 diff --git a/arch/x86/lib/tpl.c b/arch/x86/lib/tpl.c index 492a2d6521..cfefa78045 100644 --- a/arch/x86/lib/tpl.c +++ b/arch/x86/lib/tpl.c @@ -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) diff --git a/board/advantech/som-db5800-som-6867/som-db5800-som-6867.c b/board/advantech/som-db5800-som-6867/som-db5800-som-6867.c index ac12f303a3..8f4c587371 100644 --- a/board/advantech/som-db5800-som-6867/som-db5800-som-6867.c +++ b/board/advantech/som-db5800-som-6867/som-db5800-som-6867.c @@ -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[] = { diff --git a/board/atmel/common/video_display.c b/board/atmel/common/video_display.c index c7d3f8addc..cc051d2e0c 100644 --- a/board/atmel/common/video_display.c +++ b/board/atmel/common/video_display.c @@ -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 index 0000000000..32fae2108e --- /dev/null +++ b/board/atmel/sam9x60ek/Kconfig @@ -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 index 0000000000..d209249c2e --- /dev/null +++ b/board/atmel/sam9x60ek/MAINTAINERS @@ -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 index 0000000000..12a406a3bb --- /dev/null +++ b/board/atmel/sam9x60ek/Makefile @@ -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 index 0000000000..182b3aeed7 --- /dev/null +++ b/board/atmel/sam9x60ek/sam9x60ek.c @@ -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 index 0000000000..4b192b0849 --- /dev/null +++ b/board/atmel/sama5d27_wlsom1_ek/Kconfig @@ -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 index 0000000000..ff68cf01a3 --- /dev/null +++ b/board/atmel/sama5d27_wlsom1_ek/MAINTAINERS @@ -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 index 0000000000..cf827ae5e3 --- /dev/null +++ b/board/atmel/sama5d27_wlsom1_ek/Makefile @@ -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 index 0000000000..fda06c824d --- /dev/null +++ b/board/atmel/sama5d27_wlsom1_ek/sama5d27_wlsom1_ek.c @@ -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 diff --git a/board/intel/cherryhill/cherryhill.c b/board/intel/cherryhill/cherryhill.c index 695af6bb7e..c037d5b14c 100644 --- a/board/intel/cherryhill/cherryhill.c +++ b/board/intel/cherryhill/cherryhill.c @@ -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, diff --git a/board/laird/wb50n/wb50n.c b/board/laird/wb50n/wb50n.c index a2f8eaf0ba..13563abb49 100644 --- a/board/laird/wb50n/wb50n.c +++ b/board/laird/wb50n/wb50n.c @@ -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); diff --git a/cmd/Kconfig b/cmd/Kconfig index 4e61565aab..8e55b34c96 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -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. @@ -11,6 +11,13 @@ #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 * @@ -19,26 +26,66 @@ */ 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, diff --git a/cmd/nvedit_efi.c b/cmd/nvedit_efi.c index ed6d09a530..ede4cd5235 100644 --- a/cmd/nvedit_efi.c +++ b/cmd/nvedit_efi.c @@ -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 diff --git a/cmd/x86/fsp.c b/cmd/x86/fsp.c index efa183854b..b3b663021b 100644 --- a/cmd/x86/fsp.c +++ b/cmd/x86/fsp.c @@ -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; diff --git a/common/spl/Kconfig b/common/spl/Kconfig index f467eca2be..ef4fb19e52 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -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 default 0 @@ -115,7 +115,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 @@ -963,7 +963,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" @@ -1185,7 +1185,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 diff --git a/common/spl/spl.c b/common/spl/spl.c index 082fa2bd94..5fdd6d0d03 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -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(); diff --git a/common/xyzModem.c b/common/xyzModem.c index e85da74a69..6bf2375671 100644 --- a/common/xyzModem.c +++ b/common/xyzModem.c @@ -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 */ diff --git a/configs/am335x_pdu001_defconfig b/configs/am335x_pdu001_defconfig index e69f81b6e5..1a1d58d3cc 100644 --- a/configs/am335x_pdu001_defconfig +++ b/configs/am335x_pdu001_defconfig @@ -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 diff --git a/configs/evb-rk3288_defconfig b/configs/evb-rk3288_defconfig index 7b0e908f71..043ee32bb4 100644 --- a/configs/evb-rk3288_defconfig +++ b/configs/evb-rk3288_defconfig @@ -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 diff --git a/configs/ls1043ardb_nand_SECURE_BOOT_defconfig b/configs/ls1043ardb_nand_SECURE_BOOT_defconfig index 7aa8c54a12..c45fc68ac3 100644 --- a/configs/ls1043ardb_nand_SECURE_BOOT_defconfig +++ b/configs/ls1043ardb_nand_SECURE_BOOT_defconfig @@ -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 diff --git a/configs/ls1043ardb_nand_defconfig b/configs/ls1043ardb_nand_defconfig index 22ccf526f6..a9e58047ac 100644 --- a/configs/ls1043ardb_nand_defconfig +++ b/configs/ls1043ardb_nand_defconfig @@ -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 diff --git a/configs/ls1043ardb_sdcard_SECURE_BOOT_defconfig b/configs/ls1043ardb_sdcard_SECURE_BOOT_defconfig index 486a2e089b..bdfa13a0a7 100644 --- a/configs/ls1043ardb_sdcard_SECURE_BOOT_defconfig +++ b/configs/ls1043ardb_sdcard_SECURE_BOOT_defconfig @@ -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 diff --git a/configs/ls1043ardb_sdcard_defconfig b/configs/ls1043ardb_sdcard_defconfig index b3c2970207..01adbf0b19 100644 --- a/configs/ls1043ardb_sdcard_defconfig +++ b/configs/ls1043ardb_sdcard_defconfig @@ -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 diff --git a/configs/sam9x60ek_mmc_defconfig b/configs/sam9x60ek_mmc_defconfig new file mode 100644 index 0000000000..0d3746a86e --- /dev/null +++ b/configs/sam9x60ek_mmc_defconfig @@ -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 index 0000000000..cdba103fc4 --- /dev/null +++ b/configs/sam9x60ek_nandflash_defconfig @@ -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 index 0000000000..e1b292ea39 --- /dev/null +++ b/configs/sam9x60ek_qspiflash_defconfig @@ -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 index 0000000000..50a8a8e83c --- /dev/null +++ b/configs/sama5d27_wlsom1_ek_mmc_defconfig @@ -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 index 0000000000..82568e286d --- /dev/null +++ b/configs/sama5d27_wlsom1_ek_qspiflash_defconfig @@ -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 diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 0b3391a2a6..409b8a38d5 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -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 diff --git a/configs/slimbootloader_defconfig b/configs/slimbootloader_defconfig index f9fecff45e..3cbb83c7a4 100644 --- a/configs/slimbootloader_defconfig +++ b/configs/slimbootloader_defconfig @@ -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 diff --git a/configs/tinker-rk3288_defconfig b/configs/tinker-rk3288_defconfig index 9328608452..eff3b06b5c 100644 --- a/configs/tinker-rk3288_defconfig +++ b/configs/tinker-rk3288_defconfig @@ -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 diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index 5c0caebcbf..54933b5675 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -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 index 0000000000..4f4a8d4805 --- /dev/null +++ b/doc/driver-model/debugging.rst @@ -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. diff --git a/doc/driver-model/index.rst b/doc/driver-model/index.rst index ea32c36335..6d55774b4c 100644 --- a/doc/driver-model/index.rst +++ b/doc/driver-model/index.rst @@ -6,6 +6,7 @@ Driver Model .. toctree:: :maxdepth: 2 + debugging design fdt-fixup fs_firmware_loader diff --git a/doc/driver-model/pci-info.rst b/doc/driver-model/pci-info.rst index d93ab8b61d..3c1b1adf07 100644 --- a/doc/driver-model/pci-info.rst +++ b/doc/driver-model/pci-info.rst @@ -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. diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index baaf431e5e..e8f58b3f5e 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -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; diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index e8506099fd..18af0bfeaa 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -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" /* diff --git a/drivers/core/device.c b/drivers/core/device.c index 05dadf98f9..84f0f0fbf0 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -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; diff --git a/drivers/core/fdtaddr.c b/drivers/core/fdtaddr.c index 6850003a28..575798fae9 100644 --- a/drivers/core/fdtaddr.c +++ b/drivers/core/fdtaddr.c @@ -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; +} diff --git a/drivers/core/lists.c b/drivers/core/lists.c index a1f828463e..4681b3e5dd 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -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; } diff --git a/drivers/core/read.c b/drivers/core/read.c index fb3dcd9a79..9602e52d1b 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -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; +} diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index b33296542f..f217876cd2 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -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; } diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 01cfa2f788..90fbed455b 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -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]; diff --git a/drivers/gpio/pm8916_gpio.c b/drivers/gpio/pm8916_gpio.c index bbe214d5ee..74a773c099 100644 --- a/drivers/gpio/pm8916_gpio.c +++ b/drivers/gpio/pm8916_gpio.c @@ -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; } diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 509c588582..0001d105ba 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -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 diff --git a/drivers/misc/swap_case.c b/drivers/misc/swap_case.c index 6afc6d9466..11189d16c8 100644 --- a/drivers/misc/swap_case.c +++ b/drivers/misc/swap_case.c @@ -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 }, {}, }; diff --git a/drivers/mmc/atmel_sdhci.c b/drivers/mmc/atmel_sdhci.c index d930ed8da0..2b797c9bd4 100644 --- a/drivers/mmc/atmel_sdhci.c +++ b/drivers/mmc/atmel_sdhci.c @@ -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" }, { } }; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index c8f71cd0c1..6bece7f307 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -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); diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index 2fa7d8c3dc..899952d773 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -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; diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c index 719a2fd23a..c6107522be 100644 --- a/drivers/mtd/spi/sf-uclass.c +++ b/drivers/mtd/spi/sf-uclass.c @@ -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]; diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 377188e361..1a532b0e5a 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -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 }, diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c index 47f101e280..ee6b581d9e 100644 --- a/drivers/nvme/nvme.c +++ b/drivers/nvme/nvme.c @@ -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; diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h index 922f7abfe8..0e8cb221a7 100644 --- a/drivers/nvme/nvme.h +++ b/drivers/nvme/nvme.h @@ -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; diff --git a/drivers/pch/pch-uclass.c b/drivers/pch/pch-uclass.c index caf8b72803..ad4906aa58 100644 --- a/drivers/pch/pch-uclass.c +++ b/drivers/pch/pch-uclass.c @@ -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 }; diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c index 3822758354..0dcf937d9a 100644 --- a/drivers/pci/pci-emul-uclass.c +++ b/drivers/pci/pci-emul-uclass.c @@ -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, +}; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index ab3e1310eb..896cb6b23a 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -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; diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 1a3bf70834..28667bde8d 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -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); diff --git a/drivers/pci/pci_auto_common.c b/drivers/pci/pci_auto_common.c index 84908e6154..8690316610 100644 --- a/drivers/pci/pci_auto_common.c +++ b/drivers/pci/pci_auto_common.c @@ -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; } diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 2cede1211b..1d4064e376 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -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 diff --git a/drivers/pci/pci_x86.c b/drivers/pci/pci_x86.c index 520ea4649e..e76a9c6e44 100644 --- a/drivers/pci/pci_x86.c +++ b/drivers/pci/pci_x86.c @@ -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[] = { diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 6cf2be8f2b..754b6e9921 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -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; diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index 33102fc872..2f7bc24887 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -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); diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index f565ae0310..5e6293ae69 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -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 diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index cf01492295..fff4a184a0 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -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 diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index 072f7948ef..8e2d1eaa7a 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -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> diff --git a/arch/x86/include/asm/acpi_s3.h b/include/acpi_s3.h index baa848dcd1..baa848dcd1 100644 --- a/arch/x86/include/asm/acpi_s3.h +++ b/include/acpi_s3.h diff --git a/include/configs/mxs.h b/include/configs/mxs.h index 6cadd720d2..e079f8035b 100644 --- a/include/configs/mxs.h +++ b/include/configs/mxs.h @@ -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 index 0000000000..5f89ae4a51 --- /dev/null +++ b/include/configs/sam9x60ek.h @@ -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 index 0000000000..6bcbc06020 --- /dev/null +++ b/include/configs/sama5d27_wlsom1_ek.h @@ -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 diff --git a/include/dm/device.h b/include/dm/device.h index 27a6d7b9fd..d1210429e9 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -680,6 +680,15 @@ static inline bool device_is_on_pci_bus(struct udevice *dev) 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 * * This handles device which have sub-nodes in the device tree. It scans all diff --git a/include/dm/fdtaddr.h b/include/dm/fdtaddr.h index 57b326cb33..959d3bc2d6 100644 --- a/include/dm/fdtaddr.h +++ b/include/dm/fdtaddr.h @@ -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 diff --git a/include/dm/read.h b/include/dm/read.h index 803daf7620..d37fcb504d 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -249,6 +249,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); diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d4d96106b3..f431f3bf29 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -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 */ diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h index 6977995246..6e3f15c2b0 100644 --- a/include/dm/uclass-internal.h +++ b/include/dm/uclass-internal.h @@ -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); diff --git a/include/ec_commands.h b/include/ec_commands.h index 392c1f1a43..444ba61e59 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -71,6 +71,10 @@ #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 */ diff --git a/include/efi_api.h b/include/efi_api.h index 37e56da460..22396172e1 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -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 diff --git a/include/efi_loader.h b/include/efi_loader.h index 53b369945b..381da80cdc 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -12,6 +12,11 @@ #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 diff --git a/include/fdtdec.h b/include/fdtdec.h index 635f53083b..f1e58f9732 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -417,23 +417,6 @@ 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. * diff --git a/include/handoff.h b/include/handoff.h index aacb0f5ebf..75d19b1f6e 100644 --- a/include/handoff.h +++ b/include/handoff.h @@ -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 diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 709b49d393..f9964a7664 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -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 /** diff --git a/include/log.h b/include/log.h index 6d15e955d7..d8f18a6afd 100644 --- a/include/log.h +++ b/include/log.h @@ -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 diff --git a/include/ns16550.h b/include/ns16550.h index 22b89e4d6d..701efeea85 100644 --- a/include/ns16550.h +++ b/include/ns16550.h @@ -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; diff --git a/include/nvme.h b/include/nvme.h index 2c3d14d241..2cdf8ce320 100644 --- a/include/nvme.h +++ b/include/nvme.h @@ -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__ */ diff --git a/include/pci.h b/include/pci.h index 298d0d4355..8aa6636cfb 100644 --- a/include/pci.h +++ b/include/pci.h @@ -215,6 +215,10 @@ #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 @@ -1491,13 +1495,6 @@ int dm_pci_find_class(uint find_class, int index, struct udevice **devp); */ 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 * * @dev: Emulated device to read from @@ -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 diff --git a/include/spl.h b/include/spl.h index e4640f3830..c7cc2b0767 100644 --- a/include/spl.h +++ b/include/spl.h @@ -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 diff --git a/lib/Kconfig b/lib/Kconfig index 3da45a5ec3..135f0b372b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -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 diff --git a/lib/Makefile b/lib/Makefile index 2fffd68f94..d248d8626c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/display_options.c b/lib/display_options.c index cff20f3755..ec16d75e0e 100644 --- a/lib/display_options.c +++ b/lib/display_options.c @@ -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; diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index 86297bb7c1..897fc1b2e8 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -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: /* @@ -584,6 +590,20 @@ static void *dp_fill(void *buf, struct udevice *dev) 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", __FILE__, __LINE__, __func__, diff --git a/lib/efi_loader/efi_device_path_to_text.c b/lib/efi_loader/efi_device_path_to_text.c index 0f3796b373..af1adbb71e 100644 --- a/lib/efi_loader/efi_device_path_to_text.c +++ b/lib/efi_loader/efi_device_path_to_text.c @@ -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 = diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 4c554c546b..d0daf7bdeb 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -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; } diff --git a/lib/efi_selftest/efi_selftest_variables.c b/lib/efi_selftest/efi_selftest_variables.c index a6b41d1f00..5d98c029b8 100644 --- a/lib/efi_selftest/efi_selftest_variables.c +++ b/lib/efi_selftest/efi_selftest_variables.c @@ -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; diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 74430c8b2f..17736ce665 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -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; diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt index 44f50e004a..a1bf63c1f4 100644 --- a/scripts/config_whitelist.txt +++ b/scripts/config_whitelist.txt @@ -1721,7 +1721,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 diff --git a/test/dm/core.c b/test/dm/core.c index edd55b05d6..f74c430843 100644 --- a/test/dm/core.c +++ b/test/dm/core.c @@ -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; diff --git a/test/dm/pci.c b/test/dm/pci.c index c325f6600e..fb93e4c78a 100644 --- a/test/dm/pci.c +++ b/test/dm/pci.c @@ -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); diff --git a/test/dm/test-main.c b/test/dm/test-main.c index 5d79ce641d..72648162a9 100644 --- a/test/dm/test-main.c +++ b/test/dm/test-main.c @@ -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); diff --git a/tools/binman/README b/tools/binman/README index b4f6392ab7..8e0f0a8c55 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -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 ----------------- diff --git a/tools/binman/control.py b/tools/binman/control.py index 9e7587864c..cb51bc2dd4 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -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 diff --git a/tools/binman/entry.py b/tools/binman/entry.py index 6a2c6e0d92..fe8e1dd8a5 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -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) diff --git a/tools/binman/etype/image_header.py b/tools/binman/etype/image_header.py index 4b69eda1a2..b9327dd799 100644 --- a/tools/binman/etype/image_header.py +++ b/tools/binman/etype/image_header.py @@ -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): diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index 5d34fc546a..8179daf562 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -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) diff --git a/tools/binman/image.py b/tools/binman/image.py index 7b39a1ddce..2beab7fd4d 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -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): diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py index 119d02cbb2..f90b8ea7f5 100644 --- a/tools/buildman/func_test.py +++ b/tools/buildman/func_test.py @@ -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) diff --git a/tools/buildman/kconfiglib.py b/tools/buildman/kconfiglib.py index d68af056b6..3908985c7b 100644 --- a/tools/buildman/kconfiglib.py +++ b/tools/buildman/kconfiglib.py @@ -1,3409 +1,6219 @@ +# 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 +======== + +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. + +See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer +overview. + +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 Kconfiglib on the Linux kernel with the Makefile targets +============================================================== + +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: + + $ 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 + +Warning: Not passing -p1 to patch will cause the wrong file to be patched. + +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. + +Look further down for a motivation for the Makefile patch and for instructions +on how you can use Kconfiglib without it. + +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: + + $ git clone git://github.com/ulfalizer/Kconfiglib.git + $ git am Kconfiglib/makefile.patch (or 'patch -p1 < Kconfiglib/makefile.patch') + +Warning: The directory name Kconfiglib/ is significant in this case, because +it's added to PYTHONPATH by the new targets in makefile.patch. + +The targets added by the Makefile patch are described in the following +sections. + + +make kmenuconfig +---------------- + +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). + + +make guiconfig +-------------- + +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'. + + +make [ARCH=<arch>] iscriptconfig +-------------------------------- + +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'. + +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 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. + +If you want to look up a symbol by name, use the kconf.syms dictionary. + + +make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>] +---------------------------------------------------- + +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. + +See the examples/ subdirectory for example scripts. + + +make dumpvarsconfig +------------------- + +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. + +Only environment variables that are referenced via the Kconfig preprocessor +$(FOO) syntax are included. The preprocessor was added in Linux 4.18. + + +Using Kconfiglib without the Makefile targets +============================================= + +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,...)'. + +These variables are referenced as of writing (Linux 4.18), together with sample +values: + + 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) + +Older kernels only reference ARCH, SRCARCH, and KERNELVERSION. + +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. + +To run Kconfiglib without the Makefile patch, set the environment variables +manually: + + $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... python(3) + >>> import kconfiglib + >>> kconf = kconfiglib.Kconfig() # filename defaults to "Kconfig" + +Search the top-level Makefile for "Additional ARCH settings" to see other +possibilities for ARCH and SRCARCH. + + +Intro to symbol values +====================== + +Kconfiglib has the same assignment semantics as the C implementation. + +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. + +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(). + +Dependencies from parents and 'if'/'depends on' are propagated to properties, +including prompts, so these two configurations are logically equivalent: + +(1) + + menu "menu" + depends on A + + if B + + config FOO + tristate "foo" if D + default y + depends on C + + endif + + endmenu + +(2) + + menu "menu" + depends on A + + config FOO + tristate "foo" if A && B && C && D + default y if A && B && C + + endmenu + +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. + +'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. + +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. + +'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. + +For non-bool/tristate symbols, it only matters whether the visibility is n or +non-n: m visibility acts the same as y visibility. + +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. + +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. + +For a visible bool/tristate symbol FOO with value n, this line is written to +.config: + + # CONFIG_FOO is not set + +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: - - 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. + CONFIG_FOO=n -For the Linux kernel, scripts are run using +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. - $ make scriptconfig [ARCH=<arch>] SCRIPT=<path to script> [SCRIPT_ARG=<arg>] -Using the 'scriptconfig' target ensures that required environment variables -(SRCARCH, ARCH, srctree, KERNELVERSION, etc.) are set up correctly. +Intro to the menu tree +====================== -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]. +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.) -To get an interactive Python prompt with Kconfiglib preloaded and a Config -object 'c' created, run +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. - $ make iscriptconfig [ARCH=<arch>] +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). -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. +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. -The examples/ directory contains short example scripts, which can be run with -e.g. +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). - $ make scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py +This organization mirrors the C implementation. MenuNode is called +'struct menu' there, but I thought "menu" was a confusing name. -or +It is possible to give a Choice a name and define it in multiple locations, +hence why Choice.nodes is also a list. - $ make scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=kernel +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. -testsuite.py contains the test suite. See the top of the script for how to run -it. -Credits: Written by Ulf "Ulfalizer" Magnusson +Intro to expressions +==================== -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.""" +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. +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. + +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) + +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. + +***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". + +Manual evaluation examples: + + - The value of A && B is min(A.tri_value, B.tri_value) + + - The value of A || B is max(A.tri_value, B.tri_value) + + - The value of !A is 2 - A.tri_value + + - 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. + + 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. + +n/m/y are automatically converted to the corresponding constant symbols +"n"/"m"/"y" (Kconfig.n/m/y) during parsing. + +Kconfig.const_syms is a dictionary like Kconfig.syms but for constant symbols. + +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. + + +Kconfig extensions +================== + +Kconfiglib includes a couple of Kconfig extensions: + +'source' with relative path +--------------------------- + +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. + +Consider following directory tree: + + Project + +--Kconfig + | + +--src + +--Kconfig + | + +--SubSystem1 + +--Kconfig + | + +--ModuleA + +--Kconfig + +In this example, assume that src/SubSystem1/Kconfig wants to source +src/SubSystem1/ModuleA/Kconfig. + +With 'source', this statement would be used: + + source "src/SubSystem1/ModuleA/Kconfig" + +With 'rsource', this turns into + + rsource "ModuleA/Kconfig" + +If an absolute path is given to 'rsource', it acts the same as 'source'. + +'rsource' can be used to create "position-independent" Kconfig trees that can +be moved around freely. + + +Globbing 'source' +----------------- + +'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig +files. They require at least one matching file, raising a KconfigError +otherwise. + +For example, the following statement might source sub1/foofoofoo and +sub2/foobarfoo: + + source "sub[12]/foo*foo" + +The glob patterns accepted are the same as for the standard glob.glob() +function. + +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"). + +For example, the following statements will be no-ops if neither "foo" nor any +files matching "bar*" exist: + + osource "foo" + osource "bar*" + +'orsource' does a relative optional source. + +'source' and 'osource' are analogous to 'include' and '-include' in Make. + + +Generalized def_* keywords +-------------------------- + +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. + + +Extra optional warnings +----------------------- + +Some optional warnings can be controlled via environment variables: + + - 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. + + 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. + + KCONFIG_STRICT is an older alias for this environment variable, supported + for backwards compatibility. + + - 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. + + This warning can also be enabled/disabled via the Kconfig.warn_assign_undef + variable. + + +Preprocessor user functions defined in Python +--------------------------------------------- + +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). + +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. + +If the KCONFIG_FUNCTIONS environment variable is set, it gives a different +module name to use instead of 'kconfigfunctions'. + +The imported module is expected to define a global dictionary named 'functions' +that maps function names to Python functions, as follows: + + 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 + ... + + def my_other_fn(kconf, name, arg_1, arg_2, ...): + ... + + functions = { + "my-fn": (my_fn, <min.args>, <max.args>/None), + "my-other-fn": (my_other_fn, <min.args>, <max.args>/None), + ... + } + + ... + +<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. + +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. + +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. + +Once defined, user functions can be called from Kconfig in the same way as +other preprocessor functions: + + config FOO + ... + depends on $(my-fn,arg1,arg2) + +If my_fn() returns "n", this will result in + + config FOO + ... + depends on n + +Warning +******* + +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. + +Preferably, user-defined functions should be stateless. + + +Feedback +======== + +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 platform import re import sys +# 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 + + +VERSION = (12, 14, 0) + + # File layout: # # Public classes # Public functions -# Internal classes # Internal functions -# Internal global constants +# Global constants # Line length: 79 columns + # # Public classes # -class Config(object): - """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.""" +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. + + The following attributes are available. They should be treated as + read-only, and some are implemented through @property magic. + + 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. + + Undefined symbols can be recognized by Symbol.nodes being empty -- see + the 'Intro to the menu tree' section in the module docstring. + + const_syms: + A dictionary like 'syms' for constant (quoted) symbols + + named_choices: + A dictionary like 'syms' for named choices (choice FOO) + + 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. + + Note: You probably want to use 'unique_defined_syms' instead. This + attribute is mostly maintained for backwards compatibility. + + 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. + + 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). + + choices: + A list with all choices, in the same order as they appear in the Kconfig + files. + + Note: You probably want to use 'unique_choices' instead. This attribute + is mostly maintained for backwards compatibility. + + unique_choices: + Analogous to 'unique_defined_syms', for choices. Named choices can have + multiple definition locations. + + menus: + A list with all menus, in the same order as they appear in the Kconfig + files + + comments: + A list with all comments, in the same order as they appear in the Kconfig + files + + 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. + + 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. + + Note that Kconfig.sync_deps() already indirectly catches any file + modifications that change configuration output. + + env_vars: + A set() with the names of all environment variables referenced in the + Kconfig files. + + Only environment variables referenced with the preprocessor $(FOO) syntax + will be registered. The older $FOO syntax is only supported for backwards + compatibility. + + 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). + + 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. + + The note from the 'kconfig_filenames' documentation applies here too. + + n/m/y: + The predefined constant symbols n/m/y. Also available in const_syms. + + 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", + ) # # Public interface # - 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 + def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True, + encoding="utf-8"): + """ + 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 - # 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 = [] + 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 - # The set of all named choices (yes, choices can have names), indexed - # by name (a string) - self.named_choices = {} + 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 - # Lists containing all choices, menus and comments in the configuration + self.warnings = [] + + 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.syms = {} + self.const_syms = {} + self.defined_syms = [] + self.missing_syms = [] + self.named_choices = {} self.choices = [] self.menus = [] self.comments = [] - def register_special_symbol(type_, name, val): + for nmy in "n", "m", "y": 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 = "." + 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 = () + + # Parse the Kconfig files + + # Not used internally. Provided as a convenience. + self.kconfig_filenames = [filename] + self.env_vars = set() + # 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 = () + + # The current parsing location self.filename = filename - self.base_dir = self.srctree if base_dir is None else \ - os.path.expandvars(base_dir) + self.linenr = 0 - # The 'mainmenu' text - self.mainmenu_text = None + # 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 - # 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 + # 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 - self.print_warnings = print_warnings - self.print_undef_assign = print_undef_assign - self._warnings = [] + 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) - # 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 + # Close the top-level Kconfig file. __self__ fetches the 'file' object + # for the method. + self._readline.__self__.close() - # See the comment in _parse_expr(). - self._cur_item = None - self._line = None - self._filename = None - self._linenr = None - self._transform_m = None + self._parsing_kconfigs = False - # Parse the Kconfig files - self.top_block = [] - self._parse_file(filename, None, None, None, self.top_block) + # Do various menu tree post-processing + self._finalize_node(self.top_node, self.y) + + self.unique_defined_syms = _ordered_unique(self.defined_syms) + self.unique_choices = _ordered_unique(self.choices) - # Build Symbol.dep for all symbols + # Do sanity checks. Some of these depend on everything being finalized. + self._check_sym_sanity() + self._check_choice_sanity() + + # 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": + + self._check_undef_syms() + + # Build Symbol._dependents for all symbols and choices 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 + # 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) - 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) + # Add extra dependencies from choices to choice symbols that get + # awkward during dependency loop detection + self._add_choice_deps() - 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] + @property + def mainmenu_text(self): + """ + See the class documentation. + """ + return self.top_node.prompt[0] - 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 + @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 - for sym in config: - ... + return None - which relies on Config implementing __iter__() and is equivalent to + 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. - for sym in config.get_symbols(False): - ... + "# CONFIG_FOO is not set" within a .config file sets the user value of + FOO to n. The C tools work the same way. - 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 + 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. - def __iter__(self): - """Convenience function for iterating over the set of all defined - symbols in the configuration, used like + 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. - for sym in conf: - ... + See the Kconfig.__init__() docstring for raised exceptions + (OSError/IOError). KconfigError is never raised here. - 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) + filename (default: None): + Path to load configuration from (a string). Respects $srctree if set + (see the class documentation). - 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 + If 'filename' is None (the default), the configuration file to load + (if any) is calculated automatically, giving the behavior you'd + usually want: - 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 + 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 get_comments(self): - """Returns a list containing all comments in the configuration, in the - order they appear in the Kconfig files.""" - return self.comments + 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. - 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 + 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. - 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. + See the return value as well. - "# 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. + replace (default: True): + If True, all existing user values will be cleared before loading the + .config. Pass False to merge configurations. - 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". + verbose (default: None): + Limited backwards compatibility to prevent crashes. A warning is + printed if anything but None is passed. - replace (default: True): True if the configuration should replace the - old configuration; False if it should add to it. + 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. - Returns a list or warnings (hopefully empty) + Will probably be removed in some future version. + + 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)) - 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 + 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) - # Put this first so that a missing file doesn't screw up our state - filename = os.path.expandvars(filename) - line_feeder = _FileFeed(filename) + continue - self.config_filename = filename + name = match.group(1) + sym = get_sym(name) + if not sym or not sym.nodes: + self._undef_assign(name, "n", filename, linenr) + continue - # - # Read header - # + if sym.orig_type not in _BOOL_TRISTATE: + continue - def is_header_line(line): - return line is not None and line.startswith("#") and \ - not _unset_re_match(line) + val = "n" - self.config_header = None + # Done parsing the assignment. Set the value. - 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] + if sym._was_set: + self._assigned_twice(sym, val, filename, linenr) - # - # Read assignments. Hotspot for some workloads. - # + sym.set_value(val) - 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() + # If we're replacing the configuration, unset the symbols that + # didn't get set + + for sym in self.unique_defined_syms: + if not sym._was_set: + sym.unset_value() + + for choice in self.unique_choices: + if not choice._was_set: + choice.unset_value() + + def _undef_assign(self, name, val, filename, linenr): + # Called for assignments to undefined symbols during .config loading + + 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: - self._invalidate_all() + 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 _autoconf_contents(self, header): + # write_autoconf() helper. Returns the contents to write as a string, + # with 'header' at the beginning. + + # "".join()ed later + chunks = [header] + add = chunks.append + + 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 + 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") + + if filename is None: + filename = standard_config_filename() + + contents = self._config_contents(header) + if self._contents_eq(filename, contents): + return "No change to '{}'".format(filename) + + if save_old: + _save_old(filename) + + with self._open(filename, "w") as f: + f.write(contents) + + return "Configuration saved to '{}'".format(filename) + + 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. + + # node_iter() was used here before commit 3aea9f7 ("Add '# end of + # <menu>' after menus in .config"). Those comments get tricky to + # implement with it. + + for sym in self.unique_defined_syms: + sym._visited = False + + # Did we just print an '# end of ...' comment? + after_end_comment = False + + # "".join()ed later + chunks = [header] + add = chunks.append + + node = self.top_node while 1: - line = line_feeder.get_next() - if line is None: - return self._warnings + # 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) + + # Generate configuration output for the node + + item = node.item + + if item.__class__ is Symbol: + if item._visited: + continue + item._visited = True + + conf_string = item.config_string + if not conf_string: + continue - line = line.rstrip() + 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) + + elif expr_value(node.dep) and \ + ((item is MENU and expr_value(node.visibility)) or + item is COMMENT): + + add("\n#\n# {}\n#\n".format(node.prompt[0])) + after_end_comment = False + + 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. - set_match = _set_re_match(line) - if set_match: - name, val = set_match.groups() + This function is intended to be called during each build, before + compiling source files that depend on configuration symbols. - 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("\\\\", "\\") + 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.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) + + 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: - 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) + # 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: - 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 + while node.parent: + node = node.parent + if node.next: + node = node.next + break + else: + # No more nodes + return - 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]) + 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 # + # - # Kconfig parsing + # File reading # - 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 _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 - 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. + self.kconfig_filenames.append(rel_filename) - line_feeder: A _FileFeed instance feeding lines from a file. The - Kconfig language is line-based in practice. + # 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 - end_marker: The token that ends the block, e.g. T_ENDIF ("endif") for - ifs. None for files. + 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 - parent: The enclosing menu or choice, or None if we're at the top - level. + # 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 - deps: Dependencies from enclosing menus, choices and ifs. + # 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 - visible_if_deps (default: None): 'visible if' dependencies from - enclosing menus. + 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 + # - block: The list to add items to.""" + 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: - # 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 + i = _assignment_lhs_fragment_match(s, i).end() + if s.startswith("$(", i): + s, i = self._expand_macro(s, i, ()) 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 + break - tokens = self._tokenize(line, False, line_feeder.filename, - line_feeder.linenr) + 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 - t0 = tokens.get_next() - if t0 is None: - continue + # Assigned variable + name = s[:i] - # 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 - 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: + # 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() - self.choices.append(choice) + choice.direct_dep = self.n else: # Named choice + name = self._expect_str_and_eol() choice = self.named_choices.get(name) - if choice is None: + if not choice: choice = Choice() choice.name = name + choice.direct_dep = self.n self.named_choices[name] = choice - self.choices.append(choice) - choice.config = self - choice.parent = parent + self.choices.append(choice) - choice.def_locations.append((line_feeder.filename, - line_feeder.linenr)) + 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 - self._parse_properties(line_feeder, choice, deps, - visible_if_deps) + choice.nodes.append(node) - # Parse contents and put Items in choice.block - self._parse_block(line_feeder, T_ENDCHOICE, choice, deps, - visible_if_deps, choice.block) + self._parse_properties(node) + self._parse_block(_T_ENDCHOICE, node, node) + node.list = node.next - choice._determine_actual_symbols() + prev.next = prev = node - # 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 + elif t0 is _T_MAINMENU: + self.top_node.prompt = (self._expect_str_and_eol(), self.y) - # 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 + 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 - block.append(choice) + # Dependencies from 'depends on'. Will get propagated to the properties + # below. + node.dep = self.y - 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 + while self._next_line(): + t0 = self._tokens[0] - 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 + 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) - while 1: - line = line_feeder.get_next() - if line is None: - break + elif t0 is _T_DEPENDS: + if not self._check_token(_T_ON): + self._parse_error("expected 'on' after 'depends'") - filename = line_feeder.filename - linenr = line_feeder.linenr + node.dep = self._make_and(node.dep, + self._expect_expr_and_eol()) - tokens = self._tokenize(line, False, filename, linenr) + elif t0 is _T_HELP: + self._parse_help(node) - t0 = tokens.get_next() - if t0 is None: + 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 - # Cases are ordered roughly by frequency, which speeds things up a - # bit + elif t0 is _T_DEFAULT: + node.defaults.append((self._parse_expr(False), + self._parse_cond())) - if t0 == T_DEPENDS: - if not tokens.check(T_ON): - _parse_error(line, 'expected "on" after "depends"', - filename, linenr) + 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())) - parsed_deps = self._parse_expr(tokens, stmt, line, filename, - linenr) + elif t0 is _T_PROMPT: + self._parse_prompt(node) - 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 + elif t0 is _T_RANGE: + node.ranges.append((self._expect_sym(), self._expect_sym(), + self._parse_cond())) - # 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)) + elif t0 is _T_IMPLY: + if node.item.__class__ is not Symbol: + self._parse_error("only symbols can imply") - if line is None: - break + node.implies.append((self._expect_nonconst_sym(), + self._parse_cond())) - 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] + 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'") - elif tokens.check(T_DEFCONFIG_LIST): - self.defconfig_sym = stmt + env_var = self._expect_str_and_eol() + node.item.env_var = env_var - elif tokens.check(T_MODULES): + 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 stmt.name != "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; " - "it shouldn't be that hard to implement. " - "(Note that modules are still 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.)", - filename, linenr) + "wasn't used.", + self.filename, self.linenr) - 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 + 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: - _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 + 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: - # See comment in Config.__init__() - self.end_line = line - self.end_line_tokens = tokens - break + # Reuse the tokens for the non-property line later + self._reuse_tokens = True + return - # Done parsing properties. Now propagate 'depends on' and enclosing - # menu/if dependencies to expressions. + 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])) - # 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) + node.item.orig_type = new_type - # Save original dependencies from enclosing menus and ifs - stmt.deps_from_containing = deps + 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 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 + if node.prompt: + self._warn(_name_and_loc(node.item) + + " defined with multiple prompts in single location") - 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) + prompt = self._tokens[1] + self._tokens_i = 2 - if token == T_NOT: - return (NOT, self._parse_factor(feed)) + if prompt.__class__ is not str: + self._parse_error("expected prompt string") - 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 + if prompt != prompt.strip(): + self._warn(_name_and_loc(node.item) + + " has leading or trailing whitespace in its prompt") - _parse_error(self._line, "malformed expression", self._filename, - self._linenr) + # This avoid issues for e.g. reStructuredText documentation, where + # '*prompt *' is invalid + prompt = prompt.strip() - 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()). + node.prompt = (prompt, self._parse_cond()) - (I experimented with a pure regular expression implementation, but it - came out slower, less readable, and wouldn't have been as flexible.) + 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") - 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.""" + # Micro-optimization. This code is pretty hot. + readline = self._readline - s = s.strip() - if s == "" or s[0] == "#": - return _Feed([]) + # Find first non-blank (not all-space) line and get its + # indentation - if for_eval: - previous = None # The previous token seen - tokens = [] - i = 0 # The current index in the string being tokenized + while 1: + line = readline() + self.linenr += 1 + if not line: + self._empty_help(node, line) + return + if not line.isspace(): + break - 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() + len_ = len # Micro-optimization - 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)) + # 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 - append(sym) + # The help text goes on till the first non-blank line with less indent + # than the first line - else: - # Not an identifier/keyword + # Add the first line + lines = [expline[indent:]] + add_line = lines.append # Micro-optimization - while i < strlen and s[i].isspace(): - i += 1 - if i == strlen: + 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 - c = s[i] - i += 1 + 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. - # 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 + # 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) - elif c == "|": - # Invalid characters are ignored - if i >= len(s) or s[i] != "|": continue - append(T_OR) - i += 1 + return token - elif c == "!": - if i < len(s) and s[i] == "=": - append(T_UNEQUAL) - i += 1 - else: - append(T_NOT) + # 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)) - elif c == "=": append(T_EQUAL) - elif c == "(": append(T_OPEN_PAREN) - elif c == ")": append(T_CLOSE_PAREN) - elif c == "#": break # Comment + if token is _T_OPEN_PAREN: + expr_parse = self._parse_expr(transform_m) + if self._check_token(_T_CLOSE_PAREN): + return expr_parse - else: continue # Invalid characters are ignored + self._parse_error("malformed expression") - previous = tokens[-1] + # + # Caching and invalidation + # - return _Feed(tokens) + 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. - 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] + for choice in self.unique_choices: + for sym in choice.syms: + sym._dependents.add(choice) - 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 + 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() # - # Expression evaluation + # Post-parsing menu tree processing, including dependency propagation and + # implicit submenu creation # - def _eval_expr(self, expr): - """Evaluates an expression to "n", "m", or "y".""" + 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)) - # Handles e.g. an "x if y" condition where the "if y" part is missing. - if expr is None: - return "y" + # + # Misc. + # - res = self._eval_expr_rec(expr) - if res == "m": - # Promote "m" to "y" if we're running without modules. + 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, "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 + # 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. - 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 + try: + int(s) + except ValueError: + if not s.startswith(("0x", "0X")): + return False - 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 + try: + int(s, 16) + except ValueError: + return False - if expr[0] == EQUAL: - return "y" if (_str_val(expr[1]) == _str_val(expr[2])) else "n" + return True - if expr[0] == UNEQUAL: - return "y" if (_str_val(expr[1]) != _str_val(expr[2])) else "n" + 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": - _internal_error("Internal error while evaluating expression: " - "unknown operation {0}.".format(expr[0])) + 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 _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 + def _warn(self, msg, filename=None, linenr=None): + # For printing general warnings - 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 + if not self.warn: + return - # - # Dependency tracking (for caching and invalidation) - # + msg = "warning: " + msg + if filename is not None: + msg = "{}:{}: {}".format(filename, linenr, msg) - 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 + self.warnings.append(msg) + if self.warn_to_stderr: + sys.stderr.write(msg + "\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 - 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 +class Symbol(object): + """ + Represents a configuration symbol: - return rec(expr) + (menu)config FOO + ... - def _invalidate_all(self): - for sym in self.syms_iter(): - sym._invalidate() + 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). - # - # Printing and misc. - # + 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. - def _expand_sym_refs(self, s): - """Expands $-references to symbols in 's' to symbol values, or to the - empty string for undefined symbols.""" + name: + The name of the symbol, e.g. "FOO" for 'config FOO'. - while 1: - sym_ref_match = _sym_ref_re_search(s) - if sym_ref_match is None: - return s + 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. - sym_name = sym_ref_match.group(0)[1:] - sym = self.syms.get(sym_name) - expansion = "" if sym is None else sym.get_value() + 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. - s = s[:sym_ref_match.start()] + \ - expansion + \ - s[sym_ref_match.end():] + orig_type: + The type as given in the Kconfig file, without any magic applied. Used + when printing the symbol. - 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. + 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". - no_value_str: String to return when 'expr' is missing (None). + This is the symbol value that's used in relational expressions + (A = B, A != B, etc.) - 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.""" + 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. - if expr is None: - return no_value_str + 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. - 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) + This is the symbol value that's used outside of relation expressions + (A, !A, A && B, A || B). - return "{0} (value: {1})".format(_expr_to_str(expr), _expr_to_str(val)) + 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. - 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".""" + 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. - # As we deal a lot with string representations here, use some - # convenient shorthand: - s = _expr_to_str + For string/hex/int symbols, check if Symbol.visibility is non-0 (non-n) + instead to determine if the value can be changed. - # - # Common symbol/choice properties - # + Some handy 'assignable' idioms: - user_val_str = "(no user value)" if sc.user_val is None else \ - s(sc.user_val) + # 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] - # 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)") + # The lowest? + sym_low = sym.assignable[0] - # - # Symbol-specific stuff - # + # Can the symbol be set to at least m? + if sym.assignable[-1] >= 1: + ... - 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) + # Can the symbol be set to m? + if 1 in sym.assignable: + ... - return res + 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. - # - # Choice-specific stuff - # + user_value: + The user value of the symbol. None if no user value has been assigned + (via Kconfig.load_config() or Symbol.set_value()). - # Build selected symbol string - sel = sc.get_selection() - sel_str = "(no selection)" if sel is None else sel.name + Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other + symbol types. - # 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) + WARNING: Do not assign directly to this. It will break things. Use + Symbol.set_value(). - 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) + 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. -class Item(object): + 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. - """Base class for symbols and other Kconfig constructs. Subclasses are - Symbol, Choice, Menu, and Comment.""" + Symbols with the (no longer needed) 'option env=...' option generate no + configuration output, and neither does the special + 'option defconfig_list' symbol. - def is_symbol(self): - """Returns True if the item is a symbol. Short for - isinstance(item, kconfiglib.Symbol).""" - return isinstance(self, 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: - def is_choice(self): - """Returns True if the item is a choice. Short for - isinstance(item, kconfiglib.Choice).""" - return isinstance(self, Choice) + if sym.config_string: + *Write symbol, e.g. by looking sym.str_value* - def is_menu(self): - """Returns True if the item is a menu. Short for - isinstance(item, kconfiglib.Menu).""" - return isinstance(self, Menu) + This is a superset of the symbols written out by write_autoconf(). + That function skips all n-valued symbols. - def is_comment(self): - """Returns True if the item is a comment. Short for - isinstance(item, kconfiglib.Comment).""" - return isinstance(self, Comment) + 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. -class Symbol(Item): + 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. - """Represents a configuration symbol - e.g. FOO for + 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. - config FOO - ...""" + 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. - # - # Public interface - # + 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). - 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: + 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 - default BAR || FOO * + bool + depends on B config B - tristate - depends on FOO * - default m if FOO * + 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", + ) - if FOO * - config A - bool "A" - endif + # + # Public interface + # - config FOO (definition not included) - bool + @property + def type(self): + """ + See the class documentation. """ - return self.ref_locations + if self.orig_type is TRISTATE and \ + (self.choice and self.choice.tri_value == 2 or + not self.kconfig.modules.tri_value): + + return BOOL - def get_value(self): - """Calculate and return the value of the symbol. See also - Symbol.set_user_value().""" + 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.cached_val is not None: - return self.cached_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 - # 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 + # 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 - new_val = DEFAULT_VALUE[self.type] - vis = _get_visibility(self) + 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) - # This is easiest to calculate together with the value - self.write_to_conf = False + 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. - 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() + base = _TYPE_TO_BASE[self.orig_type] - self.write_to_conf = (mode != "n") + # Check if a range is in effect + for low_expr, high_expr, cond in self.ranges: + if expr_value(cond): + has_active_range = True - 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" + # 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: - # 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 + 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 - base = 16 if self.type == HEX else 10 + 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 - for l, h, cond_expr in self.ranges: - if self.config._eval_expr(cond_expr) != "n": - has_active_range = True + if use_defaults: + # No user value or invalid user value. Look at defaults. - 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 + # Used to implement the warning below + has_default = False - break + for sym, cond in self.defaults: + if expr_value(cond): + has_default = self._write_to_conf = True - if vis != "n": - self.write_to_conf = True + val = sym.str_value - 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): + if _is_base_n(val, base): + val_num = int(val, base) + else: + val_num = 0 # strtoll() on empty string - # If the user value is OK, it is stored in exactly the same - # form as specified in the assignment (with or without - # "0x", etc). + 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 - use_defaults = False - new_val = self.user_val + # 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 - 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 + self._cached_str_val = val + return val - if vis != "n": - self.write_to_conf = True - if self.user_val is not None: - new_val = self.user_val - use_defaults = False + @property + def tri_value(self): + """ + See the class documentation. + """ + if self._cached_tri_val is not None: + return self._cached_tri_val - 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 + 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_val = new_val - return new_val + self._cached_tri_val = 0 + return 0 - 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 + # 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) - 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. + val = 0 - 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. + if not self.choice: + # Non-choice symbol - 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 + 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) - 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. + 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 - 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. + # 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 - 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 + @property + def visibility(self): + """ + See the class documentation. + """ + if self._cached_vis is None: + self._cached_vis = _visibility(self) + return self._cached_vis - 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 + @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. - # Has visibility 'm' - config B - tristate "B" - depends on M_SYM + 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] - # 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. + # 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 - 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 + # 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])) - 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 + + 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 containing various information about the symbol.""" - return self.config._get_sym_or_choice_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.""" - - 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 + """ + 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 - def _invalidate(self): - if self.is_special_: - return + # - UNKNOWN == 0 + # - _visited is used during tree iteration and dep. loop detection + self.orig_type = self._visited = 0 - if self.is_choice_sym: - self.parent._invalidate() + self.nodes = [] - self.cached_val = None - self.cached_visibility = None + self.defaults = [] + self.selects = [] + self.implies = [] + self.ranges = [] - def _invalidate_dependent(self): - for sym in self._get_dependent(): - sym._invalidate() + self.user_value = \ + self.choice = \ + self.env_var = \ + self._cached_str_val = self._cached_tri_val = self._cached_vis = \ + self._cached_assignable = None - def _set_user_value_no_invalidate(self, v, suppress_load_warnings): - """Like set_user_value(), but does not invalidate any symbols. + # _write_to_conf is calculated along with the value. If True, the + # Symbol gets a .config entry. - 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. + self.is_allnoconfig_y = \ + self._was_set = \ + self._write_to_conf = False - Perhaps this could be made optional for load_config() instead.""" + # See Kconfig._build_dep() + self._dependents = set() - 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 + def _assignable(self): + # Worker function for the 'assignable' attribute - 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 + if self.orig_type not in _BOOL_TRISTATE: + return () - # 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 + # Warning: See Symbol._rec_invalidate(), and note that this is a hidden + # function call (property magic) + vis = self.visibility + if not vis: + return () - 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)) + rev_dep_val = expr_value(self.rev_dep) - self.user_val = v + if vis == 2: + if self.choice: + return (2,) - 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" + 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) - def _unset_user_value_no_recursive_invalidate(self): - self._invalidate() - self.user_val = None + if rev_dep_val == 2: + return (2,) - if self.is_choice_sym: - self.parent._unset_user_value() + # rev_dep_val == 1 - def _make_conf(self, append_fn): - if self.already_written: - return + if self.type is BOOL or expr_value(self.weak_rev_dep) == 2: + return (2,) + return (1, 2) - self.already_written = True + # vis == 1 - # Note: write_to_conf is determined in get_value() - val = self.get_value() - if not self.write_to_conf: - return + # 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,) - 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)) + # vis == rev_dep_val == 1 - elif self.type == INT or self.type == HEX: - append_fn("{0}{1}={2}".format(self.config.config_prefix, self.name, val)) + return (1,) - elif self.type == STRING: - # Escape \ and " - append_fn('{0}{1}="{2}"' - .format(self.config.config_prefix, self.name, - val.replace("\\", "\\\\").replace('"', '\\"'))) + 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: - _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 + 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. - 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.") + for node in self.nodes: + if node.prompt: + self._rec_invalidate() + return - 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) + 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 - for _, cond_expr in self.prompts: - if self.config._expr_depends_on(cond_expr, on): - return True + val = max(expr_value(self.rev_dep), + expr_value(self.weak_rev_dep), + val) - return False + # 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). -class Menu(Item): + 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. - """Represents a menu statement.""" + 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 # - 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 + @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) - 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. + # Promote m to y for boolean choices + return 2 if val == 1 and self.type is BOOL else val - recursive (default: False): True if symbols contained in items within - the menu should be included recursively.""" + @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 - return [item for item in self.get_items(recursive) if - isinstance(item, Symbol)] + @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 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) + 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] - 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) + 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 - 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 + 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 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)) + """ + 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): - """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 + """ + 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 - self.filename = None - self.linenr = None + # - UNKNOWN == 0 + # - _visited is used during dep. loop detection + self.orig_type = self._visited = 0 - 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) + self.nodes = [] -class Choice(Item): + self.syms = [] + self.defaults = [] - """Represents a choice statement. A choice can be in one of three modes: + self.name = \ + self.user_value = self.user_selection = \ + self._cached_vis = self._cached_assignable = None - "n" - The choice is not visible and no symbols can be selected. + self._cached_selection = _NO_CACHED_SELECTION - "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". + # 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 - "y" - One symbol will be "y" while the rest are "n". + # See Kconfig._build_dep() + self._dependents = set() - 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. + def _assignable(self): + # Worker function for the 'assignable' attribute - The mode changes automatically when a value is assigned to a symbol within - the choice. + # Warning: See Symbol._rec_invalidate(), and note that this is a hidden + # function call (property magic) + vis = self.visibility - See Symbol.get_visibility() too.""" + if not vis: + return () - # - # Public interface - # + 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 - 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 (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 - 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": + # 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 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 _invalidate(self): + self._cached_vis = self._cached_assignable = None + self._cached_selection = _NO_CACHED_SELECTION - def __str__(self): - """Returns a string containing various information about the choice - statement.""" - return self.config._get_sym_or_choice_str(self) + def _rec_invalidate(self): + # See Symbol._rec_invalidate() - # - # Private methods - # + 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): - """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 + # 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])) - 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 - else: - self.cached_selection = selection + @property + def orig_defaults(self): + """ + See the class documentation. + """ + return [(default, self._strip_dep(cond)) + for default, cond in self.defaults] - return selection + @property + def orig_selects(self): + """ + See the class documentation. + """ + return [(select, self._strip_dep(cond)) + for select, cond in self.selects] - def _invalidate(self): - self.cached_selection = None - self.cached_visibility = None + @property + def orig_implies(self): + """ + See the class documentation. + """ + return [(imply, self._strip_dep(cond)) + for imply, cond in self.implies] - def _unset_user_value(self): - self._invalidate() - self.user_val = None - self.user_mode = None + @property + def orig_ranges(self): + """ + See the class documentation. + """ + return [(low, high, self._strip_dep(cond)) + for low, high, cond in self.ranges] - def _make_conf(self, append_fn): - _make_block_conf(self.block, append_fn) + @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) -class Comment(Item): + if self.prompt: + res |= expr_items(self.prompt[1]) - """Represents a comment statement.""" + if self.item is MENU: + res |= expr_items(self.visibility) - # - # Public interface - # + for value, cond in self.defaults: + res |= expr_items(value) + res |= expr_items(cond) - def get_config(self): - """Returns the Config instance this comment is from.""" - return self.config + for value, cond in self.selects: + res.add(value) + res |= expr_items(cond) - def get_text(self): - """Returns the text of the comment.""" - return self.text + for value, cond in self.implies: + res.add(value) + res |= expr_items(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 + for low, high, cond in self.ranges: + res.add(low) + res.add(high) + res |= expr_items(cond) - 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) + return res - def get_visibility(self): - """Returns the visibility of the comment. See also - Symbol.get_visibility().""" - return self.config._eval_expr(self.dep_expr) + 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)]) - 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.item.__class__ in _SYMBOL_CHOICE and self.help is not None: + add("has help") + + if self.list: + add("has child") + + if self.next: + add("has next") + + add("{}:{}".format(self.filename, self.linenr)) + + return "<{}>".format(", ".join(fields)) 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)) + """ + Returns a string representation of the menu node. Matches the Kconfig + format, with any parent dependencies propagated to the 'depends on' + condition. - # - # Private methods - # + 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 __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 + 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.). - self.filename = None - self.linenr = None + The returned string does not end in a newline. + """ + return self.custom_str(standard_sc_expr_str) + + 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 _menu_comment_node_str(self, sc_expr_str_fn): + s = '{} "{}"'.format("menu" if self.item is MENU else "comment", + self.prompt[0]) + + if self.dep is not self.kconfig.y: + s += "\n\tdepends on {}".format(expr_str(self.dep, sc_expr_str_fn)) + + 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)) + + 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: + lines = ["choice " + sc.name if sc.name else "choice"] + + 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]) + + 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" + + indent_add_cond(prefix + ' "{}"'.format(escape(self.prompt[0])), + self.orig_prompt[1]) + + if sc.__class__ is Symbol: + if sc.is_allnoconfig_y: + indent_add("option allnoconfig_y") - def _make_conf(self, append_fn): - if self.config._eval_expr(self.dep_expr) != "n": - append_fn("\n#\n# {0}\n#".format(self.text)) + if sc is sc.kconfig.defconfig_list: + indent_add("option defconfig_list") -class Kconfig_Syntax_Error(Exception): - """Exception raised for syntax errors.""" - pass + if sc.env_var is not None: + indent_add('option env="{}"'.format(sc.env_var)) + + if sc is sc.kconfig.modules: + indent_add("option modules") + + for low, high, cond in self.orig_ranges: + indent_add_cond( + "range {} {}".format(sc_expr_str_fn(low), + sc_expr_str_fn(high)), + cond) + + for default, cond in self.orig_defaults: + indent_add_cond("default " + expr_str(default, sc_expr_str_fn), + cond) + + if sc.__class__ is Choice and sc.is_optional: + indent_add("optional") + + if sc.__class__ is Symbol: + for select, cond in self.orig_selects: + indent_add_cond("select " + sc_expr_str_fn(select), cond) + + for imply, cond in self.orig_implies: + indent_add_cond("imply " + sc_expr_str_fn(imply), cond) + + if self.dep is not sc.kconfig.y: + indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn)) + + if self.help is not None: + indent_add("help") + for line in self.help.splitlines(): + indent_add(" " + line) + + return "\n".join(lines) + + 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. + + # ... 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", + ) + + @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) + + 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)] - return (AND, [e1, e2]) + # 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 -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.""" - # 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 +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'\"') - # Prefer to merge argument lists if possible to reduce the number of nodes - 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]) +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(e2, tuple) and e2[0] == OR: - return (OR, e2[1] + [e1]) +# unescape() helper +_unescape_sub = re.compile(r"\\(.)").sub - return (OR, [e1, e2]) -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): +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. + + 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", " ") # -# Internal global constants +# Global constants # -# 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} +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. +# +# 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_$/.-]|\$\(|$') + +# 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'"((?:[^\\"]|\\.)*)"') diff --git a/tools/genboardscfg.py b/tools/genboardscfg.py index e9bbd15e15..4ff0bffaef 100755 --- a/tools/genboardscfg.py +++ b/tools/genboardscfg.py @@ -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) diff --git a/tools/moveconfig.py b/tools/moveconfig.py index 0bbc7c1991..b99417e9d6 100755 --- a/tools/moveconfig.py +++ b/tools/moveconfig.py @@ -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() |