diff options
141 files changed, 7469 insertions, 782 deletions
diff --git a/.travis.yml b/.travis.yml index 8a220ccc42..a53e7c6f7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,6 +78,11 @@ before_script: tar -C /tmp -xf gcc-linaro-6.3.1-2017.02-x86_64_aarch64-linux-gnu.tar.xz && tar -C /tmp -xf gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz; fi + - if [[ "${TOOLCHAIN}" == "riscv" ]]; then + wget https://github.com/PkmX/riscv-prebuilt-toolchains/releases/download/20180111/riscv32-unknown-elf-toolchain.tar.gz && + tar -C /tmp -xf riscv32-unknown-elf-toolchain.tar.gz && + echo -e "\n[toolchain-prefix]\nriscv = /tmp/riscv32-unknown-elf/bin/riscv32-unknown-elf-" >> ~/.buildman; + fi - if [[ "${QEMU_TARGET}" != "" ]]; then git clone git://git.qemu.org/qemu.git /tmp/qemu; pushd /tmp/qemu; @@ -251,6 +256,9 @@ matrix: - env: - BUILDMAN="xtensa" TOOLCHAIN="xtensa" + - env: + - BUILDMAN="riscv" + TOOLCHAIN="riscv" # QA jobs for code analytics # static code analysis with cppcheck (we can add --enable=all later) diff --git a/MAINTAINERS b/MAINTAINERS index 9b4b63b063..754db5553d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -423,6 +423,13 @@ S: Orphaned (Since 2017-01) T: git git://git.denx.de/u-boot-onenand.git F: drivers/mtd/onenand/ +RISC-V +M: Rick Chen <rick@andestech.com> +S: Maintained +T: git git://git.denx.de/u-boot-riscv.git +F: arch/riscv/ +F: tools/prelink-riscv.c + SANDBOX M: Simon Glass <sjg@chromium.org> S: Maintained @@ -811,6 +811,10 @@ ifneq ($(CONFIG_BUILD_TARGET),) ALL-y += $(CONFIG_BUILD_TARGET:"%"=%) endif +ifneq ($(CONFIG_SYS_INIT_SP_BSS_OFFSET),) +ALL-y += init_sp_bss_offset_check +endif + LDFLAGS_u-boot += $(LDFLAGS_FINAL) # Avoid 'Not enough room for program headers' error on binutils 2.28 onwards. @@ -939,6 +943,33 @@ binary_size_check: u-boot-nodtb.bin FORCE fi \ fi +ifneq ($(CONFIG_SYS_INIT_SP_BSS_OFFSET),) +ifneq ($(CONFIG_SYS_MALLOC_F_LEN),) +subtract_sys_malloc_f_len = space=$$(($${space} - $(CONFIG_SYS_MALLOC_F_LEN))) +else +subtract_sys_malloc_f_len = true +endif +# The 1/4 margin below is somewhat arbitrary. The likely initial SP usage is +# so low that the DTB could probably use 90%+ of the available space, for +# current values of CONFIG_SYS_INIT_SP_BSS_OFFSET at least. However, let's be +# safe for now and tweak this later if space becomes tight. +# A rejected alternative would be to check that some absolute minimum stack +# space was available. However, since CONFIG_SYS_INIT_SP_BSS_OFFSET is +# deliberately build-specific, to take account of build-to-build stack usage +# differences due to different feature sets, there is no common absolute value +# to check against. +init_sp_bss_offset_check: u-boot.dtb FORCE + @dtb_size=$(shell wc -c u-boot.dtb | awk '{print $$1}') ; \ + space=$(CONFIG_SYS_INIT_SP_BSS_OFFSET) ; \ + $(subtract_sys_malloc_f_len) ; \ + quarter_space=$$(($${space} / 4)) ; \ + if [ $${dtb_size} -gt $${quarter_space} ]; then \ + echo "u-boot.dtb is larger than 1 quarter of " >&2 ; \ + echo "(CONFIG_SYS_INIT_SP_BSS_OFFSET - CONFIG_SYS_MALLOC_F_LEN)" >&2 ; \ + exit 1 ; \ + fi +endif + u-boot-nodtb.bin: u-boot FORCE $(call if_changed,objcopy) $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) @@ -1264,6 +1295,10 @@ ifeq ($(CONFIG_KALLSYMS),y) $(call cmd,u-boot__) common/system_map.o endif +ifeq ($(CONFIG_RISCV),y) + @tools/prelink-riscv $@ 0 +endif + quiet_cmd_sym ?= SYM $@ cmd_sym ?= $(OBJDUMP) -t $< > $@ u-boot.sym: u-boot FORCE @@ -143,6 +143,7 @@ Directory Hierarchy: /nios2 Files generic to Altera NIOS2 architecture /openrisc Files generic to OpenRISC architecture /powerpc Files generic to PowerPC architecture + /riscv Files generic to RISC-V architecture /sandbox Files generic to HW-independent "sandbox" /sh Files generic to SH architecture /x86 Files generic to x86 architecture @@ -3510,7 +3511,7 @@ Low Level (hardware related) configuration options: globally (CONFIG_CMD_MEMORY). - CONFIG_SKIP_LOWLEVEL_INIT - [ARM, NDS32, MIPS only] If this variable is defined, then certain + [ARM, NDS32, MIPS, RISC-V only] If this variable is defined, then certain low level initializations (like setting up the memory controller) are omitted and/or U-Boot does not relocate itself into RAM. @@ -4964,6 +4965,22 @@ On NDS32, the following registers are used: NOTE: DECLARE_GLOBAL_DATA_PTR must be used with file-global scope, or current versions of GCC may "optimize" the code too much. +On RISC-V, the following registers are used: + + x0: hard-wired zero (zero) + x1: return address (ra) + x2: stack pointer (sp) + x3: global pointer (gp) + x4: thread pointer (tp) + x5: link register (t0) + x8: frame pointer (fp) + x10-x11: arguments/return values (a0-1) + x12-x17: arguments (a2-7) + x28-31: temporaries (t3-6) + pc: program counter (pc) + + ==> U-Boot will use gp to hold a pointer to the global data + Memory Management: ------------------ diff --git a/arch/Kconfig b/arch/Kconfig index 0b12ed986c..762230cd56 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -54,6 +54,10 @@ config PPC select HAVE_PRIVATE_LIBGCC select SUPPORT_OF_CONTROL +config RISCV + bool "riscv architecture" + select SUPPORT_OF_CONTROL + config SANDBOX bool "Sandbox" select BOARD_LATE_INIT @@ -194,3 +198,4 @@ source "arch/sandbox/Kconfig" source "arch/sh/Kconfig" source "arch/x86/Kconfig" source "arch/xtensa/Kconfig" +source "arch/riscv/Kconfig" diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index de323bf4b9..001ece3cf1 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -19,6 +19,36 @@ config POSITION_INDEPENDENT from almost any address. This logic relies on the relocation information that is embedded into the binary to support U-Boot relocating itself to the top-of-RAM later during execution. + +config SYS_INIT_SP_BSS_OFFSET + int + help + U-Boot typically uses a hard-coded value for the stack pointer + before relocation. Define this option to instead calculate the + initial SP at run-time. This is useful to avoid hard-coding addresses + into U-Boot, so that can be loaded and executed at arbitrary + addresses and thus avoid using arbitrary addresses at runtime. This + option's value is the offset added to &_bss_start in order to + calculate the stack pointer. This offset should be large enough so + that the early malloc region, global data (gd), and early stack usage + do not overlap any appended DTB. + +config LINUX_KERNEL_IMAGE_HEADER + bool + help + Place a Linux kernel image header at the start of the U-Boot binary. + The format of the header is described in the Linux kernel source at + Documentation/arm64/booting.txt. This feature is useful since the + image header reports the amount of memory (BSS and similar) that + U-Boot needs to use, but which isn't part of the binary. + +if LINUX_KERNEL_IMAGE_HEADER +config LNX_KRNL_IMG_TEXT_OFFSET_BASE + hex + help + The value subtracted from CONFIG_SYS_TEXT_BASE to calculate the + TEXT_OFFSET value written in to the Linux kernel image header. +endif endif config STATIC_RELA diff --git a/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h b/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h new file mode 100644 index 0000000000..3e720937f0 --- /dev/null +++ b/arch/arm/cpu/armv8/linux-kernel-image-header-vars.h @@ -0,0 +1,86 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation <www.nvidia.com> + * + * Derived from Linux kernel v4.14 files: + * + * arch/arm64/include/asm/assembler.h: + * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S + * Copyright (C) 1996-2000 Russell King + * Copyright (C) 2012 ARM Ltd. + * + * arch/arm64/kernel/head.S: + * Based on arch/arm/kernel/head.S + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2012 ARM Ltd. + * Authors: Catalin Marinas <catalin.marinas@arm.com> + * Will Deacon <will.deacon@arm.com> + * + * arch/arm64/kernel/image.h: + * Copyright (C) 2014 ARM Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * There aren't any ELF relocations we can use to endian-swap values known only + * at link time (e.g. the subtraction of two symbol addresses), so we must get + * the linker to endian-swap certain values before emitting them. + * + * Note that, in order for this to work when building the ELF64 PIE executable + * (for KASLR), these values should not be referenced via R_AARCH64_ABS64 + * relocations, since these are fixed up at runtime rather than at build time + * when PIE is in effect. So we need to split them up in 32-bit high and low + * words. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define DATA_LE32(data) \ + ((((data) & 0x000000ff) << 24) | \ + (((data) & 0x0000ff00) << 8) | \ + (((data) & 0x00ff0000) >> 8) | \ + (((data) & 0xff000000) >> 24)) +#else +#define DATA_LE32(data) ((data) & 0xffffffff) +#endif + +#define DEFINE_IMAGE_LE64(sym, data) \ + sym##_lo32 = DATA_LE32((data) & 0xffffffff); \ + sym##_hi32 = DATA_LE32((data) >> 32) + +#define __MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define __CODE_DATA_SIZE (__bss_start - _start) +#define __BSS_SIZE (__bss_end - __bss_start) +#ifdef CONFIG_SYS_INIT_SP_BSS_OFFSET +#define __MAX_EXTRA_RAM_USAGE __MAX(__BSS_SIZE, CONFIG_SYS_INIT_SP_BSS_OFFSET) +#else +#define __MAX_EXTRA_RAM_USAGE __BSS_SIZE +#endif +#define __MEM_USAGE (__CODE_DATA_SIZE + __MAX_EXTRA_RAM_USAGE) + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define __HEAD_FLAG_BE 1 +#else +#define __HEAD_FLAG_BE 0 +#endif + +#define __HEAD_FLAG_PAGE_SIZE 1 /* 4K hard-coded */ + +#define __HEAD_FLAG_PHYS_BASE 1 + +#define __HEAD_FLAGS ((__HEAD_FLAG_BE << 0) | \ + (__HEAD_FLAG_PAGE_SIZE << 1) | \ + (__HEAD_FLAG_PHYS_BASE << 3)) + +#define TEXT_OFFSET (CONFIG_SYS_TEXT_BASE - \ + CONFIG_LNX_KRNL_IMG_TEXT_OFFSET_BASE) + +/* + * These will output as part of the Image header, which should be little-endian + * regardless of the endianness of the kernel. While constant values could be + * endian swapped in head.S, all are done here for consistency. + */ +#define HEAD_SYMBOLS \ + DEFINE_IMAGE_LE64(_kernel_size_le, __MEM_USAGE); \ + DEFINE_IMAGE_LE64(_kernel_offset_le, TEXT_OFFSET); \ + DEFINE_IMAGE_LE64(_kernel_flags_le, __HEAD_FLAGS); + + HEAD_SYMBOLS diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S index f385ed40e1..7a98a1c95d 100644 --- a/arch/arm/cpu/armv8/start.S +++ b/arch/arm/cpu/armv8/start.S @@ -19,7 +19,9 @@ .globl _start _start: -#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK +#if defined(LINUX_KERNEL_IMAGE_HEADER) +#include <asm/boot0-linux-kernel-header.h> +#elif defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) /* * Various SoCs need something special and SoC-specific up front in * order to boot, allow them to set that in their boot0.h file and then diff --git a/arch/arm/cpu/armv8/u-boot.lds b/arch/arm/cpu/armv8/u-boot.lds index 22195b8834..7b76e0f9f0 100644 --- a/arch/arm/cpu/armv8/u-boot.lds +++ b/arch/arm/cpu/armv8/u-boot.lds @@ -159,4 +159,8 @@ SECTIONS /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } + +#ifdef CONFIG_LINUX_KERNEL_IMAGE_HEADER +#include "linux-kernel-image-header-vars.h" +#endif } diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 19100c95f4..e22b52cbb6 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -211,7 +211,8 @@ dtb-$(CONFIG_FSL_LSCH2) += fsl-ls1043a-qds-duart.dtb \ fsl-ls1012a-rdb.dtb \ fsl-ls1012a-frdm.dtb -dtb-$(CONFIG_ARCH_SNAPDRAGON) += dragonboard410c.dtb +dtb-$(CONFIG_TARGET_DRAGONBOARD410C) += dragonboard410c.dtb +dtb-$(CONFIG_TARGET_DRAGONBOARD820C) += dragonboard820c.dtb dtb-$(CONFIG_STM32F4) += stm32f429-disco.dtb \ stm32f469-disco.dtb diff --git a/arch/arm/dts/dragonboard410c.dts b/arch/arm/dts/dragonboard410c.dts index 7746622dda..5ccfe7f8c8 100644 --- a/arch/arm/dts/dragonboard410c.dts +++ b/arch/arm/dts/dragonboard410c.dts @@ -23,11 +23,16 @@ reg = <0 0x80000000 0 0x3da00000>; }; + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + }; + chosen { stdout-path = "/soc/serial@78b0000"; }; - soc { #address-cells = <0x1>; #size-cells = <0x1>; @@ -48,11 +53,6 @@ clock = <&clkc 4>; }; - restart@4ab000 { - compatible = "qcom,pshold"; - reg = <0x4ab000 0x4>; - }; - soc_gpios: pinctrl@1000000 { compatible = "qcom,apq8016-pinctrl"; reg = <0x1000000 0x300000>; @@ -86,6 +86,16 @@ clock-frequency = <200000000>; }; + wcnss { + bt { + compatible="qcom,wcnss-bt"; + }; + + wifi { + compatible="qcom,wcnss-wlan"; + }; + }; + spmi@200f000 { compatible = "qcom,spmi-pmic-arb"; reg = <0x200f800 0x200 0x2400000 0x400000 0x2c00000 0x400000>; diff --git a/arch/arm/dts/dragonboard820c-uboot.dtsi b/arch/arm/dts/dragonboard820c-uboot.dtsi new file mode 100644 index 0000000000..167e72c0ae --- /dev/null +++ b/arch/arm/dts/dragonboard820c-uboot.dtsi @@ -0,0 +1,19 @@ +/* + * U-Boot addition to handle Dragonboard 820c pins + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +&pm8994_pon { + key_vol_down { + gpios = <&pm8994_pon 1 0>; + label = "key_vol_down"; + }; + + key_power { + gpios = <&pm8994_pon 0 0>; + label = "key_power"; + }; +}; diff --git a/arch/arm/dts/dragonboard820c.dts b/arch/arm/dts/dragonboard820c.dts new file mode 100644 index 0000000000..64249449af --- /dev/null +++ b/arch/arm/dts/dragonboard820c.dts @@ -0,0 +1,108 @@ +/* + * Qualcomm APQ8096 based Dragonboard 820C board device tree source + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; + +#include "skeleton64.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. DB820c"; + compatible = "arrow,apq8096-db820c", "qcom,apq8096-sbc"; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + serial0 = &blsp2_uart1; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + memory { + device_type = "memory"; + reg = <0 0x80000000 0 0xc0000000>; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + soc: soc { + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0 0xffffffff>; + compatible = "simple-bus"; + + gcc: clock-controller@300000 { + compatible = "qcom,gcc-msm8996"; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + reg = <0x300000 0x90000>; + }; + + blsp2_uart1: serial@75b0000 { + compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; + reg = <0x75b0000 0x1000>; + }; + + sdhc2: sdhci@74a4900 { + compatible = "qcom,sdhci-msm-v4"; + reg = <0x74a4900 0x314>, <0x74a4000 0x800>; + index = <0x0>; + bus-width = <4>; + clock = <&gcc 0>; + clock-frequency = <200000000>; + }; + + spmi@400f000 { + compatible = "qcom,spmi-pmic-arb"; + reg = <0x400f800 0x200>, + <0x4400000 0x400000>, + <0x4c00000 0x400000>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + pmic0: pm8994@0 { + compatible = "qcom,spmi-pmic"; + reg = <0x0 0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + pm8994_pon: pm8994_pon@800 { + compatible = "qcom,pm8994-pwrkey"; + reg = <0x800 0x96>; + #gpio-cells = <2>; + gpio-controller; + gpio-bank-name="pm8994_key."; + }; + + pm8994_gpios: pm8994_gpios@c000 { + compatible = "qcom,pm8994-gpio"; + reg = <0xc000 0x400>; + gpio-controller; + gpio-count = <24>; + #gpio-cells = <2>; + gpio-bank-name="pm8994."; + }; + }; + + pmic1: pm8994@1 { + compatible = "qcom,spmi-pmic"; + reg = <0x1 0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + }; + }; + }; + +}; + +#include "dragonboard820c-uboot.dtsi" diff --git a/arch/arm/dts/tegra-u-boot.dtsi b/arch/arm/dts/tegra-u-boot.dtsi index cde591c5fc..4f692ee975 100644 --- a/arch/arm/dts/tegra-u-boot.dtsi +++ b/arch/arm/dts/tegra-u-boot.dtsi @@ -1,5 +1,11 @@ #include <config.h> +#ifdef CONFIG_SPL_TEXT_BASE +#define U_BOOT_OFFSET (CONFIG_SYS_TEXT_BASE - CONFIG_SPL_TEXT_BASE) +#else +#define U_BOOT_OFFSET 0 +#endif + / { binman { multiple-images; @@ -9,8 +15,7 @@ u-boot-spl { }; u-boot { - pos = <(CONFIG_SYS_TEXT_BASE - - CONFIG_SPL_TEXT_BASE)>; + pos = <(U_BOOT_OFFSET)>; }; }; @@ -21,8 +26,7 @@ u-boot-spl { }; u-boot { - pos = <(CONFIG_SYS_TEXT_BASE - - CONFIG_SPL_TEXT_BASE)>; + pos = <(U_BOOT_OFFSET)>; }; }; @@ -32,8 +36,7 @@ u-boot-spl { }; u-boot-nodtb { - pos = <(CONFIG_SYS_TEXT_BASE - - CONFIG_SPL_TEXT_BASE)>; + pos = <(U_BOOT_OFFSET)>; }; }; }; diff --git a/arch/arm/include/asm/boot0-linux-kernel-header.h b/arch/arm/include/asm/boot0-linux-kernel-header.h new file mode 100644 index 0000000000..ca28780daa --- /dev/null +++ b/arch/arm/include/asm/boot0-linux-kernel-header.h @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2017 NVIDIA Corporation <www.nvidia.com> + * + * Derived from Linux kernel v4.14 files: + * + * arch/arm64/include/asm/assembler.h: + * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S + * Copyright (C) 1996-2000 Russell King + * Copyright (C) 2012 ARM Ltd. + * + * arch/arm64/kernel/head.S: + * Based on arch/arm/kernel/head.S + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2012 ARM Ltd. + * Authors: Catalin Marinas <catalin.marinas@arm.com> + * Will Deacon <will.deacon@arm.com> + * + * arch/arm64/kernel/image.h: + * Copyright (C) 2014 ARM Ltd. + * + * SPDX-License-Identifier: GPL-2.0 + */ + + /* + * Emit a 64-bit absolute little endian symbol reference in a way that + * ensures that it will be resolved at build time, even when building a + * PIE binary. This requires cooperation from the linker script, which + * must emit the lo32/hi32 halves individually. + */ + .macro le64sym, sym + .long \sym\()_lo32 + .long \sym\()_hi32 + .endm + +.globl _start +_start: + /* + * DO NOT MODIFY. Image header expected by Linux boot-loaders. + */ + b reset /* branch to kernel start, magic */ + .long 0 /* reserved */ + le64sym _kernel_offset_le /* Image load offset from start of RAM, little-endian */ + le64sym _kernel_size_le /* Effective size of kernel image, little-endian */ + le64sym _kernel_flags_le /* Informative flags, little-endian */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .ascii "ARM\x64" /* Magic number */ + .long 0 /* reserved */ diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 5c62d9c144..89740657e0 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -47,7 +47,8 @@ static ulong get_sp(void) void arch_lmb_reserve(struct lmb *lmb) { - ulong sp; + ulong sp, bank_end; + int bank; /* * Booting a (Linux) kernel image @@ -63,8 +64,16 @@ void arch_lmb_reserve(struct lmb *lmb) /* adjust sp by 4K to be safe */ sp -= 4096; - lmb_reserve(lmb, sp, - gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + if (sp < gd->bd->bi_dram[bank].start) + continue; + bank_end = gd->bd->bi_dram[bank].start + + gd->bd->bi_dram[bank].size; + if (sp >= bank_end) + continue; + lmb_reserve(lmb, sp, bank_end - sp); + break; + } } __weak void board_quiesce_devices(void) diff --git a/arch/arm/lib/crt0_64.S b/arch/arm/lib/crt0_64.S index 9cb70552fe..a181283e0f 100644 --- a/arch/arm/lib/crt0_64.S +++ b/arch/arm/lib/crt0_64.S @@ -73,6 +73,9 @@ ENTRY(_main) ldr x0, =(CONFIG_TPL_STACK) #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr x0, =(CONFIG_SPL_STACK) +#elif defined(CONFIG_SYS_INIT_SP_BSS_OFFSET) + adr x0, __bss_start + add x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET #else ldr x0, =(CONFIG_SYS_INIT_SP_ADDR) #endif diff --git a/arch/arm/mach-snapdragon/Kconfig b/arch/arm/mach-snapdragon/Kconfig index dc7ba21c18..d55dc1aaa1 100644 --- a/arch/arm/mach-snapdragon/Kconfig +++ b/arch/arm/mach-snapdragon/Kconfig @@ -19,8 +19,18 @@ config TARGET_DRAGONBOARD410C - HDMI - 20-pin low speed and 40-pin high speed expanders, 4 LED, 3 buttons +config TARGET_DRAGONBOARD820C + bool "96Boards Dragonboard 820C" + help + Support for 96Boards Dragonboard 820C. This board complies with + 96Board Open Platform Specifications. Features: + - Qualcomm Snapdragon 820C SoC - APQ8096 (4xKyro CPU) + - 3GiB RAM + - 32GiB UFS drive + endchoice source "board/qualcomm/dragonboard410c/Kconfig" +source "board/qualcomm/dragonboard820c/Kconfig" endif diff --git a/arch/arm/mach-snapdragon/Makefile b/arch/arm/mach-snapdragon/Makefile index d82a04dda6..090c355b6b 100644 --- a/arch/arm/mach-snapdragon/Makefile +++ b/arch/arm/mach-snapdragon/Makefile @@ -4,5 +4,8 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += clock-apq8016.o -obj-y += sysmap-apq8016.o +obj-$(CONFIG_TARGET_DRAGONBOARD820C) += clock-apq8096.o +obj-$(CONFIG_TARGET_DRAGONBOARD820C) += sysmap-apq8096.o +obj-$(CONFIG_TARGET_DRAGONBOARD410C) += clock-apq8016.o +obj-$(CONFIG_TARGET_DRAGONBOARD410C) += sysmap-apq8016.o +obj-y += clock-snapdragon.o diff --git a/arch/arm/mach-snapdragon/clock-apq8016.c b/arch/arm/mach-snapdragon/clock-apq8016.c index da05015c32..a2424178c6 100644 --- a/arch/arm/mach-snapdragon/clock-apq8016.c +++ b/arch/arm/mach-snapdragon/clock-apq8016.c @@ -14,146 +14,12 @@ #include <errno.h> #include <asm/io.h> #include <linux/bitops.h> +#include "clock-snapdragon.h" /* GPLL0 clock control registers */ -#define GPLL0_STATUS 0x2101C #define GPLL0_STATUS_ACTIVE BIT(17) - -#define APCS_GPLL_ENA_VOTE 0x45000 #define APCS_GPLL_ENA_VOTE_GPLL0 BIT(0) -/* vote reg for blsp1 clock */ -#define APCS_CLOCK_BRANCH_ENA_VOTE 0x45004 -#define APCS_CLOCK_BRANCH_ENA_VOTE_BLSP1 BIT(10) - -/* SDC(n) clock control registers; n=1,2 */ - -/* block control register */ -#define SDCC_BCR(n) ((n * 0x1000) + 0x41000) -/* cmd */ -#define SDCC_CMD_RCGR(n) ((n * 0x1000) + 0x41004) -/* cfg */ -#define SDCC_CFG_RCGR(n) ((n * 0x1000) + 0x41008) -/* m */ -#define SDCC_M(n) ((n * 0x1000) + 0x4100C) -/* n */ -#define SDCC_N(n) ((n * 0x1000) + 0x41010) -/* d */ -#define SDCC_D(n) ((n * 0x1000) + 0x41014) -/* branch control */ -#define SDCC_APPS_CBCR(n) ((n * 0x1000) + 0x41018) -#define SDCC_AHB_CBCR(n) ((n * 0x1000) + 0x4101C) - -/* BLSP1 AHB clock (root clock for BLSP) */ -#define BLSP1_AHB_CBCR 0x1008 - -/* Uart clock control registers */ -#define BLSP1_UART2_BCR 0x3028 -#define BLSP1_UART2_APPS_CBCR 0x302C -#define BLSP1_UART2_APPS_CMD_RCGR 0x3034 -#define BLSP1_UART2_APPS_CFG_RCGR 0x3038 -#define BLSP1_UART2_APPS_M 0x303C -#define BLSP1_UART2_APPS_N 0x3040 -#define BLSP1_UART2_APPS_D 0x3044 - -/* CBCR register fields */ -#define CBCR_BRANCH_ENABLE_BIT BIT(0) -#define CBCR_BRANCH_OFF_BIT BIT(31) - -struct msm_clk_priv { - phys_addr_t base; -}; - -/* Enable clock controlled by CBC soft macro */ -static void clk_enable_cbc(phys_addr_t cbcr) -{ - setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT); - - while (readl(cbcr) & CBCR_BRANCH_OFF_BIT) - ; -} - -/* clock has 800MHz */ -static void clk_enable_gpll0(phys_addr_t base) -{ - if (readl(base + GPLL0_STATUS) & GPLL0_STATUS_ACTIVE) - return; /* clock already enabled */ - - setbits_le32(base + APCS_GPLL_ENA_VOTE, APCS_GPLL_ENA_VOTE_GPLL0); - - while ((readl(base + GPLL0_STATUS) & GPLL0_STATUS_ACTIVE) == 0) - ; -} - -#define APPS_CMD_RGCR_UPDATE BIT(0) - -/* Update clock command via CMD_RGCR */ -static void clk_bcr_update(phys_addr_t apps_cmd_rgcr) -{ - setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE); - - /* Wait for frequency to be updated. */ - while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE) - ; -} - -struct bcr_regs { - uintptr_t cfg_rcgr; - uintptr_t cmd_rcgr; - uintptr_t M; - uintptr_t N; - uintptr_t D; -}; - -/* RCGR_CFG register fields */ -#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */ - -/* sources */ -#define CFG_CLK_SRC_CXO (0 << 8) -#define CFG_CLK_SRC_GPLL0 (1 << 8) -#define CFG_CLK_SRC_MASK (7 << 8) - -/* Mask for supported fields */ -#define CFG_MASK 0x3FFF - -#define CFG_DIVIDER_MASK 0x1F - -/* root set rate for clocks with half integer and MND divider */ -static void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs, - int div, int m, int n, int source) -{ - uint32_t cfg; - /* M value for MND divider. */ - uint32_t m_val = m; - /* NOT(N-M) value for MND divider. */ - uint32_t n_val = ~((n)-(m)) * !!(n); - /* NOT 2D value for MND divider. */ - uint32_t d_val = ~(n); - - /* Program MND values */ - writel(m_val, base + regs->M); - writel(n_val, base + regs->N); - writel(d_val, base + regs->D); - - /* setup src select and divider */ - cfg = readl(base + regs->cfg_rcgr); - cfg &= ~CFG_MASK; - cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */ - - /* Set the divider; HW permits fraction dividers (+0.5), but - for simplicity, we will support integers only */ - if (div) - cfg |= (2 * div - 1) & CFG_DIVIDER_MASK; - - if (n_val) - cfg |= CFG_MODE_DUAL_EDGE; - - writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */ - - /* Inform h/w to start using the new config. */ - clk_bcr_update(base + regs->cmd_rcgr); -} - static const struct bcr_regs sdc_regs[] = { { .cfg_rcgr = SDCC_CFG_RCGR(1), @@ -171,7 +37,14 @@ static const struct bcr_regs sdc_regs[] = { } }; -/* Init clock for SDHCI controller */ +static struct gpll0_ctrl gpll0_ctrl = { + .status = GPLL0_STATUS, + .status_bit = GPLL0_STATUS_ACTIVE, + .ena_vote = APCS_GPLL_ENA_VOTE, + .vote_bit = APCS_GPLL_ENA_VOTE_GPLL0, +}; + +/* SDHCI */ static int clk_init_sdc(struct msm_clk_priv *priv, int slot, uint rate) { int div = 8; /* 100MHz default */ @@ -183,7 +56,7 @@ static int clk_init_sdc(struct msm_clk_priv *priv, int slot, uint rate) /* 800Mhz/div, gpll0 */ clk_rcg_set_rate_mnd(priv->base, &sdc_regs[slot], div, 0, 0, CFG_CLK_SRC_GPLL0); - clk_enable_gpll0(priv->base); + clk_enable_gpll0(priv->base, &gpll0_ctrl); clk_enable_cbc(priv->base + SDCC_APPS_CBCR(slot)); return rate; @@ -197,7 +70,7 @@ static const struct bcr_regs uart2_regs = { .D = BLSP1_UART2_APPS_D, }; -/* Init UART clock, 115200 */ +/* UART: 115200 */ static int clk_init_uart(struct msm_clk_priv *priv) { /* Enable iface clk */ @@ -205,7 +78,7 @@ static int clk_init_uart(struct msm_clk_priv *priv) /* 7372800 uart block clock @ GPLL0 */ clk_rcg_set_rate_mnd(priv->base, &uart2_regs, 1, 144, 15625, CFG_CLK_SRC_GPLL0); - clk_enable_gpll0(priv->base); + clk_enable_gpll0(priv->base, &gpll0_ctrl); /* Enable core clk */ clk_enable_cbc(priv->base + BLSP1_UART2_APPS_CBCR); @@ -230,33 +103,3 @@ ulong msm_set_rate(struct clk *clk, ulong rate) return 0; } } - -static int msm_clk_probe(struct udevice *dev) -{ - struct msm_clk_priv *priv = dev_get_priv(dev); - - priv->base = devfdt_get_addr(dev); - if (priv->base == FDT_ADDR_T_NONE) - return -EINVAL; - - return 0; -} - -static struct clk_ops msm_clk_ops = { - .set_rate = msm_set_rate, -}; - -static const struct udevice_id msm_clk_ids[] = { - { .compatible = "qcom,gcc-msm8916" }, - { .compatible = "qcom,gcc-apq8016" }, - { } -}; - -U_BOOT_DRIVER(clk_msm) = { - .name = "clk_msm", - .id = UCLASS_CLK, - .of_match = msm_clk_ids, - .ops = &msm_clk_ops, - .priv_auto_alloc_size = sizeof(struct msm_clk_priv), - .probe = msm_clk_probe, -}; diff --git a/arch/arm/mach-snapdragon/clock-apq8096.c b/arch/arm/mach-snapdragon/clock-apq8096.c new file mode 100644 index 0000000000..3d363d4d66 --- /dev/null +++ b/arch/arm/mach-snapdragon/clock-apq8096.c @@ -0,0 +1,62 @@ +/* + * Clock drivers for Qualcomm APQ8096 + * + * (C) Copyright 2017 Jorge Ramirez Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * Based on Little Kernel driver, simplified + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include "clock-snapdragon.h" + +/* GPLL0 clock control registers */ +#define GPLL0_STATUS_ACTIVE BIT(30) +#define APCS_GPLL_ENA_VOTE_GPLL0 BIT(0) + +static const struct bcr_regs sdc_regs = { + .cfg_rcgr = SDCC2_CFG_RCGR, + .cmd_rcgr = SDCC2_CMD_RCGR, + .M = SDCC2_M, + .N = SDCC2_N, + .D = SDCC2_D, +}; + +static const struct gpll0_ctrl gpll0_ctrl = { + .status = GPLL0_STATUS, + .status_bit = GPLL0_STATUS_ACTIVE, + .ena_vote = APCS_GPLL_ENA_VOTE, + .vote_bit = APCS_GPLL_ENA_VOTE_GPLL0, +}; + +static int clk_init_sdc(struct msm_clk_priv *priv, uint rate) +{ + int div = 3; + + clk_enable_cbc(priv->base + SDCC2_AHB_CBCR); + clk_rcg_set_rate_mnd(priv->base, &sdc_regs, div, 0, 0, + CFG_CLK_SRC_GPLL0); + clk_enable_gpll0(priv->base, &gpll0_ctrl); + clk_enable_cbc(priv->base + SDCC2_APPS_CBCR); + + return rate; +} + +ulong msm_set_rate(struct clk *clk, ulong rate) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + switch (clk->id) { + case 0: /* SDC1 */ + return clk_init_sdc(priv, rate); + break; + default: + return 0; + } +} diff --git a/arch/arm/mach-snapdragon/clock-snapdragon.c b/arch/arm/mach-snapdragon/clock-snapdragon.c new file mode 100644 index 0000000000..899b5ba6ce --- /dev/null +++ b/arch/arm/mach-snapdragon/clock-snapdragon.c @@ -0,0 +1,134 @@ +/* + * Clock drivers for Qualcomm APQ8016, APQ8096 + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Based on Little Kernel driver, simplified + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include "clock-snapdragon.h" + +/* CBCR register fields */ +#define CBCR_BRANCH_ENABLE_BIT BIT(0) +#define CBCR_BRANCH_OFF_BIT BIT(31) + +extern ulong msm_set_rate(struct clk *clk, ulong rate); + +/* Enable clock controlled by CBC soft macro */ +void clk_enable_cbc(phys_addr_t cbcr) +{ + setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT); + + while (readl(cbcr) & CBCR_BRANCH_OFF_BIT) + ; +} + +void clk_enable_gpll0(phys_addr_t base, const struct gpll0_ctrl *gpll0) +{ + if (readl(base + gpll0->status) & gpll0->status_bit) + return; /* clock already enabled */ + + setbits_le32(base + gpll0->ena_vote, gpll0->vote_bit); + + while ((readl(base + gpll0->status) & gpll0->status_bit) == 0) + ; +} + +#define APPS_CMD_RGCR_UPDATE BIT(0) + +/* Update clock command via CMD_RGCR */ +void clk_bcr_update(phys_addr_t apps_cmd_rgcr) +{ + setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE); + + /* Wait for frequency to be updated. */ + while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE) + ; +} + +#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */ + +#define CFG_MASK 0x3FFF + +#define CFG_DIVIDER_MASK 0x1F + +/* root set rate for clocks with half integer and MND divider */ +void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs, + int div, int m, int n, int source) +{ + u32 cfg; + /* M value for MND divider. */ + u32 m_val = m; + /* NOT(N-M) value for MND divider. */ + u32 n_val = ~((n) - (m)) * !!(n); + /* NOT 2D value for MND divider. */ + u32 d_val = ~(n); + + /* Program MND values */ + writel(m_val, base + regs->M); + writel(n_val, base + regs->N); + writel(d_val, base + regs->D); + + /* setup src select and divider */ + cfg = readl(base + regs->cfg_rcgr); + cfg &= ~CFG_MASK; + cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */ + + /* Set the divider; HW permits fraction dividers (+0.5), but + for simplicity, we will support integers only */ + if (div) + cfg |= (2 * div - 1) & CFG_DIVIDER_MASK; + + if (n_val) + cfg |= CFG_MODE_DUAL_EDGE; + + writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */ + + /* Inform h/w to start using the new config. */ + clk_bcr_update(base + regs->cmd_rcgr); +} + +static int msm_clk_probe(struct udevice *dev) +{ + struct msm_clk_priv *priv = dev_get_priv(dev); + + priv->base = devfdt_get_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static ulong msm_clk_set_rate(struct clk *clk, ulong rate) +{ + return msm_set_rate(clk, rate); +} + +static struct clk_ops msm_clk_ops = { + .set_rate = msm_clk_set_rate, +}; + +static const struct udevice_id msm_clk_ids[] = { + { .compatible = "qcom,gcc-msm8916" }, + { .compatible = "qcom,gcc-apq8016" }, + { .compatible = "qcom,gcc-msm8996" }, + { .compatible = "qcom,gcc-apq8096" }, + { } +}; + +U_BOOT_DRIVER(clk_msm) = { + .name = "clk_msm", + .id = UCLASS_CLK, + .of_match = msm_clk_ids, + .ops = &msm_clk_ops, + .priv_auto_alloc_size = sizeof(struct msm_clk_priv), + .probe = msm_clk_probe, +}; diff --git a/arch/arm/mach-snapdragon/clock-snapdragon.h b/arch/arm/mach-snapdragon/clock-snapdragon.h new file mode 100644 index 0000000000..d7026aa867 --- /dev/null +++ b/arch/arm/mach-snapdragon/clock-snapdragon.h @@ -0,0 +1,40 @@ +/* + * Qualcomm APQ8016, APQ8096 + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _CLOCK_SNAPDRAGON_H +#define _CLOCK_SNAPDRAGON_H + +#define CFG_CLK_SRC_CXO (0 << 8) +#define CFG_CLK_SRC_GPLL0 (1 << 8) +#define CFG_CLK_SRC_MASK (7 << 8) + +struct gpll0_ctrl { + uintptr_t status; + int status_bit; + uintptr_t ena_vote; + int vote_bit; +}; + +struct bcr_regs { + uintptr_t cfg_rcgr; + uintptr_t cmd_rcgr; + uintptr_t M; + uintptr_t N; + uintptr_t D; +}; + +struct msm_clk_priv { + phys_addr_t base; +}; + +void clk_enable_gpll0(phys_addr_t base, const struct gpll0_ctrl *pll0); +void clk_bcr_update(phys_addr_t apps_cmd_rgcr); +void clk_enable_cbc(phys_addr_t cbcr); +void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs, + int div, int m, int n, int source); + +#endif diff --git a/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h index cdbfad0def..1094b14a80 100644 --- a/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h +++ b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8016.h @@ -8,7 +8,32 @@ #ifndef _MACH_SYSMAP_APQ8016_H #define _MACH_SYSMAP_APQ8016_H -#define GICD_BASE 0x0b000000 -#define GICC_BASE 0x0a20c000 +#define GICD_BASE (0x0b000000) +#define GICC_BASE (0x0a20c000) + +/* Clocks: (from CLK_CTL_BASE) */ +#define GPLL0_STATUS (0x2101C) +#define APCS_GPLL_ENA_VOTE (0x45000) + +#define SDCC_BCR(n) ((n * 0x1000) + 0x41000) +#define SDCC_CMD_RCGR(n) ((n * 0x1000) + 0x41004) +#define SDCC_CFG_RCGR(n) ((n * 0x1000) + 0x41008) +#define SDCC_M(n) ((n * 0x1000) + 0x4100C) +#define SDCC_N(n) ((n * 0x1000) + 0x41010) +#define SDCC_D(n) ((n * 0x1000) + 0x41014) +#define SDCC_APPS_CBCR(n) ((n * 0x1000) + 0x41018) +#define SDCC_AHB_CBCR(n) ((n * 0x1000) + 0x4101C) + +/* BLSP1 AHB clock (root clock for BLSP) */ +#define BLSP1_AHB_CBCR 0x1008 + +/* Uart clock control registers */ +#define BLSP1_UART2_BCR (0x3028) +#define BLSP1_UART2_APPS_CBCR (0x302C) +#define BLSP1_UART2_APPS_CMD_RCGR (0x3034) +#define BLSP1_UART2_APPS_CFG_RCGR (0x3038) +#define BLSP1_UART2_APPS_M (0x303C) +#define BLSP1_UART2_APPS_N (0x3040) +#define BLSP1_UART2_APPS_D (0x3044) #endif diff --git a/arch/arm/mach-snapdragon/include/mach/sysmap-apq8096.h b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8096.h new file mode 100644 index 0000000000..fb89de258d --- /dev/null +++ b/arch/arm/mach-snapdragon/include/mach/sysmap-apq8096.h @@ -0,0 +1,29 @@ +/* + * Qualcomm APQ8096 sysmap + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _MACH_SYSMAP_APQ8096_H +#define _MACH_SYSMAP_APQ8096_H + +#define TLMM_BASE_ADDR (0x1010000) + +/* Strength (sdc1) */ +#define SDC1_HDRV_PULL_CTL_REG (TLMM_BASE_ADDR + 0x0012D000) + +/* Clocks: (from CLK_CTL_BASE) */ +#define GPLL0_STATUS (0x0000) +#define APCS_GPLL_ENA_VOTE (0x52000) + +#define SDCC2_BCR (0x14000) /* block reset */ +#define SDCC2_APPS_CBCR (0x14004) /* branch control */ +#define SDCC2_AHB_CBCR (0x14008) +#define SDCC2_CMD_RCGR (0x14010) +#define SDCC2_CFG_RCGR (0x14014) +#define SDCC2_M (0x14018) +#define SDCC2_N (0x1401C) +#define SDCC2_D (0x14020) + +#endif diff --git a/arch/arm/mach-snapdragon/sysmap-apq8096.c b/arch/arm/mach-snapdragon/sysmap-apq8096.c new file mode 100644 index 0000000000..cb6d1e475b --- /dev/null +++ b/arch/arm/mach-snapdragon/sysmap-apq8096.c @@ -0,0 +1,32 @@ +/* + * Qualcomm APQ8096 memory map + * + * (C) Copyright 2017 Jorge Ramirez Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/armv8/mmu.h> + +static struct mm_region apq8096_mem_map[] = { + { + .virt = 0x0UL, /* Peripheral block */ + .phys = 0x0UL, /* Peripheral block */ + .size = 0x10000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + .virt = 0x80000000UL, /* DDR */ + .phys = 0x80000000UL, /* DDR */ + .size = 0xC0000000UL, + .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | + PTE_BLOCK_INNER_SHARE + }, { + /* List terminator */ + 0, + } +}; + +struct mm_region *mem_map = apq8096_mem_map; diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 51d143687b..fd0082d22a 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -60,8 +60,14 @@ config TEGRA_ARMV7_COMMON config TEGRA_ARMV8_COMMON bool "Tegra 64-bit common options" select ARM64 + select LINUX_KERNEL_IMAGE_HEADER select TEGRA_COMMON +if TEGRA_ARMV8_COMMON +config LNX_KRNL_IMG_TEXT_OFFSET_BASE + default 0x80000000 +endif + choice prompt "Tegra SoC select" optional diff --git a/arch/arm/mach-tegra/arm64-mmu.c b/arch/arm/mach-tegra/arm64-mmu.c index a79a5192e0..3a126bdec4 100644 --- a/arch/arm/mach-tegra/arm64-mmu.c +++ b/arch/arm/mach-tegra/arm64-mmu.c @@ -12,7 +12,8 @@ #include <asm/system.h> #include <asm/armv8/mmu.h> -static struct mm_region tegra_mem_map[] = { +/* size: IO + NR_DRAM_BANKS + terminator */ +struct mm_region tegra_mem_map[1 + CONFIG_NR_DRAM_BANKS + 1] = { { .virt = 0x0UL, .phys = 0x0UL, diff --git a/arch/arm/mach-tegra/tegra186/Kconfig b/arch/arm/mach-tegra/tegra186/Kconfig index b2e53b58ca..479c0955ee 100644 --- a/arch/arm/mach-tegra/tegra186/Kconfig +++ b/arch/arm/mach-tegra/tegra186/Kconfig @@ -21,6 +21,9 @@ endchoice config SYS_SOC default "tegra186" +config SYS_INIT_SP_BSS_OFFSET + default 524288 + source "board/nvidia/p2771-0000/Kconfig" endif diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_board.c b/arch/arm/mach-tegra/tegra186/nvtboot_board.c index b94eb424aa..8ecb454443 100644 --- a/arch/arm/mach-tegra/tegra186/nvtboot_board.c +++ b/arch/arm/mach-tegra/tegra186/nvtboot_board.c @@ -1,16 +1,278 @@ /* - * Copyright (c) 2016, NVIDIA CORPORATION. + * Copyright (c) 2016-2018, NVIDIA CORPORATION. * * SPDX-License-Identifier: GPL-2.0+ */ +#include <stdlib.h> #include <common.h> #include <fdt_support.h> #include <fdtdec.h> #include <asm/arch/tegra.h> +#include <asm/armv8/mmu.h> + +DECLARE_GLOBAL_DATA_PTR; extern unsigned long nvtboot_boot_x0; +/* + * The following few functions run late during the boot process and dynamically + * calculate the load address of various binaries. To keep track of multiple + * allocations, some writable list of RAM banks must be used. tegra_mem_map[] + * is used for this purpose to avoid making yet another copy of the list of RAM + * banks. This is safe because tegra_mem_map[] is only used once during very + * early boot to create U-Boot's page tables, long before this code runs. If + * this assumption becomes invalid later, we can just fix the code to copy the + * list of RAM banks into some private data structure before running. + */ + +extern struct mm_region tegra_mem_map[]; + +static char *gen_varname(const char *var, const char *ext) +{ + size_t len_var = strlen(var); + size_t len_ext = strlen(ext); + size_t len = len_var + len_ext + 1; + char *varext = malloc(len); + + if (!varext) + return 0; + strcpy(varext, var); + strcpy(varext + len_var, ext); + return varext; +} + +static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end) +{ + u64 bank_start = tegra_mem_map[bank].virt; + u64 bank_size = tegra_mem_map[bank].size; + u64 bank_end = bank_start + bank_size; + bool keep_front = allocated_start != bank_start; + bool keep_tail = allocated_end != bank_end; + + if (keep_front && keep_tail) { + /* + * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array, + * starting at index 1 (index 0 is MMIO). So, we are at DRAM + * entry "bank" not "bank - 1" as for a typical 0-base array. + * The number of remaining DRAM entries is therefore + * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the + * current entry and shift up the remaining entries, dropping + * the last one. Thus, we must copy one fewer entry than the + * number remaining. + */ + memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank], + CONFIG_NR_DRAM_BANKS - bank - 1); + tegra_mem_map[bank].size = allocated_start - bank_start; + bank++; + tegra_mem_map[bank].virt = allocated_end; + tegra_mem_map[bank].phys = allocated_end; + tegra_mem_map[bank].size = bank_end - allocated_end; + } else if (keep_front) { + tegra_mem_map[bank].size = allocated_start - bank_start; + } else if (keep_tail) { + tegra_mem_map[bank].virt = allocated_end; + tegra_mem_map[bank].phys = allocated_end; + tegra_mem_map[bank].size = bank_end - allocated_end; + } else { + /* + * We could move all subsequent banks down in the array but + * that's not necessary for subsequent allocations to work, so + * we skip doing so. + */ + tegra_mem_map[bank].size = 0; + } +} + +static void reserve_ram(u64 start, u64 size) +{ + int bank; + u64 end = start + size; + + for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) { + u64 bank_start = tegra_mem_map[bank].virt; + u64 bank_size = tegra_mem_map[bank].size; + u64 bank_end = bank_start + bank_size; + + if (end <= bank_start || start > bank_end) + continue; + mark_ram_allocated(bank, start, end); + break; + } +} + +static u64 alloc_ram(u64 size, u64 align, u64 offset) +{ + int bank; + + for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) { + u64 bank_start = tegra_mem_map[bank].virt; + u64 bank_size = tegra_mem_map[bank].size; + u64 bank_end = bank_start + bank_size; + u64 allocated = ROUND(bank_start, align) + offset; + u64 allocated_end = allocated + size; + + if (allocated_end > bank_end) + continue; + mark_ram_allocated(bank, allocated, allocated_end); + return allocated; + } + return 0; +} + +static void set_calculated_aliases(char *aliases, u64 address) +{ + char *tmp, *alias; + int err; + + aliases = strdup(aliases); + if (!aliases) { + pr_err("strdup(aliases) failed"); + return; + } + + tmp = aliases; + while (true) { + alias = strsep(&tmp, " "); + if (!alias) + break; + debug("%s: alias: %s\n", __func__, alias); + err = env_set_hex(alias, address); + if (err) + pr_err("Could not set %s\n", alias); + } + + free(aliases); +} + +static void set_calculated_env_var(const char *var) +{ + char *var_size; + char *var_align; + char *var_offset; + char *var_aliases; + u64 size; + u64 align; + u64 offset; + char *aliases; + u64 address; + int err; + + var_size = gen_varname(var, "_size"); + if (!var_size) + return; + var_align = gen_varname(var, "_align"); + if (!var_align) + goto out_free_var_size; + var_offset = gen_varname(var, "_offset"); + if (!var_offset) + goto out_free_var_align; + var_aliases = gen_varname(var, "_aliases"); + if (!var_aliases) + goto out_free_var_offset; + + size = env_get_hex(var_size, 0); + if (!size) { + pr_err("%s not set or zero\n", var_size); + goto out_free_var_aliases; + } + align = env_get_hex(var_align, 1); + /* Handle extant variables, but with a value of 0 */ + if (!align) + align = 1; + offset = env_get_hex(var_offset, 0); + aliases = env_get(var_aliases); + + debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n", + __func__, var, size, align, offset); + if (aliases) + debug("%s: Aliases: %s\n", __func__, aliases); + + address = alloc_ram(size, align, offset); + if (!address) { + pr_err("Could not allocate %s\n", var); + goto out_free_var_aliases; + } + debug("%s: Address %llx\n", __func__, address); + + err = env_set_hex(var, address); + if (err) + pr_err("Could not set %s\n", var); + if (aliases) + set_calculated_aliases(aliases, address); + +out_free_var_aliases: + free(var_aliases); +out_free_var_offset: + free(var_offset); +out_free_var_align: + free(var_align); +out_free_var_size: + free(var_size); +} + +#ifdef DEBUG +static void dump_ram_banks(void) +{ + int bank; + + for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) { + u64 bank_start = tegra_mem_map[bank].virt; + u64 bank_size = tegra_mem_map[bank].size; + u64 bank_end = bank_start + bank_size; + + if (!bank_size) + continue; + printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1, + bank_start, bank_end, bank_size); + } +} +#endif + +static void set_calculated_env_vars(void) +{ + char *vars, *tmp, *var; + +#ifdef DEBUG + printf("RAM banks before any calculated env. var.s:\n"); + dump_ram_banks(); +#endif + + reserve_ram(nvtboot_boot_x0, fdt_totalsize(nvtboot_boot_x0)); + +#ifdef DEBUG + printf("RAM after reserving cboot DTB:\n"); + dump_ram_banks(); +#endif + + vars = env_get("calculated_vars"); + if (!vars) { + debug("%s: No env var calculated_vars\n", __func__); + return; + } + + vars = strdup(vars); + if (!vars) { + pr_err("strdup(calculated_vars) failed"); + return; + } + + tmp = vars; + while (true) { + var = strsep(&tmp, " "); + if (!var) + break; + debug("%s: var: %s\n", __func__, var); + set_calculated_env_var(var); +#ifdef DEBUG + printf("RAM banks affter allocating %s:\n", var); + dump_ram_banks(); +#endif + } + + free(vars); +} + static int set_fdt_addr(void) { int ret; @@ -60,6 +322,7 @@ static int set_ethaddr_from_nvtboot(void) int tegra_soc_board_init_late(void) { + set_calculated_env_vars(); /* * Ignore errors here; the value may not be used depending on * extlinux.conf or boot script content. diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c index 5224ef641c..2ca59747d4 100644 --- a/arch/arm/mach-tegra/tegra186/nvtboot_mem.c +++ b/arch/arm/mach-tegra/tegra186/nvtboot_mem.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, NVIDIA CORPORATION. + * Copyright (c) 2016-2018, NVIDIA CORPORATION. * * SPDX-License-Identifier: GPL-2.0+ */ @@ -8,28 +8,48 @@ #include <fdt_support.h> #include <fdtdec.h> #include <asm/arch/tegra.h> +#include <asm/armv8/mmu.h> + +#define SZ_4G 0x100000000ULL + +/* + * Size of a region that's large enough to hold the relocated U-Boot and all + * other allocations made around it (stack, heap, page tables, etc.) + * In practice, running "bdinfo" at the shell prompt, the stack reaches about + * 5MB from the address selected for ram_top as of the time of writing, + * so a 16MB region should be plenty. + */ +#define MIN_USABLE_RAM_SIZE SZ_16M +/* + * The amount of space we expect to require for stack usage. Used to validate + * that all reservations fit into the region selected for the relocation target + */ +#define MIN_USABLE_STACK_SIZE SZ_1M DECLARE_GLOBAL_DATA_PTR; extern unsigned long nvtboot_boot_x0; +extern struct mm_region tegra_mem_map[]; /* - * A parsed version of /memory/reg from the DTB that is passed to U-Boot in x0. - * - * We only support up to two banks since that's all the binary bootloader - * ever sets. We assume bank 0 is RAM below 4G and bank 1 is RAM above 4G. - * This is all a fairly safe assumption, since the L4T kernel makes the same - * assumptions, so the bootloader is unlikely to change. - * - * This is written to before relocation, and hence cannot be in .bss, since - * .bss overlaps the DTB that's appended to the U-Boot binary. The initializer - * forces this into .data and avoids this issue. This also has the nice side- - * effect of the content being valid after relocation. + * These variables are written to before relocation, and hence cannot be + * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary. + * The section attribute forces this into .data and avoids this issue. This + * also has the nice side-effect of the content being valid after relocation. + */ + +/* The number of valid entries in ram_banks[] */ +static int ram_bank_count __attribute__((section(".data"))); + +/* + * The usable top-of-RAM for U-Boot. This is both: + * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing. + * b) At the end of a region that has enough space to hold the relocated U-Boot + * and all other allocations made around it (stack, heap, page tables, etc.) */ -static struct { - u64 start; - u64 size; -} ram_banks[2] = {{1}}; +static u64 ram_top __attribute__((section(".data"))); +/* The base address of the region of RAM that ends at ram_top */ +static u64 region_base __attribute__((section(".data"))); int dram_init(void) { @@ -38,8 +58,6 @@ int dram_init(void) int node, len, i; const u32 *prop; - memset(ram_banks, 0, sizeof(ram_banks)); - na = fdtdec_get_uint(nvtboot_blob, 0, "#address-cells", 2); ns = fdtdec_get_uint(nvtboot_blob, 0, "#size-cells", 2); @@ -54,37 +72,103 @@ int dram_init(void) hang(); } - len /= (na + ns); - if (len > ARRAY_SIZE(ram_banks)) - len = ARRAY_SIZE(ram_banks); + /* Calculate the true # of base/size pairs to read */ + len /= 4; /* Convert bytes to number of cells */ + len /= (na + ns); /* Convert cells to number of banks */ + if (len > CONFIG_NR_DRAM_BANKS) + len = CONFIG_NR_DRAM_BANKS; + /* Parse the /memory node, and save useful entries */ gd->ram_size = 0; + ram_bank_count = 0; for (i = 0; i < len; i++) { - ram_banks[i].start = fdt_read_number(prop, na); + u64 bank_start, bank_end, bank_size, usable_bank_size; + + /* Extract raw memory region data from DTB */ + bank_start = fdt_read_number(prop, na); prop += na; - ram_banks[i].size = fdt_read_number(prop, ns); + bank_size = fdt_read_number(prop, ns); prop += ns; - gd->ram_size += ram_banks[i].size; + gd->ram_size += bank_size; + bank_end = bank_start + bank_size; + debug("Bank %d: %llx..%llx (+%llx)\n", i, + bank_start, bank_end, bank_size); + + /* + * Align the bank to MMU section size. This is not strictly + * necessary, since the translation table construction code + * handles page granularity without issue. However, aligning + * the MMU entries reduces the size and number of levels in the + * page table, so is worth it. + */ + bank_start = ROUND(bank_start, SZ_2M); + bank_end = bank_end & ~(SZ_2M - 1); + bank_size = bank_end - bank_start; + debug(" aligned: %llx..%llx (+%llx)\n", + bank_start, bank_end, bank_size); + if (bank_end <= bank_start) + continue; + + /* Record data used to create MMU translation tables */ + ram_bank_count++; + /* Index below is deliberately 1-based to skip MMIO entry */ + tegra_mem_map[ram_bank_count].virt = bank_start; + tegra_mem_map[ram_bank_count].phys = bank_start; + tegra_mem_map[ram_bank_count].size = bank_size; + tegra_mem_map[ram_bank_count].attrs = + PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE; + + /* Determine best bank to relocate U-Boot into */ + if (bank_end > SZ_4G) + bank_end = SZ_4G; + debug(" end %llx (usable)\n", bank_end); + usable_bank_size = bank_end - bank_start; + debug(" size %llx (usable)\n", usable_bank_size); + if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) && + (bank_end > ram_top)) { + ram_top = bank_end; + region_base = bank_start; + debug("ram top now %llx\n", ram_top); + } + } + + /* Ensure memory map contains the desired sentinel entry */ + tegra_mem_map[ram_bank_count + 1].virt = 0; + tegra_mem_map[ram_bank_count + 1].phys = 0; + tegra_mem_map[ram_bank_count + 1].size = 0; + tegra_mem_map[ram_bank_count + 1].attrs = 0; + + /* Error out if a relocation target couldn't be found */ + if (!ram_top) { + pr_err("Can't find a usable RAM top"); + hang(); } return 0; } -extern unsigned long nvtboot_boot_x0; - int dram_init_banksize(void) { int i; - for (i = 0; i < 2; i++) { - gd->bd->bi_dram[i].start = ram_banks[i].start; - gd->bd->bi_dram[i].size = ram_banks[i].size; + if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) { + pr_err("Reservations exceed chosen region size"); + hang(); + } + + for (i = 0; i < ram_bank_count; i++) { + gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt; + gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size; } +#ifdef CONFIG_PCI + gd->pci_ram_top = ram_top; +#endif + return 0; } ulong board_get_usable_ram_top(ulong total_size) { - return ram_banks[0].start + ram_banks[0].size; + return ram_top; } diff --git a/arch/arm/mach-tegra/tegra210/Kconfig b/arch/arm/mach-tegra/tegra210/Kconfig index 3637473051..250738aed3 100644 --- a/arch/arm/mach-tegra/tegra210/Kconfig +++ b/arch/arm/mach-tegra/tegra210/Kconfig @@ -40,6 +40,9 @@ endchoice config SYS_SOC default "tegra210" +config SYS_INIT_SP_BSS_OFFSET + default 524288 + source "board/nvidia/e2220-1170/Kconfig" source "board/nvidia/p2371-0000/Kconfig" source "board/nvidia/p2371-2180/Kconfig" diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig new file mode 100644 index 0000000000..c50be37c97 --- /dev/null +++ b/arch/riscv/Kconfig @@ -0,0 +1,42 @@ +menu "RISCV architecture" + depends on RISCV + +config SYS_ARCH + default "riscv" + +choice + prompt "Target select" + optional + +config TARGET_NX25_AE250 + bool "Support nx25-ae250" + +endchoice + +source "board/AndesTech/nx25-ae250/Kconfig" + +choice + prompt "CPU selection" + default CPU_RISCV_32 + +config CPU_RISCV_32 + bool "RISCV 32 bit" + select 32BIT + help + Choose this option to build an U-Boot for RISCV32 architecture. + +config CPU_RISCV_64 + bool "RISCV 64 bit" + select 64BIT + help + Choose this option to build an U-Boot for RISCV64 architecture. + +endchoice + +config 32BIT + bool + +config 64BIT + bool + +endmenu diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile new file mode 100644 index 0000000000..09d24db7a9 --- /dev/null +++ b/arch/riscv/Makefile @@ -0,0 +1,11 @@ +# +# Copyright (C) 2017 Andes Technology Corporation. +# Rick Chen, Andes Technology Corporation <rick@andestech.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +head-y := arch/riscv/cpu/$(CPU)/start.o + +libs-y += arch/riscv/cpu/$(CPU)/ +libs-y += arch/riscv/lib/ diff --git a/arch/riscv/config.mk b/arch/riscv/config.mk new file mode 100644 index 0000000000..6b681c4286 --- /dev/null +++ b/arch/riscv/config.mk @@ -0,0 +1,33 @@ +# +# (C) Copyright 2000-2002 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# Copyright (c) 2017 Microsemi Corporation. +# Padmarao Begari, Microsemi Corporation <padmarao.begari@microsemi.com> +# +# Copyright (C) 2017 Andes Technology Corporation +# Rick Chen, Andes Technology Corporation <rick@andestech.com> +# +# SPDX-License-Identifier: GPL-2.0+ + +ifeq ($(CROSS_COMPILE),) +CROSS_COMPILE := riscv32-unknown-linux-gnu- +endif + +32bit-emul := elf32lriscv +64bit-emul := elf64lriscv + +ifdef CONFIG_32BIT +PLATFORM_LDFLAGS += -m $(32bit-emul) +endif + +ifdef CONFIG_64BIT +PLATFORM_LDFLAGS += -m $(64bit-emul) +endif + +CONFIG_STANDALONE_LOAD_ADDR = 0x00000000 \ + -T $(srctree)/examples/standalone/riscv.lds + +PLATFORM_CPPFLAGS += -ffixed-gp -fpic +PLATFORM_RELFLAGS += -fno-strict-aliasing -fno-common -gdwarf-2 +LDFLAGS_u-boot += --gc-sections -static -pie diff --git a/arch/riscv/cpu/nx25/Makefile b/arch/riscv/cpu/nx25/Makefile new file mode 100644 index 0000000000..5fcf1007c2 --- /dev/null +++ b/arch/riscv/cpu/nx25/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2017 Andes Technology Corporation +# Rick Chen, Andes Technology Corporation <rick@andestech.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +extra-y = start.o + +obj-y := cpu.o diff --git a/arch/riscv/cpu/nx25/cpu.c b/arch/riscv/cpu/nx25/cpu.c new file mode 100644 index 0000000000..5478f4f2de --- /dev/null +++ b/arch/riscv/cpu/nx25/cpu.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* CPU specific code */ +#include <common.h> +#include <command.h> +#include <watchdog.h> +#include <asm/cache.h> + +/* + * cleanup_before_linux() is called just before we call linux + * it prepares the processor for linux + * + * we disable interrupt and caches. + */ +int cleanup_before_linux(void) +{ + disable_interrupts(); + + /* turn off I/D-cache */ + + return 0; +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + disable_interrupts(); + panic("nx25-ae250 wdt not support yet.\n"); +} diff --git a/arch/riscv/cpu/nx25/start.S b/arch/riscv/cpu/nx25/start.S new file mode 100644 index 0000000000..6a076639d3 --- /dev/null +++ b/arch/riscv/cpu/nx25/start.S @@ -0,0 +1,291 @@ +/* + * Startup Code for RISC-V Core + * + * Copyright (c) 2017 Microsemi Corporation. + * Copyright (c) 2017 Padmarao Begari <Padmarao.Begari@microsemi.com> + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm-offsets.h> +#include <config.h> +#include <common.h> +#include <elf.h> +#include <asm/encoding.h> + +#ifdef CONFIG_32BIT +#define LREG lw +#define SREG sw +#define REGBYTES 4 +#define RELOC_TYPE R_RISCV_32 +#define SYM_INDEX 0x8 +#define SYM_SIZE 0x10 +#else +#define LREG ld +#define SREG sd +#define REGBYTES 8 +#define RELOC_TYPE R_RISCV_64 +#define SYM_INDEX 0x20 +#define SYM_SIZE 0x18 +#endif + +.section .text +.globl _start +_start: + j handle_reset + +nmi_vector: + j nmi_vector + +trap_vector: + j trap_entry + +.global trap_entry +handle_reset: + la t0, trap_entry + csrw mtvec, t0 + csrwi mstatus, 0 + csrwi mie, 0 + +/* + * Do CPU critical regs init only at reboot, + * not when booting from ram + */ +#ifdef CONFIG_INIT_CRITICAL + jal cpu_init_crit /* Do CPU critical regs init */ +#endif + +/* + * Set stackpointer in internal/ex RAM to call board_init_f + */ +call_board_init_f: + li t0, -16 + li t1, CONFIG_SYS_INIT_SP_ADDR + and sp, t1, t0 /* force 16 byte alignment */ + +#ifdef CONFIG_DEBUG_UART + jal debug_uart_init +#endif + +call_board_init_f_0: + mv a0, sp + jal board_init_f_alloc_reserve + mv sp, a0 + jal board_init_f_init_reserve + + mv a0, zero /* a0 <-- boot_flags = 0 */ + la t5, board_init_f + jr t5 /* jump to board_init_f() */ + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + */ +.globl relocate_code +relocate_code: + mv s2, a0 /* save addr_sp */ + mv s3, a1 /* save addr of gd */ + mv s4, a2 /* save addr of destination */ + +/* + *Set up the stack + */ +stack_setup: + mv sp, s2 + la t0, _start + sub t6, s4, t0 /* t6 <- relocation offset */ + beq t0, s4, clear_bss /* skip relocation */ + + mv t1, s4 /* t1 <- scratch for copy_loop */ + la t3, __bss_start + sub t3, t3, t0 /* t3 <- __bss_start_ofs */ + add t2, t0, t3 /* t2 <- source end address */ + +copy_loop: + LREG t5, 0(t0) + addi t0, t0, REGBYTES + SREG t5, 0(t1) + addi t1, t1, REGBYTES + blt t0, t2, copy_loop + +/* + * Update dynamic relocations after board_init_f + */ +fix_rela_dyn: + la t1, __rel_dyn_start + la t2, __rel_dyn_end + beq t1, t2, clear_bss + add t1, t1, t6 /* t1 <- rela_dyn_start in RAM */ + add t2, t2, t6 /* t2 <- rela_dyn_end in RAM */ + +/* + * skip first reserved entry: address, type, addend + */ + bne t1, t2, 7f + +6: + LREG t5, -(REGBYTES*2)(t1) /* t5 <-- relocation info:type */ + li t3, R_RISCV_RELATIVE /* reloc type R_RISCV_RELATIVE */ + bne t5, t3, 8f /* skip non-RISCV_RELOC entries */ + LREG t3, -(REGBYTES*3)(t1) + LREG t5, -(REGBYTES)(t1) /* t5 <-- addend */ + add t5, t5, t6 /* t5 <-- location to fix up in RAM */ + add t3, t3, t6 /* t3 <-- location to fix up in RAM */ + SREG t5, 0(t3) +7: + addi t1, t1, (REGBYTES*3) + ble t1, t2, 6b + +8: + la t4, __dyn_sym_start + add t4, t4, t6 + +9: + LREG t5, -(REGBYTES*2)(t1) /* t5 <-- relocation info:type */ + srli t0, t5, SYM_INDEX /* t0 <--- sym table index */ + andi t5, t5, 0xFF /* t5 <--- relocation type */ + li t3, RELOC_TYPE + bne t5, t3, 10f /* skip non-addned entries */ + + LREG t3, -(REGBYTES*3)(t1) + li t5, SYM_SIZE + mul t0, t0, t5 + add s1, t4, t0 + LREG t5, REGBYTES(s1) + add t5, t5, t6 /* t5 <-- location to fix up in RAM */ + add t3, t3, t6 /* t3 <-- location to fix up in RAM */ + SREG t5, 0(t3) +10: + addi t1, t1, (REGBYTES*3) + ble t1, t2, 9b + +/* + * trap update +*/ + la t0, trap_entry + add t0, t0, t6 + csrw mtvec, t0 + +clear_bss: + la t0, __bss_start /* t0 <- rel __bss_start in FLASH */ + add t0, t0, t6 /* t0 <- rel __bss_start in RAM */ + la t1, __bss_end /* t1 <- rel __bss_end in FLASH */ + add t1, t1, t6 /* t1 <- rel __bss_end in RAM */ + li t2, 0x00000000 /* clear */ + beq t0, t1, call_board_init_r + +clbss_l: + SREG t2, 0(t0) /* clear loop... */ + addi t0, t0, REGBYTES + bne t0, t1, clbss_l + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ +call_board_init_r: + la t0, board_init_r + mv t4, t0 /* offset of board_init_r() */ + add t4, t4, t6 /* real address of board_init_r() */ +/* + * setup parameters for board_init_r + */ + mv a0, s3 /* gd_t */ + mv a1, s4 /* dest_addr */ + +/* + * jump to it ... + */ + jr t4 /* jump to board_init_r() */ + +/* + * trap entry + */ +trap_entry: + addi sp, sp, -32*REGBYTES + SREG x1, 1*REGBYTES(sp) + SREG x2, 2*REGBYTES(sp) + SREG x3, 3*REGBYTES(sp) + SREG x4, 4*REGBYTES(sp) + SREG x5, 5*REGBYTES(sp) + SREG x6, 6*REGBYTES(sp) + SREG x7, 7*REGBYTES(sp) + SREG x8, 8*REGBYTES(sp) + SREG x9, 9*REGBYTES(sp) + SREG x10, 10*REGBYTES(sp) + SREG x11, 11*REGBYTES(sp) + SREG x12, 12*REGBYTES(sp) + SREG x13, 13*REGBYTES(sp) + SREG x14, 14*REGBYTES(sp) + SREG x15, 15*REGBYTES(sp) + SREG x16, 16*REGBYTES(sp) + SREG x17, 17*REGBYTES(sp) + SREG x18, 18*REGBYTES(sp) + SREG x19, 19*REGBYTES(sp) + SREG x20, 20*REGBYTES(sp) + SREG x21, 21*REGBYTES(sp) + SREG x22, 22*REGBYTES(sp) + SREG x23, 23*REGBYTES(sp) + SREG x24, 24*REGBYTES(sp) + SREG x25, 25*REGBYTES(sp) + SREG x26, 26*REGBYTES(sp) + SREG x27, 27*REGBYTES(sp) + SREG x28, 28*REGBYTES(sp) + SREG x29, 29*REGBYTES(sp) + SREG x30, 30*REGBYTES(sp) + SREG x31, 31*REGBYTES(sp) + csrr a0, mcause + csrr a1, mepc + mv a2, sp + jal handle_trap + csrw mepc, a0 + +/* + * Remain in M-mode after mret + */ + li t0, MSTATUS_MPP + csrs mstatus, t0 + LREG x1, 1*REGBYTES(sp) + LREG x2, 2*REGBYTES(sp) + LREG x3, 3*REGBYTES(sp) + LREG x4, 4*REGBYTES(sp) + LREG x5, 5*REGBYTES(sp) + LREG x6, 6*REGBYTES(sp) + LREG x7, 7*REGBYTES(sp) + LREG x8, 8*REGBYTES(sp) + LREG x9, 9*REGBYTES(sp) + LREG x10, 10*REGBYTES(sp) + LREG x11, 11*REGBYTES(sp) + LREG x12, 12*REGBYTES(sp) + LREG x13, 13*REGBYTES(sp) + LREG x14, 14*REGBYTES(sp) + LREG x15, 15*REGBYTES(sp) + LREG x16, 16*REGBYTES(sp) + LREG x17, 17*REGBYTES(sp) + LREG x18, 18*REGBYTES(sp) + LREG x19, 19*REGBYTES(sp) + LREG x20, 20*REGBYTES(sp) + LREG x21, 21*REGBYTES(sp) + LREG x22, 22*REGBYTES(sp) + LREG x23, 23*REGBYTES(sp) + LREG x24, 24*REGBYTES(sp) + LREG x25, 25*REGBYTES(sp) + LREG x26, 26*REGBYTES(sp) + LREG x27, 27*REGBYTES(sp) + LREG x28, 28*REGBYTES(sp) + LREG x29, 29*REGBYTES(sp) + LREG x30, 30*REGBYTES(sp) + LREG x31, 31*REGBYTES(sp) + addi sp, sp, 32*REGBYTES + mret + +#ifdef CONFIG_INIT_CRITICAL +cpu_init_crit: + ret +#endif diff --git a/arch/riscv/cpu/nx25/u-boot.lds b/arch/riscv/cpu/nx25/u-boot.lds new file mode 100644 index 0000000000..936fd779aa --- /dev/null +++ b/arch/riscv/cpu/nx25/u-boot.lds @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +OUTPUT_ARCH("riscv") +ENTRY(_start) + +SECTIONS +{ + . = ALIGN(4); + .text : + { + arch/riscv/cpu/nx25/start.o (.text) + *(.text) + } + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(4); + .data : { + __global_pointer$ = . + 0x800; + *(.data*) + } + . = ALIGN(4); + + .got : { + __got_start = .; + *(.got.plt) *(.got) + __got_end = .; + } + + . = ALIGN(4); + + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(4); + + /DISCARD/ : { *(.rela.plt*) } + .rela.dyn : { + __rel_dyn_start = .; + *(.rela*) + __rel_dyn_end = .; + } + + . = ALIGN(4); + + .dynsym : { + __dyn_sym_start = .; + *(.dynsym) + __dyn_sym_end = .; + } + + . = ALIGN(4); + + _end = .; + + .bss : { + __bss_start = .; + *(.bss) + . = ALIGN(4); + __bss_end = .; + } + +} diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile new file mode 100644 index 0000000000..718b99f694 --- /dev/null +++ b/arch/riscv/dts/Makefile @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +dtb-$(CONFIG_TARGET_NX25_AE250) += ae250.dtb +targets += $(dtb-y) + +DTC_FLAGS += -R 4 -p 0x1000 + +PHONY += dtbs +dtbs: $(addprefix $(obj)/, $(dtb-y)) + @: + +clean-files := *.dtb diff --git a/arch/riscv/dts/ae250.dts b/arch/riscv/dts/ae250.dts new file mode 100644 index 0000000000..5dc4fb04be --- /dev/null +++ b/arch/riscv/dts/ae250.dts @@ -0,0 +1,96 @@ +/dts-v1/; +/ { + compatible = "riscv32 nx25"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + + aliases { + uart0 = &serial0; + ethernet0 = &mac0; + spi0 = &spi; + } ; + + chosen { + bootargs = "console=ttyS0,38400n8 earlyprintk=uart8250-32bit,0xf0300000 debug loglevel=7"; + stdout-path = "uart0:38400n8"; + tick-timer = &timer0; + }; + + memory@0 { + device_type = "memory"; + reg = <0x00000000 0x40000000>; + }; + + spiclk: virt_100mhz { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <100000000>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + cpu@0 { + compatible = "andestech,n13"; + reg = <0>; + /* FIXME: to fill correct frqeuency */ + clock-frequency = <60000000>; + }; + }; + + intc: interrupt-controller { + compatible = "andestech,atnointc010"; + #interrupt-cells = <1>; + interrupt-controller; + }; + + serial0: serial@f0300000 { + compatible = "andestech,uart16550", "ns16550a"; + reg = <0xf0300000 0x1000>; + interrupts = <7 4>; + clock-frequency = <19660800>; + reg-shift = <2>; + reg-offset = <32>; + no-loopback-test = <1>; + }; + + timer0: timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0xf0400000 0x1000>; + interrupts = <2 4>; + clock-frequency = <40000000>; + }; + + mac0: mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0xe0100000 0x1000>; + interrupts = <25 4>; + }; + + mmc0: mmc@f0e00000 { + compatible = "andestech,atsdc010"; + max-frequency = <100000000>; + fifo-depth = <0x10>; + reg = <0xf0e00000 0x1000>; + interrupts = <17 4>; + }; + + spi: spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0xf0b00000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + num-cs = <1>; + clocks = <&spiclk>; + interrupts = <3 4>; + flash@0 { + compatible = "spi-flash"; + spi-max-frequency = <50000000>; + reg = <0>; + spi-cpol; + spi-cpha; + }; + }; + +}; diff --git a/arch/riscv/include/asm/bitops.h b/arch/riscv/include/asm/bitops.h new file mode 100644 index 0000000000..55d420fdfb --- /dev/null +++ b/arch/riscv/include/asm/bitops.h @@ -0,0 +1,172 @@ +/* + * Copyright 1995, Russell King. + * Various bits and pieces copyrights include: + * Linus Torvalds (test_bit). + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + * + * Please note that the code in this file should never be included + * from user space. Many of these are not implemented in assembler + * since they would be too costly. Also, they require priviledged + * instructions (which are not available from user mode) to ensure + * that they are atomic. + */ + +#ifndef __ASM_RISCV_BITOPS_H +#define __ASM_RISCV_BITOPS_H + +#ifdef __KERNEL__ + +#include <asm/system.h> +#include <asm-generic/bitops/fls.h> +#include <asm-generic/bitops/__fls.h> +#include <asm-generic/bitops/fls64.h> +#include <asm-generic/bitops/__ffs.h> + +#define smp_mb__before_clear_bit() do { } while (0) +#define smp_mb__after_clear_bit() do { } while (0) + +/* + * Function prototypes to keep gcc -Wall happy. + */ +static inline void __set_bit(int nr, void *addr) +{ + int *a = (int *)addr; + int mask; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a |= mask; +} + +static inline void __clear_bit(int nr, void *addr) +{ + int *a = (int *)addr; + int mask; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + *a &= ~mask; +} + +static inline void __change_bit(int nr, void *addr) +{ + int mask; + unsigned long *ADDR = (unsigned long *)addr; + + ADDR += nr >> 5; + mask = 1 << (nr & 31); + *ADDR ^= mask; +} + +static inline int __test_and_set_bit(int nr, void *addr) +{ + int mask, retval; + unsigned int *a = (unsigned int *)addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *a) != 0; + *a |= mask; + return retval; +} + +static inline int __test_and_clear_bit(int nr, void *addr) +{ + int mask, retval; + unsigned int *a = (unsigned int *)addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *a) != 0; + *a &= ~mask; + return retval; +} + +static inline int __test_and_change_bit(int nr, void *addr) +{ + int mask, retval; + unsigned int *a = (unsigned int *)addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + retval = (mask & *a) != 0; + *a ^= mask; + return retval; +} + +/* + * This routine doesn't need to be atomic. + */ +static inline int test_bit(int nr, const void *addr) +{ + return ((unsigned char *)addr)[nr >> 3] & (1U << (nr & 7)); +} + +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +static inline unsigned long ffz(unsigned long word) +{ + int k; + + word = ~word; + k = 31; + if (word & 0x0000ffff) { + k -= 16; word <<= 16; + } + if (word & 0x00ff0000) { + k -= 8; word <<= 8; + } + if (word & 0x0f000000) { + k -= 4; word <<= 4; + } + if (word & 0x30000000) { + k -= 2; word <<= 2; + } + if (word & 0x40000000) + k -= 1; + + return k; +} + +/* + * ffs: find first bit set. This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ + +/* + * redefined in include/linux/bitops.h + * #define ffs(x) generic_ffs(x) + */ + +/* + * hweightN: returns the hamming weight (i.e. the number + * of bits set) of a N-bit word + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +#define ext2_set_bit test_and_set_bit +#define ext2_clear_bit test_and_clear_bit +#define ext2_test_bit test_bit +#define ext2_find_first_zero_bit find_first_zero_bit +#define ext2_find_next_zero_bit find_next_zero_bit + +/* Bitmap functions for the minix filesystem. */ +#define minix_test_and_set_bit(nr, addr) test_and_set_bit(nr, addr) +#define minix_set_bit(nr, addr) set_bit(nr, addr) +#define minix_test_and_clear_bit(nr, addr) test_and_clear_bit(nr, addr) +#define minix_test_bit(nr, addr) test_bit(nr, addr) +#define minix_find_first_zero_bit(addr, size) find_first_zero_bit(addr, size) + +#endif /* __KERNEL__ */ + +#endif /* __ASM_RISCV_BITOPS_H */ diff --git a/arch/riscv/include/asm/bootm.h b/arch/riscv/include/asm/bootm.h new file mode 100644 index 0000000000..0a644bb58b --- /dev/null +++ b/arch/riscv/include/asm/bootm.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * Copyright (C) 2011 + * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef NDS32_BOOTM_H +#define NDS32_BOOTM_H + +#include <asm/setup.h> + +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) +# define BOOTM_ENABLE_TAGS 1 +#else +# define BOOTM_ENABLE_TAGS 0 +#endif + +#ifdef CONFIG_SETUP_MEMORY_TAGS +# define BOOTM_ENABLE_MEMORY_TAGS 1 +#else +# define BOOTM_ENABLE_MEMORY_TAGS 0 +#endif + +#ifdef CONFIG_CMDLINE_TAG + #define BOOTM_ENABLE_CMDLINE_TAG 1 +#else + #define BOOTM_ENABLE_CMDLINE_TAG 0 +#endif + +#ifdef CONFIG_INITRD_TAG + #define BOOTM_ENABLE_INITRD_TAG 1 +#else + #define BOOTM_ENABLE_INITRD_TAG 0 +#endif + +#ifdef CONFIG_SERIAL_TAG + #define BOOTM_ENABLE_SERIAL_TAG 1 +void get_board_serial(struct tag_serialnr *serialnr); +#else + #define BOOTM_ENABLE_SERIAL_TAG 0 +static inline void get_board_serial(struct tag_serialnr *serialnr) +{ +} +#endif + +#ifdef CONFIG_REVISION_TAG + #define BOOTM_ENABLE_REVISION_TAG 1 +u32 get_board_rev(void); +#else + #define BOOTM_ENABLE_REVISION_TAG 0 +static inline u32 get_board_rev(void) +{ + return 0; +} +#endif + +#endif diff --git a/arch/riscv/include/asm/byteorder.h b/arch/riscv/include/asm/byteorder.h new file mode 100644 index 0000000000..d26ac5688f --- /dev/null +++ b/arch/riscv/include/asm/byteorder.h @@ -0,0 +1,35 @@ +/* + * linux/include/asm-arm/byteorder.h + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * ARM Endian-ness. In little endian mode, the data bus is connected such + * that byte accesses appear as: + * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 + * and word accesses (data or instruction) appear as: + * d0...d31 + * + * When in big endian mode, byte accesses appear as: + * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7 + * and word accesses (data or instruction) appear as: + * d0...d31 + */ + +#ifndef __ASM_RISCV_BYTEORDER_H +#define __ASM_RISCV_BYTEORDER_H + +#include <asm/types.h> + +#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) +# define __BYTEORDER_HAS_U64__ +# define __SWAB_64_THRU_32__ +#endif + +#ifdef __RISCVEB__ +#include <linux/byteorder/big_endian.h> +#else +#include <linux/byteorder/little_endian.h> +#endif + +#endif diff --git a/arch/riscv/include/asm/cache.h b/arch/riscv/include/asm/cache.h new file mode 100644 index 0000000000..facf0720aa --- /dev/null +++ b/arch/riscv/include/asm/cache.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_RISCV_CACHE_H +#define _ASM_RISCV_CACHE_H + +/* + * The current upper bound for RISCV L1 data cache line sizes is 32 bytes. + * We use that value for aligning DMA buffers unless the board config has + * specified an alternate cache line size. + */ +#ifdef CONFIG_SYS_CACHELINE_SIZE +#define ARCH_DMA_MINALIGN CONFIG_SYS_CACHELINE_SIZE +#else +#define ARCH_DMA_MINALIGN 32 +#endif + +#endif /* _ASM_RISCV_CACHE_H */ diff --git a/arch/riscv/include/asm/config.h b/arch/riscv/include/asm/config.h new file mode 100644 index 0000000000..5f94eb02e2 --- /dev/null +++ b/arch/riscv/include/asm/config.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_CONFIG_H_ +#define _ASM_CONFIG_H_ + +#define CONFIG_LMB + +#endif diff --git a/arch/riscv/include/asm/encoding.h b/arch/riscv/include/asm/encoding.h new file mode 100644 index 0000000000..5ff6d59149 --- /dev/null +++ b/arch/riscv/include/asm/encoding.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017 Microsemi Corporation. + * Padmarao Begari, Microsemi Corporation <padmarao.begari@microsemi.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef RISCV_CSR_ENCODING_H +#define RISCV_CSR_ENCODING_H + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_PUM 0x00040000 +#define MSTATUS_VM 0x1F000000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define MCAUSE32_CAUSE 0x7FFFFFFF +#define MCAUSE64_CAUSE 0x7FFFFFFFFFFFFFFF +#define MCAUSE32_INT 0x80000000 +#define MCAUSE64_INT 0x8000000000000000 + +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_PUM 0x00040000 +#define SSTATUS32_SD 0x80000000 +#define SSTATUS64_SD 0x8000000000000000 + +#define MIP_SSIP BIT(IRQ_S_SOFT) +#define MIP_HSIP BIT(IRQ_H_SOFT) +#define MIP_MSIP BIT(IRQ_M_SOFT) +#define MIP_STIP BIT(IRQ_S_TIMER) +#define MIP_HTIP BIT(IRQ_H_TIMER) +#define MIP_MTIP BIT(IRQ_M_TIMER) +#define MIP_SEIP BIT(IRQ_S_EXT) +#define MIP_HEIP BIT(IRQ_H_EXT) +#define MIP_MEIP BIT(IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +#define VM_MBARE 0 +#define VM_MBB 1 +#define VM_MBBID 2 +#define VM_SV32 8 +#define VM_SV39 9 +#define VM_SV48 10 + +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +#define DEFAULT_RSTVEC 0x00001000 +#define DEFAULT_NMIVEC 0x00001004 +#define DEFAULT_MTVEC 0x00001010 +#define CONFIG_STRING_ADDR 0x0000100C +#define EXT_IO_BASE 0x40000000 +#define DRAM_BASE 0x80000000 + +// page table entry (PTE) fields +#define PTE_V 0x001 // Valid +#define PTE_TYPE 0x01E // Type +#define PTE_R 0x020 // Referenced +#define PTE_D 0x040 // Dirty +#define PTE_SOFT 0x380 // Reserved for Software + +#define PTE_TYPE_TABLE 0x00 +#define PTE_TYPE_TABLE_GLOBAL 0x02 +#define PTE_TYPE_URX_SR 0x04 +#define PTE_TYPE_URWX_SRW 0x06 +#define PTE_TYPE_UR_SR 0x08 +#define PTE_TYPE_URW_SRW 0x0A +#define PTE_TYPE_URX_SRX 0x0C +#define PTE_TYPE_URWX_SRWX0x0E +#define PTE_TYPE_SR 0x10 +#define PTE_TYPE_SRW 0x12 +#define PTE_TYPE_SRX 0x14 +#define PTE_TYPE_SRWX 0x16 +#define PTE_TYPE_SR_GLOBAL 0x18 +#define PTE_TYPE_SRW_GLOBAL 0x1A +#define PTE_TYPE_SRX_GLOBAL 0x1C +#define PTE_TYPE_SRWX_GLOBAL 0x1E + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) ((0x0000000AU >> ((PTE) & 0x1F)) & 1) +#define PTE_UR(PTE) ((0x0000AAA0U >> ((PTE) & 0x1F)) & 1) +#define PTE_UW(PTE) ((0x00008880U >> ((PTE) & 0x1F)) & 1) +#define PTE_UX(PTE) ((0x0000A0A0U >> ((PTE) & 0x1F)) & 1) +#define PTE_SR(PTE) ((0xAAAAAAA0U >> ((PTE) & 0x1F)) & 1) +#define PTE_SW(PTE) ((0x88888880U >> ((PTE) & 0x1F)) & 1) +#define PTE_SX(PTE) ((0xA0A0A000U >> ((PTE) & 0x1F)) & 1) + +#define PTE_CHECK_PERM(PTE, SUPERVISOR, STORE, FETCH) \ + ((STORE) ? ((SUPERVISOR) ? PTE_SW(PTE) : PTE_UW(PTE)) : \ + (FETCH) ? ((SUPERVISOR) ? PTE_SX(PTE) : PTE_UX(PTE)) : \ + ((SUPERVISOR) ? PTE_SR(PTE) : PTE_UR(PTE))) + +#ifdef __riscv +#ifdef CONFIG_64BIT +# define MSTATUS_SD MSTATUS64_SD +# define SSTATUS_SD SSTATUS64_SD +# define MCAUSE_INT MCAUSE64_INT +# define MCAUSE_CAUSE MCAUSE64_CAUSE +# define RISCV_PGLEVEL_BITS 9 +#else +# define MSTATUS_SD MSTATUS32_SD +# define SSTATUS_SD SSTATUS32_SD +# define RISCV_PGLEVEL_BITS 10 +# define MCAUSE_INT MCAUSE32_INT +# define MCAUSE_CAUSE MCAUSE32_CAUSE +#endif +#define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE BIT(RISCV_PGSHIFT) + +#ifndef __ASSEMBLER__ + +#ifdef __GNUC__ + +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + +#define write_csr(reg, val) ({ \ +if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + asm volatile ("csrw " #reg ", %0" :: "i"(val)); \ +else \ + asm volatile ("csrw " #reg ", %0" :: "r"(val)); }) + +#define swap_csr(reg, val) ({ unsigned long __tmp; \ +if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "i"(val)); \ +else \ + asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "r"(val)); \ + __tmp; }) + +#define set_csr(reg, bit) ({ unsigned long __tmp; \ +if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ +else \ + asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + __tmp; }) + +#define clear_csr(reg, bit) ({ unsigned long __tmp; \ +if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "i"(bit)); \ +else \ + asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "r"(bit)); \ + __tmp; }) + +#define rdtime() read_csr(time) +#define rdcycle() read_csr(cycle) +#define rdinstret() read_csr(instret) + +#endif +#endif +#endif +#endif diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h new file mode 100644 index 0000000000..0cce98ab53 --- /dev/null +++ b/arch/riscv/include/asm/global_data.h @@ -0,0 +1,22 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Copyright (c) 2017 Microsemi Corporation. + * Padmarao Begari, Microsemi Corporation <padmarao.begari@microsemi.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_GBL_DATA_H +#define __ASM_GBL_DATA_H + +/* Architecture-specific global data */ +struct arch_global_data { +}; + +#include <asm-generic/global_data.h> + +#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("gp") + +#endif /* __ASM_GBL_DATA_H */ diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h new file mode 100644 index 0000000000..e7f63ed8a9 --- /dev/null +++ b/arch/riscv/include/asm/io.h @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0 + * + */ +#ifndef __ASM_RISCV_IO_H +#define __ASM_RISCV_IO_H + +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <asm/byteorder.h> + +static inline void sync(void) +{ +} + +/* + * Given a physical address and a length, return a virtual address + * that can be used to access the memory range with the caching + * properties specified by "flags". + */ +#define MAP_NOCACHE (0) +#define MAP_WRCOMBINE (0) +#define MAP_WRBACK (0) +#define MAP_WRTHROUGH (0) + +#ifdef CONFIG_ARCH_MAP_SYSMEM +static inline void *map_sysmem(phys_addr_t paddr, unsigned long len) +{ + if (paddr < PHYS_SDRAM_0_SIZE + PHYS_SDRAM_1_SIZE) + paddr = paddr | 0x40000000; + return (void *)(uintptr_t)paddr; +} + +static inline void *unmap_sysmem(const void *vaddr) +{ + phys_addr_t paddr = (phys_addr_t)vaddr; + + paddr = paddr & ~0x40000000; + return (void *)(uintptr_t)paddr; +} + +static inline phys_addr_t map_to_sysmem(const void *ptr) +{ + return (phys_addr_t)(uintptr_t)ptr; +} +#endif + +static inline void * +map_physmem(phys_addr_t paddr, unsigned long len, unsigned long flags) +{ + return (void *)paddr; +} + +/* + * Take down a mapping set up by map_physmem(). + */ +static inline void unmap_physmem(void *vaddr, unsigned long flags) +{ +} + +static inline phys_addr_t virt_to_phys(void *vaddr) +{ + return (phys_addr_t)(vaddr); +} + +/* + * Generic virtual read/write. Note that we don't support half-word + * read/writes. We define __arch_*[bl] here, and leave __arch_*w + * to the architecture specific code. + */ +#define __arch_getb(a) (*(unsigned char *)(a)) +#define __arch_getw(a) (*(unsigned short *)(a)) +#define __arch_getl(a) (*(unsigned int *)(a)) +#define __arch_getq(a) (*(unsigned long *)(a)) + +#define __arch_putb(v, a) (*(unsigned char *)(a) = (v)) +#define __arch_putw(v, a) (*(unsigned short *)(a) = (v)) +#define __arch_putl(v, a) (*(unsigned int *)(a) = (v)) +#define __arch_putq(v, a) (*(unsigned long *)(a) = (v)) + +#define __raw_writeb(v, a) __arch_putb(v, a) +#define __raw_writew(v, a) __arch_putw(v, a) +#define __raw_writel(v, a) __arch_putl(v, a) +#define __raw_writeq(v, a) __arch_putq(v, a) + +#define __raw_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(a) +#define __raw_readq(a) __arch_getq(a) + +/* + * TODO: The kernel offers some more advanced versions of barriers, it might + * have some advantages to use them instead of the simple one here. + */ +#define dmb() __asm__ __volatile__ ("" : : : "memory") +#define __iormb() dmb() +#define __iowmb() dmb() + +static inline void writeb(u8 val, volatile void __iomem *addr) +{ + __iowmb(); + __arch_putb(val, addr); +} + +static inline void writew(u16 val, volatile void __iomem *addr) +{ + __iowmb(); + __arch_putw(val, addr); +} + +static inline void writel(u32 val, volatile void __iomem *addr) +{ + __iowmb(); + __arch_putl(val, addr); +} + +static inline void writeq(u64 val, volatile void __iomem *addr) +{ + __iowmb(); + __arch_putq(val, addr); +} + +static inline u8 readb(const volatile void __iomem *addr) +{ + u8 val; + + val = __arch_getb(addr); + __iormb(); + return val; +} + +static inline u16 readw(const volatile void __iomem *addr) +{ + u16 val; + + val = __arch_getw(addr); + __iormb(); + return val; +} + +static inline u32 readl(const volatile void __iomem *addr) +{ + u32 val; + + val = __arch_getl(addr); + __iormb(); + return val; +} + +static inline u64 readq(const volatile void __iomem *addr) +{ + u32 val; + + val = __arch_getq(addr); + __iormb(); + return val; +} + +/* + * The compiler seems to be incapable of optimising constants + * properly. Spell it out to the compiler in some cases. + * These are only valid for small values of "off" (< 1<<12) + */ +#define __raw_base_writeb(val, base, off) __arch_base_putb(val, base, off) +#define __raw_base_writew(val, base, off) __arch_base_putw(val, base, off) +#define __raw_base_writel(val, base, off) __arch_base_putl(val, base, off) + +#define __raw_base_readb(base, off) __arch_base_getb(base, off) +#define __raw_base_readw(base, off) __arch_base_getw(base, off) +#define __raw_base_readl(base, off) __arch_base_getl(base, off) + +#define out_arch(type, endian, a, v) __raw_write##type(cpu_to_##endian(v), a) +#define in_arch(type, endian, a) endian##_to_cpu(__raw_read##type(a)) + +#define out_le32(a, v) out_arch(l, le32, a, v) +#define out_le16(a, v) out_arch(w, le16, a, v) + +#define in_le32(a) in_arch(l, le32, a) +#define in_le16(a) in_arch(w, le16, a) + +#define out_be32(a, v) out_arch(l, be32, a, v) +#define out_be16(a, v) out_arch(w, be16, a, v) + +#define in_be32(a) in_arch(l, be32, a) +#define in_be16(a) in_arch(w, be16, a) + +#define out_8(a, v) __raw_writeb(v, a) +#define in_8(a) __raw_readb(a) + +/* + * Clear and set bits in one shot. These macros can be used to clear and + * set multiple bits in a register using a single call. These macros can + * also be used to set a multiple-bit bit pattern using a mask, by + * specifying the mask in the 'clear' parameter and the new bit pattern + * in the 'set' parameter. + */ + +#define clrbits(type, addr, clear) \ + out_##type((addr), in_##type(addr) & ~(clear)) + +#define setbits(type, addr, set) \ + out_##type((addr), in_##type(addr) | (set)) + +#define clrsetbits(type, addr, clear, set) \ + out_##type((addr), (in_##type(addr) & ~(clear)) | (set)) + +#define clrbits_be32(addr, clear) clrbits(be32, addr, clear) +#define setbits_be32(addr, set) setbits(be32, addr, set) +#define clrsetbits_be32(addr, clear, set) clrsetbits(be32, addr, clear, set) + +#define clrbits_le32(addr, clear) clrbits(le32, addr, clear) +#define setbits_le32(addr, set) setbits(le32, addr, set) +#define clrsetbits_le32(addr, clear, set) clrsetbits(le32, addr, clear, set) + +#define clrbits_be16(addr, clear) clrbits(be16, addr, clear) +#define setbits_be16(addr, set) setbits(be16, addr, set) +#define clrsetbits_be16(addr, clear, set) clrsetbits(be16, addr, clear, set) + +#define clrbits_le16(addr, clear) clrbits(le16, addr, clear) +#define setbits_le16(addr, set) setbits(le16, addr, set) +#define clrsetbits_le16(addr, clear, set) clrsetbits(le16, addr, clear, set) + +#define clrbits_8(addr, clear) clrbits(8, addr, clear) +#define setbits_8(addr, set) setbits(8, addr, set) +#define clrsetbits_8(addr, clear, set) clrsetbits(8, addr, clear, set) + +/* + * Now, pick up the machine-defined IO definitions + * #include <asm/arch/io.h> + */ + +/* + * IO port access primitives + * ------------------------- + * + * The NDS32 doesn't have special IO access instructions just like ARM; + * all IO is memory mapped. + * Note that these are defined to perform little endian accesses + * only. Their primary purpose is to access PCI and ISA peripherals. + * + * Note that for a big endian machine, this implies that the following + * big endian mode connectivity is in place, as described by numerious + * ARM documents: + * + * PCI: D0-D7 D8-D15 D16-D23 D24-D31 + * ARM: D24-D31 D16-D23 D8-D15 D0-D7 + * + * The machine specific io.h include defines __io to translate an "IO" + * address to a memory address. + * + * Note that we prevent GCC re-ordering or caching values in expressions + * by introducing sequence points into the in*() definitions. Note that + * __raw_* do not guarantee this behaviour. + * + * The {in,out}[bwl] macros are for emulating x86-style PCI/ISA IO space. + */ +#ifdef __io +#define outb(v, p) __raw_writeb(v, __io(p)) +#define outw(v, p) __raw_writew(cpu_to_le16(v), __io(p)) +#define outl(v, p) __raw_writel(cpu_to_le32(v), __io(p)) + +#define inb(p) ({ unsigned int __v = __raw_readb(__io(p)); __v; }) +#define inw(p) ({ unsigned int __v = le16_to_cpu(__raw_readw(__io(p))); __v; }) +#define inl(p) ({ unsigned int __v = le32_to_cpu(__raw_readl(__io(p))); __v; }) + +#define outsb(p, d, l) writesb(__io(p), d, l) +#define outsw(p, d, l) writesw(__io(p), d, l) +#define outsl(p, d, l) writesl(__io(p), d, l) + +#define insb(p, d, l) readsb(__io(p), d, l) +#define insw(p, d, l) readsw(__io(p), d, l) +#define insl(p, d, l) readsl(__io(p), d, l) + +static inline void readsb(unsigned int *addr, void *data, int bytelen) +{ + unsigned char *ptr; + unsigned char *ptr2; + + ptr = (unsigned char *)addr; + ptr2 = (unsigned char *)data; + + while (bytelen) { + *ptr2 = *ptr; + ptr2++; + bytelen--; + } +} + +static inline void readsw(unsigned int *addr, void *data, int wordlen) +{ + unsigned short *ptr; + unsigned short *ptr2; + + ptr = (unsigned short *)addr; + ptr2 = (unsigned short *)data; + + while (wordlen) { + *ptr2 = *ptr; + ptr2++; + wordlen--; + } +} + +static inline void readsl(unsigned int *addr, void *data, int longlen) +{ + unsigned int *ptr; + unsigned int *ptr2; + + ptr = (unsigned int *)addr; + ptr2 = (unsigned int *)data; + + while (longlen) { + *ptr2 = *ptr; + ptr2++; + longlen--; + } +} + +static inline void writesb(unsigned int *addr, const void *data, int bytelen) +{ + unsigned char *ptr; + unsigned char *ptr2; + + ptr = (unsigned char *)addr; + ptr2 = (unsigned char *)data; + + while (bytelen) { + *ptr = *ptr2; + ptr2++; + bytelen--; + } +} + +static inline void writesw(unsigned int *addr, const void *data, int wordlen) +{ + unsigned short *ptr; + unsigned short *ptr2; + + ptr = (unsigned short *)addr; + ptr2 = (unsigned short *)data; + + while (wordlen) { + *ptr = *ptr2; + ptr2++; + wordlen--; + } +} + +static inline void writesl(unsigned int *addr, const void *data, int longlen) +{ + unsigned int *ptr; + unsigned int *ptr2; + + ptr = (unsigned int *)addr; + ptr2 = (unsigned int *)data; + + while (longlen) { + *ptr = *ptr2; + ptr2++; + longlen--; + } +} +#endif + +#define outb_p(val, port) outb((val), (port)) +#define outw_p(val, port) outw((val), (port)) +#define outl_p(val, port) outl((val), (port)) +#define inb_p(port) inb((port)) +#define inw_p(port) inw((port)) +#define inl_p(port) inl((port)) + +#define outsb_p(port, from, len) outsb(port, from, len) +#define outsw_p(port, from, len) outsw(port, from, len) +#define outsl_p(port, from, len) outsl(port, from, len) +#define insb_p(port, to, len) insb(port, to, len) +#define insw_p(port, to, len) insw(port, to, len) +#define insl_p(port, to, len) insl(port, to, len) + +/* + * DMA-consistent mapping functions. These allocate/free a region of + * uncached, unwrite-buffered mapped memory space for use with DMA + * devices. This is the "generic" version. The PCI specific version + * is in pci.h + */ + +/* + * String version of IO memory access ops: + */ + +/* + * If this architecture has PCI memory IO, then define the read/write + * macros. These should only be used with the cookie passed from + * ioremap. + */ +#ifdef __mem_pci + +#define readb(c) ({ unsigned int __v = \ + __raw_readb(__mem_pci(c)); __v; }) +#define readw(c) ({ unsigned int __v = \ + le16_to_cpu(__raw_readw(__mem_pci(c))); __v; }) +#define readl(c) ({ unsigned int __v = \ + le32_to_cpu(__raw_readl(__mem_pci(c))); __v; }) + +#define writeb(v, c) __raw_writeb(v, __mem_pci(c)) +#define writew(v, c) __raw_writew(cpu_to_le16(v), __mem_pci(c)) +#define writel(v, c) __raw_writel(cpu_to_le32(v), __mem_pci(c)) + +#define memset_io(c, v, l) _memset_io(__mem_pci(c), (v), (l)) +#define memcpy_fromio(a, c, l) _memcpy_fromio((a), __mem_pci(c), (l)) +#define memcpy_toio(c, a, l) _memcpy_toio(__mem_pci(c), (a), (l)) + +#define eth_io_copy_and_sum(s, c, l, b) \ + eth_copy_and_sum((s), __mem_pci(c), (l), (b)) + +static inline int +check_signature(unsigned long io_addr, const unsigned char *signature, + int length) +{ + int retval = 0; + + do { + if (readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: + return retval; +} +#endif /* __mem_pci */ + +/* + * If this architecture has ISA IO, then define the isa_read/isa_write + * macros. + */ +#ifdef __mem_isa + +#define isa_readb(addr) __raw_readb(__mem_isa(addr)) +#define isa_readw(addr) __raw_readw(__mem_isa(addr)) +#define isa_readl(addr) __raw_readl(__mem_isa(addr)) +#define isa_writeb(val, addr) __raw_writeb(val, __mem_isa(addr)) +#define isa_writew(val, addr) __raw_writew(val, __mem_isa(addr)) +#define isa_writel(val, addr) __raw_writel(val, __mem_isa(addr)) +#define isa_memset_io(a, b, c) _memset_io(__mem_isa(a), (b), (c)) +#define isa_memcpy_fromio(a, b, c) _memcpy_fromio((a), __mem_isa(b), (c)) +#define isa_memcpy_toio(a, b, c) _memcpy_toio(__mem_isa((a)), (b), (c)) + +#define isa_eth_io_copy_and_sum(a, b, c, d) \ + eth_copy_and_sum((a), __mem_isa(b), (c), (d)) + +static inline int +isa_check_signature(unsigned long io_addr, const unsigned char *signature, + int length) +{ + int retval = 0; + + do { + if (isa_readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: + return retval; +} + +#else /* __mem_isa */ + +#define isa_readb(addr) (__readwrite_bug("isa_readb"), 0) +#define isa_readw(addr) (__readwrite_bug("isa_readw"), 0) +#define isa_readl(addr) (__readwrite_bug("isa_readl"), 0) +#define isa_writeb(val, addr) __readwrite_bug("isa_writeb") +#define isa_writew(val, addr) __readwrite_bug("isa_writew") +#define isa_writel(val, addr) __readwrite_bug("isa_writel") +#define isa_memset_io(a, b, c) __readwrite_bug("isa_memset_io") +#define isa_memcpy_fromio(a, b, c) __readwrite_bug("isa_memcpy_fromio") +#define isa_memcpy_toio(a, b, c) __readwrite_bug("isa_memcpy_toio") + +#define isa_eth_io_copy_and_sum(a, b, c, d) \ + __readwrite_bug("isa_eth_io_copy_and_sum") + +#define isa_check_signature(io, sig, len) (0) + +#endif /* __mem_isa */ +#endif /* __KERNEL__ */ +#endif /* __ASM_RISCV_IO_H */ diff --git a/arch/riscv/include/asm/linkage.h b/arch/riscv/include/asm/linkage.h new file mode 100644 index 0000000000..60d5317797 --- /dev/null +++ b/arch/riscv/include/asm/linkage.h @@ -0,0 +1,12 @@ +/* + * U-Boot - linkage.h + * + * Copyright (c) 2005-2007 Analog Devices Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +#endif diff --git a/arch/riscv/include/asm/mach-types.h b/arch/riscv/include/asm/mach-types.h new file mode 100644 index 0000000000..f70b407998 --- /dev/null +++ b/arch/riscv/include/asm/mach-types.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_RISCV_MACH_TYPE_H +#define __ASM_RISCV_MACH_TYPE_H + +#ifndef __ASSEMBLY__ +/* The type of machine we're running on */ +extern unsigned int __machine_arch_type; +#endif + +#define MACH_TYPE_AE250 1 + +#ifdef CONFIG_ARCH_AE250 +# ifdef machine_arch_type +# undef machine_arch_type +# define machine_arch_type __machine_arch_type +# else +# define machine_arch_type MACH_TYPE_AE250 +# endif +# define machine_is_ae250() (machine_arch_type == MACH_TYPE_AE250) +#else +# define machine_is_ae250() (1) +#endif + +#endif /* __ASM_RISCV_MACH_TYPE_H */ diff --git a/arch/riscv/include/asm/posix_types.h b/arch/riscv/include/asm/posix_types.h new file mode 100644 index 0000000000..6892b66814 --- /dev/null +++ b/arch/riscv/include/asm/posix_types.h @@ -0,0 +1,89 @@ +/* + * linux/include/asm-arm/posix_types.h + * + * Copyright (C) 1996-1998 Russell King. + * + * Copyright (C) 2011 Andes Technology Corporation + * Copyright (C) 2010 Shawn Lin (nobuhiro@andestech.com) + * Copyright (C) 2011 Macpaul Lin (macpaul@andestech.com) + * Copyright (C) 2017 Rick Chen (rick@andestech.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 27-06-1996 RMK Created + * 25-10-2017 Modified for arch RISCV + */ +#ifndef __ARCH_RISCV_POSIX_TYPES_H +#define __ARCH_RISCV_POSIX_TYPES_H + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +typedef unsigned short __kernel_dev_t; +typedef unsigned long __kernel_ino_t; +typedef unsigned short __kernel_mode_t; +typedef unsigned short __kernel_nlink_t; +typedef long __kernel_off_t; +typedef int __kernel_pid_t; +typedef unsigned short __kernel_ipc_pid_t; +typedef unsigned short __kernel_uid_t; +typedef unsigned short __kernel_gid_t; +#ifdef __GNUC__ +typedef __SIZE_TYPE__ __kernel_size_t; +#else +typedef unsigned int __kernel_size_t; +#endif +typedef int __kernel_ssize_t; +typedef int __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_daddr_t; +typedef char *__kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; + +#ifdef __GNUC__ +typedef long long __kernel_loff_t; +#endif + +typedef struct { +#if defined(__KERNEL__) || defined(__USE_ALL) + int val[2]; +#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */ + int __val[2]; +#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */ +} __kernel_fsid_t; + +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) + +#undef __FD_SET +#define __FD_SET(fd, fdsetp) \ + (((fd_set *)fdsetp)->fds_bits[fd >> 5] |= (1 << (fd & 31))) + +#undef __FD_CLR +#define __FD_CLR(fd, fdsetp) \ + (((fd_set *)fdsetp)->fds_bits[fd >> 5] &= ~(1 << (fd & 31))) + +#undef __FD_ISSET +#define __FD_ISSET(fd, fdsetp) \ + ((((fd_set *)fdsetp)->fds_bits[fd >> 5] & (1 << (fd & 31))) != 0) + +#undef __FD_ZERO +#define __FD_ZERO(fdsetp) \ + (memset(fdsetp, 0, sizeof(*(fd_set *)fdsetp))) + +#endif + +#endif /* __ARCH_RISCV_POSIX_TYPES_H */ diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h new file mode 100644 index 0000000000..fe3575298e --- /dev/null +++ b/arch/riscv/include/asm/processor.h @@ -0,0 +1,26 @@ +/* + * linux/include/asm-arm/processor.h + * + * Copyright (C) 1995-2002 Russell King + * + * Copyright (C) 2011 Andes Technology Corporation + * Copyright (C) 2010 Shawn Lin (nobuhiro@andestech.com) + * Copyright (C) 2011 Macpaul Lin (macpaul@andestech.com) + * Copyright (C) 2017 Rick Chen (rick@andestech.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_RISCV_PROCESSOR_H +#define __ASM_RISCV_PROCESSOR_H + +/************************************************************** + * CAUTION: + * - do not implement for RISCV Arch yet. + * - so far some files include /asm/processor.h, but + * no one uses the macros defined in this head file. + **************************************************************/ + +#endif /* __ASM_RISCV_PROCESSOR_H */ diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h new file mode 100644 index 0000000000..76d68698bb --- /dev/null +++ b/arch/riscv/include/asm/ptrace.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Microsemi Corporation. + * Copyright (c) 2017 Padmarao Begari <Padmarao.Begari@microsemi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_RISCV_PTRACE_H +#define __ASM_RISCV_PTRACE_H + +struct pt_regs { + unsigned long sepc; + unsigned long ra; + unsigned long sp; + unsigned long gp; + unsigned long tp; + unsigned long t0; + unsigned long t1; + unsigned long t2; + unsigned long s0; + unsigned long s1; + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + unsigned long s2; + unsigned long s3; + unsigned long s4; + unsigned long s5; + unsigned long s6; + unsigned long s7; + unsigned long s8; + unsigned long s9; + unsigned long s10; + unsigned long s11; + unsigned long t3; + unsigned long t4; + unsigned long t5; + unsigned long t6; + /* Supervisor CSRs */ + unsigned long sstatus; + unsigned long sbadaddr; + unsigned long scause; +}; + +#ifdef CONFIG_64BIT +#define REG_FMT "%016lx" +#else +#define REG_FMT "%08lx" +#endif + +#define user_mode(regs) (((regs)->sstatus & SR_PS) == 0) + +/* Helpers for working with the instruction pointer */ +#define GET_IP(regs) ((regs)->sepc) +#define SET_IP(regs, val) (GET_IP(regs) = (val)) + +static inline unsigned long instruction_pointer(struct pt_regs *regs) +{ + return GET_IP(regs); +} + +static inline void instruction_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + SET_IP(regs, val); +} + +#define profile_pc(regs) instruction_pointer(regs) + +/* Helpers for working with the user stack pointer */ +#define GET_USP(regs) ((regs)->sp) +#define SET_USP(regs, val) (GET_USP(regs) = (val)) + +static inline unsigned long user_stack_pointer(struct pt_regs *regs) +{ + return GET_USP(regs); +} + +static inline void user_stack_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + SET_USP(regs, val); +} + +/* Helpers for working with the frame pointer */ +#define GET_FP(regs) ((regs)->s0) +#define SET_FP(regs, val) (GET_FP(regs) = (val)) + +static inline unsigned long frame_pointer(struct pt_regs *regs) +{ + return GET_FP(regs); +} + +static inline void frame_pointer_set(struct pt_regs *regs, + unsigned long val) +{ + SET_FP(regs, val); +} + +#endif /* __ASM_RISCV_PTRACE_H */ diff --git a/arch/riscv/include/asm/sections.h b/arch/riscv/include/asm/sections.h new file mode 100644 index 0000000000..9faa099fb9 --- /dev/null +++ b/arch/riscv/include/asm/sections.h @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_RISCV_SECTIONS_H +#define __ASM_RISCV_SECTIONS_H + +#include <asm-generic/sections.h> + +#endif diff --git a/arch/riscv/include/asm/setup.h b/arch/riscv/include/asm/setup.h new file mode 100644 index 0000000000..731b0d96aa --- /dev/null +++ b/arch/riscv/include/asm/setup.h @@ -0,0 +1,191 @@ +/* + * linux/arch/nds32/include/asm/setup.h + * + * Copyright (C) 1997-1999 Russell King + * Copyright (C) 2008 Andes Technology Corporation + * Copyright (C) 2013 Ken Kuo (ken_kuo@andestech.com) + * Copyright (C) 2017 Rick Chen (rick@andestech.com) + * + * SPDX-License-Identifier: GPL-2.0 + * + * Structure passed to kernel to tell it about the + * hardware it's running on. See Documentation/arm/Setup + * for more info. + */ +#ifndef __RISCV_SETUP_H +#define __RISCV_SETUP_H + +#define COMMAND_LINE_SIZE 256 + +/* The list ends with an ATAG_NONE node. */ +#define ATAG_NONE 0x00000000 + +struct tag_header { + u32 size; + u32 tag; +}; + +/* The list must start with an ATAG_CORE node */ +#define ATAG_CORE 0x54410001 + +struct tag_core { + u32 flags; /* bit 0 = read-only */ + u32 pagesize; + u32 rootdev; +}; + +/* it is allowed to have multiple ATAG_MEM nodes */ +#define ATAG_MEM 0x54410002 + +struct tag_mem32 { + u32 size; + u32 start; /* physical start address */ +}; + +/* VGA text type displays */ +#define ATAG_VIDEOTEXT 0x54410003 + +struct tag_videotext { + u8 x; + u8 y; + u16 video_page; + u8 video_mode; + u8 video_cols; + u16 video_ega_bx; + u8 video_lines; + u8 video_isvga; + u16 video_points; +}; + +/* describes how the ramdisk will be used in kernel */ +#define ATAG_RAMDISK 0x54410004 + +struct tag_ramdisk { + u32 flags; /* bit 0 = load, bit 1 = prompt */ + u32 size; /* decompressed ramdisk size in _kilo_ bytes */ + u32 start; /* starting block of floppy-based RAM disk image */ +}; + +/* + * this one accidentally used virtual addresses - as such, + * it's deprecated. + * describes where the compressed ramdisk image lives (virtual address) + */ +#define ATAG_INITRD 0x54410005 + +/* describes where the compressed ramdisk image lives (physical address) */ +#define ATAG_INITRD2 0x54420005 + +struct tag_initrd { + u32 start; /* physical start address */ + u32 size; /* size of compressed ramdisk image in bytes */ +}; + +/* board serial number. "64 bits should be enough for everybody" */ +#define ATAG_SERIAL 0x54410006 + +struct tag_serialnr { + u32 low; + u32 high; +}; + +/* board revision */ +#define ATAG_REVISION 0x54410007 + +struct tag_revision { + u32 rev; +}; + +/* initial values for vesafb-type framebuffers. see struct screen_info + * in include/linux/tty.h + */ +#define ATAG_VIDEOLFB 0x54410008 + +struct tag_videolfb { + u16 lfb_width; + u16 lfb_height; + u16 lfb_depth; + u16 lfb_linelength; + u32 lfb_base; + u32 lfb_size; + u8 red_size; + u8 red_pos; + u8 green_size; + u8 green_pos; + u8 blue_size; + u8 blue_pos; + u8 rsvd_size; + u8 rsvd_pos; +}; + +/* command line: \0 terminated string */ +#define ATAG_CMDLINE 0x54410009 + +struct tag_cmdline { + char cmdline[COMMAND_LINE_SIZE]; +}; + +struct tag { + struct tag_header hdr; + union { + struct tag_core core; + struct tag_mem32 mem; + struct tag_videotext videotext; + struct tag_ramdisk ramdisk; + struct tag_initrd initrd; + struct tag_serialnr serialnr; + struct tag_revision revision; + struct tag_videolfb videolfb; + struct tag_cmdline cmdline; + } u; +}; + +struct tagtable { + u32 tag; + int (*parse)(const struct tag *); +}; + +#define tag_member_present(tag, member) \ + ((unsigned long)(&((struct tag *)0L)->member + 1) \ + <= (tag)->hdr.size * 4) + +#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) +#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) + +#define for_each_tag(t, base) \ + for (t = base; t->hdr.size; t = tag_next(t)) + +#ifdef __KERNEL__ + +#define __tag __used __attribute__((__section__(".taglist"))) +#define __tagtable(tag, fn) \ +static struct tagtable __tagtable_##fn __tag = { tag, fn } + +/* + * Memory map description + */ +#define NR_BANKS 8 + +struct meminfo { + int nr_banks; + struct { + unsigned long start; + unsigned long size; + int node; + } bank[NR_BANKS]; +}; + +/* + * Early command line parameters. + */ +struct early_params { + const char *arg; + void (*fn)(char **p); +}; + +#define __early_param(name, fn) \ +static struct early_params __early_##fn __used \ +__attribute__((__section__("__early_param"))) = { name, fn } + +#endif +#endif diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h new file mode 100644 index 0000000000..038cdaea72 --- /dev/null +++ b/arch/riscv/include/asm/string.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Andes Technology Corporation + * Copyright (C) 2010 Shawn Lin (nobuhiro@andestech.com) + * Copyright (C) 2011 Macpaul Lin (macpaul@andestech.com) + * Copyright (C) 2017 Rick Chen (rick@andestech.com) + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __ASM_RISCV_STRING_H +#define __ASM_RISCV_STRING_H + +/* + * We don't do inline string functions, since the + * optimised inline asm versions are not small. + */ + +#undef __HAVE_ARCH_STRRCHR +#undef __HAVE_ARCH_STRCHR +#undef __HAVE_ARCH_MEMCPY +#undef __HAVE_ARCH_MEMMOVE +#undef __HAVE_ARCH_MEMCHR +#undef __HAVE_ARCH_MEMZERO +#undef __HAVE_ARCH_MEMSET + +#ifdef CONFIG_MARCO_MEMSET +#define memset(p, v, n) \ + ({ \ + if ((n) != 0) { \ + if (__builtin_constant_p((v)) && (v) == 0) \ + __memzero((p), (n)); \ + else \ + memset((p), (v), (n)); \ + } \ + (p); \ + }) + +#define memzero(p, n) ({ if ((n) != 0) __memzero((p), (n)); (p); }) +#endif + +#endif /* __ASM_RISCV_STRING_H */ diff --git a/arch/riscv/include/asm/system.h b/arch/riscv/include/asm/system.h new file mode 100644 index 0000000000..443a3005d8 --- /dev/null +++ b/arch/riscv/include/asm/system.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ASM_RISCV_SYSTEM_H +#define __ASM_RISCV_SYSTEM_H + +/* + * Interrupt configuring macros. + * + * TODO + * + */ + +#endif /* __ASM_RISCV_SYSTEM_H */ diff --git a/arch/riscv/include/asm/types.h b/arch/riscv/include/asm/types.h new file mode 100644 index 0000000000..9797206e5e --- /dev/null +++ b/arch/riscv/include/asm/types.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 Andes Technology Corporation + * Copyright (C) 2010 Shawn Lin (nobuhiro@andestech.com) + * Copyright (C) 2011 Macpaul Lin (macpaul@andestech.com) + * Copyright (C) 2017 Rick Chen (rick@andestech.com) + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __ASM_RISCV_TYPES_H +#define __ASM_RISCV_TYPES_H + +typedef unsigned short umode_t; + +/* + * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the + * header files exported to user space + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif + +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +#ifdef __KERNEL__ + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +typedef signed long long s64; +typedef unsigned long long u64; + +#define BITS_PER_LONG 32 + +#include <stddef.h> + +typedef u32 dma_addr_t; + +typedef unsigned long phys_addr_t; +typedef unsigned long phys_size_t; + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/riscv/include/asm/u-boot-riscv.h b/arch/riscv/include/asm/u-boot-riscv.h new file mode 100644 index 0000000000..18099cd260 --- /dev/null +++ b/arch/riscv/include/asm/u-boot-riscv.h @@ -0,0 +1,21 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _U_BOOT_RISCV_H_ +#define _U_BOOT_RISCV_H_ 1 + +/* cpu/.../cpu.c */ +int cleanup_before_linux(void); + +/* board/.../... */ +int board_init(void); + +#endif /* _U_BOOT_RISCV_H_ */ diff --git a/arch/riscv/include/asm/u-boot.h b/arch/riscv/include/asm/u-boot.h new file mode 100644 index 0000000000..ddf7a6310f --- /dev/null +++ b/arch/riscv/include/asm/u-boot.h @@ -0,0 +1,46 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + ******************************************************************** + * NOTE: This header file defines an interface to U-Boot. Including + * this (unmodified) header file in another file is considered normal + * use of U-Boot, and does *not* fall under the heading of "derived + * work". + ******************************************************************** + */ + +#ifndef _U_BOOT_H_ +#define _U_BOOT_H_ 1 + +#include <asm/u-boot-riscv.h> + +#include <environment.h> + +typedef struct bd_info { + unsigned long bi_arch_number; /* unique id for this board */ + unsigned long bi_boot_params; /* where this board expects params */ + unsigned long bi_memstart; /* start of DRAM memory */ + unsigned long bi_memsize; /* size of DRAM memory in bytes */ + unsigned long bi_flashstart; /* start of FLASH memory */ + unsigned long bi_flashsize; /* size of FLASH memory */ + unsigned long bi_flashoffset; /* reserved area for startup monitor */ + unsigned char bi_enetaddr[6]; + + struct /* RAM configuration */ + { + unsigned long start; + unsigned long size; + } bi_dram[CONFIG_NR_DRAM_BANKS]; +} bd_t; + +/* For image.h:image_check_target_arch() */ +#define IH_ARCH_DEFAULT IH_ARCH_RISCV + +#endif /* _U_BOOT_H_ */ diff --git a/arch/riscv/include/asm/unaligned.h b/arch/riscv/include/asm/unaligned.h new file mode 100644 index 0000000000..6cecbbb211 --- /dev/null +++ b/arch/riscv/include/asm/unaligned.h @@ -0,0 +1 @@ +#include <asm-generic/unaligned.h> diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile new file mode 100644 index 0000000000..323cf3e835 --- /dev/null +++ b/arch/riscv/lib/Makefile @@ -0,0 +1,14 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# Copyright (C) 2017 Andes Technology Corporation +# Rick Chen, Andes Technology Corporation <rick@andestech.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CMD_BOOTM) += bootm.o +obj-$(CONFIG_CMD_GO) += boot.o +obj-y += cache.o +obj-y += interrupts.o diff --git a/arch/riscv/lib/boot.c b/arch/riscv/lib/boot.c new file mode 100644 index 0000000000..39ba9b49ff --- /dev/null +++ b/arch/riscv/lib/boot.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +DECLARE_GLOBAL_DATA_PTR; + +unsigned long do_go_exec(ulong (*entry)(int, char * const []), + int argc, char * const argv[]) +{ + cleanup_before_linux(); + + return entry(argc, argv); +} diff --git a/arch/riscv/lib/bootm.c b/arch/riscv/lib/bootm.c new file mode 100644 index 0000000000..44ce38b614 --- /dev/null +++ b/arch/riscv/lib/bootm.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2011 Andes Technology Corporation + * Shawn Lin, Andes Technology Corporation <nobuhiro@andestech.com> + * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com> + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <image.h> +#include <u-boot/zlib.h> +#include <asm/byteorder.h> +#include <asm/bootm.h> + +DECLARE_GLOBAL_DATA_PTR; + +int arch_fixup_fdt(void *blob) +{ + return 0; +} + +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) +static void setup_start_tag(bd_t *bd); + +# ifdef CONFIG_SETUP_MEMORY_TAGS +static void setup_memory_tags(bd_t *bd); +# endif +static void setup_commandline_tag(bd_t *bd, char *commandline); + +# ifdef CONFIG_INITRD_TAG +static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end); +# endif +static void setup_end_tag(bd_t *bd); + +static struct tag *params; +#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ + +int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) +{ + bd_t *bd = gd->bd; + char *s; + int machid = bd->bi_arch_number; + void (*theKernel)(int zero, int arch, uint params); + +#ifdef CONFIG_CMDLINE_TAG + char *commandline = env_get("bootargs"); +#endif + + /* + * allow the PREP bootm subcommand, it is required for bootm to work + */ + if (flag & BOOTM_STATE_OS_PREP) + return 0; + + if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) + return 1; + + theKernel = (void (*)(int, int, uint))images->ep; + + s = env_get("machid"); + if (s) { + machid = simple_strtoul(s, NULL, 16); + printf("Using machid 0x%x from environment\n", machid); + } + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + + debug("## Transferring control to Linux (at address %08lx) ...\n", + (ulong)theKernel); + + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { +#ifdef CONFIG_OF_LIBFDT + debug("using: FDT\n"); + if (image_setup_linux(images)) { + printf("FDT creation failed! hanging..."); + hang(); + } +#endif + } else if (BOOTM_ENABLE_TAGS) { +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) + setup_start_tag(bd); +#ifdef CONFIG_SERIAL_TAG + setup_serial_tag(¶ms); +#endif +#ifdef CONFIG_REVISION_TAG + setup_revision_tag(¶ms); +#endif +#ifdef CONFIG_SETUP_MEMORY_TAGS + setup_memory_tags(bd); +#endif +#ifdef CONFIG_CMDLINE_TAG + setup_commandline_tag(bd, commandline); +#endif +#ifdef CONFIG_INITRD_TAG + if (images->rd_start && images->rd_end) + setup_initrd_tag(bd, images->rd_start, images->rd_end); +#endif + setup_end_tag(bd); +#endif + + /* we assume that the kernel is in place */ + printf("\nStarting kernel ...\n\n"); + +#ifdef CONFIG_USB_DEVICE + { + extern void udc_disconnect(void); + udc_disconnect(); + } +#endif + } + cleanup_before_linux(); + if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) + theKernel(0, machid, (unsigned long)images->ft_addr); + else + theKernel(0, machid, bd->bi_boot_params); + /* does not return */ + + return 1; +} + +#if defined(CONFIG_SETUP_MEMORY_TAGS) || \ + defined(CONFIG_CMDLINE_TAG) || \ + defined(CONFIG_INITRD_TAG) || \ + defined(CONFIG_SERIAL_TAG) || \ + defined(CONFIG_REVISION_TAG) +static void setup_start_tag(bd_t *bd) +{ + params = (struct tag *)bd->bi_boot_params; + + params->hdr.tag = ATAG_CORE; + params->hdr.size = tag_size(tag_core); + + params->u.core.flags = 0; + params->u.core.pagesize = 0; + params->u.core.rootdev = 0; + + params = tag_next(params); +} + +#ifdef CONFIG_SETUP_MEMORY_TAGS +static void setup_memory_tags(bd_t *bd) +{ + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + params->hdr.tag = ATAG_MEM; + params->hdr.size = tag_size(tag_mem32); + + params->u.mem.start = bd->bi_dram[i].start; + params->u.mem.size = bd->bi_dram[i].size; + + params = tag_next(params); + } +} +#endif /* CONFIG_SETUP_MEMORY_TAGS */ + +static void setup_commandline_tag(bd_t *bd, char *commandline) +{ + char *p; + + if (!commandline) + return; + + /* eat leading white space */ + for (p = commandline; *p == ' '; p++) + ; + + /* skip non-existent command lines so the kernel will still + * use its default command line. + */ + if (*p == '\0') + return; + + params->hdr.tag = ATAG_CMDLINE; + params->hdr.size = + (sizeof(struct tag_header) + strlen(p) + 1 + 4) >> 2; + + strcpy(params->u.cmdline.cmdline, p) + ; + + params = tag_next(params); +} + +#ifdef CONFIG_INITRD_TAG +static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end) +{ + /* an ATAG_INITRD node tells the kernel where the compressed + * ramdisk can be found. ATAG_RDIMG is a better name, actually. + */ + params->hdr.tag = ATAG_INITRD2; + params->hdr.size = tag_size(tag_initrd); + + params->u.initrd.start = initrd_start; + params->u.initrd.size = initrd_end - initrd_start; + + params = tag_next(params); +} +#endif /* CONFIG_INITRD_TAG */ + +#ifdef CONFIG_SERIAL_TAG +void setup_serial_tag(struct tag **tmp) +{ + struct tag *params; + struct tag_serialnr serialnr; + void get_board_serial(struct tag_serialnr *serialnr); + + params = *tmp; + get_board_serial(&serialnr); + params->hdr.tag = ATAG_SERIAL; + params->hdr.size = tag_size(tag_serialnr); + params->u.serialnr.low = serialnr.low; + params->u.serialnr.high = serialnr.high; + params = tag_next(params); + *tmp = params; +} +#endif + +#ifdef CONFIG_REVISION_TAG +void setup_revision_tag(struct tag **in_params) +{ + u32 rev; + u32 get_board_rev(void); + + rev = get_board_rev(); + params->hdr.tag = ATAG_REVISION; + params->hdr.size = tag_size(tag_revision); + params->u.revision.rev = rev; + params = tag_next(params); +} +#endif /* CONFIG_REVISION_TAG */ + +static void setup_end_tag(bd_t *bd) +{ + params->hdr.tag = ATAG_NONE; + params->hdr.size = 0; +} + +#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ diff --git a/arch/riscv/lib/cache.c b/arch/riscv/lib/cache.c new file mode 100644 index 0000000000..948656fdf2 --- /dev/null +++ b/arch/riscv/lib/cache.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +void flush_dcache_range(unsigned long start, unsigned long end) +{ +} + +void invalidate_icache_range(unsigned long start, unsigned long end) +{ +} + +void invalidate_dcache_range(unsigned long start, unsigned long end) +{ +} + +void flush_cache(unsigned long addr, unsigned long size) +{ +} + +void icache_enable(void) +{ +} + +void icache_disable(void) +{ +} + +int icache_status(void) +{ + return 0; +} + +void dcache_enable(void) +{ +} + +void dcache_disable(void) +{ +} + +int dcache_status(void) +{ + return 0; +} diff --git a/arch/riscv/lib/interrupts.c b/arch/riscv/lib/interrupts.c new file mode 100644 index 0000000000..075db8ba46 --- /dev/null +++ b/arch/riscv/lib/interrupts.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016-17 Microsemi Corporation. + * Padmarao Begari, Microsemi Corporation <padmarao.begari@microsemi.com> + * + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/ptrace.h> +#include <asm/system.h> +#include <asm/encoding.h> + +static void _exit_trap(int code, uint epc, struct pt_regs *regs); + +int interrupt_init(void) +{ + return 0; +} + +/* + * enable interrupts + */ +void enable_interrupts(void) +{ +} + +/* + * disable interrupts + */ +int disable_interrupts(void) +{ + return 0; +} + +uint handle_trap(uint mcause, uint epc, struct pt_regs *regs) +{ + uint is_int; + + is_int = (mcause & MCAUSE_INT); + if ((is_int) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT)) + external_interrupt(0); /* handle_m_ext_interrupt */ + else if ((is_int) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER)) + timer_interrupt(0); /* handle_m_timer_interrupt */ + else + _exit_trap(mcause, epc, regs); + + return epc; +} + +/* + *Entry Point for PLIC Interrupt Handler + */ +__attribute__((weak)) void external_interrupt(struct pt_regs *regs) +{ +} + +__attribute__((weak)) void timer_interrupt(struct pt_regs *regs) +{ +} + +static void _exit_trap(int code, uint epc, struct pt_regs *regs) +{ + static const char *exception_code[] = { + "Instruction address misaligned", + "Instruction access fault", + "Illegal instruction", + "Breakpoint", + "Load address misaligned" + }; + + printf("exception code: %d , %s , epc %08x , ra %08lx\n", + code, exception_code[code], epc, regs->ra); +} diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index e67d428eb2..3f9e788e6c 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -478,6 +478,13 @@ wdt0: wdt@0 { compatible = "sandbox,wdt"; }; + + chosen { + chosen-test { + compatible = "denx,u-boot-fdt-test"; + reg = <9 1>; + }; + }; }; #include "sandbox_pmic.dtsi" diff --git a/board/AndesTech/nx25-ae250/Kconfig b/board/AndesTech/nx25-ae250/Kconfig new file mode 100644 index 0000000000..2fb3234bad --- /dev/null +++ b/board/AndesTech/nx25-ae250/Kconfig @@ -0,0 +1,24 @@ +if TARGET_NX25_AE250 + +config SYS_CPU + default "nx25" + +config SYS_BOARD + default "nx25-ae250" + +config SYS_VENDOR + default "AndesTech" + +config SYS_SOC + default "ae250" + +config SYS_CONFIG_NAME + default "nx25-ae250" + +config ENV_SIZE + default 0x2000 if ENV_IS_IN_SPI_FLASH + +config ENV_OFFSET + default 0x140000 if ENV_IS_IN_SPI_FLASH + +endif diff --git a/board/AndesTech/nx25-ae250/MAINTAINERS b/board/AndesTech/nx25-ae250/MAINTAINERS new file mode 100644 index 0000000000..1bff127c64 --- /dev/null +++ b/board/AndesTech/nx25-ae250/MAINTAINERS @@ -0,0 +1,6 @@ +NX25-AE250 BOARD +M: Rick Chen <rick@andestech.com> +S: Maintained +F: board/AndesTech/nx25-ae250/ +F: include/configs/nx25-ae250.h +F: configs/nx25-ae250_defconfig diff --git a/board/AndesTech/nx25-ae250/Makefile b/board/AndesTech/nx25-ae250/Makefile new file mode 100644 index 0000000000..66b681492c --- /dev/null +++ b/board/AndesTech/nx25-ae250/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) 2017 Andes Technology Corporation. +# Rick Chen, Andes Technology Corporation <rick@andestech.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := nx25-ae250.o diff --git a/board/AndesTech/nx25-ae250/nx25-ae250.c b/board/AndesTech/nx25-ae250/nx25-ae250.c new file mode 100644 index 0000000000..12f2d3520b --- /dev/null +++ b/board/AndesTech/nx25-ae250/nx25-ae250.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/mach-types.h> +#include <common.h> +#if defined(CONFIG_FTMAC100) && !defined(CONFIG_DM_ETH) +#include <netdev.h> +#endif +#include <linux/io.h> +#include <faraday/ftsdc010.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Miscellaneous platform dependent initializations + */ + +int board_init(void) +{ + gd->bd->bi_arch_number = MACH_TYPE_AE250; + gd->bd->bi_boot_params = PHYS_SDRAM_0 + 0x400; + + return 0; +} + +int dram_init(void) +{ + unsigned long sdram_base = PHYS_SDRAM_0; + unsigned long expected_size = PHYS_SDRAM_0_SIZE + PHYS_SDRAM_1_SIZE; + unsigned long actual_size; + + actual_size = get_ram_size((void *)sdram_base, expected_size); + gd->ram_size = actual_size; + + if (expected_size != actual_size) { + printf("Warning: Only %lu of %lu MiB SDRAM is working\n", + actual_size >> 20, expected_size >> 20); + } + + return 0; +} + +int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = PHYS_SDRAM_0; + gd->bd->bi_dram[0].size = PHYS_SDRAM_0_SIZE; + gd->bd->bi_dram[1].start = PHYS_SDRAM_1; + gd->bd->bi_dram[1].size = PHYS_SDRAM_1_SIZE; + + return 0; +} + +#if defined(CONFIG_FTMAC100) && !defined(CONFIG_DM_ETH) +int board_eth_init(bd_t *bd) +{ + return ftmac100_initialize(bd); +} +#endif + +ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info) +{ + return 0; +} + +int board_mmc_init(bd_t *bis) +{ +#ifndef CONFIG_DM_MMC +#ifdef CONFIG_FTSDC010 + ftsdc010_mmc_init(0); +#endif +#endif + return 0; +} diff --git a/board/qualcomm/dragonboard410c/Makefile b/board/qualcomm/dragonboard410c/Makefile index cd678088fa..5082383be4 100644 --- a/board/qualcomm/dragonboard410c/Makefile +++ b/board/qualcomm/dragonboard410c/Makefile @@ -5,4 +5,5 @@ # obj-y := dragonboard410c.o +obj-y += lowlevel_init.o extra-y += head.o diff --git a/board/qualcomm/dragonboard410c/dragonboard410c.c b/board/qualcomm/dragonboard410c/dragonboard410c.c index 848e27848b..9a600952a6 100644 --- a/board/qualcomm/dragonboard410c/dragonboard410c.c +++ b/board/qualcomm/dragonboard410c/dragonboard410c.c @@ -10,12 +10,27 @@ #include <dm.h> #include <usb.h> #include <asm/gpio.h> +#include <fdt_support.h> DECLARE_GLOBAL_DATA_PTR; +/* pointer to the device tree ammended by the firmware */ +extern void *fw_dtb; + +void *board_fdt_blob_setup(void) +{ + if (fdt_magic(fw_dtb) != FDT_MAGIC) { + printf("Firmware provided invalid dtb!\n"); + return NULL; + } + + return fw_dtb; +} + int dram_init(void) { gd->ram_size = PHYS_SDRAM_1_SIZE; + return 0; } @@ -27,7 +42,6 @@ int dram_init_banksize(void) return 0; } - int board_prepare_usb(enum usb_init_type type) { static struct udevice *pmic_gpio; @@ -96,11 +110,6 @@ int board_prepare_usb(enum usb_init_type type) return 0; } -int board_init(void) -{ - return 0; -} - /* Check for vol- button - if pressed - stop autoboot */ int misc_init_r(void) { @@ -134,3 +143,48 @@ int misc_init_r(void) return 0; } + +int board_init(void) +{ + return 0; +} + +int ft_board_setup(void *blob, bd_t *bd) +{ + int offset, len, i; + const char *mac; + struct { + const char *compatible; + const char *property; + } fix[] = { + [0] = { + /* update the kernel's dtb with wlan mac */ + .compatible = "qcom,wcnss-wlan", + .property = "local-mac-address", + }, + [1] = { + /* update the kernel's dtb with bt mac */ + .compatible = "qcom,wcnss-bt", + .property = "local-bd-address", + }, + }; + + for (i = 0; i < sizeof(fix) / sizeof(fix[0]); i++) { + offset = fdt_node_offset_by_compatible(gd->fdt_blob, -1, + fix[i].compatible); + if (offset < 0) + continue; + + mac = fdt_getprop(gd->fdt_blob, offset, fix[i].property, &len); + if (mac) + do_fixup_by_compat(blob, fix[i].compatible, + fix[i].property, mac, ARP_HLEN, 1); + } + + return 0; +} + +void reset_cpu(ulong addr) +{ + psci_system_reset(); +} diff --git a/board/qualcomm/dragonboard410c/lowlevel_init.S b/board/qualcomm/dragonboard410c/lowlevel_init.S new file mode 100644 index 0000000000..15b2d0c7fd --- /dev/null +++ b/board/qualcomm/dragonboard410c/lowlevel_init.S @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2016 + * Cédric Schieli <cschieli@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> + +.align 8 +.global fw_dtb +fw_dtb: + .dword 0x0 + +/* + * Routine: save_boot_params (called after reset from start.S) + * Description: save ATAG/FDT address provided by the firmware at boot time + */ + +.global save_boot_params +save_boot_params: + + /* The firmware provided ATAG/FDT address can be found in r2/x0 */ + adr x8, fw_dtb + str x0, [x8] + + /* Returns */ + b save_boot_params_ret diff --git a/board/qualcomm/dragonboard820c/Kconfig b/board/qualcomm/dragonboard820c/Kconfig new file mode 100644 index 0000000000..aff9af5271 --- /dev/null +++ b/board/qualcomm/dragonboard820c/Kconfig @@ -0,0 +1,15 @@ +if TARGET_DRAGONBOARD820C + +config SYS_BOARD + default "dragonboard820c" + +config SYS_VENDOR + default "qualcomm" + +config SYS_SOC + default "apq8096" + +config SYS_CONFIG_NAME + default "dragonboard820c" + +endif diff --git a/board/qualcomm/dragonboard820c/MAINTAINERS b/board/qualcomm/dragonboard820c/MAINTAINERS new file mode 100644 index 0000000000..a157033bf0 --- /dev/null +++ b/board/qualcomm/dragonboard820c/MAINTAINERS @@ -0,0 +1,6 @@ +DRAGONBOARD820C BOARD +M: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> +S: Maintained +F: board/qualcomm/dragonboard820c/ +F: include/configs/dragonboard820c.h +F: configs/dragonboard820c_defconfig diff --git a/board/qualcomm/dragonboard820c/Makefile b/board/qualcomm/dragonboard820c/Makefile new file mode 100644 index 0000000000..a1ce4b210f --- /dev/null +++ b/board/qualcomm/dragonboard820c/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y := dragonboard820c.o +extra-y += head.o diff --git a/board/qualcomm/dragonboard820c/dragonboard820c.c b/board/qualcomm/dragonboard820c/dragonboard820c.c new file mode 100644 index 0000000000..6040787cff --- /dev/null +++ b/board/qualcomm/dragonboard820c/dragonboard820c.c @@ -0,0 +1,163 @@ +/* + * Board init file for Dragonboard 820C + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/arch/sysmap-apq8096.h> +#include <linux/arm-smccc.h> +#include <linux/psci.h> +#include <common.h> +#include <dm.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <asm/psci.h> +#include <asm/gpio.h> + +DECLARE_GLOBAL_DATA_PTR; + +int dram_init(void) +{ + gd->ram_size = PHYS_SDRAM_SIZE; + + return 0; +} + +int dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = PHYS_SDRAM_1; + gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; + + gd->bd->bi_dram[1].start = PHYS_SDRAM_2; + gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE; + + return 0; +} + +static void sdhci_power_init(void) +{ + const u32 TLMM_PULL_MASK = 0x3; + const u32 TLMM_HDRV_MASK = 0x7; + + struct tlmm_cfg { + u32 bit; /* bit in the register */ + u8 mask; /* mask clk/dat/cmd control */ + u8 val; + }; + + /* bit offsets in the sdc tlmm register */ + enum { SDC1_DATA_HDRV = 0, + SDC1_CMD_HDRV = 3, + SDC1_CLK_HDRV = 6, + SDC1_DATA_PULL = 9, + SDC1_CMD_PULL = 11, + SDC1_CLK_PULL = 13, + SDC1_RCLK_PULL = 15, + }; + + enum { TLMM_PULL_DOWN = 0x1, + TLMM_PULL_UP = 0x3, + TLMM_NO_PULL = 0x0, + }; + + enum { TLMM_CUR_VAL_10MA = 0x04, + TLMM_CUR_VAL_16MA = 0x07, + }; + int i; + + /* drive strength configs for sdhc pins */ + const struct tlmm_cfg hdrv[] = { + + { SDC1_CLK_HDRV, TLMM_CUR_VAL_16MA, TLMM_HDRV_MASK, }, + { SDC1_CMD_HDRV, TLMM_CUR_VAL_10MA, TLMM_HDRV_MASK, }, + { SDC1_DATA_HDRV, TLMM_CUR_VAL_10MA, TLMM_HDRV_MASK, }, + }; + + /* pull configs for sdhc pins */ + const struct tlmm_cfg pull[] = { + + { SDC1_CLK_PULL, TLMM_NO_PULL, TLMM_PULL_MASK, }, + { SDC1_CMD_PULL, TLMM_PULL_UP, TLMM_PULL_MASK, }, + { SDC1_DATA_PULL, TLMM_PULL_UP, TLMM_PULL_MASK, }, + }; + + const struct tlmm_cfg rclk[] = { + + { SDC1_RCLK_PULL, TLMM_PULL_DOWN, TLMM_PULL_MASK,}, + }; + + for (i = 0; i < ARRAY_SIZE(hdrv); i++) + clrsetbits_le32(SDC1_HDRV_PULL_CTL_REG, + hdrv[i].mask << hdrv[i].bit, + hdrv[i].val << hdrv[i].bit); + + for (i = 0; i < ARRAY_SIZE(pull); i++) + clrsetbits_le32(SDC1_HDRV_PULL_CTL_REG, + pull[i].mask << pull[i].bit, + pull[i].val << pull[i].bit); + + for (i = 0; i < ARRAY_SIZE(rclk); i++) + clrsetbits_le32(SDC1_HDRV_PULL_CTL_REG, + rclk[i].mask << rclk[i].bit, + rclk[i].val << rclk[i].bit); +} + +static void show_psci_version(void) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res); + + printf("PSCI: v%ld.%ld\n", + PSCI_VERSION_MAJOR(res.a0), + PSCI_VERSION_MINOR(res.a0)); +} + +int board_init(void) +{ + sdhci_power_init(); + show_psci_version(); + + return 0; +} + +void reset_cpu(ulong addr) +{ + psci_system_reset(); +} + +/* Check for vol- button - if pressed - stop autoboot */ +int misc_init_r(void) +{ + struct udevice *pon; + struct gpio_desc resin; + int node, ret; + + ret = uclass_get_device_by_name(UCLASS_GPIO, "pm8994_pon@800", &pon); + if (ret < 0) { + printf("Failed to find PMIC pon node. Check device tree\n"); + return 0; + } + + node = fdt_subnode_offset(gd->fdt_blob, dev_of_offset(pon), + "key_vol_down"); + if (node < 0) { + printf("Failed to find key_vol_down node. Check device tree\n"); + return 0; + } + + if (gpio_request_by_name_nodev(offset_to_ofnode(node), "gpios", 0, + &resin, 0)) { + printf("Failed to request key_vol_down button.\n"); + return 0; + } + + if (dm_gpio_get_value(&resin)) { + env_set("bootdelay", "-1"); + printf("Power button pressed - dropping to console.\n"); + } + + return 0; +} diff --git a/board/qualcomm/dragonboard820c/head.S b/board/qualcomm/dragonboard820c/head.S new file mode 100644 index 0000000000..06d82d5831 --- /dev/null +++ b/board/qualcomm/dragonboard820c/head.S @@ -0,0 +1,34 @@ +/* + * ARM64 header for proper chain-loading with Little Kernel. + * + * Little Kernel shipped with Dragonboard820C boots standard Linux images for + * ARM64. This file adds header that is required to boot U-Boot properly. + * + * For details see: + * https://www.kernel.org/doc/Documentation/arm64/booting.txt + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> + +/* + * per document in linux/Doc/arm64/booting.text + */ +.global _arm64_header +_arm64_header: + b _start + .word 0 + .quad CONFIG_SYS_TEXT_BASE-PHYS_SDRAM_1 /* Image load offset, LE */ + .quad 0 /* Effective size of kernel image, little-endian */ + .quad 0 /* kernel flags, little-endian */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .quad 0 /* reserved */ + .byte 0x41 /* Magic number, "ARM\x64" */ + .byte 0x52 + .byte 0x4d + .byte 0x64 + .word 0 /* reserved (used for PE COFF offset) */ diff --git a/board/qualcomm/dragonboard820c/readme.txt b/board/qualcomm/dragonboard820c/readme.txt new file mode 100644 index 0000000000..1f310b31c7 --- /dev/null +++ b/board/qualcomm/dragonboard820c/readme.txt @@ -0,0 +1,459 @@ +# +# (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +================================================================================ + What is working (enough to boot a distro from SD card) +================================================================================ + - UART + - SD card + - PSCI reset + - Environment in EXT4 partition 1 in SD card (check defconfig for details) + dont forget to insert the card in the SD slot before booting if you + are going to make mods to the environment + +================================================================================ + Build & Run instructions +================================================================================ + +1) Install mkbootimg and dtbTool from Codeaurora: + + git://codeaurora.org/quic/kernel/skales + commit 8492547e404e969262d9070dee9bdd15668bb70f worked for me. + +2) Setup CROSS_COMPILE to aarch64 compiler or if you use ccache just do + CROSS_COMPILE="ccache aarch64-linux-gnu-" + +3) cd to the u-boot tree + + $ make dragonboard820c_config + $ make -j `nproc` + +4) generate fake, empty ramdisk (can have 0 bytes) + + $ touch rd + +5) Generate qualcomm device tree table with dtbTool + + $ dtbTool -o dt.img arch/arm/dts + +6) Generate Android boot image with mkbootimg: + + $ mkbootimg --kernel=u-boot-dtb.bin \ + --output=u-boot.img \ + --dt=dt.img \ + --pagesize 4096 \ + --base 0x80000000 \ + --ramdisk=rd \ + --cmdline="" + +7) Reboot the board into fastboot mode + - plug the board micro-usb to your laptop usb host. + - reboot the board with vol- button pressed + +8) Boot the uboot image using fastboot + + $ fastboot boot u-boot.img + + or flash it to the UFS drive boot partition: + + $ fastboot flash boot u-boot.img + $ fastboot reboot + + +================================================================================ + To boot a linux kernel from SDHCI with the ROOTFS on an NFS share: +================================================================================ + +1) create an EXT4 partition on the SD card (must be partition #1) + +2) build the kernel image and dtb (documented extensively somewhere else) + +3) copy the drivers to the NFS partition (ie: 192.168.1.2 /exports/db820c-rootfs) + +4) add the u-boot headers to the image: + + $ mkimage -A arm64 \ + -O linux \ + -C none \ + -T kernel \ + -a 0x80080000 \ + -e 0x80080000 \ + -n Dragonboard820c \ + -d $kernel/arch/arm64/boot/Image \ + uImage + +5) copy the generated uImage and the device tree binary to the SD card EXT4 + partition + + $ cp uImage /mnt/boot/ + $ cp apq8096-db820c.dtb /mnt/boot/ + +6) on the SD card create /extlinux/extlinux.conf as follows: + + default nfs + prompt 1 + timeout 10 + + LABEL nfs + MENU NFS entry + LINUX /uImage + FDT /apq8096-db820c.dtb + APPEND root=/dev/nfs rw \ + nfsroot=192.168.1.2:/exports/db829c-rootfs,v3,tcp \ + rootwait \ + ip=dhcp consoleblank=0 \ + console=tty0 \ + console=ttyMSM0,115200n8 \ + earlyprintk earlycon=msm_serial_dm,0x75b0000 \ + androidboot.bootdevice=624000.ufshc \ + androidboot.verifiedbootstate=orange \ + androidboot.ver0 + +7) remove the SD card from the laptop and insert it back to the db820 board. + the SD card EXT4 partition#1 should contain: + /uImage + /apq8096-db820c.dtb + /extlinux/extlinux.conf + +8) reboot the db820 board + +================================================================================ + Successful boot sequence +================================================================================ + +Format: Log Type - Time(microsec) - Message - Optional Info +Log Type: B - Since Boot(Power On Reset), D - Delta, S - Statistic +S - QC_IMAGE_VERSION_STRING=BOOT.XF.1.0-00301 +S - IMAGE_VARIANT_STRING=M8996LAB +S - OEM_IMAGE_VERSION_STRING=crm-ubuntu68 +S - Boot Interface: UFS +S - Secure Boot: Off +S - Boot Config @ 0x00076044 = 0x000001c9 +S - JTAG ID @ 0x000760f4 = 0x4003e0e1 +S - OEM ID @ 0x000760f8 = 0x00000000 +S - Serial Number @ 0x00074138 = 0x2e8844ce +S - OEM Config Row 0 @ 0x00074188 = 0x0000000000000000 +S - OEM Config Row 1 @ 0x00074190 = 0x0000000000000000 +S - Feature Config Row 0 @ 0x000741a0 = 0x0050000010000100 +S - Feature Config Row 1 @ 0x000741a8 = 0x00fff00001ffffff +S - Core 0 Frequency, 1228 MHz +B - 0 - PBL, Start +B - 10412 - bootable_media_detect_entry, Start +B - 47480 - bootable_media_detect_success, Start +B - 47481 - elf_loader_entry, Start +B - 49027 - auth_hash_seg_entry, Start +B - 49129 - auth_hash_seg_exit, Start +B - 82403 - elf_segs_hash_verify_entry, Start +B - 84905 - PBL, End +B - 86955 - SBL1, Start +B - 182969 - usb: hs_phy_nondrive_start +B - 183305 - usb: PLL lock success - 0x3 +B - 186294 - usb: hs_phy_nondrive_finish +B - 190442 - boot_flash_init, Start +D - 30 - boot_flash_init, Delta +B - 197548 - sbl1_ddr_set_default_params, Start +D - 30 - sbl1_ddr_set_default_params, Delta +B - 205509 - boot_config_data_table_init, Start +D - 200659 - boot_config_data_table_init, Delta - (60 Bytes) +B - 410713 - CDT Version:3,Platform ID:24,Major ID:1,Minor ID:0,Subtype:0 +B - 415410 - Image Load, Start +D - 22570 - PMIC Image Loaded, Delta - (37272 Bytes) +B - 437980 - pm_device_init, Start +B - 443744 - PON REASON:PM0:0x200000061 PM1:0x200000021 +B - 480161 - PM_SET_VAL:Skip +D - 40016 - pm_device_init, Delta +B - 482083 - pm_driver_init, Start +D - 2928 - pm_driver_init, Delta +B - 488671 - pm_sbl_chg_init, Start +D - 91 - pm_sbl_chg_init, Delta +B - 495442 - vsense_init, Start +D - 0 - vsense_init, Delta +B - 505171 - Pre_DDR_clock_init, Start +D - 396 - Pre_DDR_clock_init, Delta +B - 509045 - ddr_initialize_device, Start +B - 512766 - 8996 v3.x detected, Max frequency = 1.8 GHz +B - 522373 - ddr_initialize_device, Delta +B - 522404 - DDR ID, Rank 0, Rank 1, 0x6, 0x300, 0x300 +B - 526247 - Basic DDR tests done +B - 594994 - clock_init, Start +D - 274 - clock_init, Delta +B - 598349 - Image Load, Start +D - 4331 - QSEE Dev Config Image Loaded, Delta - (46008 Bytes) +B - 603808 - Image Load, Start +D - 5338 - APDP Image Loaded, Delta - (0 Bytes) +B - 612409 - usb: UFS Serial - 2f490ecf +B - 616801 - usb: fedl, vbus_low +B - 620431 - Image Load, Start +D - 55418 - QSEE Image Loaded, Delta - (1640572 Bytes) +B - 675849 - Image Load, Start +D - 2013 - SEC Image Loaded, Delta - (4096 Bytes) +B - 683413 - sbl1_efs_handle_cookies, Start +D - 457 - sbl1_efs_handle_cookies, Delta +B - 691892 - Image Load, Start +D - 14396 - QHEE Image Loaded, Delta - (254184 Bytes) +B - 706319 - Image Load, Start +D - 14061 - RPM Image Loaded, Delta - (223900 Bytes) +B - 721111 - Image Load, Start +D - 3233 - STI Image Loaded, Delta - (0 Bytes) +B - 727913 - Image Load, Start +D - 34709 - APPSBL Image Loaded, Delta - (748716 Bytes) +B - 762713 - SBL1, End +D - 680028 - SBL1, Delta +S - Flash Throughput, 94000 KB/s (2959024 Bytes, 31250 us) +S - DDR Frequency, 1017 MHz +Android Bootloader - UART_DM Initialized!!! + +[0] BUILD_VERSION= +[0] BUILD_DATE=16:07:51 - Nov 17 2017 +[0] welcome to lk +[10] platform_init() +[10] target_init() +[10] RPM GLink Init +[10] Opening RPM Glink Port success +[10] Opening SSR Glink Port success +[20] Glink Connection between APPS and RPM established +[20] Glink Connection between APPS and RPM established +[40] UFS init success +[80] Qseecom Init Done in Appsbl +[80] secure app region addr=0x86600000 size=0x2200000[90] TZ App region notif returned with status:0 addr:86600000 size:35651584 +[100] TZ App log region register returned with status:0 addr:916d4000 size:4096 +[100] Qseecom TZ Init Done in Appsbl +[120] Loading cmnlib done +[120] qseecom_start_app: Loading app keymaster for the first time +[150] <8>keymaster: "\"KEYMASTER Init \"" +[160] Selected panel: none +Skip panel configuration +[160] pm8x41_get_is_cold_boot: cold boot +[170] boot_verifier: Device is in ORANGE boot state. +[180] Device is unlocked! Skipping verification... +[180] Loading (boot) image (348160): start +[190] Loading (boot) image (348160): done +[190] use_signed_kernel=1, is_unlocked=1, is_tampered=0. +[200] Your device has been unlocked and cant be trusted. +Wait for 5 seconds before proceeding + +[5200] mdtp: mdtp_img loaded +[5210] mdtp: is_test_mode: test mode is set to 1 +[5210] mdtp: read_metadata: SUCCESS +[5230] LK SEC APP Handle: 0x1 +[5230] Return value from recv_data: 14 +[5240] Return value from recv_data: 14 +[5250] Return value from recv_data: 14 +[5260] DTB Total entry: 1, DTB version: 3 +[5260] Using DTB entry 0x00000123/00000000/0x00000018/0 for device 0x00000123/00030001/0x00010018/0 +[5270] cmdline: androidboot.bootdevice=624000.ufshc androidboot.verifiedbootstate=orange androidboot.veritymode=enforcing androidboot.serialno=2f490ecf androidboot.baseband=apq mdss_mdp.panel=0 +[5290] Updating device tree: start +[5290] Updating device tree: done +[5290] Return value from recv_data: 14 +[5300] RPM GLINK UnInit +[5300] Qseecom De-Init Done in Appsbl +[5300] booting linux @ 0x80080000, ramdisk @ 0x82200000 (0), tags/device tree @ 0x82000000 +[5310] Jumping to kernel via monitor + +U-Boot 2017.11-00145-ge895117 (Nov 29 2017 - 10:04:06 +0100) +Qualcomm-DragonBoard 820C + +DRAM: 3 GiB +PSCI: v1.0 +MMC: sdhci@74a4900: 0 +In: serial@75b0000 +Out: serial@75b0000 +Err: serial@75b0000 +Net: Net Initialization Skipped +No ethernet found. +Hit any key to stop autoboot: 0 +switch to partitions #0, OK +mmc0 is current device +Scanning mmc 0:1... +Found /extlinux/extlinux.conf +Retrieving file: /extlinux/extlinux.conf +433 bytes read in 71 ms (5.9 KiB/s) +1: nfs root + +Retrieving file: /uImage +19397184 bytes read in 2024 ms (9.1 MiB/s) +append: root=/dev/nfs rw nfsroot=192.168.1.2:/db820c/rootfs,v3,tcp rootwait ip=dhcp consoleblank=0 console=tty0 console=ttyMSM0,115200n8 earlyprintk earlycon=msm_serial_dm,0x75b0000 androidboot.bootdevice=624000.ufshc androidboot.verifiedbootstate=orange androidboot.ver0 + +Retrieving file: /apq8096-db820c.dtb +38134 bytes read in 37 ms (1005.9 KiB/s) + +## Booting kernel from Legacy Image at 95000000 ... + Image Name: Dragonboard820c + Image Type: AArch64 Linux Kernel Image (uncompressed) + Data Size: 19397120 Bytes = 18.5 MiB + Load Address: 80080000 + Entry Point: 80080000 + Verifying Checksum ... OK +## Flattened Device Tree blob at 93000000 + Booting using the fdt blob at 0x93000000 + Loading Kernel Image ... OK + Using Device Tree in place at 0000000093000000, end 000000009300c4f5 + +Starting kernel ... + +[ 0.000000] Booting Linux on physical CPU 0x0 +[ 0.000000] Linux version 4.11.3-30039-g5a922a1 (jramirez@igloo) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05) ) #1 SMP PREEMPT Wed Oct 18 10:21:11 CEST 2017 +[ 0.000000] Boot CPU: AArch64 Processor [511f2112] +[ 0.000000] earlycon: msm_serial_dm0 at MMIO 0x00000000075b0000 (options '') +[ 0.000000] bootconsole [msm_serial_dm0] enabled +[ 0.000000] efi: Getting EFI parameters from FDT: +[ 0.000000] efi: UEFI not found. +[ 0.000000] OF: reserved mem: OVERLAP DETECTED! +[ 0.000000] adsp@8ea00000 (0x000000008ea00000--0x0000000090400000) overlaps with gpu@8f200000 (0x000000008f200000--0x000000008f300000) +[ 0.000000] Reserved memory: created DMA memory pool at 0x000000008f200000, size 1 MiB +[ 0.000000] OF: reserved mem: initialized node gpu@8f200000, compatible id shared-dma-pool +[ 0.000000] Reserved memory: created DMA memory pool at 0x0000000090400000, size 8 MiB +[ 0.000000] OF: reserved mem: initialized node venus@90400000, compatible id shared-dma-pool +[ 0.000000] cma: Reserved 128 MiB at 0x00000000b8000000 +[ 0.000000] NUMA: No NUMA configuration found +[ 0.000000] NUMA: Faking a node at [mem 0x0000000000000000-0x00000000bfffffff] +[ 0.000000] NUMA: Adding memblock [0x80000000 - 0x857fffff] on node 0 +[ 0.000000] NUMA: Adding memblock [0x91800000 - 0xbfffffff] on node 0 +[ 0.000000] NUMA: Initmem setup node 0 [mem 0x80000000-0xbfffffff] +[ 0.000000] NUMA: NODE_DATA [mem 0xb7fb6680-0xb7fb817f] +[ 0.000000] Zone ranges: +[ 0.000000] DMA [mem 0x0000000080000000-0x00000000bfffffff] +[ 0.000000] Normal empty +[ 0.000000] Movable zone start for each node +[ 0.000000] Early memory node ranges +[ 0.000000] node 0: [mem 0x0000000080000000-0x00000000857fffff] +[ 0.000000] node 0: [mem 0x0000000091800000-0x00000000bfffffff] +[ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x00000000bfffffff] +[ 0.000000] psci: probing for conduit method from DT. +[ 0.000000] psci: PSCIv1.0 detected in firmware. +[ 0.000000] psci: Using standard PSCI v0.2 function IDs +[ 0.000000] psci: MIGRATE_INFO_TYPE not supported. +[ 0.000000] percpu: Embedded 23 pages/cpu @ffff8000de9a3000 s57240 r8192 d28776 u94208 +[ 0.000000] pcpu-alloc: s57240 r8192 d28776 u94208 alloc=23*4096 +[ 0.000000] pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 +[ 0.000000] Detected PIPT I-cache on CPU0 +[ 0.000000] Built 1 zonelists in Node order, mobility grouping on. Total pages: 720293 +[ 0.000000] Policy zone: Normal +[ 0.000000] Kernel command line: root=/dev/nfs rw nfsroot=192.168.1.2:/db820c/rootfs,v3,tcp rootwait ip=dhcp consoleblank=0 +console=tty0 console=ttyMSM0,115200n8 earlyprintk earlycon=msm_serial_dm,0x75b0000 androidboot.bootdevice=624000.ufshc androidboot.verifiedbootstate=orange a +ndroidboot.ver0 +[ 0.000000] PID hash table entries: 4096 (order: 3, 32768 bytes) +[ 0.000000] software IO TLB [mem 0xd3fff000-0xd7fff000] (64MB) mapped at [ffff800053fff000-ffff800057ffefff] +[ 0.000000] Memory: 2644172K/2926908K available (11196K kernel code, 1470K rwdata, 5132K rodata, 1088K init, 449K bss, 151664K reserved, 131072K cma-reser +ved) +[ 0.000000] Virtual kernel memory layout: +[ 0.000000] modules : 0xffff000000000000 - 0xffff000008000000 ( 128 MB) +[ 0.000000] vmalloc : 0xffff000008000000 - 0xffff7dffbfff0000 (129022 GB) +[ 0.000000] .text : 0xffff000008080000 - 0xffff000008b70000 ( 11200 KB) +[ 0.000000] .rodata : 0xffff000008b70000 - 0xffff000009080000 ( 5184 KB) +[ 0.000000] .init : 0xffff000009080000 - 0xffff000009190000 ( 1088 KB) +[ 0.000000] .data : 0xffff000009190000 - 0xffff0000092ffa00 ( 1471 KB) +[ 0.000000] .bss : 0xffff0000092ffa00 - 0xffff00000937014c ( 450 KB) +[ 0.000000] fixed : 0xffff7dfffe7fd000 - 0xffff7dfffec00000 ( 4108 KB) +[ 0.000000] PCI I/O : 0xffff7dfffee00000 - 0xffff7dffffe00000 ( 16 MB) +[ 0.000000] vmemmap : 0xffff7e0000000000 - 0xffff800000000000 ( 2048 GB maximum) +[ 0.000000] 0xffff7e0000000000 - 0xffff7e00037a93c0 ( 55 MB actual) +[ 0.000000] memory : 0xffff800000000000 - 0xffff8000dea4f000 ( 3562 MB) +[ 0.000000] SLUB: HWalign=128, Order=0-3, MinObjects=0, CPUs=4, Nodes=1 +[ 0.000000] Preemptible hierarchical RCU implementation. +[ 0.000000] Build-time adjustment of leaf fanout to 64. +[ 0.000000] RCU restricting CPUs from NR_CPUS=64 to nr_cpu_ids=4. +[ 0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=64, nr_cpu_ids=4 +[ 0.000000] NR_IRQS:64 nr_irqs:64 0 +[ 0.000000] GICv3: CPU0: found redistributor 0 region 0:0x0000000009c00000 +[ 0.000000] GICv2m: range[mem 0x09bd0000-0x09bd0fff], SPI[544:639] +[ 0.000000] arm_arch_timer: Architected cp15 and mmio timer(s) running at 19.20MHz (virt/virt). +[ 0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x46d987e47, max_idle_ns: 440795202767 ns +[ 0.000002] sched_clock: 56 bits at 19MHz, resolution 52ns, wraps every 4398046511078ns + +[....] + + +Some kernel information: + +root@linaro-developer:~# cat /proc/cpuinfo +processor : 0 +BogoMIPS : 38.40 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x51 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0x211 +CPU revision : 2 + +processor : 1 +BogoMIPS : 38.40 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x51 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0x211 +CPU revision : 2 + +processor : 2 +BogoMIPS : 38.40 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x51 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0x205 +CPU revision : 2 + +processor : 3 +BogoMIPS : 38.40 +Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid +CPU implementer : 0x51 +CPU architecture: 8 +CPU variant : 0x1 +CPU part : 0x205 +CPU revision : 2 + +root@linaro-developer:~# uname -a +Linux linaro-developer 4.11.3-30039-g5a922a1 #1 SMP PREEMPT Wed Oct 18 10:21:11 CEST 2017 aarch64 GNU/Linux + +root@linaro-developer:~# cat /proc/cmdline +root=/dev/nfs rw nfsroot=192.168.1.2:/db820c/rootfs,v3,tcp rootwait ip=dhcp consoleblank=0 console=tty0 console=ttyMSM0,115200n8 earlyprintk earlycon=msm_serial_dm,0x75b0000 androidboot.bootdevice=624000.ufshc androidboot.verifiedbootstate=orange androidboot.ver0 + +root@linaro-developer:~# cat /proc/meminfo +MemTotal: 2776332 kB +MemFree: 2593696 kB +MemAvailable: 2561432 kB +Buffers: 0 kB +Cached: 94744 kB +SwapCached: 0 kB +Active: 43888 kB +Inactive: 72972 kB +Active(anon): 22968 kB +Inactive(anon): 24616 kB +Active(file): 20920 kB +Inactive(file): 48356 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 0 kB +SwapFree: 0 kB +Dirty: 0 kB +Writeback: 0 kB +AnonPages: 22120 kB +Mapped: 29284 kB +Shmem: 25468 kB +Slab: 32876 kB +SReclaimable: 12924 kB +SUnreclaim: 19952 kB +KernelStack: 2144 kB +PageTables: 928 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 1388164 kB +Committed_AS: 204192 kB +VmallocTotal: 135290290112 kB +VmallocUsed: 0 kB +VmallocChunk: 0 kB +AnonHugePages: 2048 kB +ShmemHugePages: 0 kB +ShmemPmdMapped: 0 kB +CmaTotal: 131072 kB +CmaFree: 130356 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB diff --git a/board/qualcomm/dragonboard820c/u-boot.lds b/board/qualcomm/dragonboard820c/u-boot.lds new file mode 100644 index 0000000000..b84b4ac8b3 --- /dev/null +++ b/board/qualcomm/dragonboard820c/u-boot.lds @@ -0,0 +1,106 @@ +/* + * Override linker script for fastboot-readable images + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Based on arch/arm/cpu/armv8/u-boot.lds (Just add header) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) +ENTRY(_arm64_header) +SECTIONS +{ + . = 0x00000000; + + . = ALIGN(8); + .text : + { + *(.__image_copy_start) + board/qualcomm/dragonboard820c/head.o (.text*) + CPUDIR/start.o (.text*) + *(.text*) + } + + . = ALIGN(8); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(8); + .data : { + *(.data*) + } + + . = ALIGN(8); + + . = .; + + . = ALIGN(8); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(8); + + .efi_runtime : { + __efi_runtime_start = .; + *(efi_runtime_text) + *(efi_runtime_data) + __efi_runtime_stop = .; + } + + .efi_runtime_rel : { + __efi_runtime_rel_start = .; + *(.relaefi_runtime_text) + *(.relaefi_runtime_data) + __efi_runtime_rel_stop = .; + } + + . = ALIGN(8); + + .image_copy_end : + { + *(.__image_copy_end) + } + + . = ALIGN(8); + + .rel_dyn_start : + { + *(.__rel_dyn_start) + } + + .rela.dyn : { + *(.rela*) + } + + .rel_dyn_end : + { + *(.__rel_dyn_end) + } + + _end = .; + + . = ALIGN(8); + + .bss_start : { + KEEP(*(.__bss_start)); + } + + .bss : { + *(.bss*) + . = ALIGN(8); + } + + .bss_end : { + KEEP(*(.__bss_end)); + } + + /DISCARD/ : { *(.dynsym) } + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } +} diff --git a/cmd/bdinfo.c b/cmd/bdinfo.c index 27ffcd55bc..c7ebad17d1 100644 --- a/cmd/bdinfo.c +++ b/cmd/bdinfo.c @@ -417,6 +417,21 @@ int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 0; } +#elif defined(CONFIG_RISCV) + +int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + bd_t *bd = gd->bd; + + print_num("arch_number", bd->bi_arch_number); + print_bi_boot_params(bd); + print_bi_dram(bd); + print_eth_ip_addr(); + print_baudrate(); + + return 0; +} + #elif defined(CONFIG_ARC) int do_bdinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) @@ -23,7 +23,12 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); - printf("Tran Speed: %d\n", mmc->tran_speed); + printf("Bus Speed: %d\n", mmc->clock); +#if CONFIG_IS_ENABLED(MMC_VERBOSE) + printf("Mode : %s\n", mmc_mode_name(mmc->selected_mode)); + mmc_dump_capabilities("card capabilities", mmc->card_caps); + mmc_dump_capabilities("host capabilities", mmc->host_caps); +#endif printf("Rd Block Len: %d\n", mmc->read_bl_len); printf("%s version %d.%d", IS_SD(mmc) ? "SD" : "MMC", @@ -40,15 +45,19 @@ static void print_mmcinfo(struct mmc *mmc) printf("Bus Width: %d-bit%s\n", mmc->bus_width, mmc->ddr_mode ? " DDR" : ""); +#if CONFIG_IS_ENABLED(MMC_WRITE) puts("Erase Group Size: "); print_size(((u64)mmc->erase_grp_size) << 9, "\n"); +#endif if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) { bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0; bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR); +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) puts("HC WP Group Size: "); print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n"); +#endif puts("User Capacity: "); print_size(mmc->capacity_user, usr_enh ? " ENH" : ""); @@ -297,6 +306,8 @@ static int do_mmc_read(cmd_tbl_t *cmdtp, int flag, return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; } + +#if CONFIG_IS_ENABLED(MMC_WRITE) static int do_mmc_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -355,6 +366,8 @@ static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; } +#endif + static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { @@ -433,6 +446,7 @@ static int do_mmc_list(cmd_tbl_t *cmdtp, int flag, return CMD_RET_SUCCESS; } +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) static int parse_hwpart_user(struct mmc_hwpart_conf *pconf, int argc, char * const argv[]) { @@ -582,6 +596,7 @@ static int do_mmc_hwpartition(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; } } +#endif #ifdef CONFIG_SUPPORT_EMMC_BOOT static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag, @@ -785,13 +800,17 @@ static int do_mmc_bkops_enable(cmd_tbl_t *cmdtp, int flag, static cmd_tbl_t cmd_mmc[] = { U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), +#if CONFIG_IS_ENABLED(MMC_WRITE) U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), +#endif U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""), U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""), U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""), U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""), +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""), +#endif #ifdef CONFIG_SUPPORT_EMMC_BOOT U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""), U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""), diff --git a/cmd/mvebu/bubt.c b/cmd/mvebu/bubt.c index a1997ac0d3..23fb8cd807 100644 --- a/cmd/mvebu/bubt.c +++ b/cmd/mvebu/bubt.c @@ -110,7 +110,7 @@ static ulong get_load_addr(void) /******************************************************************** * eMMC services ********************************************************************/ -#ifdef CONFIG_DM_MMC +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(MMC_WRITE) static int mmc_burn_image(size_t image_size) { struct mmc *mmc; diff --git a/common/Makefile b/common/Makefile index 14166209fe..c7bde239c1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_IO_TRACE) += iotrace.o obj-y += memsize.o obj-y += stdio.o +ifndef CONFIG_SPL_BUILD # This option is not just y/n - it can have a numeric value ifdef CONFIG_FASTBOOT_FLASH obj-y += image-sparse.o @@ -119,6 +120,7 @@ ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV obj-y += fb_nand.o endif endif +endif ifdef CONFIG_CMD_EEPROM_LAYOUT obj-y += eeprom/eeprom_field.o eeprom/eeprom_layout.o diff --git a/common/board_f.c b/common/board_f.c index e46eceda7d..0bdce64ca5 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -218,7 +218,7 @@ static int setup_mon_len(void) gd->mon_len = (ulong)&_end - (ulong)_init; #elif defined(CONFIG_NIOS2) || defined(CONFIG_XTENSA) gd->mon_len = CONFIG_SYS_MONITOR_LEN; -#elif defined(CONFIG_NDS32) || defined(CONFIG_SH) +#elif defined(CONFIG_NDS32) || defined(CONFIG_SH) || defined(CONFIG_RISCV) gd->mon_len = (ulong)(&__bss_end) - (ulong)(&_start); #elif defined(CONFIG_SYS_MONITOR_BASE) /* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */ diff --git a/common/board_r.c b/common/board_r.c index 09167c13cc..2a9df6b716 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -126,7 +126,7 @@ static int initr_reloc_global_data(void) { #ifdef __ARM__ monitor_flash_len = _end - __image_copy_start; -#elif defined(CONFIG_NDS32) +#elif defined(CONFIG_NDS32) || defined(CONFIG_RISCV) monitor_flash_len = (ulong)&_end - (ulong)&_start; #elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2) monitor_flash_len = (ulong)&__init_end - gd->relocaddr; @@ -704,7 +704,7 @@ static init_fnc_t init_sequence_r[] = { #ifdef CONFIG_DM initr_dm, #endif -#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) +#if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) board_init, /* Setup chipselects */ #endif /* diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 9d35f41233..d686b1ecbd 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -301,6 +301,7 @@ config SPL_ENV_SUPPORT config SPL_SAVEENV bool "Support save environment" depends on SPL_ENV_SUPPORT + select SPL_MMC_WRITE if ENV_IS_IN_MMC help Enable save environment support in SPL after setenv. By default the saveenv option is not provided in SPL, but some boards need @@ -415,6 +416,14 @@ config SPL_MMC_SUPPORT this option to build the drivers in drivers/mmc as part of an SPL build. +config SPL_MMC_WRITE + bool "MMC/SD/SDIO card support for write operations in SPL" + depends on SPL_MMC_SUPPORT + default n + help + Enable write access to MMC and SD Cards in SPL + + config SPL_MPC8XXX_INIT_DDR_SUPPORT bool "Support MPC8XXX DDR init" help diff --git a/configs/am335x_hs_evm_defconfig b/configs/am335x_hs_evm_defconfig index 78cf48dde7..63e7a077ae 100644 --- a/configs/am335x_hs_evm_defconfig +++ b/configs/am335x_hs_evm_defconfig @@ -13,10 +13,12 @@ CONFIG_ANDROID_BOOT_IMAGE=y CONFIG_FIT_IMAGE_POST_PROCESS=y CONFIG_SPL_LOAD_FIT=y CONFIG_SPL_FIT_IMAGE_POST_PROCESS=y +CONFIG_LOGLEVEL=3 CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_VERSION_VARIABLE=y CONFIG_ARCH_MISC_INIT=y CONFIG_SPL=y +CONFIG_SPL_FIT_IMAGE_TINY=y # CONFIG_SPL_ENV_SUPPORT is not set # CONFIG_SPL_EXT_SUPPORT is not set CONFIG_SPL_MTD_SUPPORT=y @@ -37,6 +39,7 @@ CONFIG_DFU_RAM=y CONFIG_DM_I2C=y CONFIG_MISC=y CONFIG_DM_MMC=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_MMC_OMAP_HS=y CONFIG_NAND=y CONFIG_SPI_FLASH=y @@ -60,5 +63,6 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x0451 CONFIG_USB_GADGET_PRODUCT_NUM=0xd022 CONFIG_USB_GADGET_DOWNLOAD=y CONFIG_USB_ETHER=y +CONFIG_SPL_TINY_MEMSET=y CONFIG_RSA=y CONFIG_LZO=y diff --git a/configs/dragonboard410c_defconfig b/configs/dragonboard410c_defconfig index b71bff7592..4389f52b5c 100644 --- a/configs/dragonboard410c_defconfig +++ b/configs/dragonboard410c_defconfig @@ -30,7 +30,6 @@ CONFIG_DM_PMIC=y CONFIG_PMIC_PM8916=y CONFIG_MSM_SERIAL=y CONFIG_SPMI_MSM=y -CONFIG_SYSRESET=y CONFIG_USB=y CONFIG_DM_USB=y CONFIG_USB_EHCI_HCD=y @@ -44,3 +43,8 @@ CONFIG_USB_ETHER_ASIX88179=y CONFIG_USB_ETHER_MCS7830=y CONFIG_USB_ETHER_SMSC95XX=y CONFIG_OF_LIBFDT_OVERLAY=y +CONFIG_OF_CONTROL=y +CONFIG_ENV_IS_IN_MMC=y +CONFIG_OF_BOARD_SETUP=y +CONFIG_PSCI_RESET=y +CONFIG_OF_SEPARATE=y diff --git a/configs/dragonboard820c_defconfig b/configs/dragonboard820c_defconfig new file mode 100644 index 0000000000..8af54aa3c4 --- /dev/null +++ b/configs/dragonboard820c_defconfig @@ -0,0 +1,43 @@ +CONFIG_ARM=y +CONFIG_ARM_SMCCC=y +CONFIG_ARCH_SNAPDRAGON=y +CONFIG_TARGET_DRAGONBOARD820C=y +CONFIG_IDENT_STRING="\nQualcomm-DragonBoard 820C" +CONFIG_DEFAULT_DEVICE_TREE="dragonboard820c" +CONFIG_USE_BOOTARGS=y +# CONFIG_DISPLAY_CPUINFO is not set +# CONFIG_DISPLAY_BOARDINFO is not set +CONFIG_BOOTARGS="console=ttyMSM0,115200n8" +CONFIG_SYS_PROMPT="dragonboard820c => " +CONFIG_HUSH_PARSER=y +CONFIG_CMD_BOOTEFI=y +CONFIG_EFI_LOADER=y +CONFIG_CMD_BOOTEFI_HELLO=y +CONFIG_CMD_PXE=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_PART=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_GPT=y +CONFIG_CMD_MMC=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_PMIC=y +CONFIG_OF_CONTROL=y +CONFIG_MSM_SERIAL=y +CONFIG_SPMI_MSM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SDHCI=y +CONFIG_DM_MMC=y +CONFIG_DM_GPIO=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_PM8916=y +CONFIG_PM8916_GPIO=y +CONFIG_CLK=y +CONFIG_PSCI_RESET=y +CONFIG_ENV_IS_IN_EXT4=y +CONFIG_ENV_EXT4_INTERFACE="mmc" +CONFIG_ENV_EXT4_DEVICE_AND_PART="0:1" +CONFIG_ENV_EXT4_FILE="/uboot.env" diff --git a/configs/nx25-ae250_defconfig b/configs/nx25-ae250_defconfig new file mode 100644 index 0000000000..1f68b66f4f --- /dev/null +++ b/configs/nx25-ae250_defconfig @@ -0,0 +1,36 @@ +CONFIG_RISCV=y +CONFIG_TARGET_NX25_AE250=y +CONFIG_DEFAULT_DEVICE_TREE="ae250" +CONFIG_FIT=y +CONFIG_BOOTDELAY=3 +CONFIG_SYS_PROMPT="RISC-V # " +CONFIG_CMD_MMC=y +CONFIG_CMD_SF=y +CONFIG_CMD_SF_TEST=y +# CONFIG_CMD_SETEXPR is not set +CONFIG_CMD_DHCP=y +CONFIG_CMD_PING=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_FAT=y +CONFIG_OF_CONTROL=y +CONFIG_ENV_IS_IN_SPI_FLASH=y +CONFIG_NET_RANDOM_ETHADDR=y +CONFIG_DM=y +CONFIG_CLK=y +CONFIG_MMC=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_DM_MMC=y +CONFIG_MMC_NDS32=y +CONFIG_FTSDC010=y +CONFIG_DM_ETH=y +CONFIG_FTMAC100=y +CONFIG_BAUDRATE=38400 +CONFIG_DM_SERIAL=y +CONFIG_SYS_NS16550=y +CONFIG_DM_SPI=y +CONFIG_ATCSPI200_SPI=y +CONFIG_TIMER=y +CONFIG_ATCPIT100_TIMER=y diff --git a/configs/omapl138_lcdk_defconfig b/configs/omapl138_lcdk_defconfig index 8d6b12f5b7..0d4506ec44 100644 --- a/configs/omapl138_lcdk_defconfig +++ b/configs/omapl138_lcdk_defconfig @@ -8,6 +8,7 @@ CONFIG_SPL_MMC_SUPPORT=y CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_NAND_SUPPORT=y CONFIG_BOOTDELAY=3 +CONFIG_LOGLEVEL=3 CONFIG_VERSION_VARIABLE=y # CONFIG_DISPLAY_CPUINFO is not set # CONFIG_DISPLAY_BOARDINFO is not set diff --git a/configs/openrd_base_defconfig b/configs/openrd_base_defconfig index 1a829b763f..f91e0f9edb 100644 --- a/configs/openrd_base_defconfig +++ b/configs/openrd_base_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/openrd_client_defconfig b/configs/openrd_client_defconfig index 7a95b5b54f..e65c437d88 100644 --- a/configs/openrd_client_defconfig +++ b/configs/openrd_client_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/openrd_ultimate_defconfig b/configs/openrd_ultimate_defconfig index 757be16f53..dfd809dbbb 100644 --- a/configs/openrd_ultimate_defconfig +++ b/configs/openrd_ultimate_defconfig @@ -25,6 +25,7 @@ CONFIG_CMD_UBI=y CONFIG_ISO_PARTITION=y CONFIG_ENV_IS_IN_NAND=y CONFIG_MVSATA_IDE=y +# CONFIG_MMC_HW_PARTITIONING is not set CONFIG_SYS_NS16550=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y diff --git a/doc/README.NX25 b/doc/README.NX25 new file mode 100644 index 0000000000..9f054e5cf2 --- /dev/null +++ b/doc/README.NX25 @@ -0,0 +1,46 @@ +NX25 is Andes CPU IP to adopt RISC-V architecture. + +Features +======== + +CPU Core + - 5-stage in-order execution pipeline + - Hardware Multiplier + - radix-2/radix-4/radix-16/radix-256/fast + - Hardware Divider + - Optional branch prediction + - Machine mode and optional user mode + - Optional performance monitoring + +ISA + - RV64I base integer instructions + - RVC for 16-bit compressed instructions + - RVM for multiplication and division instructions + +Memory subsystem + - I & D local memory + - Size: 4KB to 16MB + - Memory subsyetem soft-error protection + - Protection scheme: parity-checking or error-checking-and-correction (ECC) + - Automatic hardware error correction + +Bus + - Interface Protocol + - Synchronous AHB (32-bit/64-bit data-width), or + - Synchronous AXI4 (64-bit data-width) + +Power management + - Wait for interrupt (WFI) mode + +Debug + - Configurable number of breakpoints: 2/4/8 + - External Debug Module + - AHB slave port + - External JTAG debug transport module + +Platform Level Interrupt Controller (PLIC) + - AHB slave port + - Configurable number of interrupts: 1-1023 + - Configurable number of interrupt priorities: 3/7/15/63/127/255 + - Configurable number of targets: 1-16 + - Preempted interrupt priority stack diff --git a/doc/README.ae250 b/doc/README.ae250 new file mode 100644 index 0000000000..a80bb39b15 --- /dev/null +++ b/doc/README.ae250 @@ -0,0 +1,137 @@ +Andes Technology SoC AE250 +=========================== + +AE250 is the mainline SoC produced by Andes Technology using NX25 CPU core +base on RISC-V architecture. + +AE250 has integrated both AHB and APB bus and many periphals for application +and product development. + +NX25-AE250 +========= + +NX25-AE250 is the SoC with AE250 hardcore CPU. + +Configurations +============== + +CONFIG_SKIP_LOWLEVEL_INIT: + If you want to boot this system from SPI ROM and bypass e-bios (the + other boot loader on ROM). You should undefine CONFIG_SKIP_LOWLEVEL_INIT + in "include/configs/nx25-ae250.h". + +Build and boot steps +==================== + +build: +1. Prepare the toolchains and make sure the $PATH to toolchains is correct. +2. Use `make nx25-ae250_defconfig` in u-boot root to build the image. + +Verification +==================== + +Target +==================== +1. startup +2. relocation +3. timer driver +4. uart driver +5. mac driver +6. mmc driver +7. spi driver + +Steps +==================== +1. Define CONFIG_SKIP_LOWLEVEL_INIT to build u-boot which is loaded via gdb from ram. +2. Undefine CONFIG_SKIP_LOWLEVEL_INIT to build u-boot which is booted from spi rom. +3. Ping a server by mac driver +4. Scan sd card and copy u-boot image which is booted from flash to ram by sd driver. +5. Burn this u-boot image to spi rom by spi driver +6. Re-boot u-boot from spi flash with power off and power on. + +Messages +==================== +U-Boot 2018.01-rc2-00033-g824f89a (Dec 21 2017 - 16:51:26 +0800) + +DRAM: 1 GiB +MMC: mmc@f0e00000: 0 +SF: Detected mx25u1635e with page size 256 Bytes, erase size 4 KiB, total 2 MiB +In: serial@f0300000 +Out: serial@f0300000 +Err: serial@f0300000 +Net: +Warning: mac@e0100000 (eth0) using random MAC address - be:dd:d7:e4:e8:10 +eth0: mac@e0100000 + +RISC-V # version +U-Boot 2018.01-rc2-00033-gb265b91-dirty (Dec 22 2017 - 13:54:21 +0800) + +riscv32-unknown-linux-gnu-gcc (GCC) 7.2.0 +GNU ld (GNU Binutils) 2.29 + +RISC-V # setenv ipaddr 10.0.4.200 ; +RISC-V # setenv serverip 10.0.4.97 ; +RISC-V # ping 10.0.4.97 ; +Using mac@e0100000 device +host 10.0.4.97 is alive + +RISC-V # mmc rescan +RISC-V # fatls mmc 0:1 + 318907 u-boot-ae250-64.bin + 1252 hello_world_ae250_32.bin + 328787 u-boot-ae250-32.bin + +3 file(s), 0 dir(s) + +RISC-V # sf probe 0:0 50000000 0 +SF: Detected mx25u1635e with page size 256 Bytes, erase size 4 KiB, total 2 MiB + +RISC-V # sf test 0x100000 0x1000 +SPI flash test: +0 erase: 36 ticks, 111 KiB/s 0.888 Mbps +1 check: 29 ticks, 137 KiB/s 1.096 Mbps +2 write: 40 ticks, 100 KiB/s 0.800 Mbps +3 read: 20 ticks, 200 KiB/s 1.600 Mbps +Test passed +0 erase: 36 ticks, 111 KiB/s 0.888 Mbps +1 check: 29 ticks, 137 KiB/s 1.096 Mbps +2 write: 40 ticks, 100 KiB/s 0.800 Mbps +3 read: 20 ticks, 200 KiB/s 1.600 Mbps + +RISC-V # fatload mmc 0:1 0x600000 u-boot-ae250-32.bin +reading u-boot-ae250-32.bin +328787 bytes read in 324 ms (990.2 KiB/s) + +RISC-V # sf erase 0x0 0x51000 +SF: 331776 bytes @ 0x0 Erased: OK + +RISC-V # sf write 0x600000 0x0 0x50453 +device 0 offset 0x0, size 0x50453 +SF: 328787 bytes @ 0x0 Written: OK + +RISC-V # crc32 0x600000 0x50453 +crc32 for 00600000 ... 00650452 ==> 692dc44a + +RISC-V # crc32 0x80000000 0x50453 +crc32 for 80000000 ... 80050452 ==> 692dc44a +RISC-V # + +*** power-off and power-on, this U-Boot is booted from spi flash *** + +U-Boot 2018.01-rc2-00032-gf67dd47-dirty (Dec 21 2017 - 13:56:03 +0800) + +DRAM: 1 GiB +MMC: mmc@f0e00000: 0 +SF: Detected mx25u1635e with page size 256 Bytes, erase size 4 KiB, total 2 MiB +In: serial@f0300000 +Out: serial@f0300000 +Err: serial@f0300000 +Net: +Warning: mac@e0100000 (eth0) using random MAC address - ee:4c:58:29:32:f5 +eth0: mac@e0100000 +RISC-V # + +TODO +==================== + +Boot bbl and riscv-linux diff --git a/doc/README.standalone b/doc/README.standalone index 659a12f6cb..28ebde1dec 100644 --- a/doc/README.standalone +++ b/doc/README.standalone @@ -58,6 +58,7 @@ Design Notes on Exporting U-Boot Functions to Standalone Applications: Blackfin 0x00001000 0x00001000 NDS32 0x00300000 0x00300000 Nios II 0x02000000 0x02000000 + RISC-V 0x00600000 0x00600000 For example, the "hello world" application may be loaded and executed on a PowerPC board with the following commands: diff --git a/drivers/core/root.c b/drivers/core/root.c index 976e2c4fdd..36336b65b6 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -266,6 +266,17 @@ static int dm_scan_fdt_node(struct udevice *parent, const void *blob, for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) { + /* "chosen" node isn't a device itself but may contain some: */ + if (!strcmp(fdt_get_name(blob, offset, NULL), "chosen")) { + pr_debug("parsing subnodes of \"chosen\"\n"); + + err = dm_scan_fdt_node(parent, blob, offset, + pre_reloc_only); + if (err && !ret) + ret = err; + continue; + } + if (pre_reloc_only && !dm_fdt_pre_reloc(blob, offset)) continue; diff --git a/drivers/gpio/pm8916_gpio.c b/drivers/gpio/pm8916_gpio.c index 9ec2a24b3e..42f068ecb6 100644 --- a/drivers/gpio/pm8916_gpio.c +++ b/drivers/gpio/pm8916_gpio.c @@ -29,7 +29,7 @@ DECLARE_GLOBAL_DATA_PTR; #define REG_STATUS_VAL_MASK 0x1 /* MODE_CTL */ -#define REG_CTL 0x40 +#define REG_CTL 0x40 #define REG_CTL_MODE_MASK 0x70 #define REG_CTL_MODE_INPUT 0x00 #define REG_CTL_MODE_INOUT 0x20 @@ -183,7 +183,7 @@ static int pm8916_gpio_probe(struct udevice *dev) return -ENODEV; reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); - if (reg != 0x5) + if (reg != 0x5 && reg != 0x1) return -ENODEV; return 0; @@ -203,6 +203,7 @@ static int pm8916_gpio_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id pm8916_gpio_ids[] = { { .compatible = "qcom,pm8916-gpio" }, + { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ { } }; @@ -278,6 +279,7 @@ static int pm8941_pwrkey_ofdata_to_platdata(struct udevice *dev) struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); uc_priv->gpio_count = 2; + uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name"); if (uc_priv->bank_name == NULL) uc_priv->bank_name = "pm8916_key"; @@ -286,6 +288,7 @@ static int pm8941_pwrkey_ofdata_to_platdata(struct udevice *dev) static const struct udevice_id pm8941_pwrkey_ids[] = { { .compatible = "qcom,pm8916-pwrkey" }, + { .compatible = "qcom,pm8994-pwrkey" }, { } }; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 8fbeaa740d..ab0627a8af 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -10,6 +10,13 @@ config MMC If you want MMC/SD/SDIO support, you should say Y here and also to your specific host controller driver. +config MMC_WRITE + bool "support for MMC/SD write operations" + depends on MMC + default y + help + Enable write access to MMC and SD Cards + config DM_MMC bool "Enable MMC controllers using Driver Model" depends on DM @@ -42,6 +49,75 @@ config ARM_PL180_MMCI If you have an ARM(R) platform with a Multimedia Card slot, say Y or M here. +config MMC_QUIRKS + bool "Enable quirks" + default y + help + Some cards and hosts may sometimes behave unexpectedly (quirks). + This option enable workarounds to handle those quirks. Some of them + are enabled by default, other may require additionnal flags or are + enabled by the host driver. + +config MMC_HW_PARTITIONING + bool "Support for HW partitioning command(eMMC)" + default y + help + This adds a command and an API to do hardware partitioning on eMMC + devices. + +config MMC_IO_VOLTAGE + bool "Support IO voltage configuration" + help + IO voltage configuration allows selecting the voltage level of the IO + lines (not the level of main supply). This is required for UHS + support. For eMMC this not mandatory, but not enabling this option may + prevent the driver of using the faster modes. + +config SPL_MMC_IO_VOLTAGE + bool "Support IO voltage configuration in SPL" + default n + help + IO voltage configuration allows selecting the voltage level of the IO + lines (not the level of main supply). This is required for UHS + support. For eMMC this not mandatory, but not enabling this option may + prevent the driver of using the faster modes. + +config MMC_UHS_SUPPORT + bool "enable UHS support" + depends on MMC_IO_VOLTAGE + help + The Ultra High Speed (UHS) bus is available on some SDHC and SDXC + cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus + frequency can go up to 208MHz (SDR104) + +config SPL_MMC_UHS_SUPPORT + bool "enable UHS support in SPL" + depends on SPL_MMC_IO_VOLTAGE + help + The Ultra High Speed (UHS) bus is available on some SDHC and SDXC + cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus + frequency can go up to 208MHz (SDR104) + +config MMC_HS200_SUPPORT + bool "enable HS200 support" + help + The HS200 mode is support by some eMMC. The bus frequency is up to + 200MHz. This mode requires tuning the IO. + + +config SPL_MMC_HS200_SUPPORT + bool "enable HS200 support in SPL" + help + The HS200 mode is support by some eMMC. The bus frequency is up to + 200MHz. This mode requires tuning the IO. + +config MMC_VERBOSE + bool "Output more information about the MMC" + default y + help + Enable the output of more information about the card such as the + operating mode. + config SPL_MMC_TINY bool "Tiny MMC framework in SPL" help diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9af375b044..64b6f21c61 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -7,6 +7,7 @@ obj-y += mmc.o obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o +obj-$(CONFIG_$(SPL_)MMC_WRITE) += mmc_write.o ifndef CONFIG_$(SPL_)BLK obj-y += mmc_legacy.o @@ -16,9 +17,6 @@ obj-$(CONFIG_SUPPORT_EMMC_BOOT) += mmc_boot.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o -obj-$(CONFIG_SPL_SAVEENV) += mmc_write.o -else -obj-y += mmc_write.o endif obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 5edd383c68..e40575e589 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -168,6 +168,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, if (host->dev_index > 4) { printf("DWMMC%d: Can't get the dev index\n", host->dev_index); + free(priv); return -EINVAL; } @@ -178,6 +179,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, base = fdtdec_get_addr(blob, node, "reg"); if (!base) { printf("DWMMC%d: Can't get base address\n", host->dev_index); + free(priv); return -EINVAL; } host->ioaddr = (void *)base; @@ -187,6 +189,7 @@ static int exynos_dwmci_get_config(const void *blob, int node, if (err) { printf("DWMMC%d: Can't get sdr-timings for devider\n", host->dev_index); + free(priv); return -EINVAL; } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 499d622c6d..71c62f4233 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -647,7 +647,11 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) esdhc_write32(®s->clktunectrlstatus, 0x0); /* Put VEND_SPEC to default value */ - esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); + if (priv->vs18_enable) + esdhc_write32(®s->vendorspec, (VENDORSPEC_INIT | + ESDHC_VENDORSPEC_VSELECT)); + else + esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); /* Disable DLL_CTRL delay line */ esdhc_write32(®s->dllctrl, 0x0); @@ -665,7 +669,7 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) #endif /* Set the initial clock speed */ - mmc_set_clock(mmc, 400000); + mmc_set_clock(mmc, 400000, false); /* Disable the BRR and BWR bits in IRQSTAT */ esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); @@ -676,9 +680,6 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) /* Set timout to the maximum value */ esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); - if (priv->vs18_enable) - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); - return 0; } diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 22154d13d7..1c108b5470 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -74,6 +74,20 @@ static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) cmdr, cmdr & 0x3F, arg, status, msg); } +static inline void mci_set_blklen(atmel_mci_t *mci, int blklen) +{ + unsigned int version = atmel_mci_get_version(mci); + + blklen &= 0xfffc; + + /* MCI IP version >= 0x200 has blkr */ + if (version >= 0x200) + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)), + &mci->blkr); + else + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr); +} + /* Setup for MCI Clock and Block Size */ #ifdef CONFIG_DM_MMC static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen) @@ -124,7 +138,6 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2); else priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2; - blklen &= 0xfffc; mr = MMCI_BF(CLKDIV, clkdiv); @@ -138,14 +151,10 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) */ if (version >= 0x500) mr |= MMCI_BF(CLKODD, clkodd); - else - mr |= MMCI_BF(BLKLEN, blklen); writel(mr, &mci->mr); - /* MCI IP version >= 0x200 has blkr */ - if (version >= 0x200) - writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + mci_set_blklen(mci, blklen); if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS) writel(MMCI_BIT(HSMODE), &mci->cfg); @@ -236,7 +245,6 @@ static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, { struct atmel_mci_plat *plat = dev_get_platdata(dev); struct atmel_mci_priv *priv = dev_get_priv(dev); - struct mmc *mmc = mmc_get_mmc_dev(dev); atmel_mci_t *mci = plat->mci; #else static int @@ -257,11 +265,13 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ cmdr = mci_encode_cmd(cmd, data, &error_flags); + mci_set_blklen(mci, data->blocksize); + /* For multi blocks read/write, set the block register */ if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) || (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) - writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len), - &mci->blkr); + writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize), + &mci->blkr); /* Send the command */ writel(cmd->cmdarg, &mci->argr); @@ -295,17 +305,15 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (data) { u32 word_count, block_count; u32* ioptr; - u32 sys_blocksize, dummy, i; + u32 i; u32 (*mci_data_op) (atmel_mci_t *mci, u32* data, u32 error_flags); if (data->flags & MMC_DATA_READ) { mci_data_op = mci_data_read; - sys_blocksize = mmc->read_bl_len; ioptr = (u32*)data->dest; } else { mci_data_op = mci_data_write; - sys_blocksize = mmc->write_bl_len; ioptr = (u32*)data->src; } @@ -328,16 +336,6 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 1, cnt, 0); } #endif -#ifdef DEBUG - if (!status && word_count < (sys_blocksize / 4)) - printf("filling rest of block...\n"); -#endif - /* fill the rest of a full block */ - while (!status && word_count < (sys_blocksize / 4)) { - status = mci_data_op(mci, &dummy, - error_flags); - word_count++; - } if (status) { dump_cmd(cmdr, cmd->cmdarg, status, "Data Transfer Failed"); diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c index 4652fab45e..a2cd5d3a44 100644 --- a/drivers/mmc/meson_gx_mmc.c +++ b/drivers/mmc/meson_gx_mmc.c @@ -250,7 +250,7 @@ static int meson_mmc_probe(struct udevice *dev) mmc->priv = pdata; upriv->mmc = mmc; - mmc_set_clock(mmc, cfg->f_min); + mmc_set_clock(mmc, cfg->f_min, false); /* reset all status bits */ meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS); diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c index 5dda20cda5..26c6ab7ad1 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -10,7 +10,6 @@ #include <dm.h> #include <dm/device-internal.h> #include <dm/lists.h> -#include <dm/root.h> #include "mmc_private.h" DECLARE_GLOBAL_DATA_PTR; @@ -51,6 +50,35 @@ int mmc_set_ios(struct mmc *mmc) return dm_mmc_set_ios(mmc->dev); } +void dm_mmc_send_init_stream(struct udevice *dev) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (ops->send_init_stream) + ops->send_init_stream(dev); +} + +void mmc_send_init_stream(struct mmc *mmc) +{ + dm_mmc_send_init_stream(mmc->dev); +} + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (!ops->wait_dat0) + return -ENOSYS; + return ops->wait_dat0(dev, state, timeout); +} + +int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) +{ + return dm_mmc_wait_dat0(mmc->dev, state, timeout); +} +#endif + int dm_mmc_get_wp(struct udevice *dev) { struct dm_mmc_ops *ops = mmc_get_ops(dev); @@ -79,6 +107,73 @@ int mmc_getcd(struct mmc *mmc) return dm_mmc_get_cd(mmc->dev); } +#ifdef MMC_SUPPORTS_TUNING +int dm_mmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (!ops->execute_tuning) + return -ENOSYS; + return ops->execute_tuning(dev, opcode); +} + +int mmc_execute_tuning(struct mmc *mmc, uint opcode) +{ + return dm_mmc_execute_tuning(mmc->dev, opcode); +} +#endif + +int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg) +{ + int val; + + val = dev_read_u32_default(dev, "bus-width", 1); + + switch (val) { + case 0x8: + cfg->host_caps |= MMC_MODE_8BIT; + /* fall through */ + case 0x4: + cfg->host_caps |= MMC_MODE_4BIT; + /* fall through */ + case 0x1: + cfg->host_caps |= MMC_MODE_1BIT; + break; + default: + debug("warning: %s invalid bus-width property. using 1-bit\n", + dev_read_name(dev)); + cfg->host_caps |= MMC_MODE_1BIT; + break; + } + + cfg->f_max = dev_read_u32_default(dev, "max-frequency", 52000000); + + if (dev_read_bool(dev, "cap-sd-highspeed")) + cfg->host_caps |= MMC_CAP(SD_HS); + if (dev_read_bool(dev, "cap-mmc-highspeed")) + cfg->host_caps |= MMC_CAP(MMC_HS); + if (dev_read_bool(dev, "sd-uhs-sdr12")) + cfg->host_caps |= MMC_CAP(UHS_SDR12); + if (dev_read_bool(dev, "sd-uhs-sdr25")) + cfg->host_caps |= MMC_CAP(UHS_SDR25); + if (dev_read_bool(dev, "sd-uhs-sdr50")) + cfg->host_caps |= MMC_CAP(UHS_SDR50); + if (dev_read_bool(dev, "sd-uhs-sdr104")) + cfg->host_caps |= MMC_CAP(UHS_SDR104); + if (dev_read_bool(dev, "sd-uhs-ddr50")) + cfg->host_caps |= MMC_CAP(UHS_DDR50); + if (dev_read_bool(dev, "mmc-ddr-1_8v")) + cfg->host_caps |= MMC_CAP(MMC_DDR_52); + if (dev_read_bool(dev, "mmc-ddr-1_2v")) + cfg->host_caps |= MMC_CAP(MMC_DDR_52); + if (dev_read_bool(dev, "mmc-hs200-1_8v")) + cfg->host_caps |= MMC_CAP(MMC_HS_200); + if (dev_read_bool(dev, "mmc-hs200-1_2v")) + cfg->host_caps |= MMC_CAP(MMC_HS_200); + + return 0; +} + struct mmc *mmc_get_mmc_dev(struct udevice *dev) { struct mmc_uclass_priv *upriv; @@ -275,7 +370,7 @@ static int mmc_blk_probe(struct udevice *dev) static const struct blk_ops mmc_blk_ops = { .read = mmc_bread, -#ifndef CONFIG_SPL_BUILD +#if CONFIG_IS_ENABLED(MMC_WRITE) .write = mmc_bwrite, .erase = mmc_berase, #endif diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 38d2e07dd5..53c819187e 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -22,13 +22,9 @@ #include <div64.h> #include "mmc_private.h" -static const unsigned int sd_au_size[] = { - 0, SZ_16K / 512, SZ_32K / 512, - SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, - SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, - SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, - SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, -}; +static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); +static int mmc_power_cycle(struct mmc *mmc); +static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps); #if CONFIG_IS_ENABLED(MMC_TINY) static struct mmc mmc_static; @@ -54,6 +50,14 @@ struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) #endif #if !CONFIG_IS_ENABLED(DM_MMC) + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) +{ + return -ENOSYS; +} +#endif + __weak int board_mmc_getwp(struct mmc *mmc) { return -1; @@ -149,6 +153,71 @@ void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd) } #endif +#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG) +const char *mmc_mode_name(enum bus_mode mode) +{ + static const char *const names[] = { + [MMC_LEGACY] = "MMC legacy", + [SD_LEGACY] = "SD Legacy", + [MMC_HS] = "MMC High Speed (26MHz)", + [SD_HS] = "SD High Speed (50MHz)", + [UHS_SDR12] = "UHS SDR12 (25MHz)", + [UHS_SDR25] = "UHS SDR25 (50MHz)", + [UHS_SDR50] = "UHS SDR50 (100MHz)", + [UHS_SDR104] = "UHS SDR104 (208MHz)", + [UHS_DDR50] = "UHS DDR50 (50MHz)", + [MMC_HS_52] = "MMC High Speed (52MHz)", + [MMC_DDR_52] = "MMC DDR52 (52MHz)", + [MMC_HS_200] = "HS200 (200MHz)", + }; + + if (mode >= MMC_MODES_END) + return "Unknown mode"; + else + return names[mode]; +} +#endif + +static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode) +{ + static const int freqs[] = { + [SD_LEGACY] = 25000000, + [MMC_HS] = 26000000, + [SD_HS] = 50000000, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + [UHS_SDR12] = 25000000, + [UHS_SDR25] = 50000000, + [UHS_SDR50] = 100000000, + [UHS_DDR50] = 50000000, +#ifdef MMC_SUPPORTS_TUNING + [UHS_SDR104] = 208000000, +#endif +#endif + [MMC_HS_52] = 52000000, + [MMC_DDR_52] = 52000000, +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + [MMC_HS_200] = 200000000, +#endif + }; + + if (mode == MMC_LEGACY) + return mmc->legacy_speed; + else if (mode >= MMC_MODES_END) + return 0; + else + return freqs[mode]; +} + +static int mmc_select_mode(struct mmc *mmc, enum bus_mode mode) +{ + mmc->selected_mode = mode; + mmc->tran_speed = mmc_mode2freq(mmc, mode); + mmc->ddr_mode = mmc_is_mode_ddr(mode); + debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), + mmc->tran_speed / 1000000); + return 0; +} + #if !CONFIG_IS_ENABLED(DM_MMC) int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { @@ -179,10 +248,11 @@ int mmc_send_status(struct mmc *mmc, int timeout) (cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG) break; - else if (cmd.response[0] & MMC_STATUS_MASK) { + + if (cmd.response[0] & MMC_STATUS_MASK) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Status Error: 0x%08X\n", - cmd.response[0]); + pr_err("Status Error: 0x%08X\n", + cmd.response[0]); #endif return -ECOMM; } @@ -198,7 +268,7 @@ int mmc_send_status(struct mmc *mmc, int timeout) mmc_trace_state(mmc, &cmd); if (timeout <= 0) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Timeout waiting card ready\n"); + pr_err("Timeout waiting card ready\n"); #endif return -ETIMEDOUT; } @@ -209,6 +279,7 @@ int mmc_send_status(struct mmc *mmc, int timeout) int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; + int err; if (mmc->ddr_mode) return 0; @@ -217,8 +288,95 @@ int mmc_set_blocklen(struct mmc *mmc, int len) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = len; - return mmc_send_cmd(mmc, &cmd, NULL); + err = mmc_send_cmd(mmc, &cmd, NULL); + +#ifdef CONFIG_MMC_QUIRKS + if (err && (mmc->quirks & MMC_QUIRK_RETRY_SET_BLOCKLEN)) { + int retries = 4; + /* + * It has been seen that SET_BLOCKLEN may fail on the first + * attempt, let's try a few more time + */ + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) + break; + } while (retries--); + } +#endif + + return err; +} + +#ifdef MMC_SUPPORTS_TUNING +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error) +{ + struct mmc_cmd cmd; + struct mmc_data data; + const u8 *tuning_block_pattern; + int size, err; + + if (mmc->bus_width == 8) { + tuning_block_pattern = tuning_blk_pattern_8bit; + size = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->bus_width == 4) { + tuning_block_pattern = tuning_blk_pattern_4bit; + size = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + + ALLOC_CACHE_ALIGN_BUFFER(u8, data_buf, size); + + cmd.cmdidx = opcode; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1; + + data.dest = (void *)data_buf; + data.blocks = 1; + data.blocksize = size; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + if (err) + return err; + + if (memcmp(data_buf, tuning_block_pattern, size)) + return -EIO; + + return 0; } +#endif static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) @@ -252,7 +410,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("mmc fail to send stop cmd\n"); + pr_err("mmc fail to send stop cmd\n"); #endif return 0; } @@ -292,8 +450,8 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, if ((start + blkcnt) > block_dev->lba) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", - start + blkcnt, block_dev->lba); + pr_err("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, block_dev->lba); #endif return 0; } @@ -339,7 +497,69 @@ static int mmc_go_idle(struct mmc *mmc) return 0; } -static int sd_send_op_cond(struct mmc *mmc) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage) +{ + struct mmc_cmd cmd; + int err = 0; + + /* + * Send CMD11 only if the request is to switch the card to + * 1.8V signalling. + */ + if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) + return mmc_set_signal_voltage(mmc, signal_voltage); + + cmd.cmdidx = SD_CMD_SWITCH_UHS18V; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + if (!mmc_host_is_spi(mmc) && (cmd.response[0] & MMC_STATUS_ERROR)) + return -EIO; + + /* + * The card should drive cmd and dat[0:3] low immediately + * after the response of cmd11, but wait 100 us to be sure + */ + err = mmc_wait_dat0(mmc, 0, 100); + if (err == -ENOSYS) + udelay(100); + else if (err) + return -ETIMEDOUT; + + /* + * During a signal voltage level switch, the clock must be gated + * for 5 ms according to the SD spec + */ + mmc_set_clock(mmc, mmc->clock, true); + + err = mmc_set_signal_voltage(mmc, signal_voltage); + if (err) + return err; + + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + mdelay(10); + mmc_set_clock(mmc, mmc->clock, false); + + /* + * Failure to switch is indicated by the card holding + * dat[0:3] low. Wait for at least 1 ms according to spec + */ + err = mmc_wait_dat0(mmc, 1, 1000); + if (err == -ENOSYS) + udelay(1000); + else if (err) + return -ETIMEDOUT; + + return 0; +} +#endif + +static int sd_send_op_cond(struct mmc *mmc, bool uhs_en) { int timeout = 1000; int err; @@ -371,6 +591,9 @@ static int sd_send_op_cond(struct mmc *mmc) if (mmc->version == SD_VERSION_2) cmd.cmdarg |= OCR_HCS; + if (uhs_en) + cmd.cmdarg |= OCR_S18R; + err = mmc_send_cmd(mmc, &cmd, NULL); if (err) @@ -401,6 +624,15 @@ static int sd_send_op_cond(struct mmc *mmc) mmc->ocr = cmd.response[0]; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + if (uhs_en && !(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000) + == 0x41000000) { + err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); + if (err) + return err; + } +#endif + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); mmc->rca = 0; @@ -546,53 +778,86 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) } -static int mmc_change_freq(struct mmc *mmc) +static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode) { - ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - char cardtype; int err; + int speed_bits; - mmc->card_caps = 0; + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); - if (mmc_host_is_spi(mmc)) - return 0; + switch (mode) { + case MMC_HS: + case MMC_HS_52: + case MMC_DDR_52: + speed_bits = EXT_CSD_TIMING_HS; + break; +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + case MMC_HS_200: + speed_bits = EXT_CSD_TIMING_HS200; + break; +#endif + case MMC_LEGACY: + speed_bits = EXT_CSD_TIMING_LEGACY; + break; + default: + return -EINVAL; + } + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + speed_bits); + if (err) + return err; - /* Only version 4 supports high-speed */ - if (mmc->version < MMC_VERSION_4) - return 0; + if ((mode == MMC_HS) || (mode == MMC_HS_52)) { + /* Now check to see that it worked */ + err = mmc_send_ext_csd(mmc, test_csd); + if (err) + return err; - mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; + /* No high-speed support */ + if (!test_csd[EXT_CSD_HS_TIMING]) + return -ENOTSUPP; + } - err = mmc_send_ext_csd(mmc, ext_csd); + return 0; +} - if (err) - return err; +static int mmc_get_capabilities(struct mmc *mmc) +{ + u8 *ext_csd = mmc->ext_csd; + char cardtype; - cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; + mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY); - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); + if (mmc_host_is_spi(mmc)) + return 0; - if (err) - return err; + /* Only version 4 supports high-speed */ + if (mmc->version < MMC_VERSION_4) + return 0; - /* Now check to see that it worked */ - err = mmc_send_ext_csd(mmc, ext_csd); + if (!ext_csd) { + pr_err("No ext_csd found!\n"); /* this should enver happen */ + return -ENOTSUPP; + } - if (err) - return err; + mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; - /* No high-speed support */ - if (!ext_csd[EXT_CSD_HS_TIMING]) - return 0; + cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f; + mmc->cardtype = cardtype; - /* High Speed is set, there are two types: 52MHz and 26MHz */ +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V | + EXT_CSD_CARD_TYPE_HS200_1_8V)) { + mmc->card_caps |= MMC_MODE_HS200; + } +#endif if (cardtype & EXT_CSD_CARD_TYPE_52) { - if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) + if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) mmc->card_caps |= MMC_MODE_DDR_52MHz; - mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - } else { - mmc->card_caps |= MMC_MODE_HS; + mmc->card_caps |= MMC_MODE_HS_52MHz; } + if (cardtype & EXT_CSD_CARD_TYPE_26) + mmc->card_caps |= MMC_MODE_HS; return 0; } @@ -625,10 +890,46 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num) return 0; } +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) +static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) +{ + int forbidden = 0; + bool change = false; + + if (part_num & PART_ACCESS_MASK) + forbidden = MMC_CAP(MMC_HS_200); + + if (MMC_CAP(mmc->selected_mode) & forbidden) { + debug("selected mode (%s) is forbidden for part %d\n", + mmc_mode_name(mmc->selected_mode), part_num); + change = true; + } else if (mmc->selected_mode != mmc->best_mode) { + debug("selected mode is not optimal\n"); + change = true; + } + + if (change) + return mmc_select_mode_and_width(mmc, + mmc->card_caps & ~forbidden); + + return 0; +} +#else +static inline int mmc_boot_part_access_chk(struct mmc *mmc, + unsigned int part_num) +{ + return 0; +} +#endif + int mmc_switch_part(struct mmc *mmc, unsigned int part_num) { int ret; + ret = mmc_boot_part_access_chk(mmc, part_num); + if (ret) + return ret; + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, (mmc->part_config & ~PART_ACCESS_MASK) | (part_num & PART_ACCESS_MASK)); @@ -645,6 +946,7 @@ int mmc_switch_part(struct mmc *mmc, unsigned int part_num) return ret; } +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) int mmc_hwpart_config(struct mmc *mmc, const struct mmc_hwpart_conf *conf, enum mmc_hwpart_conf_mode mode) @@ -663,17 +965,17 @@ int mmc_hwpart_config(struct mmc *mmc, return -EINVAL; if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) { - printf("eMMC >= 4.4 required for enhanced user data area\n"); + pr_err("eMMC >= 4.4 required for enhanced user data area\n"); return -EMEDIUMTYPE; } if (!(mmc->part_support & PART_SUPPORT)) { - printf("Card does not support partitioning\n"); + pr_err("Card does not support partitioning\n"); return -EMEDIUMTYPE; } if (!mmc->hc_wp_grp_size) { - printf("Card does not define HC WP group size\n"); + pr_err("Card does not define HC WP group size\n"); return -EMEDIUMTYPE; } @@ -681,7 +983,7 @@ int mmc_hwpart_config(struct mmc *mmc, if (conf->user.enh_size) { if (conf->user.enh_size % mmc->hc_wp_grp_size || conf->user.enh_start % mmc->hc_wp_grp_size) { - printf("User data enhanced area not HC WP group " + pr_err("User data enhanced area not HC WP group " "size aligned\n"); return -EINVAL; } @@ -700,7 +1002,7 @@ int mmc_hwpart_config(struct mmc *mmc, for (pidx = 0; pidx < 4; pidx++) { if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) { - printf("GP%i partition not HC WP group size " + pr_err("GP%i partition not HC WP group size " "aligned\n", pidx+1); return -EINVAL; } @@ -712,7 +1014,7 @@ int mmc_hwpart_config(struct mmc *mmc, } if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) { - printf("Card does not support enhanced attribute\n"); + pr_err("Card does not support enhanced attribute\n"); return -EMEDIUMTYPE; } @@ -725,7 +1027,7 @@ int mmc_hwpart_config(struct mmc *mmc, (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) + ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]; if (tot_enh_size_mult > max_enh_size_mult) { - printf("Total enhanced size exceeds maximum (%u > %u)\n", + pr_err("Total enhanced size exceeds maximum (%u > %u)\n", tot_enh_size_mult, max_enh_size_mult); return -EMEDIUMTYPE; } @@ -759,7 +1061,7 @@ int mmc_hwpart_config(struct mmc *mmc, if (ext_csd[EXT_CSD_PARTITION_SETTING] & EXT_CSD_PARTITION_SETTING_COMPLETED) { - printf("Card already partitioned\n"); + pr_err("Card already partitioned\n"); return -EPERM; } @@ -838,6 +1140,7 @@ int mmc_hwpart_config(struct mmc *mmc, return 0; } +#endif #if !CONFIG_IS_ENABLED(DM_MMC) int mmc_getcd(struct mmc *mmc) @@ -878,16 +1181,19 @@ static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) } -static int sd_change_freq(struct mmc *mmc) +static int sd_get_capabilities(struct mmc *mmc) { int err; struct mmc_cmd cmd; - ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); - ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); + ALLOC_CACHE_ALIGN_BUFFER(__be32, scr, 2); + ALLOC_CACHE_ALIGN_BUFFER(__be32, switch_status, 16); struct mmc_data data; int timeout; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + u32 sd3_bus_mode; +#endif - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(SD_LEGACY); if (mmc_host_is_spi(mmc)) return 0; @@ -964,32 +1270,115 @@ retry_scr: } /* If high-speed isn't supported, we return */ - if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) - return 0; + if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) + mmc->card_caps |= MMC_CAP(SD_HS); - /* - * If the host doesn't support SD_HIGHSPEED, do not switch card to - * HIGHSPEED mode even if the card support SD_HIGHSPPED. - * This can avoid furthur problem when the card runs in different - * mode between the host. - */ - if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && - (mmc->cfg->host_caps & MMC_MODE_HS))) +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + /* Version before 3.0 don't support UHS modes */ + if (mmc->version < SD_VERSION_3) return 0; - err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); + sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f; + if (sd3_bus_mode & SD_MODE_UHS_SDR104) + mmc->card_caps |= MMC_CAP(UHS_SDR104); + if (sd3_bus_mode & SD_MODE_UHS_SDR50) + mmc->card_caps |= MMC_CAP(UHS_SDR50); + if (sd3_bus_mode & SD_MODE_UHS_SDR25) + mmc->card_caps |= MMC_CAP(UHS_SDR25); + if (sd3_bus_mode & SD_MODE_UHS_SDR12) + mmc->card_caps |= MMC_CAP(UHS_SDR12); + if (sd3_bus_mode & SD_MODE_UHS_DDR50) + mmc->card_caps |= MMC_CAP(UHS_DDR50); +#endif + + return 0; +} + +static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) +{ + int err; + + ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); + int speed; + + switch (mode) { + case SD_LEGACY: + speed = UHS_SDR12_BUS_SPEED; + break; + case SD_HS: + speed = HIGH_SPEED_BUS_SPEED; + break; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + case UHS_SDR12: + speed = UHS_SDR12_BUS_SPEED; + break; + case UHS_SDR25: + speed = UHS_SDR25_BUS_SPEED; + break; + case UHS_SDR50: + speed = UHS_SDR50_BUS_SPEED; + break; + case UHS_DDR50: + speed = UHS_DDR50_BUS_SPEED; + break; + case UHS_SDR104: + speed = UHS_SDR104_BUS_SPEED; + break; +#endif + default: + return -EINVAL; + } + + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status); + if (err) + return err; + if ((__be32_to_cpu(switch_status[4]) >> 24) != speed) + return -ENOTSUPP; + + return 0; +} + +int sd_select_bus_width(struct mmc *mmc, int w) +{ + int err; + struct mmc_cmd cmd; + + if ((w != 4) && (w != 1)) + return -EINVAL; + + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); if (err) return err; - if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) - mmc->card_caps |= MMC_MODE_HS; + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + if (w == 4) + cmd.cmdarg = 2; + else if (w == 1) + cmd.cmdarg = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; return 0; } +#if CONFIG_IS_ENABLED(MMC_WRITE) static int sd_read_ssr(struct mmc *mmc) { + static const unsigned int sd_au_size[] = { + 0, SZ_16K / 512, SZ_32K / 512, + SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, + SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, + SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, + SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, + SZ_64M / 512, + }; int err, i; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); @@ -1043,7 +1432,7 @@ retry_ssr: return 0; } - +#endif /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ static const int fbase[] = { @@ -1075,15 +1464,42 @@ static const u8 multipliers[] = { 80, }; +static inline int bus_width(uint cap) +{ + if (cap == MMC_MODE_8BIT) + return 8; + if (cap == MMC_MODE_4BIT) + return 4; + if (cap == MMC_MODE_1BIT) + return 1; + pr_warn("invalid bus witdh capability 0x%x\n", cap); + return 0; +} + #if !CONFIG_IS_ENABLED(DM_MMC) -static void mmc_set_ios(struct mmc *mmc) +#ifdef MMC_SUPPORTS_TUNING +static int mmc_execute_tuning(struct mmc *mmc, uint opcode) { + return -ENOTSUPP; +} +#endif + +static void mmc_send_init_stream(struct mmc *mmc) +{ +} + +static int mmc_set_ios(struct mmc *mmc) +{ + int ret = 0; + if (mmc->cfg->ops->set_ios) - mmc->cfg->ops->set_ios(mmc); + ret = mmc->cfg->ops->set_ios(mmc); + + return ret; } #endif -void mmc_set_clock(struct mmc *mmc, uint clock) +int mmc_set_clock(struct mmc *mmc, uint clock, bool disable) { if (clock > mmc->cfg->f_max) clock = mmc->cfg->f_max; @@ -1092,27 +1508,619 @@ void mmc_set_clock(struct mmc *mmc, uint clock) clock = mmc->cfg->f_min; mmc->clock = clock; + mmc->clk_disable = disable; - mmc_set_ios(mmc); + return mmc_set_ios(mmc); } -static void mmc_set_bus_width(struct mmc *mmc, uint width) +static int mmc_set_bus_width(struct mmc *mmc, uint width) { mmc->bus_width = width; - mmc_set_ios(mmc); + return mmc_set_ios(mmc); +} + +#if CONFIG_IS_ENABLED(MMC_VERBOSE) || defined(DEBUG) +/* + * helper function to display the capabilities in a human + * friendly manner. The capabilities include bus width and + * supported modes. + */ +void mmc_dump_capabilities(const char *text, uint caps) +{ + enum bus_mode mode; + + printf("%s: widths [", text); + if (caps & MMC_MODE_8BIT) + printf("8, "); + if (caps & MMC_MODE_4BIT) + printf("4, "); + if (caps & MMC_MODE_1BIT) + printf("1, "); + printf("\b\b] modes ["); + for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) + if (MMC_CAP(mode) & caps) + printf("%s, ", mmc_mode_name(mode)); + printf("\b\b]\n"); +} +#endif + +struct mode_width_tuning { + enum bus_mode mode; + uint widths; +#ifdef MMC_SUPPORTS_TUNING + uint tuning; +#endif +}; + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) +int mmc_voltage_to_mv(enum mmc_voltage voltage) +{ + switch (voltage) { + case MMC_SIGNAL_VOLTAGE_000: return 0; + case MMC_SIGNAL_VOLTAGE_330: return 3300; + case MMC_SIGNAL_VOLTAGE_180: return 1800; + case MMC_SIGNAL_VOLTAGE_120: return 1200; + } + return -EINVAL; +} + +static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) +{ + int err; + + if (mmc->signal_voltage == signal_voltage) + return 0; + + mmc->signal_voltage = signal_voltage; + err = mmc_set_ios(mmc); + if (err) + debug("unable to set voltage (err %d)\n", err); + + return err; +} +#else +static inline int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage) +{ + return 0; +} +#endif + +static const struct mode_width_tuning sd_modes_by_pref[] = { +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +#ifdef MMC_SUPPORTS_TUNING + { + .mode = UHS_SDR104, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + .tuning = MMC_CMD_SEND_TUNING_BLOCK + }, +#endif + { + .mode = UHS_SDR50, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = UHS_DDR50, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = UHS_SDR25, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, +#endif + { + .mode = SD_HS, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + { + .mode = UHS_SDR12, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, +#endif + { + .mode = SD_LEGACY, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + } +}; + +#define for_each_sd_mode_by_pref(caps, mwt) \ + for (mwt = sd_modes_by_pref;\ + mwt < sd_modes_by_pref + ARRAY_SIZE(sd_modes_by_pref);\ + mwt++) \ + if (caps & MMC_CAP(mwt->mode)) + +static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) +{ + int err; + uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; + const struct mode_width_tuning *mwt; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; +#else + bool uhs_en = false; +#endif + uint caps; + +#ifdef DEBUG + mmc_dump_capabilities("sd card", card_caps); + mmc_dump_capabilities("host", mmc->host_caps); +#endif + + /* Restrict card's capabilities by what the host can do */ + caps = card_caps & mmc->host_caps; + + if (!uhs_en) + caps &= ~UHS_CAPS; + + for_each_sd_mode_by_pref(caps, mwt) { + uint *w; + + for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { + if (*w & caps & mwt->widths) { + debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(*w), + mmc_mode2freq(mmc, mwt->mode) / 1000000); + + /* configure the bus width (card + host) */ + err = sd_select_bus_width(mmc, bus_width(*w)); + if (err) + goto error; + mmc_set_bus_width(mmc, bus_width(*w)); + + /* configure the bus mode (card) */ + err = sd_set_card_speed(mmc, mwt->mode); + if (err) + goto error; + + /* configure the bus mode (host) */ + mmc_select_mode(mmc, mwt->mode); + mmc_set_clock(mmc, mmc->tran_speed, false); + +#ifdef MMC_SUPPORTS_TUNING + /* execute tuning if needed */ + if (mwt->tuning && !mmc_host_is_spi(mmc)) { + err = mmc_execute_tuning(mmc, + mwt->tuning); + if (err) { + debug("tuning failed\n"); + goto error; + } + } +#endif + +#if CONFIG_IS_ENABLED(MMC_WRITE) + err = sd_read_ssr(mmc); + if (!err) + pr_warn("unable to read ssr\n"); +#endif + if (!err) + return 0; + +error: + /* revert to a safer bus speed */ + mmc_select_mode(mmc, SD_LEGACY); + mmc_set_clock(mmc, mmc->tran_speed, false); + } + } + } + + printf("unable to select a mode\n"); + return -ENOTSUPP; +} + +/* + * read the compare the part of ext csd that is constant. + * This can be used to check that the transfer is working + * as expected. + */ +static int mmc_read_and_compare_ext_csd(struct mmc *mmc) +{ + int err; + const u8 *ext_csd = mmc->ext_csd; + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); + + if (mmc->version < MMC_VERSION_4) + return 0; + + err = mmc_send_ext_csd(mmc, test_csd); + if (err) + return err; + + /* Only compare read only fields */ + if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && + ext_csd[EXT_CSD_HC_WP_GRP_SIZE] + == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && + ext_csd[EXT_CSD_REV] + == test_csd[EXT_CSD_REV] && + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && + memcmp(&ext_csd[EXT_CSD_SEC_CNT], + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) + return 0; + + return -EBADMSG; +} + +#if CONFIG_IS_ENABLED(MMC_IO_VOLTAGE) +static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, + uint32_t allowed_mask) +{ + u32 card_mask = 0; + + switch (mode) { + case MMC_HS_200: + if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V) + card_mask |= MMC_SIGNAL_VOLTAGE_180; + if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V) + card_mask |= MMC_SIGNAL_VOLTAGE_120; + break; + case MMC_DDR_52: + if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) + card_mask |= MMC_SIGNAL_VOLTAGE_330 | + MMC_SIGNAL_VOLTAGE_180; + if (mmc->cardtype & EXT_CSD_CARD_TYPE_DDR_1_2V) + card_mask |= MMC_SIGNAL_VOLTAGE_120; + break; + default: + card_mask |= MMC_SIGNAL_VOLTAGE_330; + break; + } + + while (card_mask & allowed_mask) { + enum mmc_voltage best_match; + + best_match = 1 << (ffs(card_mask & allowed_mask) - 1); + if (!mmc_set_signal_voltage(mmc, best_match)) + return 0; + + allowed_mask &= ~best_match; + } + + return -ENOTSUPP; +} +#else +static inline int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode, + uint32_t allowed_mask) +{ + return 0; +} +#endif + +static const struct mode_width_tuning mmc_modes_by_pref[] = { +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) + { + .mode = MMC_HS_200, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, + .tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200 + }, +#endif + { + .mode = MMC_DDR_52, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, + }, + { + .mode = MMC_HS_52, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = MMC_HS, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = MMC_LEGACY, + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, + } +}; + +#define for_each_mmc_mode_by_pref(caps, mwt) \ + for (mwt = mmc_modes_by_pref;\ + mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\ + mwt++) \ + if (caps & MMC_CAP(mwt->mode)) + +static const struct ext_csd_bus_width { + uint cap; + bool is_ddr; + uint ext_csd_bits; +} ext_csd_bus_width[] = { + {MMC_MODE_8BIT, true, EXT_CSD_DDR_BUS_WIDTH_8}, + {MMC_MODE_4BIT, true, EXT_CSD_DDR_BUS_WIDTH_4}, + {MMC_MODE_8BIT, false, EXT_CSD_BUS_WIDTH_8}, + {MMC_MODE_4BIT, false, EXT_CSD_BUS_WIDTH_4}, + {MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1}, +}; + +#define for_each_supported_width(caps, ddr, ecbv) \ + for (ecbv = ext_csd_bus_width;\ + ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\ + ecbv++) \ + if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap)) + +static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) +{ + int err; + const struct mode_width_tuning *mwt; + const struct ext_csd_bus_width *ecbw; + +#ifdef DEBUG + mmc_dump_capabilities("mmc", card_caps); + mmc_dump_capabilities("host", mmc->host_caps); +#endif + + /* Restrict card's capabilities by what the host can do */ + card_caps &= mmc->host_caps; + + /* Only version 4 of MMC supports wider bus widths */ + if (mmc->version < MMC_VERSION_4) + return 0; + + if (!mmc->ext_csd) { + debug("No ext_csd found!\n"); /* this should enver happen */ + return -ENOTSUPP; + } + + mmc_set_clock(mmc, mmc->legacy_speed, false); + + for_each_mmc_mode_by_pref(card_caps, mwt) { + for_each_supported_width(card_caps & mwt->widths, + mmc_is_mode_ddr(mwt->mode), ecbw) { + enum mmc_voltage old_voltage; + debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(ecbw->cap), + mmc_mode2freq(mmc, mwt->mode) / 1000000); + old_voltage = mmc->signal_voltage; + err = mmc_set_lowest_voltage(mmc, mwt->mode, + MMC_ALL_SIGNAL_VOLTAGE); + if (err) + continue; + + /* configure the bus width (card + host) */ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ecbw->ext_csd_bits & ~EXT_CSD_DDR_FLAG); + if (err) + goto error; + mmc_set_bus_width(mmc, bus_width(ecbw->cap)); + + /* configure the bus speed (card) */ + err = mmc_set_card_speed(mmc, mwt->mode); + if (err) + goto error; + + /* + * configure the bus width AND the ddr mode (card) + * The host side will be taken care of in the next step + */ + if (ecbw->ext_csd_bits & EXT_CSD_DDR_FLAG) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ecbw->ext_csd_bits); + if (err) + goto error; + } + + /* configure the bus mode (host) */ + mmc_select_mode(mmc, mwt->mode); + mmc_set_clock(mmc, mmc->tran_speed, false); +#ifdef MMC_SUPPORTS_TUNING + + /* execute tuning if needed */ + if (mwt->tuning) { + err = mmc_execute_tuning(mmc, mwt->tuning); + if (err) { + debug("tuning failed\n"); + goto error; + } + } +#endif + + /* do a transfer to check the configuration */ + err = mmc_read_and_compare_ext_csd(mmc); + if (!err) + return 0; +error: + mmc_set_signal_voltage(mmc, old_voltage); + /* if an error occured, revert to a safer bus mode */ + mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); + mmc_select_mode(mmc, MMC_LEGACY); + mmc_set_bus_width(mmc, 1); + } + } + + pr_err("unable to select a mode\n"); + + return -ENOTSUPP; +} + +static int mmc_startup_v4(struct mmc *mmc) +{ + int err, i; + u64 capacity; + bool has_parts = false; + bool part_completed; + static const u32 mmc_versions[] = { + MMC_VERSION_4, + MMC_VERSION_4_1, + MMC_VERSION_4_2, + MMC_VERSION_4_3, + MMC_VERSION_4_41, + MMC_VERSION_4_5, + MMC_VERSION_5_0, + MMC_VERSION_5_1 + }; + + ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); + + if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4)) + return 0; + + /* check ext_csd version and capacity */ + err = mmc_send_ext_csd(mmc, ext_csd); + if (err) + goto error; + + /* store the ext csd for future reference */ + if (!mmc->ext_csd) + mmc->ext_csd = malloc(MMC_MAX_BLOCK_LEN); + if (!mmc->ext_csd) + return -ENOMEM; + memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN); + + if (ext_csd[EXT_CSD_REV] > ARRAY_SIZE(mmc_versions)) + return -EINVAL; + + mmc->version = mmc_versions[ext_csd[EXT_CSD_REV]]; + + if (mmc->version >= MMC_VERSION_4_2) { + /* + * According to the JEDEC Standard, the value of + * ext_csd's capacity is valid if the value is more + * than 2GB + */ + capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 + | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 + | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 + | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + capacity *= MMC_MAX_BLOCK_LEN; + if ((capacity >> 20) > 2 * 1024) + mmc->capacity_user = capacity; + } + + /* The partition data may be non-zero but it is only + * effective if PARTITION_SETTING_COMPLETED is set in + * EXT_CSD, so ignore any data if this bit is not set, + * except for enabling the high-capacity group size + * definition (see below). + */ + part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & + EXT_CSD_PARTITION_SETTING_COMPLETED); + + /* store the partition info of emmc */ + mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || + ext_csd[EXT_CSD_BOOT_MULT]) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + if (part_completed && + (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) + mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + uint mult = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + if (mult) + has_parts = true; + if (!part_completed) + continue; + mmc->capacity_gp[i] = mult; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->capacity_gp[i] <<= 19; + } + +#ifndef CONFIG_SPL_BUILD + if (part_completed) { + mmc->enh_user_size = + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) + + (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) + + ext_csd[EXT_CSD_ENH_SIZE_MULT]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + mmc->enh_user_size <<= 19; + mmc->enh_user_start = + (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24) + + (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) + + (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) + + ext_csd[EXT_CSD_ENH_START_ADDR]; + if (mmc->high_capacity) + mmc->enh_user_start <<= 9; + } +#endif + + /* + * Host needs to enable ERASE_GRP_DEF bit if device is + * partitioned. This bit will be lost every time after a reset + * or power off. This will affect erase size. + */ + if (part_completed) + has_parts = true; + if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && + (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) + has_parts = true; + if (has_parts) { + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GROUP_DEF, 1); + + if (err) + goto error; + + ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; + } + + if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { +#if CONFIG_IS_ENABLED(MMC_WRITE) + /* Read out group size from ext_csd */ + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; +#endif + /* + * if high capacity and partition setting completed + * SEC_COUNT is valid even if it is smaller than 2 GiB + * JEDEC Standard JESD84-B45, 6.2.4 + */ + if (mmc->high_capacity && part_completed) { + capacity = (ext_csd[EXT_CSD_SEC_CNT]) | + (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | + (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | + (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); + capacity *= MMC_MAX_BLOCK_LEN; + mmc->capacity_user = capacity; + } + } +#if CONFIG_IS_ENABLED(MMC_WRITE) + else { + /* Calculate the group size from the csd value. */ + int erase_gsz, erase_gmul; + + erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; + erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; + mmc->erase_grp_size = (erase_gsz + 1) + * (erase_gmul + 1); + } +#endif +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) + mmc->hc_wp_grp_size = 1024 + * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; +#endif + + mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; + + return 0; +error: + if (mmc->ext_csd) { + free(mmc->ext_csd); + mmc->ext_csd = NULL; + } + return err; } static int mmc_startup(struct mmc *mmc) { int err, i; uint mult, freq; - u64 cmult, csize, capacity; + u64 cmult, csize; struct mmc_cmd cmd; - ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); - ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); - bool has_parts = false; - bool part_completed; struct blk_desc *bdesc; #ifdef CONFIG_MMC_SPI_CRC_ON @@ -1121,7 +2129,6 @@ static int mmc_startup(struct mmc *mmc) cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 1; err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) return err; } @@ -1135,6 +2142,21 @@ static int mmc_startup(struct mmc *mmc) err = mmc_send_cmd(mmc, &cmd, NULL); +#ifdef CONFIG_MMC_QUIRKS + if (err && (mmc->quirks & MMC_QUIRK_RETRY_SEND_CID)) { + int retries = 4; + /* + * It has been seen that SEND_CID may fail on the first + * attempt, let's try a few more time + */ + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (!err) + break; + } while (retries--); + } +#endif + if (err) return err; @@ -1203,15 +2225,18 @@ static int mmc_startup(struct mmc *mmc) freq = fbase[(cmd.response[0] & 0x7)]; mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; - mmc->tran_speed = freq * mult; + mmc->legacy_speed = freq * mult; + mmc_select_mode(mmc, MMC_LEGACY); mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); +#if CONFIG_IS_ENABLED(MMC_WRITE) if (IS_SD(mmc)) mmc->write_bl_len = mmc->read_bl_len; else mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); +#endif if (mmc->high_capacity) { csize = (mmc->csd[1] & 0x3f) << 16 @@ -1233,15 +2258,17 @@ static int mmc_startup(struct mmc *mmc) if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) mmc->read_bl_len = MMC_MAX_BLOCK_LEN; +#if CONFIG_IS_ENABLED(MMC_WRITE) if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) mmc->write_bl_len = MMC_MAX_BLOCK_LEN; +#endif if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { cmd.cmdidx = MMC_CMD_SET_DSR; cmd.cmdarg = (mmc->dsr & 0xffff) << 16; cmd.resp_type = MMC_RSP_NONE; if (mmc_send_cmd(mmc, &cmd, NULL)) - printf("MMC: SET_DSR failed\n"); + pr_warn("MMC: SET_DSR failed\n"); } /* Select the card, and put it into Transfer Mode */ @@ -1258,299 +2285,42 @@ static int mmc_startup(struct mmc *mmc) /* * For SD, its erase group is always one sector */ +#if CONFIG_IS_ENABLED(MMC_WRITE) mmc->erase_grp_size = 1; +#endif mmc->part_config = MMCPART_NOAVAILABLE; - if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { - /* check ext_csd version and capacity */ - err = mmc_send_ext_csd(mmc, ext_csd); - if (err) - return err; - if (ext_csd[EXT_CSD_REV] >= 2) { - /* - * According to the JEDEC Standard, the value of - * ext_csd's capacity is valid if the value is more - * than 2GB - */ - capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 - | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 - | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 - | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - capacity *= MMC_MAX_BLOCK_LEN; - if ((capacity >> 20) > 2 * 1024) - mmc->capacity_user = capacity; - } - switch (ext_csd[EXT_CSD_REV]) { - case 1: - mmc->version = MMC_VERSION_4_1; - break; - case 2: - mmc->version = MMC_VERSION_4_2; - break; - case 3: - mmc->version = MMC_VERSION_4_3; - break; - case 5: - mmc->version = MMC_VERSION_4_41; - break; - case 6: - mmc->version = MMC_VERSION_4_5; - break; - case 7: - mmc->version = MMC_VERSION_5_0; - break; - case 8: - mmc->version = MMC_VERSION_5_1; - break; - } - - /* The partition data may be non-zero but it is only - * effective if PARTITION_SETTING_COMPLETED is set in - * EXT_CSD, so ignore any data if this bit is not set, - * except for enabling the high-capacity group size - * definition (see below). */ - part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & - EXT_CSD_PARTITION_SETTING_COMPLETED); - - /* store the partition info of emmc */ - mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; - if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || - ext_csd[EXT_CSD_BOOT_MULT]) - mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; - if (part_completed && - (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) - mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; - - mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; - - mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; - - for (i = 0; i < 4; i++) { - int idx = EXT_CSD_GP_SIZE_MULT + i * 3; - uint mult = (ext_csd[idx + 2] << 16) + - (ext_csd[idx + 1] << 8) + ext_csd[idx]; - if (mult) - has_parts = true; - if (!part_completed) - continue; - mmc->capacity_gp[i] = mult; - mmc->capacity_gp[i] *= - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - mmc->capacity_gp[i] <<= 19; - } - - if (part_completed) { - mmc->enh_user_size = - (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) + - (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) + - ext_csd[EXT_CSD_ENH_SIZE_MULT]; - mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - mmc->enh_user_size <<= 19; - mmc->enh_user_start = - (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) + - (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) + - (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) + - ext_csd[EXT_CSD_ENH_START_ADDR]; - if (mmc->high_capacity) - mmc->enh_user_start <<= 9; - } - - /* - * Host needs to enable ERASE_GRP_DEF bit if device is - * partitioned. This bit will be lost every time after a reset - * or power off. This will affect erase size. - */ - if (part_completed) - has_parts = true; - if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && - (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) - has_parts = true; - if (has_parts) { - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_ERASE_GROUP_DEF, 1); - - if (err) - return err; - else - ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; - } - - if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { - /* Read out group size from ext_csd */ - mmc->erase_grp_size = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; - /* - * if high capacity and partition setting completed - * SEC_COUNT is valid even if it is smaller than 2 GiB - * JEDEC Standard JESD84-B45, 6.2.4 - */ - if (mmc->high_capacity && part_completed) { - capacity = (ext_csd[EXT_CSD_SEC_CNT]) | - (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | - (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | - (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); - capacity *= MMC_MAX_BLOCK_LEN; - mmc->capacity_user = capacity; - } - } else { - /* Calculate the group size from the csd value. */ - int erase_gsz, erase_gmul; - erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; - erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; - mmc->erase_grp_size = (erase_gsz + 1) - * (erase_gmul + 1); - } - - mmc->hc_wp_grp_size = 1024 - * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - - mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; - } - - err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart); + err = mmc_startup_v4(mmc); if (err) return err; - if (IS_SD(mmc)) - err = sd_change_freq(mmc); - else - err = mmc_change_freq(mmc); - + err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart); if (err) return err; - /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; - if (IS_SD(mmc)) { - if (mmc->card_caps & MMC_MODE_4BIT) { - cmd.cmdidx = MMC_CMD_APP_CMD; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = mmc->rca << 16; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = 2; - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - mmc_set_bus_width(mmc, 4); - } - - err = sd_read_ssr(mmc); + err = sd_get_capabilities(mmc); if (err) return err; - - if (mmc->card_caps & MMC_MODE_HS) - mmc->tran_speed = 50000000; - else - mmc->tran_speed = 25000000; - } else if (mmc->version >= MMC_VERSION_4) { - /* Only version 4 of MMC supports wider bus widths */ - int idx; - - /* An array of possible bus widths in order of preference */ - static unsigned ext_csd_bits[] = { - EXT_CSD_DDR_BUS_WIDTH_8, - EXT_CSD_DDR_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_8, - EXT_CSD_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_1, - }; - - /* An array to map CSD bus widths to host cap bits */ - static unsigned ext_to_hostcaps[] = { - [EXT_CSD_DDR_BUS_WIDTH_4] = - MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, - [EXT_CSD_DDR_BUS_WIDTH_8] = - MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, - [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, - [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, - }; - - /* An array to map chosen bus width to an integer */ - static unsigned widths[] = { - 8, 4, 8, 4, 1, - }; - - for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { - unsigned int extw = ext_csd_bits[idx]; - unsigned int caps = ext_to_hostcaps[extw]; - - /* - * If the bus width is still not changed, - * don't try to set the default again. - * Otherwise, recover from switch attempts - * by switching to 1-bit bus width. - */ - if (extw == EXT_CSD_BUS_WIDTH_1 && - mmc->bus_width == 1) { - err = 0; - break; - } - - /* - * Check to make sure the card and controller support - * these capabilities - */ - if ((mmc->card_caps & caps) != caps) - continue; - - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, extw); - - if (err) - continue; - - mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; - mmc_set_bus_width(mmc, widths[idx]); - - err = mmc_send_ext_csd(mmc, test_csd); - - if (err) - continue; - - /* Only compare read only fields */ - if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] - == test_csd[EXT_CSD_PARTITIONING_SUPPORT] && - ext_csd[EXT_CSD_HC_WP_GRP_SIZE] - == test_csd[EXT_CSD_HC_WP_GRP_SIZE] && - ext_csd[EXT_CSD_REV] - == test_csd[EXT_CSD_REV] && - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] - == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] && - memcmp(&ext_csd[EXT_CSD_SEC_CNT], - &test_csd[EXT_CSD_SEC_CNT], 4) == 0) - break; - else - err = -EBADMSG; - } - + err = sd_select_mode_and_width(mmc, mmc->card_caps); + } else { + err = mmc_get_capabilities(mmc); if (err) return err; - - if (mmc->card_caps & MMC_MODE_HS) { - if (mmc->card_caps & MMC_MODE_HS_52MHz) - mmc->tran_speed = 52000000; - else - mmc->tran_speed = 26000000; - } + mmc_select_mode_and_width(mmc, mmc->card_caps); } - mmc_set_clock(mmc, mmc->tran_speed); + if (err) + return err; + + mmc->best_mode = mmc->selected_mode; /* Fix the block length for DDR mode */ if (mmc->ddr_mode) { mmc->read_bl_len = MMC_MAX_BLOCK_LEN; +#if CONFIG_IS_ENABLED(MMC_WRITE) mmc->write_bl_len = MMC_MAX_BLOCK_LEN; +#endif } /* fill in device description */ @@ -1618,22 +2388,18 @@ __weak void board_mmc_power_init(void) static int mmc_power_init(struct mmc *mmc) { #if CONFIG_IS_ENABLED(DM_MMC) -#if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD) - struct udevice *vmmc_supply; +#if CONFIG_IS_ENABLED(DM_REGULATOR) int ret; ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", - &vmmc_supply); - if (ret) { + &mmc->vmmc_supply); + if (ret) debug("%s: No vmmc supply\n", mmc->dev->name); - return 0; - } - ret = regulator_set_enable(vmmc_supply, true); - if (ret) { - puts("Error enabling VMMC supply\n"); - return ret; - } + ret = device_get_supply_regulator(mmc->dev, "vqmmc-supply", + &mmc->vqmmc_supply); + if (ret) + debug("%s: No vqmmc supply\n", mmc->dev->name); #endif #else /* !CONFIG_DM_MMC */ /* @@ -1645,11 +2411,86 @@ static int mmc_power_init(struct mmc *mmc) return 0; } +/* + * put the host in the initial state: + * - turn on Vdd (card power supply) + * - configure the bus width and clock to minimal values + */ +static void mmc_set_initial_state(struct mmc *mmc) +{ + int err; + + /* First try to set 3.3V. If it fails set to 1.8V */ + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_330); + if (err != 0) + err = mmc_set_signal_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); + if (err != 0) + pr_warn("mmc: failed to set signal voltage\n"); + + mmc_select_mode(mmc, MMC_LEGACY); + mmc_set_bus_width(mmc, 1); + mmc_set_clock(mmc, 0, false); +} + +static int mmc_power_on(struct mmc *mmc) +{ +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) + if (mmc->vmmc_supply) { + int ret = regulator_set_enable(mmc->vmmc_supply, true); + + if (ret) { + puts("Error enabling VMMC supply\n"); + return ret; + } + } +#endif + return 0; +} + +static int mmc_power_off(struct mmc *mmc) +{ + mmc_set_clock(mmc, 1, true); +#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR) + if (mmc->vmmc_supply) { + int ret = regulator_set_enable(mmc->vmmc_supply, false); + + if (ret) { + debug("Error disabling VMMC supply\n"); + return ret; + } + } +#endif + return 0; +} + +static int mmc_power_cycle(struct mmc *mmc) +{ + int ret; + + ret = mmc_power_off(mmc); + if (ret) + return ret; + /* + * SD spec recommends at least 1ms of delay. Let's wait for 2ms + * to be on the safer side. + */ + udelay(2000); + return mmc_power_on(mmc); +} + int mmc_start_init(struct mmc *mmc) { bool no_card; + bool uhs_en = supports_uhs(mmc->cfg->host_caps); int err; + /* + * all hosts are capable of 1 bit bus-width and able to use the legacy + * timings. + */ + mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) | + MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT; + /* we pretend there's no card when init is NULL */ no_card = mmc_getcd(mmc) == 0; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -1673,6 +2514,26 @@ int mmc_start_init(struct mmc *mmc) if (err) return err; +#ifdef CONFIG_MMC_QUIRKS + mmc->quirks = MMC_QUIRK_RETRY_SET_BLOCKLEN | + MMC_QUIRK_RETRY_SEND_CID; +#endif + + err = mmc_power_cycle(mmc); + if (err) { + /* + * if power cycling is not supported, we should not try + * to use the UHS modes, because we wouldn't be able to + * recover from an error during the UHS initialization. + */ + debug("Unable to do a full power cycle. Disabling the UHS modes for safety\n"); + uhs_en = false; + mmc->host_caps &= ~UHS_CAPS; + err = mmc_power_on(mmc); + } + if (err) + return err; + #if CONFIG_IS_ENABLED(DM_MMC) /* The device has already been probed ready for use */ #else @@ -1682,8 +2543,10 @@ int mmc_start_init(struct mmc *mmc) return err; #endif mmc->ddr_mode = 0; - mmc_set_bus_width(mmc, 1); - mmc_set_clock(mmc, 1); + +retry: + mmc_set_initial_state(mmc); + mmc_send_init_stream(mmc); /* Reset the Card */ err = mmc_go_idle(mmc); @@ -1698,7 +2561,12 @@ int mmc_start_init(struct mmc *mmc) err = mmc_send_if_cond(mmc); /* Now try to get the SD card's operating condition */ - err = sd_send_op_cond(mmc); + err = sd_send_op_cond(mmc, uhs_en); + if (err && uhs_en) { + uhs_en = false; + mmc_power_cycle(mmc); + goto retry; + } /* If the command timed out, we check for an MMC card */ if (err == -ETIMEDOUT) { @@ -1706,7 +2574,7 @@ int mmc_start_init(struct mmc *mmc) if (err) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("Card did not respond to voltage select!\n"); + pr_err("Card did not respond to voltage select!\n"); #endif return -EOPNOTSUPP; } @@ -1812,7 +2680,7 @@ static int mmc_probe(bd_t *bis) uclass_foreach_dev(dev, uc) { ret = device_probe(dev); if (ret) - printf("%s - probe failed: %d\n", dev->name, ret); + pr_err("%s - probe failed: %d\n", dev->name, ret); } return 0; diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h index 1290eed590..a9be4b0102 100644 --- a/drivers/mmc/mmc_private.h +++ b/drivers/mmc/mmc_private.h @@ -28,7 +28,7 @@ ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, void *dst); #endif -#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV)) +#if CONFIG_IS_ENABLED(MMC_WRITE) #if CONFIG_IS_ENABLED(BLK) ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, @@ -40,7 +40,7 @@ ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, ulong mmc_berase(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt); #endif -#else /* CONFIG_SPL_BUILD and CONFIG_SPL_SAVEENV is not defined */ +#else /* CONFIG_SPL_MMC_WRITE is not defined */ /* declare dummies to reduce code size. */ diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index efa43896fc..30443d13a0 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -28,9 +28,9 @@ #include <mmc.h> #include <part.h> #include <i2c.h> -#include <twl4030.h> -#include <twl6030.h> +#if defined(CONFIG_OMAP54XX) || defined(CONFIG_OMAP44XX) #include <palmas.h> +#endif #include <asm/io.h> #include <asm/arch/mmc_host_def.h> #if !defined(CONFIG_SOC_KEYSTONE) diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c index fdb29a5505..8a5d256c11 100644 --- a/drivers/mmc/sandbox_mmc.c +++ b/drivers/mmc/sandbox_mmc.c @@ -48,9 +48,12 @@ static int sandbox_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, cmd->response[1] = 10 << 16; /* 1 << block_len */ break; case SD_CMD_SWITCH_FUNC: { + if (!data) + break; u32 *resp = (u32 *)data->dest; - resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY); + if ((cmd->cmdarg & 0xF) == UHS_SDR12_BUS_SPEED) + resp[4] = (cmd->cmdarg & 0xF) << 24; break; } case MMC_CMD_READ_SINGLE_BLOCK: diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 11d1f0c24c..e2ddf5dccd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -157,7 +157,6 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; - sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT; /* We shouldn't wait for data inihibit for stop commands, even @@ -181,6 +180,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, udelay(1000); } + sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS); + mask = SDHCI_INT_RESPONSE; if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; @@ -201,7 +202,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ - if (data != 0) { + if (data) { sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); mode = SDHCI_TRNS_BLK_CNT_EN; trans_bytes = data->blocks * data->blocksize; @@ -249,7 +250,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDHCI_SDMA - if (data != 0) { + if (data) { trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); flush_cache(start_addr, trans_bytes); } diff --git a/drivers/power/power_core.c b/drivers/power/power_core.c index b72286d429..46840a33e0 100644 --- a/drivers/power/power_core.c +++ b/drivers/power/power_core.c @@ -47,36 +47,6 @@ int pmic_set_output(struct pmic *p, u32 reg, int out, int on) return 0; } -static void pmic_show_info(struct pmic *p) -{ - printf("PMIC: %s\n", p->name); -} - -static int pmic_dump(struct pmic *p) -{ - int i, ret; - u32 val; - - if (!p) { - puts("Wrong PMIC name!\n"); - return -ENODEV; - } - - pmic_show_info(p); - for (i = 0; i < p->number_of_regs; i++) { - ret = pmic_reg_read(p, i, &val); - if (ret) - puts("PMIC: Registers dump failed\n"); - - if (!(i % 8)) - printf("\n0x%02x: ", i); - - printf("%08x ", val); - } - puts("\n"); - return 0; -} - struct pmic *pmic_alloc(void) { struct pmic *p; @@ -108,7 +78,33 @@ struct pmic *pmic_get(const char *s) return NULL; } -const char *power_get_interface(int interface) +#ifndef CONFIG_SPL_BUILD +static int pmic_dump(struct pmic *p) +{ + int i, ret; + u32 val; + + if (!p) { + puts("Wrong PMIC name!\n"); + return -ENODEV; + } + + printf("PMIC: %s\n", p->name); + for (i = 0; i < p->number_of_regs; i++) { + ret = pmic_reg_read(p, i, &val); + if (ret) + puts("PMIC: Registers dump failed\n"); + + if (!(i % 8)) + printf("\n0x%02x: ", i); + + printf("%08x ", val); + } + puts("\n"); + return 0; +} + +static const char *power_get_interface(int interface) { const char *power_interface[] = {"I2C", "SPI", "|+|-|"}; return power_interface[interface]; @@ -125,7 +121,7 @@ static void pmic_list_names(void) } } -int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +static int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { u32 ret, reg, val; char *cmd, *name; @@ -221,3 +217,4 @@ U_BOOT_CMD( "pmic name bat state - write register\n" "pmic name bat charge - write register\n" ); +#endif diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c index c226913f9e..dd7a1ead11 100644 --- a/drivers/spmi/spmi-msm.c +++ b/drivers/spmi/spmi-msm.c @@ -17,6 +17,10 @@ DECLARE_GLOBAL_DATA_PTR; +/* PMIC Arbiter configuration registers */ +#define PMIC_ARB_VERSION 0x0000 +#define PMIC_ARB_VERSION_V2_MIN 0x20010000 + #define ARB_CHANNEL_OFFSET(n) (0x4 * (n)) #define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000) @@ -148,6 +152,8 @@ static int msm_spmi_probe(struct udevice *dev) struct udevice *parent = dev->parent; struct msm_spmi_priv *priv = dev_get_priv(dev); int node = dev_of_offset(dev); + u32 hw_ver; + bool is_v1; int i; priv->arb_chnl = devfdt_get_addr(dev); @@ -155,6 +161,12 @@ static int msm_spmi_probe(struct udevice *dev) dev_of_offset(parent), node, "reg", 1, NULL, false); priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, dev_of_offset(parent), node, "reg", 2, NULL, false); + + hw_ver = readl(priv->arb_chnl + PMIC_ARB_VERSION - 0x800); + is_v1 = (hw_ver < PMIC_ARB_VERSION_V2_MIN); + + dev_dbg(dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2), hw_ver); + if (priv->arb_chnl == FDT_ADDR_T_NONE || priv->spmi_core == FDT_ADDR_T_NONE || priv->spmi_obs == FDT_ADDR_T_NONE) diff --git a/drivers/sysreset/Makefile b/drivers/sysreset/Makefile index 2e9598e300..000c288eeb 100644 --- a/drivers/sysreset/Makefile +++ b/drivers/sysreset/Makefile @@ -8,10 +8,8 @@ obj-$(CONFIG_SYSRESET) += sysreset-uclass.o obj-$(CONFIG_SYSRESET_PSCI) += sysreset_psci.o obj-$(CONFIG_SYSRESET_SYSCON) += sysreset_syscon.o obj-$(CONFIG_SYSRESET_WATCHDOG) += sysreset_watchdog.o - obj-$(CONFIG_ARCH_ROCKCHIP) += sysreset_rockchip.o obj-$(CONFIG_SANDBOX) += sysreset_sandbox.o -obj-$(CONFIG_ARCH_SNAPDRAGON) += sysreset_snapdragon.o obj-$(CONFIG_ARCH_STI) += sysreset_sti.o obj-$(CONFIG_TARGET_XTFPGA) += sysreset_xtfpga.o obj-$(CONFIG_ARCH_ASPEED) += sysreset_ast.o diff --git a/drivers/sysreset/sysreset_snapdragon.c b/drivers/sysreset/sysreset_snapdragon.c deleted file mode 100644 index 9869813978..0000000000 --- a/drivers/sysreset/sysreset_snapdragon.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Qualcomm APQ8016 reset controller driver - * - * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <dm.h> -#include <errno.h> -#include <sysreset.h> -#include <asm/io.h> - -DECLARE_GLOBAL_DATA_PTR; - -static int msm_sysreset_request(struct udevice *dev, enum sysreset_t type) -{ - phys_addr_t addr = devfdt_get_addr(dev); - if (!addr) - return -EINVAL; - writel(0, addr); - return -EINPROGRESS; -} - -static struct sysreset_ops msm_sysreset_ops = { - .request = msm_sysreset_request, -}; - -static const struct udevice_id msm_sysreset_ids[] = { - { .compatible = "qcom,pshold" }, - { } -}; - -U_BOOT_DRIVER(msm_reset) = { - .name = "msm_sysreset", - .id = UCLASS_SYSRESET, - .of_match = msm_sysreset_ids, - .ops = &msm_sysreset_ops, -}; diff --git a/env/Kconfig b/env/Kconfig index bef6e89bfc..692f8633b8 100644 --- a/env/Kconfig +++ b/env/Kconfig @@ -81,6 +81,13 @@ config ENV_IS_IN_FAT - CONFIG_FAT_WRITE: This must be enabled. Otherwise it cannot save the environment file. +config ENV_IS_IN_EXT4 + bool "Environment is in a EXT4 filesystem" + depends on !CHAIN_OF_TRUST + select EXT4_WRITE + help + Define this if you want to use the EXT4 file system for the environment. + config ENV_IS_IN_FLASH bool "Environment in flash memory" depends on !CHAIN_OF_TRUST @@ -396,6 +403,38 @@ config ENV_FAT_FILE It's a string of the FAT file name. This file use to store the environment. +config ENV_EXT4_INTERFACE + string "Name of the block device for the environment" + depends on ENV_IS_IN_EXT4 + help + Define this to a string that is the name of the block device. + +config ENV_EXT4_DEVICE_AND_PART + string "Device and partition for where to store the environemt in EXT4" + depends on ENV_IS_IN_EXT4 + help + Define this to a string to specify the partition of the device. It can + be as following: + + "D:P", "D:0", "D", "D:" or "D:auto" (D, P are integers. And P >= 1) + - "D:P": device D partition P. Error occurs if device D has no + partition table. + - "D:0": device D. + - "D" or "D:": device D partition 1 if device D has partition + table, or the whole device D if has no partition + table. + - "D:auto": first partition in device D with bootable flag set. + If none, first valid partition in device D. If no + partition table then means device D. + +config ENV_EXT4_FILE + string "Name of the EXT4 file to use for the environemnt" + depends on ENV_IS_IN_EXT4 + default "uboot.env" + help + It's a string of the EXT4 file name. This file use to store the + environment (explicit path to the file) + if ARCH_SUNXI config ENV_OFFSET @@ -32,6 +32,8 @@ static enum env_location env_get_default_location(void) return ENVL_EEPROM; else if IS_ENABLED(CONFIG_ENV_IS_IN_FAT) return ENVL_FAT; + else if IS_ENABLED(CONFIG_ENV_IS_IN_EXT4) + return ENVL_EXT4; else if IS_ENABLED(CONFIG_ENV_IS_IN_FLASH) return ENVL_FLASH; else if IS_ENABLED(CONFIG_ENV_IS_IN_MMC) diff --git a/env/ext4.c b/env/ext4.c index 65202213d3..9cdf28e79f 100644 --- a/env/ext4.c +++ b/env/ext4.c @@ -46,9 +46,9 @@ static int env_ext4_save(void) if (err) return err; - part = blk_get_device_part_str(EXT4_ENV_INTERFACE, - EXT4_ENV_DEVICE_AND_PART, - &dev_desc, &info, 1); + part = blk_get_device_part_str(CONFIG_ENV_EXT4_INTERFACE, + CONFIG_ENV_EXT4_DEVICE_AND_PART, + &dev_desc, &info, 1); if (part < 0) return 1; @@ -57,16 +57,19 @@ static int env_ext4_save(void) if (!ext4fs_mount(info.size)) { printf("\n** Unable to use %s %s for saveenv **\n", - EXT4_ENV_INTERFACE, EXT4_ENV_DEVICE_AND_PART); + CONFIG_ENV_EXT4_INTERFACE, + CONFIG_ENV_EXT4_DEVICE_AND_PART); return 1; } - err = ext4fs_write(EXT4_ENV_FILE, (void *)&env_new, sizeof(env_t)); + err = ext4fs_write(CONFIG_ENV_EXT4_FILE, (void *)&env_new, + sizeof(env_t)); ext4fs_close(); if (err == -1) { printf("\n** Unable to write \"%s\" from %s%d:%d **\n", - EXT4_ENV_FILE, EXT4_ENV_INTERFACE, dev, part); + CONFIG_ENV_EXT4_FILE, CONFIG_ENV_EXT4_INTERFACE, dev, + part); return 1; } @@ -84,9 +87,9 @@ static int env_ext4_load(void) int err; loff_t off; - part = blk_get_device_part_str(EXT4_ENV_INTERFACE, - EXT4_ENV_DEVICE_AND_PART, - &dev_desc, &info, 1); + part = blk_get_device_part_str(CONFIG_ENV_EXT4_INTERFACE, + CONFIG_ENV_EXT4_DEVICE_AND_PART, + &dev_desc, &info, 1); if (part < 0) goto err_env_relocate; @@ -95,16 +98,19 @@ static int env_ext4_load(void) if (!ext4fs_mount(info.size)) { printf("\n** Unable to use %s %s for loading the env **\n", - EXT4_ENV_INTERFACE, EXT4_ENV_DEVICE_AND_PART); + CONFIG_ENV_EXT4_INTERFACE, + CONFIG_ENV_EXT4_DEVICE_AND_PART); goto err_env_relocate; } - err = ext4_read_file(EXT4_ENV_FILE, buf, 0, CONFIG_ENV_SIZE, &off); + err = ext4_read_file(CONFIG_ENV_EXT4_FILE, buf, 0, CONFIG_ENV_SIZE, + &off); ext4fs_close(); if (err == -1) { printf("\n** Unable to read \"%s\" from %s%d:%d **\n", - EXT4_ENV_FILE, EXT4_ENV_INTERFACE, dev, part); + CONFIG_ENV_EXT4_FILE, CONFIG_ENV_EXT4_INTERFACE, dev, + part); goto err_env_relocate; } diff --git a/examples/standalone/riscv.lds b/examples/standalone/riscv.lds new file mode 100644 index 0000000000..7d8c482250 --- /dev/null +++ b/examples/standalone/riscv.lds @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) +SECTIONS +{ + . = ALIGN(4); + .text : + { + *(.text) + } + + . = ALIGN(4); + .data : { + __global_pointer$ = . + 0x800; + *(.data) + } + + . = ALIGN(4); + + .got : { + __got_start = .; + *(.got) + __got_end = .; + } + + . = ALIGN(4); + __bss_start = .; + .bss : { *(.bss) } + __bss_end = .; + + . = ALIGN(4); + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + + _end = .; +} diff --git a/examples/standalone/stubs.c b/examples/standalone/stubs.c index 9c7a8c00f8..fadde669fa 100644 --- a/examples/standalone/stubs.c +++ b/examples/standalone/stubs.c @@ -172,6 +172,18 @@ gd_t *global_data; " lwi $r16, [$r16 + (%1)]\n" \ " jr $r16\n" \ : : "i"(offsetof(gd_t, jt)), "i"(FO(x)) : "$r16"); +#elif defined(CONFIG_RISCV) +/* + * t7 holds the pointer to the global_data. gp is call clobbered. + */ +#define EXPORT_FUNC(f, a, x, ...) \ + asm volatile ( \ +" .globl " #x "\n" \ +#x ":\n" \ +" lw x19, %0(gp)\n" \ +" lw x19, %1(x19)\n" \ +" jr x19\n" \ + : : "i"(offsetof(gd_t, jt)), "i"(FO(x)) : "x19"); #elif defined(CONFIG_ARC) /* * r25 holds the pointer to the global_data. r10 is call clobbered. diff --git a/include/configs/dragonboard410c.h b/include/configs/dragonboard410c.h index d2447b27d8..530d667da8 100644 --- a/include/configs/dragonboard410c.h +++ b/include/configs/dragonboard410c.h @@ -23,7 +23,7 @@ #define CONFIG_SYS_TEXT_BASE 0x80080000 #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0) #define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + 0x80000) -#define CONFIG_SYS_BOOTM_LEN 0x1000000 /* 16MB max kernel size */ +#define CONFIG_SYS_BOOTM_LEN SZ_64M /* UART */ @@ -92,7 +92,7 @@ REFLASH(dragonboard/u-boot.img, 8)\ "initrd_high=0xffffffffffffffff\0" \ "linux_image=Image\0" \ "kernel_addr_r=0x81000000\0"\ - "fdtfile=apq8016-sbc.dtb\0" \ + "fdtfile=qcom/apq8016-sbc.dtb\0" \ "fdt_addr_r=0x83000000\0"\ "ramdisk_addr_r=0x84000000\0"\ "scriptaddr=0x90000000\0"\ diff --git a/include/configs/dragonboard820c.h b/include/configs/dragonboard820c.h new file mode 100644 index 0000000000..010bc44b33 --- /dev/null +++ b/include/configs/dragonboard820c.h @@ -0,0 +1,74 @@ +/* + * Board configuration file for Dragonboard 410C + * + * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIGS_DRAGONBOARD820C_H +#define __CONFIGS_DRAGONBOARD820C_H + +#include <linux/sizes.h> +#include <asm/arch/sysmap-apq8096.h> + +#define CONFIG_MISC_INIT_R /* To stop autoboot */ + +/* Physical Memory Map */ +#define CONFIG_NR_DRAM_BANKS 2 + +#define PHYS_SDRAM_SIZE 0xC0000000 +#define PHYS_SDRAM_1 0x80000000 +#define PHYS_SDRAM_1_SIZE 0x60000000 +#define PHYS_SDRAM_2 0x100000000 +#define PHYS_SDRAM_2_SIZE 0x5ea4ffff + +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 +#define CONFIG_SYS_TEXT_BASE 0x80080000 +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0) +#define CONFIG_SYS_LOAD_ADDR (CONFIG_SYS_SDRAM_BASE + 0x80000) +#define CONFIG_SYS_BOOTM_LEN SZ_64M +#define CONFIG_SYS_LDSCRIPT "board/qualcomm/dragonboard820c/u-boot.lds" + +/* Generic Timer Definitions */ +#define COUNTER_FREQUENCY 19000000 + +/* Partition table support */ +#define HAVE_BLOCK_DEVICE + +/* BOOTP options */ +#define CONFIG_BOOTP_BOOTFILESIZE + +#ifndef CONFIG_SPL_BUILD +#include <config_distro_defaults.h> +#include <config_distro_bootcmd.h> +#endif + +#define BOOT_TARGET_DEVICES(func) \ + func(MMC, mmc, 0) + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "loadaddr=0x95000000\0" \ + "fdt_high=0xffffffffffffffff\0" \ + "initrd_high=0xffffffffffffffff\0" \ + "linux_image=uImage\0" \ + "kernel_addr_r=0x95000000\0"\ + "fdtfile=qcom/apq8096-db820c.dtb\0" \ + "fdt_addr_r=0x93000000\0"\ + "ramdisk_addr_r=0x91000000\0"\ + "scriptaddr=0x90000000\0"\ + "pxefile_addr_r=0x90100000\0"\ + BOOTENV + +#define CONFIG_EXT4_WRITE +#define CONFIG_ENV_SIZE 0x4000 +#define CONFIG_ENV_VARS_UBOOT_CONFIG + +/* Size of malloc() pool */ +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_8M) + +/* Monitor Command Prompt */ +#define CONFIG_SYS_CBSIZE 512 +#define CONFIG_SYS_MAXARGS 64 + +#endif diff --git a/include/configs/nx25-ae250.h b/include/configs/nx25-ae250.h new file mode 100644 index 0000000000..b5237972c9 --- /dev/null +++ b/include/configs/nx25-ae250.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 Andes Technology Corporation + * Rick Chen, Andes Technology Corporation <rick@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +/* + * CPU and Board Configuration Options + */ +#define CONFIG_SKIP_LOWLEVEL_INIT + +#define CONFIG_CMDLINE_EDITING +#define CONFIG_BOOTP_SEND_HOSTNAME +#define CONFIG_BOOTP_SERVERIP + +#ifdef CONFIG_SKIP_LOWLEVEL_INIT +#define CONFIG_SYS_TEXT_BASE 0x00000000 +#ifdef CONFIG_OF_CONTROL +#undef CONFIG_OF_SEPARATE +#define CONFIG_OF_EMBED +#endif +#else +#define CONFIG_SYS_TEXT_BASE 0x80000000 +#endif + +/* + * Miscellaneous configurable options + */ +#define CONFIG_SYS_LONGHELP /* undef to save memory */ +#define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ + +/* + * Print Buffer Size + */ +#define CONFIG_SYS_PBSIZE \ + (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16) + +/* + * max number of command args + */ +#define CONFIG_SYS_MAXARGS 16 + +/* + * Boot Argument Buffer Size + */ +#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE + +/* + * Size of malloc() pool + * 512kB is suggested, (CONFIG_ENV_SIZE + 128 * 1024) was not enough + */ +#define CONFIG_SYS_MALLOC_LEN (512 << 10) + +/* + * Physical Memory Map + */ +#define CONFIG_NR_DRAM_BANKS 2 +#define PHYS_SDRAM_0 0x00000000 /* SDRAM Bank #1 */ +#define PHYS_SDRAM_1 \ + (PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE) /* SDRAM Bank #2 */ +#define PHYS_SDRAM_0_SIZE 0x20000000 /* 512 MB */ +#define PHYS_SDRAM_1_SIZE 0x20000000 /* 512 MB */ +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_0 + +/* + * Serial console configuration + */ +#define CONFIG_CONS_INDEX 1 +#define CONFIG_SYS_NS16550_SERIAL +#ifndef CONFIG_DM_SERIAL +#define CONFIG_SYS_NS16550_REG_SIZE -4 +#endif +#define CONFIG_SYS_NS16550_CLK 19660800 + +/* + * SD (MMC) controller + */ +#define CONFIG_FTSDC010_NUMBER 1 +#define CONFIG_FTSDC010_SDIO + +/* Init Stack Pointer */ +#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000000 - \ + GENERATED_GBL_DATA_SIZE) + +/* + * Load address and memory test area should agree with + * arch/riscv/config.mk. Be careful not to overwrite U-Boot itself. + */ +#define CONFIG_SYS_LOAD_ADDR 0x100000 /* SDRAM */ + +/* + * memtest works on 512 MB in DRAM + */ +#define CONFIG_SYS_MEMTEST_START PHYS_SDRAM_0 +#define CONFIG_SYS_MEMTEST_END (PHYS_SDRAM_0 + PHYS_SDRAM_0_SIZE) + +/* environments */ +#define CONFIG_ENV_SPI_BUS 0 +#define CONFIG_ENV_SPI_CS 0 +#define CONFIG_ENV_SPI_MAX_HZ 50000000 +#define CONFIG_ENV_SPI_MODE 0 +#define CONFIG_ENV_SECT_SIZE 0x1000 +#define CONFIG_ENV_OVERWRITE + +/* SPI FLASH */ +#define CONFIG_SF_DEFAULT_BUS 0 +#define CONFIG_SF_DEFAULT_CS 0 +#define CONFIG_SF_DEFAULT_SPEED 1000000 +#define CONFIG_SF_DEFAULT_MODE 0 + +/* + * For booting Linux, the board info and command line data + * have to be in the first 16 MB of memory, since this is + * the maximum mapped by the Linux kernel during initialization. + */ + +/* Initial Memory map for Linux*/ +#define CONFIG_SYS_BOOTMAPSZ (64 << 20) +/* Increase max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN (64 << 20) + +#endif /* __CONFIG_H */ diff --git a/include/configs/p2771-0000.h b/include/configs/p2771-0000.h index 564069a665..29940a63de 100644 --- a/include/configs/p2771-0000.h +++ b/include/configs/p2771-0000.h @@ -24,9 +24,32 @@ /* PCI host support */ +#define BOARD_EXTRA_ENV_SETTINGS \ + "calculated_vars=kernel_addr_r fdt_addr_r scriptaddr pxefile_addr_r " \ + "ramdisk_addr_r\0" \ + "kernel_addr_r_align=00200000\0" \ + "kernel_addr_r_offset=00080000\0" \ + "kernel_addr_r_size=02000000\0" \ + "kernel_addr_r_aliases=loadaddr\0" \ + "fdt_addr_r_align=00200000\0" \ + "fdt_addr_r_offset=00000000\0" \ + "fdt_addr_r_size=00200000\0" \ + "scriptaddr_align=00200000\0" \ + "scriptaddr_offset=00000000\0" \ + "scriptaddr_size=00200000\0" \ + "pxefile_addr_r_align=00200000\0" \ + "pxefile_addr_r_offset=00000000\0" \ + "pxefile_addr_r_size=00200000\0" \ + "ramdisk_addr_r_align=00200000\0" \ + "ramdisk_addr_r_offset=00000000\0" \ + "ramdisk_addr_r_size=02000000\0" + #include "tegra-common-post.h" /* Crystal is 38.4MHz. clk_m runs at half that rate */ #define COUNTER_FREQUENCY 19200000 +#undef CONFIG_NR_DRAM_BANKS +#define CONFIG_NR_DRAM_BANKS (1024 + 2) + #endif diff --git a/include/configs/poplar.h b/include/configs/poplar.h index 1c39ed153f..8a12b526a8 100644 --- a/include/configs/poplar.h +++ b/include/configs/poplar.h @@ -18,7 +18,7 @@ #define CONFIG_NR_DRAM_BANKS 2 /* SYS */ -#define CONFIG_SYS_BOOTM_LEN 0x1400000 +#define CONFIG_SYS_BOOTM_LEN SZ_64M #define CONFIG_SYS_INIT_SP_ADDR 0x200000 #define CONFIG_SYS_LOAD_ADDR 0x800000 #define CONFIG_SYS_MALLOC_LEN SZ_32M diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h index 3cdd9741b2..2d98a6fa64 100644 --- a/include/configs/tegra-common.h +++ b/include/configs/tegra-common.h @@ -78,17 +78,21 @@ #define CONFIG_SYS_BOOTMAPSZ (256 << 20) /* 256M */ +#ifndef CONFIG_ARM64 #define CONFIG_SYS_INIT_RAM_ADDR CONFIG_STACKBASE #define CONFIG_SYS_INIT_RAM_SIZE CONFIG_SYS_MALLOC_LEN #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_INIT_RAM_ADDR + \ CONFIG_SYS_INIT_RAM_SIZE - \ GENERATED_GBL_DATA_SIZE) +#endif +#ifndef CONFIG_ARM64 /* Defines for SPL */ #define CONFIG_SPL_FRAMEWORK #define CONFIG_SPL_MAX_FOOTPRINT (CONFIG_SYS_TEXT_BASE - \ CONFIG_SPL_TEXT_BASE) #define CONFIG_SYS_SPL_MALLOC_SIZE 0x00010000 +#endif /* Misc utility code */ #define CONFIG_BOUNCE_BUFFER diff --git a/include/configs/tegra186-common.h b/include/configs/tegra186-common.h index 98e4fc2d25..1c8772a117 100644 --- a/include/configs/tegra186-common.h +++ b/include/configs/tegra186-common.h @@ -14,11 +14,6 @@ */ #define V_NS16550_CLK 408000000 /* 408MHz (pllp_out0) */ -/* - * Miscellaneous configurable options - */ -#define CONFIG_STACKBASE 0x82800000 /* 40MB */ - /*----------------------------------------------------------------------- * Physical Memory Map */ @@ -60,9 +55,4 @@ "fdt_addr_r=0x82000000\0" \ "ramdisk_addr_r=0x82100000\0" -/* Defines for SPL */ -#define CONFIG_SPL_TEXT_BASE 0x80108000 -#define CONFIG_SYS_SPL_MALLOC_START 0x80090000 -#define CONFIG_SPL_STACK 0x800ffffc - #endif diff --git a/include/configs/tegra210-common.h b/include/configs/tegra210-common.h index 4c05576a90..35735f3b78 100644 --- a/include/configs/tegra210-common.h +++ b/include/configs/tegra210-common.h @@ -15,11 +15,6 @@ */ #define V_NS16550_CLK 408000000 /* 408MHz (pllp_out0) */ -/* - * Miscellaneous configurable options - */ -#define CONFIG_STACKBASE 0x82800000 /* 40MB */ - /*----------------------------------------------------------------------- * Physical Memory Map */ @@ -60,11 +55,6 @@ "fdt_addr_r=0x82000000\0" \ "ramdisk_addr_r=0x82100000\0" -/* Defines for SPL */ -#define CONFIG_SPL_TEXT_BASE 0x80108000 -#define CONFIG_SYS_SPL_MALLOC_START 0x80090000 -#define CONFIG_SPL_STACK 0x800ffffc - /* For USB EHCI controller */ #define CONFIG_EHCI_IS_TDI #define CONFIG_USB_EHCI_TXFIFO_THRESH 0x10 diff --git a/include/elf.h b/include/elf.h index aaecac799e..fe2128f378 100644 --- a/include/elf.h +++ b/include/elf.h @@ -613,6 +613,11 @@ unsigned long elf_hash(const unsigned char *name); #define R_AARCH64_NONE 0 /* No relocation. */ #define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +/* RISC-V relocations */ +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_RELATIVE 3 + #ifndef __ASSEMBLER__ int valid_elf_image(unsigned long addr); #endif diff --git a/include/fdtdec.h b/include/fdtdec.h index 0fb3e07212..4afb9ac501 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -990,7 +990,8 @@ int fdtdec_setup(void); /** * Board-specific FDT initialization. Returns the address to a device tree blob. - * Called when CONFIG_OF_BOARD is defined. + * Called when CONFIG_OF_BOARD is defined, or if CONFIG_OF_SEPARATE is defined + * and the board implements it. */ void *board_fdt_blob_setup(void); diff --git a/include/image.h b/include/image.h index a128a623e5..a41a8369c6 100644 --- a/include/image.h +++ b/include/image.h @@ -190,6 +190,7 @@ enum { IH_ARCH_ARC, /* Synopsys DesignWare ARC */ IH_ARCH_X86_64, /* AMD x86_64, Intel and Via */ IH_ARCH_XTENSA, /* Xtensa */ + IH_ARCH_RISCV, /* RISC-V */ IH_ARCH_COUNT, }; diff --git a/include/mmc.h b/include/mmc.h index 010ebe048c..a46eaed746 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -15,6 +15,13 @@ #include <linux/compiler.h> #include <part.h> +#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) +#define MMC_SUPPORTS_TUNING +#endif +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) +#define MMC_SUPPORTS_TUNING +#endif + /* SD/MMC version bits; 8 flags, 8 major, 8 minor, 8 change */ #define SD_VERSION_SD (1U << 31) #define MMC_VERSION_MMC (1U << 30) @@ -52,12 +59,17 @@ #define MMC_VERSION_5_0 MAKE_MMC_VERSION(5, 0, 0) #define MMC_VERSION_5_1 MAKE_MMC_VERSION(5, 1, 0) -#define MMC_MODE_HS (1 << 0) -#define MMC_MODE_HS_52MHz (1 << 1) -#define MMC_MODE_4BIT (1 << 2) -#define MMC_MODE_8BIT (1 << 3) -#define MMC_MODE_SPI (1 << 4) -#define MMC_MODE_DDR_52MHz (1 << 5) +#define MMC_CAP(mode) (1 << mode) +#define MMC_MODE_HS (MMC_CAP(MMC_HS) | MMC_CAP(SD_HS)) +#define MMC_MODE_HS_52MHz MMC_CAP(MMC_HS_52) +#define MMC_MODE_DDR_52MHz MMC_CAP(MMC_DDR_52) +#define MMC_MODE_HS200 MMC_CAP(MMC_HS_200) + +#define MMC_MODE_8BIT BIT(30) +#define MMC_MODE_4BIT BIT(29) +#define MMC_MODE_1BIT BIT(28) +#define MMC_MODE_SPI BIT(27) + #define SD_DATA_4BIT 0x00040000 @@ -82,6 +94,8 @@ #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_CMD_SEND_TUNING_BLOCK 19 +#define MMC_CMD_SEND_TUNING_BLOCK_HS200 21 #define MMC_CMD_SET_BLOCK_COUNT 23 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 @@ -109,12 +123,34 @@ #define SD_CMD_APP_SEND_OP_COND 41 #define SD_CMD_APP_SEND_SCR 51 +static inline bool mmc_is_tuning_cmd(uint cmdidx) +{ + if ((cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) || + (cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) + return true; + return false; +} + /* SCR definitions in different words */ #define SD_HIGHSPEED_BUSY 0x00020000 #define SD_HIGHSPEED_SUPPORTED 0x00020000 +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 + +#define SD_MODE_UHS_SDR12 BIT(UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 BIT(UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 BIT(UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 BIT(UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 BIT(UHS_DDR50_BUS_SPEED) + #define OCR_BUSY 0x80000000 #define OCR_HCS 0x40000000 +#define OCR_S18R 0x1000000 #define OCR_VOLTAGE_MASK 0x007FFF80 #define OCR_ACCESS_MODE 0x60000000 @@ -206,11 +242,23 @@ #define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \ | EXT_CSD_CARD_TYPE_DDR_1_2V) +#define EXT_CSD_CARD_TYPE_HS200_1_8V BIT(4) /* Card can run at 200MHz */ + /* SDR mode @1.8V I/O */ +#define EXT_CSD_CARD_TYPE_HS200_1_2V BIT(5) /* Card can run at 200MHz */ + /* SDR mode @1.2V I/O */ +#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \ + EXT_CSD_CARD_TYPE_HS200_1_2V) + #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ #define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ #define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ +#define EXT_CSD_DDR_FLAG BIT(2) /* Flag for DDR mode */ + +#define EXT_CSD_TIMING_LEGACY 0 /* no high speed */ +#define EXT_CSD_TIMING_HS 1 /* HS */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ #define EXT_CSD_BOOT_ACK_ENABLE (1 << 6) #define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3) @@ -265,6 +313,20 @@ #define ENHNCD_SUPPORT (0x2) #define PART_ENH_ATTRIB (0x1f) +#define MMC_QUIRK_RETRY_SEND_CID BIT(0) +#define MMC_QUIRK_RETRY_SET_BLOCKLEN BIT(1) + +enum mmc_voltage { + MMC_SIGNAL_VOLTAGE_000 = 0, + MMC_SIGNAL_VOLTAGE_120 = 1, + MMC_SIGNAL_VOLTAGE_180 = 2, + MMC_SIGNAL_VOLTAGE_330 = 4, +}; + +#define MMC_ALL_SIGNAL_VOLTAGE (MMC_SIGNAL_VOLTAGE_120 |\ + MMC_SIGNAL_VOLTAGE_180 |\ + MMC_SIGNAL_VOLTAGE_330) + /* Maximum block size for MMC */ #define MMC_MAX_BLOCK_LEN 512 @@ -347,6 +409,14 @@ struct dm_mmc_ops { int (*set_ios)(struct udevice *dev); /** + * send_init_stream() - send the initialization stream: 74 clock cycles + * This is used after power up before sending the first command + * + * @dev: Device to update + */ + void (*send_init_stream)(struct udevice *dev); + + /** * get_cd() - See whether a card is present * * @dev: Device to check @@ -361,6 +431,30 @@ struct dm_mmc_ops { * @return 0 if write-enabled, 1 if write-protected, -ve on error */ int (*get_wp)(struct udevice *dev); + +#ifdef MMC_SUPPORTS_TUNING + /** + * execute_tuning() - Start the tuning process + * + * @dev: Device to start the tuning + * @opcode: Command opcode to send + * @return 0 if OK, -ve on error + */ + int (*execute_tuning)(struct udevice *dev, uint opcode); +#endif + +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + /** + * wait_dat0() - wait until dat0 is in the target state + * (CLK must be running during the wait) + * + * @dev: Device to check + * @state: target state + * @timeout: timeout in us + * @return 0 if dat0 is in the target state, -ve on error + */ + int (*wait_dat0)(struct udevice *dev, int state, int timeout); +#endif }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -368,13 +462,19 @@ struct dm_mmc_ops { int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data); int dm_mmc_set_ios(struct udevice *dev); +void dm_mmc_send_init_stream(struct udevice *dev); int dm_mmc_get_cd(struct udevice *dev); int dm_mmc_get_wp(struct udevice *dev); +int dm_mmc_execute_tuning(struct udevice *dev, uint opcode); +int dm_mmc_wait_dat0(struct udevice *dev, int state, int timeout); /* Transition functions for compatibility */ int mmc_set_ios(struct mmc *mmc); +void mmc_send_init_stream(struct mmc *mmc); int mmc_getcd(struct mmc *mmc); int mmc_getwp(struct mmc *mmc); +int mmc_execute_tuning(struct mmc *mmc, uint opcode); +int mmc_wait_dat0(struct mmc *mmc, int state, int timeout); #else struct mmc_ops { @@ -406,6 +506,50 @@ struct sd_ssr { unsigned int erase_offset; /* In milliseconds */ }; +enum bus_mode { + MMC_LEGACY, + SD_LEGACY, + MMC_HS, + SD_HS, + MMC_HS_52, + MMC_DDR_52, + UHS_SDR12, + UHS_SDR25, + UHS_SDR50, + UHS_DDR50, + UHS_SDR104, + MMC_HS_200, + MMC_MODES_END +}; + +const char *mmc_mode_name(enum bus_mode mode); +void mmc_dump_capabilities(const char *text, uint caps); + +static inline bool mmc_is_mode_ddr(enum bus_mode mode) +{ + if (mode == MMC_DDR_52) + return true; +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + else if (mode == UHS_DDR50) + return true; +#endif + else + return false; +} + +#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \ + MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \ + MMC_CAP(UHS_DDR50)) + +static inline bool supports_uhs(uint caps) +{ +#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) + return (caps & UHS_CAPS) ? true : false; +#else + return false; +#endif +} + /* * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device * with mmc_get_mmc_dev(). @@ -421,9 +565,12 @@ struct mmc { void *priv; uint has_init; int high_capacity; + bool clk_disable; /* true if the clock can be turned off */ uint bus_width; uint clock; + enum mmc_voltage signal_voltage; uint card_caps; + uint host_caps; uint ocr; uint dsr; uint dsr_imp; @@ -436,18 +583,27 @@ struct mmc { u8 wr_rel_set; u8 part_config; uint tran_speed; + uint legacy_speed; /* speed for the legacy mode provided by the card */ uint read_bl_len; +#if CONFIG_IS_ENABLED(MMC_WRITE) uint write_bl_len; uint erase_grp_size; /* in 512-byte sectors */ +#endif +#if CONFIG_IS_ENABLED(MMC_HW_PARTITIONING) uint hc_wp_grp_size; /* in 512-byte sectors */ +#endif +#if CONFIG_IS_ENABLED(MMC_WRITE) struct sd_ssr ssr; /* SD status register */ +#endif u64 capacity; u64 capacity_user; u64 capacity_boot; u64 capacity_rpmb; u64 capacity_gp[4]; +#ifndef CONFIG_SPL_BUILD u64 enh_user_start; u64 enh_user_size; +#endif #if !CONFIG_IS_ENABLED(BLK) struct blk_desc block_dev; #endif @@ -457,7 +613,21 @@ struct mmc { int ddr_mode; #if CONFIG_IS_ENABLED(DM_MMC) struct udevice *dev; /* Device for this MMC controller */ +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vmmc_supply; /* Main voltage regulator (Vcc)*/ + struct udevice *vqmmc_supply; /* IO voltage regulator (Vccq)*/ #endif +#endif + u8 *ext_csd; + u32 cardtype; /* cardtype read from the MMC */ + enum mmc_voltage current_voltage; + enum bus_mode selected_mode; /* mode currently used */ + enum bus_mode best_mode; /* best mode is the supported mode with the + * highest bandwidth. It may not always be the + * operating mode due to limitations when + * accessing the boot partitions + */ + u32 quirks; }; struct mmc_hwpart_conf { @@ -507,8 +677,36 @@ void mmc_destroy(struct mmc *mmc); int mmc_unbind(struct udevice *dev); int mmc_initialize(bd_t *bis); int mmc_init(struct mmc *mmc); +int mmc_send_tuning(struct mmc *mmc, u32 opcode, int *cmd_error); + +/** + * mmc_of_parse() - Parse the device tree to get the capabilities of the host + * + * @dev: MMC device + * @cfg: MMC configuration + * @return 0 if OK, -ve on error + */ +int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg); + int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size); -void mmc_set_clock(struct mmc *mmc, uint clock); + +/** + * mmc_voltage_to_mv() - Convert a mmc_voltage in mV + * + * @voltage: The mmc_voltage to convert + * @return the value in mV if OK, -EINVAL on error (invalid mmc_voltage value) + */ +int mmc_voltage_to_mv(enum mmc_voltage voltage); + +/** + * mmc_set_clock() - change the bus clock + * @mmc: MMC struct + * @clock: bus frequency in Hz + * @disable: flag indicating if the clock must on or off + * @return 0 if OK, -ve on error + */ +int mmc_set_clock(struct mmc *mmc, uint clock, bool disable); + struct mmc *find_mmc_device(int dev_num); int mmc_set_dev(int dev_num); void print_mmc_devices(char separator); diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 30ec6b92b2..6b138faf85 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -1272,6 +1272,28 @@ static int uncompress_blob(const void *src, ulong sz_src, void **dstp) # endif #endif +#if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE) +/* + * For CONFIG_OF_SEPARATE, the board may optionally implement this to + * provide and/or fixup the fdt. + */ +__weak void *board_fdt_blob_setup(void) +{ + void *fdt_blob = NULL; +#ifdef CONFIG_SPL_BUILD + /* FDT is at end of BSS unless it is in a different memory region */ + if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) + fdt_blob = (ulong *)&_image_binary_end; + else + fdt_blob = (ulong *)&__bss_end; +#else + /* FDT is at end of image */ + fdt_blob = (ulong *)&_end; +#endif + return fdt_blob; +} +#endif + int fdtdec_setup(void) { #if CONFIG_IS_ENABLED(OF_CONTROL) @@ -1285,18 +1307,7 @@ int fdtdec_setup(void) # else gd->fdt_blob = __dtb_dt_begin; # endif -# elif defined CONFIG_OF_SEPARATE -# ifdef CONFIG_SPL_BUILD - /* FDT is at end of BSS unless it is in a different memory region */ - if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) - gd->fdt_blob = (ulong *)&_image_binary_end; - else - gd->fdt_blob = (ulong *)&__bss_end; -# else - /* FDT is at end of image */ - gd->fdt_blob = (ulong *)&_end; -# endif -# elif defined(CONFIG_OF_BOARD) +# elif defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE) /* Allow the board to override the fdt address. */ gd->fdt_blob = board_fdt_blob_setup(); # elif defined(CONFIG_OF_HOSTFILE) diff --git a/test/dm/bus.c b/test/dm/bus.c index 7006d4163d..1da398ae3a 100644 --- a/test/dm/bus.c +++ b/test/dm/bus.c @@ -105,7 +105,7 @@ UCLASS_DRIVER(testbus) = { /* Test that we can probe for children */ static int dm_test_bus_children(struct unit_test_state *uts) { - int num_devices = 6; + int num_devices = 7; struct udevice *bus; struct uclass *uc; diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index dcc2ef8b65..920ccbf016 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -167,7 +167,7 @@ int dm_check_devices(struct unit_test_state *uts, int num_devices) /* Test that FDT-based binding works correctly */ static int dm_test_fdt(struct unit_test_state *uts) { - const int num_devices = 6; + const int num_devices = 7; struct udevice *dev; struct uclass *uc; int ret; diff --git a/tools/Makefile b/tools/Makefile index 4d32fe5910..571f571ec9 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -185,6 +185,7 @@ hostprogs-$(CONFIG_KIRKWOOD) += kwboot hostprogs-$(CONFIG_ARCH_MVEBU) += kwboot hostprogs-y += proftool hostprogs-$(CONFIG_STATIC_RELA) += relocate-rela +hostprogs-$(CONFIG_RISCV) += prelink-riscv hostprogs-y += fdtgrep fdtgrep-objs += $(LIBFDT_OBJS) fdtgrep.o diff --git a/tools/prelink-riscv.c b/tools/prelink-riscv.c new file mode 100644 index 0000000000..632d2da6ba --- /dev/null +++ b/tools/prelink-riscv.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Andes Technology + * Chih-Mao Chen <cmchen@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Statically process runtime relocations on RISC-V ELF images + * so that it can be directly executed when loaded at LMA + * without fixup. Both RV32 and RV64 are supported. + */ + +#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error "Only little-endian host is supported" +#endif + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <elf.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#ifndef EM_RISCV +#define EM_RISCV 243 +#endif + +#ifndef R_RISCV_32 +#define R_RISCV_32 1 +#endif + +#ifndef R_RISCV_64 +#define R_RISCV_64 2 +#endif + +#ifndef R_RISCV_RELATIVE +#define R_RISCV_RELATIVE 3 +#endif + +const char *argv0; + +#define die(fmt, ...) \ + do { \ + fprintf(stderr, "%s: " fmt "\n", argv0, ## __VA_ARGS__); \ + exit(EXIT_FAILURE); \ + } while (0) + +#define PRELINK_INC_BITS 32 +#include "prelink-riscv.inc" +#undef PRELINK_INC_BITS + +#define PRELINK_INC_BITS 64 +#include "prelink-riscv.inc" +#undef PRELINK_INC_BITS + +int main(int argc, const char *const *argv) +{ + argv0 = argv[0]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s <u-boot>\n", argv0); + exit(EXIT_FAILURE); + } + + int fd = open(argv[1], O_RDWR, 0); + + if (fd < 0) + die("Cannot open %s: %s", argv[1], strerror(errno)); + + struct stat st; + + if (fstat(fd, &st) < 0) + die("Cannot stat %s: %s", argv[1], strerror(errno)); + + void *data = + mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) + die("Cannot mmap %s: %s", argv[1], strerror(errno)); + + close(fd); + + unsigned char *e_ident = (unsigned char *)data; + + if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) + die("Invalid ELF file %s", argv[1]); + + bool is64 = e_ident[EI_CLASS] == ELFCLASS64; + + if (is64) + prelink64(data); + else + prelink32(data); + + return 0; +} diff --git a/tools/prelink-riscv.inc b/tools/prelink-riscv.inc new file mode 100644 index 0000000000..c07d930f7e --- /dev/null +++ b/tools/prelink-riscv.inc @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 Andes Technology + * Chih-Mao Chen <cmchen@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Statically process runtime relocations on RISC-V ELF images + * so that it can be directly executed when loaded at LMA + * without fixup. Both RV32 and RV64 are supported. + */ + +#define CONCAT_IMPL(x, y) x##y +#define CONCAT(x, y) CONCAT_IMPL(x, y) +#define CONCAT3(x, y, z) CONCAT(CONCAT(x, y), z) + +#define prelink_nn CONCAT(prelink, PRELINK_INC_BITS) +#define uintnn_t CONCAT3(uint, PRELINK_INC_BITS, _t) +#define get_offset_nn CONCAT(get_offset_, PRELINK_INC_BITS) +#define Elf_Ehdr CONCAT3(Elf, PRELINK_INC_BITS, _Ehdr) +#define Elf_Phdr CONCAT3(Elf, PRELINK_INC_BITS, _Phdr) +#define Elf_Rela CONCAT3(Elf, PRELINK_INC_BITS, _Rela) +#define Elf_Sym CONCAT3(Elf, PRELINK_INC_BITS, _Sym) +#define Elf_Dyn CONCAT3(Elf, PRELINK_INC_BITS, _Dyn) +#define Elf_Addr CONCAT3(Elf, PRELINK_INC_BITS, _Addr) +#define ELF_R_TYPE CONCAT3(ELF, PRELINK_INC_BITS, _R_TYPE) +#define ELF_R_SYM CONCAT3(ELF, PRELINK_INC_BITS, _R_SYM) + +static void* get_offset_nn (void* data, Elf_Phdr* phdrs, size_t phnum, Elf_Addr addr) +{ + Elf_Phdr *p; + + for (p = phdrs; p < phdrs + phnum; ++p) + if (p->p_vaddr <= addr && p->p_vaddr + p->p_memsz > addr) + return data + p->p_offset + (addr - p->p_vaddr); + + return NULL; +} + +static void prelink_nn(void *data) +{ + Elf_Ehdr *ehdr = data; + Elf_Phdr *p; + Elf_Dyn *dyn; + Elf_Rela *r; + + if (ehdr->e_machine != EM_RISCV) + die("Machine type is not RISC-V"); + + Elf_Phdr *phdrs = data + ehdr->e_phoff; + + Elf_Dyn *dyns = NULL; + for (p = phdrs; p < phdrs + ehdr->e_phnum; ++p) { + if (p->p_type == PT_DYNAMIC) { + dyns = data + p->p_offset; + break; + } + } + + if (dyns == NULL) + die("No dynamic section found"); + + Elf_Rela *rela_dyn = NULL; + size_t rela_count = 0; + Elf_Sym *dynsym = NULL; + for (dyn = dyns;; ++dyn) { + if (dyn->d_tag == DT_NULL) + break; + else if (dyn->d_tag == DT_RELA) + rela_dyn = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); + else if (dyn->d_tag == DT_RELASZ) + rela_count = dyn->d_un.d_val / sizeof(Elf_Rela); + else if (dyn->d_tag == DT_SYMTAB) + dynsym = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); + + } + + if (rela_dyn == NULL) + die("No .rela.dyn found"); + + if (dynsym == NULL) + die("No .dynsym found"); + + for (r = rela_dyn; r < rela_dyn + rela_count; ++r) { + void* buf = get_offset_nn(data, phdrs, ehdr->e_phnum, r->r_offset); + + if (buf == NULL) + continue; + + if (ELF_R_TYPE(r->r_info) == R_RISCV_RELATIVE) + *((uintnn_t*) buf) = r->r_addend; + else if (ELF_R_TYPE(r->r_info) == R_RISCV_32) + *((uint32_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; + else if (ELF_R_TYPE(r->r_info) == R_RISCV_64) + *((uint64_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; + } +} + +#undef prelink_nn +#undef uintnn_t +#undef get_offset_nn +#undef Elf_Ehdr +#undef Elf_Phdr +#undef Elf_Rela +#undef Elf_Sym +#undef Elf_Dyn +#undef Elf_Addr +#undef ELF_R_TYPE +#undef ELF_R_SYM + +#undef CONCAT_IMPL +#undef CONCAT +#undef CONCAT3 |