Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1679
-gerrit
commit bcf55e951fbed7fa4067e44fbd338029dc84a82f Author: David Hendricks dhendrix@chromium.org Date: Tue Oct 30 20:47:51 2012 -0700
WIP: initial Snow board (ARM/Exynos 5250) support
This pulls in lots of sources from U-Boot for the Snow board, which is used in Google's Chromebook based on the Exynos 5250 SoC from Samsung. This patch will likely be broken down significantly, I'm just uploading it as a draft for convenience.
When compiling in the ChromeOS SDK chroot environment, use "CROSS_COMPILE=armv7a-cros-linux-gnueabi-". You may also use crossgcc with "-p armv7a-eabi".
For now this patch also includes a config.snow file which sets the Kconfig options for Exynos5250 (cp config.snow .config && make oldconfig && make).
Overview of the code structure: - src/arch/armv7 - Common ARMv7 architecture files. This includes common headers and libraries.
- src/cpu/samsung - Samsung SoC files, including Exynos5 (a Cortex-A15 implementation). Since this is an SoC we'll forego the x86-style {north,south}bridge and cpu distinction. We may try to split some stuff out before the final version if prudent.
- src/mainboard/google/snow - Mainboard directory (public name is "snow"). There are references to the Samsung SMDK5250 which is the development kit. Maybe we can add that as a separate board along with the Samsung Arndale (www.arndaleboard.org).
TODO items: - The difference between romstage and ramstage is confusing (in context of this code). board_init_f() seems like it should be done in ramstage, since caches should be set up when start.S calls it. - Remove superfluous Linux code - Split out changes to console (e.g. src/console/Kconfig) - Incorporate dianders' DRAM fix from u-boot - Make sure CBFS works... - Do something about device tree / ACPI - (Re)move config.snow - Remove unneeded S5P stuff
Get rid of linux include files. Merge asm/system.h into system.h, put in conditional __ASSEMBlY__ code.
get rid of asm/. We're not linux and much of this junk is not needed. It builds without the asm/ directory.
Change-Id: Ic3ed72c88948af0ecb37f5ea9f2056451738146a Signed-off-by: David Hendricks dhendrix@chromium.org Signed-off-by: Stefan Reinauer reinauer@google.com --- Makefile | 4 +- config.snow | 323 +++++++ src/arch/armv7/Kconfig | 52 ++ src/arch/armv7/Makefile.inc | 324 +++++++ src/arch/armv7/boot/Makefile.inc | 13 + src/arch/armv7/boot/acpi.c | 791 ++++++++++++++++ src/arch/armv7/boot/acpigen.c | 729 +++++++++++++++ src/arch/armv7/boot/boot.c | 189 ++++ src/arch/armv7/boot/coreboot_table.c | 711 +++++++++++++++ src/arch/armv7/boot/multiboot.c | 77 ++ src/arch/armv7/boot/tables.c | 100 +++ src/arch/armv7/boot/wakeup.S | 94 ++ src/arch/armv7/bootblock_simple.c | 51 ++ src/arch/armv7/coreboot_ram.ld | 128 +++ src/arch/armv7/cpu.c | 101 +++ src/arch/armv7/include/arch/atomic.h | 111 +++ src/arch/armv7/include/arch/boot/boot.h | 8 + src/arch/armv7/include/arch/byteorder.h | 27 + src/arch/armv7/include/arch/coreboot_tables.h | 25 + src/arch/armv7/include/arch/cpu.h | 21 + src/arch/armv7/include/arch/gpio.h | 98 ++ src/arch/armv7/include/arch/hlt.h | 10 + src/arch/armv7/include/arch/io.h | 427 +++++++++ src/arch/armv7/include/arch/pci_ops.h | 19 + src/arch/armv7/include/arch/types.h | 53 ++ src/arch/armv7/include/armv7.h | 76 ++ src/arch/armv7/include/assembler.h | 60 ++ src/arch/armv7/include/bootblock_common.h | 69 ++ src/arch/armv7/include/cache.h | 56 ++ src/arch/armv7/include/clocks.h | 44 + src/arch/armv7/include/common.h | 494 ++++++++++ src/arch/armv7/include/div64.h | 233 +++++ src/arch/armv7/include/global_data.h | 108 +++ src/arch/armv7/include/hang.h | 20 + src/arch/armv7/include/mmio_conf.h | 6 + src/arch/armv7/include/ptrace.h | 30 + src/arch/armv7/include/smp/spinlock.h | 52 ++ src/arch/armv7/include/stdint.h | 81 ++ src/arch/armv7/include/system.h | 123 +++ src/arch/armv7/include/utils.h | 56 ++ src/arch/armv7/ldscript_failover.lb | 74 ++ src/arch/armv7/ldscript_fallback_cbfs.lb | 51 ++ src/arch/armv7/lib/Makefile.inc | 26 + src/arch/armv7/lib/Makefile.uboot | 95 ++ src/arch/armv7/lib/_divsi3.S | 142 +++ src/arch/armv7/lib/_udivsi3.S | 93 ++ src/arch/armv7/lib/_uldivmod.S | 157 ++++ src/arch/armv7/lib/bootm.c | 405 +++++++++ src/arch/armv7/lib/c_start.S | 9 + src/arch/armv7/lib/cache-cp15.c | 304 +++++++ src/arch/armv7/lib/cache_v7.c | 427 +++++++++ src/arch/armv7/lib/div.c | 5 + src/arch/armv7/lib/div0.c | 32 + src/arch/armv7/lib/div64.S | 208 +++++ src/arch/armv7/lib/eabi_compat.c | 33 + src/arch/armv7/lib/hang_spl.c | 16 + src/arch/armv7/lib/id.inc | 18 + src/arch/armv7/lib/id.lds | 6 + src/arch/armv7/lib/interrupts.c | 198 ++++ src/arch/armv7/lib/memcpy.S | 243 +++++ src/arch/armv7/lib/memset.S | 126 +++ src/arch/armv7/lib/reset.c | 53 ++ src/arch/armv7/lib/romstage_console.c | 75 ++ src/arch/armv7/lib/syslib.c | 70 ++ src/arch/armv7/romstage.ld | 119 +++ src/arch/armv7/start.S | 543 +++++++++++ src/arch/x86/include/stddef.h | 24 - src/console/Kconfig | 24 +- src/cpu/Kconfig | 9 + src/cpu/Makefile.inc | 1 + src/cpu/samsung/Kconfig | 46 + src/cpu/samsung/Makefile.inc | 5 + src/cpu/samsung/exynos5-common/Makefile.inc | 2 + src/cpu/samsung/exynos5-common/soc.c | 30 + src/cpu/samsung/exynos5-common/spl_boot.c | 460 ++++++++++ src/cpu/samsung/exynos5250/Kconfig | 28 + src/cpu/samsung/exynos5250/Makefile.inc | 32 + src/cpu/samsung/exynos5250/ace_sha.c | 118 +++ src/cpu/samsung/exynos5250/clock.c | 610 +++++++++++++ src/cpu/samsung/exynos5250/clock_init.c | 1189 +++++++++++++++++++++++++ src/cpu/samsung/exynos5250/dmc_common.c | 200 +++++ src/cpu/samsung/exynos5250/dmc_init_ddr3.c | 249 ++++++ src/cpu/samsung/exynos5250/exynos_cache.c | 90 ++ src/cpu/samsung/exynos5250/lowlevel_init.S | 32 + src/cpu/samsung/exynos5250/lowlevel_init_c.c | 120 +++ src/cpu/samsung/exynos5250/pinmux.c | 303 +++++++ src/cpu/samsung/exynos5250/power.c | 202 +++++ src/cpu/samsung/exynos5250/sata.c | 431 +++++++++ src/cpu/samsung/exynos5250/setup.h | 7 +- src/cpu/samsung/exynos5250/soc.c | 47 + src/cpu/samsung/exynos5250/spl.c | 41 + src/cpu/samsung/exynos5250/tzpc_init.c | 57 ++ src/cpu/samsung/exynos5250/uart.c | 235 +++++ src/cpu/samsung/s5p-common/Makefile.inc | 12 + src/cpu/samsung/s5p-common/Makefile.uboot | 49 + src/cpu/samsung/s5p-common/cpu_info.c | 128 +++ src/cpu/samsung/s5p-common/pwm.c | 205 +++++ src/cpu/samsung/s5p-common/s5p_gpio.c | 490 ++++++++++ src/cpu/samsung/s5p-common/sromc.c | 49 + src/cpu/samsung/s5p-common/timer.c | 153 ++++ src/cpu/samsung/s5p-common/wdt.c | 59 ++ src/drivers/Kconfig | 1 + src/drivers/Makefile.inc | 1 + src/drivers/maxim/Kconfig | 5 + src/drivers/maxim/Makefile.inc | 20 + src/drivers/maxim/max77686.c | 257 ++++++ src/include/cbfs_core.h | 3 + src/include/compiler.h | 140 +++ src/include/device/i2c.h | 279 ++++++ src/include/device/power/max77686.h | 134 +++ src/include/stddef.h | 24 + src/include/uart.h | 33 + src/lib/Makefile.inc | 2 +- src/mainboard/Kconfig | 16 + src/mainboard/google/Kconfig | 17 + src/mainboard/google/snow/Kconfig | 162 ++++ src/mainboard/google/snow/Makefile.inc | 36 + src/mainboard/google/snow/board.h | 33 + src/mainboard/google/snow/devicetree.cb | 7 + src/mainboard/google/snow/mainboard.c | 943 ++++++++++++++++++++ src/mainboard/google/snow/romstage.c | 291 ++++++ src/mainboard/google/snow/smdk5250_spl.c | 123 +++ 122 files changed, 17754 insertions(+), 32 deletions(-)
diff --git a/Makefile b/Makefile index 6e8bd0d..3c537a5 100644 --- a/Makefile +++ b/Makefile @@ -113,14 +113,14 @@ else
include $(HAVE_DOTCONFIG)
-ARCHDIR-$(CONFIG_ARCH_ARMV7) := armv7 +ARCHDIR-$(CONFIG_ARCH_ARMV7) := armv7 ARCHDIR-$(CONFIG_ARCH_X86) := x86
ARCH-y := $(ARCHDIR-y)
# If architecture folder name is different from GCC binutils architecture name, # override here. -ARCH-$(CONFIG_ARCH_ARMV7) := armv7 +ARCH-$(CONFIG_ARCH_ARMV7) := armv7 ARCH-$(CONFIG_ARCH_X86) := i386
CC := $(CC_$(ARCH-y)) diff --git a/config.snow b/config.snow new file mode 100644 index 0000000..2aaae18 --- /dev/null +++ b/config.snow @@ -0,0 +1,323 @@ +# +# Automatically generated make config: don't edit +# coreboot version: 4.0-3132-g0af95ea +# Tue Dec 4 14:41:24 2012 +# + +# +# General setup +# +# CONFIG_EXPERT is not set +CONFIG_LOCALVERSION="" +CONFIG_CBFS_PREFIX="fallback" +CONFIG_COMPILER_GCC=y +# CONFIG_COMPILER_LLVM_CLANG is not set +# CONFIG_SCANBUILD_ENABLE is not set +# CONFIG_CCACHE is not set +# CONFIG_USE_OPTION_TABLE is not set +CONFIG_COMPRESS_RAMSTAGE=y +CONFIG_INCLUDE_CONFIG_FILE=y +# CONFIG_EARLY_CBMEM_INIT is not set +# CONFIG_USE_BLOBS is not set +# CONFIG_REQUIRES_BLOB is not set + +# +# Mainboard +# +# CONFIG_VENDOR_AAEON is not set +# CONFIG_VENDOR_ABIT is not set +# CONFIG_VENDOR_ADVANSUS is not set +# CONFIG_VENDOR_ADVANTECH is not set +# CONFIG_VENDOR_AMD is not set +# CONFIG_VENDOR_AOPEN is not set +# CONFIG_VENDOR_ARIMA is not set +# CONFIG_VENDOR_ARTECGROUP is not set +# CONFIG_VENDOR_ASI is not set +# CONFIG_VENDOR_ASROCK is not set +# CONFIG_VENDOR_ASUS is not set +# CONFIG_VENDOR_A_TREND is not set +# CONFIG_VENDOR_AVALUE is not set +# CONFIG_VENDOR_AXUS is not set +# CONFIG_VENDOR_AZZA is not set +# CONFIG_VENDOR_BACHMANN is not set +# CONFIG_VENDOR_BCOM is not set +# CONFIG_VENDOR_BIFFEROS is not set +# CONFIG_VENDOR_BIOSTAR is not set +# CONFIG_VENDOR_BROADCOM is not set +# CONFIG_VENDOR_COMPAQ is not set +# CONFIG_VENDOR_DIGITALLOGIC is not set +# CONFIG_VENDOR_EAGLELION is not set +# CONFIG_VENDOR_ECS is not set +# CONFIG_VENDOR_EMULATION is not set +# CONFIG_VENDOR_GETAC is not set +# CONFIG_VENDOR_GIGABYTE is not set +CONFIG_VENDOR_GOOGLE=y +# CONFIG_VENDOR_HP is not set +# CONFIG_VENDOR_IBASE is not set +# CONFIG_VENDOR_IBM is not set +# CONFIG_VENDOR_IEI is not set +# CONFIG_VENDOR_INTEL is not set +# CONFIG_VENDOR_IWAVE is not set +# CONFIG_VENDOR_IWILL is not set +# CONFIG_VENDOR_JETWAY is not set +# CONFIG_VENDOR_KONTRON is not set +# CONFIG_VENDOR_LANNER is not set +# CONFIG_VENDOR_LENOVO is not set +# CONFIG_VENDOR_LIPPERT is not set +# CONFIG_VENDOR_MITAC is not set +# CONFIG_VENDOR_MSI is not set +# CONFIG_VENDOR_NEC is not set +# CONFIG_VENDOR_NEWISYS is not set +# CONFIG_VENDOR_NOKIA is not set +# CONFIG_VENDOR_NVIDIA is not set +# CONFIG_VENDOR_PCENGINES is not set +# CONFIG_VENDOR_RCA is not set +# CONFIG_VENDOR_RODA is not set +# CONFIG_VENDOR_SAMSUNG is not set +# CONFIG_VENDOR_SIEMENS is not set +# CONFIG_VENDOR_SOYO is not set +# CONFIG_VENDOR_SUNW is not set +# CONFIG_VENDOR_SUPERMICRO is not set +# CONFIG_VENDOR_TECHNEXION is not set +# CONFIG_VENDOR_TECHNOLOGIC is not set +# CONFIG_VENDOR_TELEVIDEO is not set +# CONFIG_VENDOR_THOMSON is not set +# CONFIG_VENDOR_TRAVERSE is not set +# CONFIG_VENDOR_TYAN is not set +# CONFIG_VENDOR_VIA is not set +# CONFIG_VENDOR_WINENT is not set +# CONFIG_VENDOR_WYSE is not set +CONFIG_BOARD_SPECIFIC_OPTIONS=y +CONFIG_MAINBOARD_DIR="google/snow" +CONFIG_MAINBOARD_PART_NUMBER="SNOW" +CONFIG_MAINBOARD_VENDOR="Samsung" +CONFIG_MAX_CPUS=2 +CONFIG_RAMTOP=0x40100000 +CONFIG_HEAP_SIZE=0x4000 +CONFIG_RAMBASE=0x40000000 +CONFIG_WARNINGS_ARE_ERRORS=y +# CONFIG_ONBOARD_VGA_IS_PRIMARY is not set +# CONFIG_VGA_BIOS is not set +# CONFIG_CONSOLE_POST is not set +# CONFIG_PCI_ROM_RUN is not set +CONFIG_ACPI_SSDTX_NUM=0 +CONFIG_BOARD_GOOGLE_SNOW=y +CONFIG_SPL_TEXT_BASE=0x02023400 +CONFIG_SPL_MAX_SIZE=0x3800 +CONFIG_DRAM_SIZE_MB=2048 +CONFIG_NR_DRAM_BANKS=1 +# CONFIG_CONSOLE_SERIAL_UART0 is not set +# CONFIG_CONSOLE_SERIAL_UART1 is not set +# CONFIG_CONSOLE_SERIAL_UART2 is not set +CONFIG_CONSOLE_SERIAL_UART3=y +CONFIG_CONSOLE_SERIAL_UART_ADDRESS=0x12c30000 +CONFIG_SYS_I2C_SPEED=100000 +CONFIG_SYS_I2C_SLAVE=0x0 +CONFIG_I2C_MULTI_BUS=y +CONFIG_VDD_ARM_MV=1300 +CONFIG_VDD_INT_UV=1012500 +CONFIG_VDD_MIF_MV=1000 +CONFIG_VDD_G3D_MV=1200 +CONFIG_VDD_LDO2_MV=1500 +CONFIG_VDD_LDO3_MV=1800 +CONFIG_VDD_LDO5_MV=1800 +CONFIG_VDD_LDO10_MV=1800 +# CONFIG_MMCONF_SUPPORT_DEFAULT is not set +# CONFIG_POWER_BUTTON_FORCE_ENABLE is not set +# CONFIG_IOAPIC is not set +CONFIG_TTYS0_BAUD=115200 +CONFIG_DEFAULT_CONSOLE_LOGLEVEL=8 +CONFIG_MAXIMUM_CONSOLE_LOGLEVEL=8 +# CONFIG_CONSOLE_SERIAL8250 is not set +# CONFIG_USBDEBUG is not set +CONFIG_BOARD_ROMSIZE_KB_4096=y +# CONFIG_COREBOOT_ROMSIZE_KB_64 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_128 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_256 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_512 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_1024 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_2048 is not set +CONFIG_COREBOOT_ROMSIZE_KB_4096=y +# CONFIG_COREBOOT_ROMSIZE_KB_8192 is not set +# CONFIG_COREBOOT_ROMSIZE_KB_16384 is not set +CONFIG_COREBOOT_ROMSIZE_KB=4096 +CONFIG_ROM_SIZE=0x400000 +CONFIG_CACHE_ROM_SIZE=0x400000 +# CONFIG_ARCH_X86 is not set +# CONFIG_ARCH_ARM is not set +CONFIG_ARCH_ARMV7=y +CONFIG_MAX_REBOOT_CNT=3 +CONFIG_BOOTBLOCK_SOURCE="bootblock_simple.c" + +# +# Architecture (armv7) +# +CONFIG_SPL_BUILD=y +# CONFIG_EABI_COMPAT is not set +CONFIG_ARM_BOOTBLOCK_SIMPLE=y +# CONFIG_ARM_BOOTBLOCK_NORMAL is not set +CONFIG_ARM_DCACHE_POLICY_WRITEBACK=y +# CONFIG_ARM_DCACHE_POLICY_WRITETHROUGH is not set + +# +# Chipset +# + +# +# CPU +# +CONFIG_CPU_SAMSUNG_EXYNOS=y +CONFIG_CPU_SAMSUNG_EXYNOS5=y +# CONFIG_SKIP_LOWLEVEL_INIT is not set +CONFIG_IRAM_BOTTOM=0x02020000 +CONFIG_IRAM_TOP=0x02077fff +CONFIG_SYS_INIT_SP_ADDR=0x0204F800 +CONFIG_IRAM_STACK=0x0204F800 +CONFIG_XIP_ROM_SIZE=0x20000 +# CONFIG_EXYNOS_ACE_SHA is not set +# CONFIG_SATA_AHCI is not set +CONFIG_SYS_TEXT_BASE=0x43e00000 +CONFIG_SYS_SDRAM_BASE=0x40000000 +CONFIG_HAVE_INIT_TIMER=y +CONFIG_HIGH_SCRATCH_MEMORY_SIZE=0x0 +# CONFIG_CPU_MICROCODE_IN_CBFS is not set +# CONFIG_CPU_MICROCODE_CBFS_GENERATE is not set +# CONFIG_CPU_MICROCODE_CBFS_EXTERNAL is not set +CONFIG_CPU_MICROCODE_CBFS_NONE=y + +# +# Northbridge +# +CONFIG_VIDEO_MB=0 +# CONFIG_NORTHBRIDGE_AMD_AGESA is not set +# CONFIG_AMD_NB_CIMX is not set +# CONFIG_NORTHBRIDGE_AMD_CIMX_RD890 is not set +CONFIG_CBFS_SIZE=0x400000 + +# +# Southbridge +# +# CONFIG_AMD_SB_CIMX is not set +# CONFIG_SOUTHBRIDGE_AMD_CIMX_SB800 is not set +# CONFIG_SOUTHBRIDGE_AMD_CIMX_SB900 is not set +# CONFIG_SPI_FLASH is not set + +# +# Super I/O +# + +# +# Embedded Controllers +# + +# +# Devices +# +# CONFIG_VGA_ROM_RUN is not set +# CONFIG_ON_DEVICE_ROM_RUN is not set +# CONFIG_MULTIPLE_VGA_ADAPTERS is not set +# CONFIG_PCI is not set +# CONFIG_PCIEXP_COMMON_CLOCK is not set +# CONFIG_PCIEXP_ASPM is not set +CONFIG_PCI_BUS_SEGN_BITS=0 + +# +# VGA BIOS +# + +# +# Generic Drivers +# +# CONFIG_IPMI_KCS is not set +# CONFIG_DRIVERS_OXFORD_OXPCIE is not set +# CONFIG_RTL8168_ROM_DISABLE is not set +# CONFIG_DRIVERS_SIL_3114 is not set +CONFIG_DRIVER_MAXIM_77686=y +# CONFIG_MMCONF_SUPPORT is not set + +# +# Console +# +CONFIG_SERIAL_CONSOLE=y +# CONFIG_CONSOLE_SERIAL8250MEM is not set +CONFIG_CONSOLE_SERIAL_NONSTANDARD_MEM=y +CONFIG_CONSOLE_SERIAL_115200=y +# CONFIG_CONSOLE_SERIAL_57600 is not set +# CONFIG_CONSOLE_SERIAL_38400 is not set +# CONFIG_CONSOLE_SERIAL_19200 is not set +# CONFIG_CONSOLE_SERIAL_9600 is not set +# CONFIG_HAVE_USBDEBUG is not set +# CONFIG_CONSOLE_NE2K is not set +CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_8=y +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_7 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_6 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_5 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_4 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_3 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_2 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_1 is not set +# CONFIG_MAXIMUM_CONSOLE_LOGLEVEL_0 is not set +CONFIG_DEFAULT_CONSOLE_LOGLEVEL_8=y +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_7 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_6 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_5 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_4 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_3 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_2 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_1 is not set +# CONFIG_DEFAULT_CONSOLE_LOGLEVEL_0 is not set +# CONFIG_CONSOLE_LOGBUF is not set +# CONFIG_NO_POST is not set +# CONFIG_CMOS_POST is not set +# CONFIG_IO_POST is not set +CONFIG_HAVE_UART_IO_MAPPED=y +CONFIG_HAVE_UART_MEMORY_MAPPED=y +# CONFIG_HAVE_ACPI_RESUME is not set +# CONFIG_HAVE_ACPI_SLIC is not set +# CONFIG_HAVE_HARD_RESET is not set +# CONFIG_HAVE_OPTION_TABLE is not set +# CONFIG_PIRQ_ROUTE is not set +# CONFIG_HAVE_SMI_HANDLER is not set +# CONFIG_PCI_IO_CFG_EXT is not set +# CONFIG_USE_WATCHDOG_ON_BOOT is not set +# CONFIG_VGA is not set +# CONFIG_GFXUMA is not set +CONFIG_MAX_PIRQ_LINKS=4 + +# +# System tables +# +CONFIG_WRITE_HIGH_TABLES=y +CONFIG_MULTIBOOT=y +# CONFIG_GENERATE_ACPI_TABLES is not set +# CONFIG_GENERATE_MP_TABLE is not set +# CONFIG_GENERATE_PIRQ_TABLE is not set + +# +# Payload +# +CONFIG_PAYLOAD_NONE=y +# CONFIG_PAYLOAD_ELF is not set +# CONFIG_PAYLOAD_SEABIOS is not set +# CONFIG_PAYLOAD_FILO is not set +# CONFIG_COMPRESSED_PAYLOAD_NRV2B is not set + +# +# Debugging +# +# CONFIG_GDB_STUB is not set +# CONFIG_HAVE_DEBUG_RAM_SETUP is not set +# CONFIG_HAVE_DEBUG_CAR is not set +# CONFIG_HAVE_DEBUG_SMBUS is not set +# CONFIG_DEBUG_MALLOC is not set +# CONFIG_DEBUG_ACPI is not set +# CONFIG_LLSHELL is not set +# CONFIG_TRACE is not set +# CONFIG_AP_CODE_IN_CAR is not set +# CONFIG_RAMINIT_SYSINFO is not set +# CONFIG_ENABLE_APIC_EXT_ID is not set +# CONFIG_POWER_BUTTON_DEFAULT_ENABLE is not set +# CONFIG_POWER_BUTTON_DEFAULT_DISABLE is not set +# CONFIG_POWER_BUTTON_FORCE_DISABLE is not set +# CONFIG_POWER_BUTTON_IS_OPTIONAL is not set +# CONFIG_CHROMEOS is not set diff --git a/src/arch/armv7/Kconfig b/src/arch/armv7/Kconfig index 00e6549..f92911d 100644 --- a/src/arch/armv7/Kconfig +++ b/src/arch/armv7/Kconfig @@ -1,3 +1,55 @@ menu "Architecture (armv7)"
+config SPL_BUILD + bool "Build second-phase bootloader (SPL)" + default y + +config EABI_COMPAT + bool "Toolchain is EABI compatible" + default n + +# Maximum reboot count +# TODO: Improve description. +config MAX_REBOOT_CNT + int + default 3 + +choice + prompt "Bootblock behaviour" + default ARM_BOOTBLOCK_SIMPLE + +config ARM_BOOTBLOCK_SIMPLE + bool "Always load fallback" + +config ARM_BOOTBLOCK_NORMAL + bool "Switch to normal if non-volatile memory says so" + +endchoice + +config BOOTBLOCK_SOURCE + string + default "bootblock_simple.c" if ARM_BOOTBLOCK_SIMPLE + default "bootblock_normal.c" if ARM_BOOTBLOCK_NORMAL + +config UPDATE_IMAGE + bool "Update existing coreboot.rom image" + default n + depends on TINY_BOOTBLOCK + help + If this option is enabled, no new coreboot.rom file + is created. Instead it is expected that there already + is a suitable file for further processing. + The bootblock will not be modified. + +config BOOTBLOCK_SOC_INIT + string + +# FIXME: Should cache policy be set on a per-CPU basis? +# FIXME(dhendrix): Stefan sayz to make a smart decision and not prompt the user. +config ARM_DCACHE_POLICY_WRITEBACK + bool y + +config ARM_DCACHE_POLICY_WRITETHROUGH + bool n + endmenu diff --git a/src/arch/armv7/Makefile.inc b/src/arch/armv7/Makefile.inc new file mode 100644 index 0000000..076a93f --- /dev/null +++ b/src/arch/armv7/Makefile.inc @@ -0,0 +1,324 @@ +################################################################################ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2012 The ChromiumOS Authors +## Copyright (C) 2012 Alexandru Gagniuc mr.nuke.me@gmail.com +## Copyright (C) 2009-2010 coresystems GmbH +## Copyright (C) 2009 Ronald G. Minnich +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## +################################################################################ + +# Take care of subdirectories +subdirs-y += boot/ +subdirs-y += lib/ +# subdirs-y += smp/ + +################################################################################ +# Build the final rom image +COREBOOT_ROM_DEPENDENCIES:= +ifeq ($(CONFIG_PAYLOAD_ELF),y) +COREBOOT_ROM_DEPENDENCIES+=$(CONFIG_PAYLOAD_FILE) +endif +#ifeq ($(CONFIG_AP_CODE_IN_CAR),y) +#COREBOOT_ROM_DEPENDENCIES+=$(objcbfs)/coreboot_ap.elf +#endif + +extract_nth=$(word $(1), $(subst |, ,$(2))) + +ifneq ($(CONFIG_UPDATE_IMAGE),y) +prebuild-files = \ + $(foreach file,$(cbfs-files), \ + $(CBFSTOOL) $@.tmp \ + add$(if $(filter stage,$(call extract_nth,3,$(file))),-stage)$(if $(filter payload,$(call extract_nth,3,$(file))),-payload) \ + $(call extract_nth,1,$(file)) \ + $(call extract_nth,2,$(file)) $(if $(filter-out stage payload,$(call extract_nth,3,$(file))),$(call extract_nth,3,$(file))) \ + $(call extract_nth,4,$(file)) &&) +prebuilt-files = $(foreach file,$(cbfs-files), $(call extract_nth,1,$(file))) + +$(obj)/coreboot.pre1: $(objcbfs)/bootblock.bin $$(prebuilt-files) $(CBFSTOOL) + $(CBFSTOOL) $@.tmp create -m armv7 -s $(CONFIG_COREBOOT_ROMSIZE_KB)K \ + -B $(objcbfs)/bootblock.bin -a 64 \ + -o $$(( $(CONFIG_ROM_SIZE) - $(CONFIG_CBFS_SIZE) )) + $(prebuild-files) true + mv $@.tmp $@ +else +.PHONY: $(obj)/coreboot.pre1 +$(obj)/coreboot.pre1: $(CBFSTOOL) + mv $(obj)/coreboot.rom $@ +endif + +$(obj)/coreboot.rom: $(obj)/coreboot.pre $(objcbfs)/coreboot_ram.elf $(CBFSTOOL) $(call strip_quotes,$(COREBOOT_ROM_DEPENDENCIES)) + @printf " CBFS $(subst $(obj)/,,$(@))\n" + cp $(obj)/coreboot.pre $@.tmp + if [ -f $(objcbfs)/coreboot_ap.elf ]; \ + then \ + $(CBFSTOOL) $@.tmp add-stage -f $(objcbfs)/coreboot_ap.elf -n $(CONFIG_CBFS_PREFIX)/coreboot_ap -c $(CBFS_COMPRESS_FLAG) ; \ + fi + $(CBFSTOOL) $@.tmp add-stage -f $(objcbfs)/coreboot_ram.elf -n $(CONFIG_CBFS_PREFIX)/coreboot_ram -c $(CBFS_COMPRESS_FLAG) +ifeq ($(CONFIG_PAYLOAD_NONE),y) + @printf " PAYLOAD \e[1;31mnone (as specified by user)\e[0m\n" +endif +ifeq ($(CONFIG_PAYLOAD_ELF),y) + @printf " PAYLOAD $(CONFIG_PAYLOAD_FILE) (compression: $(CBFS_PAYLOAD_COMPRESS_NAME))\n" + $(CBFSTOOL) $@.tmp add-payload $(CONFIG_PAYLOAD_FILE) $(CONFIG_CBFS_PREFIX)/payload $(CBFS_PAYLOAD_COMPRESS_FLAG) +endif +ifeq ($(CONFIG_INCLUDE_CONFIG_FILE),y) + @printf " CONFIG $(DOTCONFIG)\n" + if [ -f $(DOTCONFIG) ]; then \ + echo "# This image was built using git revision" `git rev-parse HEAD` > $(obj)/config.tmp ; \ + sed -e '/^#/d' -e '/^ *$$/d' $(DOTCONFIG) >> $(obj)/config.tmp ; \ + $(CBFSTOOL) $@.tmp add -f $(obj)/config.tmp -n config -t raw; rm -f $(obj)/config.tmp ; fi +endif + mv $@.tmp $@ + @printf " CBFSPRINT $(subst $(obj)/,,$(@))\n\n" + $(CBFSTOOL) $@ print + +bootsplash.jpg-file := $(call strip_quotes,$(CONFIG_BOOTSPLASH_FILE)) +bootsplash.jpg-type := bootsplash + +################################################################################ +# armv7 specific tools + +################################################################################ +# Common recipes for all stages + +$(objcbfs)/%.bin: $(objcbfs)/%.elf + @printf " OBJCOPY $(subst $(obj)/,,$(@))\n" + $(OBJCOPY) -O binary $< $@ + +$(objcbfs)/%.elf: $(objcbfs)/%.debug + @printf " OBJCOPY $(subst $(obj)/,,$(@))\n" + cp $< $@.tmp + $(NM) -n $@.tmp | sort > $(basename $@).map + $(OBJCOPY) --strip-debug $@.tmp + $(OBJCOPY) --add-gnu-debuglink=$< $@.tmp + mv $@.tmp $@ + +################################################################################ +# Build the coreboot_ram (stage 2) + +$(objcbfs)/coreboot_ram.debug: $(objgenerated)/coreboot_ram.o $(src)/arch/armv7/coreboot_ram.ld + @printf " CC $(subst $(obj)/,,$(@))\n" +ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) + $(LD) -m armelf_linux_eabi -o $@ -L$(obj) $< -T $(src)/arch/armv7/coreboot_ram.ld +else + $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(src)/arch/armv7/coreboot_ram.ld $< +endif + +$(objgenerated)/coreboot_ram.o: $$(ramstage-objs) $(LIBGCC_FILE_NAME) + @printf " CC $(subst $(obj)/,,$(@))\n" +ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) + $(LD) -m -m armelf_linux_eabi -r -o $@ --wrap __divdi3 --wrap __udivdi3 --wrap __moddi3 --wrap __umoddi3 --wrap __uidiv --wrap __do_div64 --start-group $(ramstage-objs) $(LIBGCC_FILE_NAME) --end-group +else + $(CC) -nostdlib -r -o $@ -Wl,--start-group $(ramstage-objs) $(LIBGCC_FILE_NAME) -Wl,--end-group +endif + +################################################################################ +# Ramstage for AP CPU (AMD K8, obsolete?) + +#$(objcbfs)/coreboot_ap.debug: $(objgenerated)/coreboot_ap.o $(src)/arch/armv7/init/ldscript_apc.lb +# @printf " CC $(subst $(obj)/,,$(@))\n" +# $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(src)/arch/armv7/init/ldscript_apc.lb $< + +#$(objgenerated)/coreboot_ap.o: $(src)/mainboard/$(MAINBOARDDIR)/ap_romstage.c $(OPTION_TABLE_H) +# @printf " CC $(subst $(obj)/,,$(@))\n" +# $(CC) -MMD $(CFLAGS) -I$(src) -D__PRE_RAM__ -I. -I$(obj) -c $< -o $@ + +################################################################################ +# done + +# For various headers imported from Linux +CFLAGS += -D__KERNEL__ +CFLAGS += -D__LINUX_ARM_ARCH__=7 +INCLUDES += -Isrc/include/linux +INCLUDES += -Isrc/include/linux/uapi + +# FIXME(dhendrix): trying to split start.S apart... +crt0s = $(src)/arch/armv7/start.S +#crt0s = $(src)/arch/armv7/romstage.S +ldscripts = +ldscripts += $(src)/arch/armv7/romstage.ld + +#crt0s += $(src)/cpu/arm/fpu_enable.inc +# FIXME: CONFIG_NEON or something similar for ARM? +#ifeq ($(CONFIG_SSE),y) +#crt0s += $(src)/cpu/arm/sse_enable.inc +#endif + +crt0s += $(cpu_incs) +crt0s += $(cpu_incs-y) + +ifeq ($(CONFIG_LLSHELL),y) +crt0s += $(src)/arch/armv7/llshell/llshell.inc +endif + +crt0s += $(obj)/mainboard/$(MAINBOARDDIR)/romstage.inc + +$(obj)/mainboard/$(MAINBOARDDIR)/romstage.pre.inc: $(src)/mainboard/$(MAINBOARDDIR)/romstage.c $(OPTION_TABLE_H) $(obj)/build.h $(obj)/config.h + @printf " CC romstage.inc\n" + $(CC) -MMD $(CFLAGS) -D__PRE_RAM__ -I$(src) -I. -I$(obj) -c -S $< -o $@ + +$(obj)/mainboard/$(MAINBOARDDIR)/romstage.inc: $(obj)/mainboard/$(MAINBOARDDIR)/romstage.pre.inc + @printf " POST romstage.inc\n" + sed -e 's/.rodata/.rom.data/g' -e 's/^.text/.section .rom.text/g' \ + -e 's/^.section .text/.section .rom.text/g' $^ > $@.tmp + mv $@.tmp $@ + +# Things that appear in every board +romstage-srcs += $(objgenerated)/crt0.s +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/mainboard.c +ifeq ($(CONFIG_GENERATE_PIRQ_TABLE),y) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/irq_tables.c +endif +ifeq ($(CONFIG_BOARD_HAS_HARD_RESET),y) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/reset.c +endif +ifeq ($(CONFIG_GENERATE_ACPI_TABLES),y) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/dsdt.asl +# make doesn't have arithmetic operators or greater-than comparisons +ifeq ($(subst 5,4,$(CONFIG_ACPI_SSDTX_NUM)),4) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/ssdt2.asl +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/ssdt3.asl +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/ssdt4.asl +endif +ifeq ($(CONFIG_ACPI_SSDTX_NUM),5) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/ssdt5.asl +endif +ifeq ($(CONFIG_BOARD_HAS_FADT),y) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/fadt.c +endif +endif + +ifeq ($(CONFIG_HAVE_BUS_CONFIG),y) +ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/get_bus_conf.c +endif + +################################################################################ +# Build the final rom image + +$(obj)/coreboot.pre: $(objcbfs)/romstage_xip.elf $(obj)/coreboot.pre1 $(CBFSTOOL) + @printf " CBFS $(subst $(obj)/,,$(@))\n" + cp $(obj)/coreboot.pre1 $@.tmp + $(CBFSTOOL) $@.tmp add-stage \ + -f $(objcbfs)/romstage_xip.elf \ + -n $(CONFIG_CBFS_PREFIX)/romstage -c none \ + -b $(shell cat $(objcbfs)/base_xip.txt) + mv $@.tmp $@ + +################################################################################ +# Build the bootblock + +#bootblock_lds = $(src)/arch/armv7/ldscript_fallback_cbfs.lb +bootblock_lds = $(src)/arch/armv7/lib/id.lds +#bootblock_lds = $(src)/arch/armv7/romstage.ld +bootblock_lds += $(chipset_bootblock_lds) + +bootblock_inc += $(src)/arch/armv7/lib/id.inc +bootblock_inc += $(chipset_bootblock_inc) + +# FIXME: CONFIG_NEON or something similar for ARM? +#ifeq ($(CONFIG_SSE),y) +#bootblock_inc += $(src)/cpu/x86/sse_enable.inc +#endif +#bootblock_inc += $(objgenerated)/bootblock.inc + +$(objgenerated)/bootblock.ld: $$(bootblock_lds) $(obj)/ldoptions + @printf " GEN $(subst $(obj)/,,$(@))\n" + printf '$(foreach ldscript,ldoptions $(bootblock_lds),INCLUDE "$(ldscript)"\n)' > $@ + +$(objgenerated)/bootblock_inc.S: $$(bootblock_inc) + @printf " GEN $(subst $(obj)/,,$(@))\n" + printf '$(foreach crt0,$(bootblock_inc),#include "$(crt0)"\n)' > $@ + +$(objgenerated)/bootblock.o: $(objgenerated)/bootblock.s + @printf " CC $(subst $(obj)/,,$(@))\n" + $(CC) -Wa,-acdlns -c -o $@ $< > $(basename $@).disasm + +$(objgenerated)/bootblock.s: $(objgenerated)/bootblock_inc.S $(obj)/config.h $(obj)/build.h + @printf " CC $(subst $(obj)/,,$(@))\n" + $(CC) -MMD -x assembler-with-cpp -E -I$(src)/include -I$(src)/arch/armv7/include -I$(obj) -include $(obj)/build.h -include $(obj)/config.h -I. -I$(src) $< -o $@ + +#$(objgenerated)/bootblock.inc: $(src)/arch/armv7/init/$(subst ",,$(CONFIG_BOOTBLOCK_SOURCE)) $(objutil)/romcc/romcc $(OPTION_TABLE_H) +# @printf " ROMCC $(subst $(obj)/,,$(@))\n" +# $(CC) $(INCLUDES) -MM -MT$(objgenerated)/bootblock.inc \ +# $< > $(objgenerated)/bootblock.inc.d +# $(ROMCC) -c -S $(bootblock_romccflags) $(ROMCCFLAGS) -I. $(INCLUDES) $< -o $@ + +$(objcbfs)/bootblock.debug: $(objgenerated)/bootblock.o $(objgenerated)/bootblock.ld + @printf " LINK $(subst $(obj)/,,$(@))\n" +ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) + $(LD) -m armelf_linux_eabi -static -o $@.tmp -L$(obj) $< -T $(objgenerated)/bootblock.ld +else + $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(objgenerated)/bootblock.ld $< +endif + +################################################################################ +# Build the romstage + +# FIXME(dhendrix): added debug printfs +$(objcbfs)/romstage_null.debug: $$(romstage-objs) $(objgenerated)/romstage_null.ld + @printf " LINK $(subst $(obj)/,,$(@))\n" +ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) + $(LD) -nostdlib -nostartfiles -static -o $@ -L$(obj) $(romstage-objs) -T $(objgenerated)/romstage_null.ld +else + $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(objgenerated)/romstage_null.ld -Wl,--start-group $(romstage-objs) $(LIBGCC_FILE_NAME) -Wl,--end-group +endif + +$(objcbfs)/romstage_xip.debug: $$(romstage-objs) $(objgenerated)/romstage_xip.ld + @printf " LINK $(subst $(obj)/,,$(@))\n" +ifeq ($(CONFIG_COMPILER_LLVM_CLANG),y) + $(LD) -nostdlib -nostartfiles -static -o $@ -L$(obj) $(romstage-objs) -T $(objgenerated)/romstage_xip.ld +else + $(CC) -nostdlib -nostartfiles -static -o $@ -L$(obj) -T $(objgenerated)/romstage_xip.ld -Wl,--start-group $(romstage-objs) $(LIBGCC_FILE_NAME) -Wl,--end-group +endif + +$(objgenerated)/romstage_null.ld: $$(ldscripts) $(obj)/ldoptions + @printf " GEN $(subst $(obj)/,,$(@))\n" + rm -f $@ + printf "ROMSTAGE_BASE = 0x0;\n" > $@.tmp + printf '$(foreach ldscript,ldoptions $(ldscripts),INCLUDE "$(ldscript:$(obj)/%=%)"\n)' >> $@.tmp + mv $@.tmp $@ + +$(objgenerated)/romstage_xip.ld: $(objgenerated)/romstage_null.ld $(objcbfs)/base_xip.txt + @printf " GEN $(subst $(obj)/,,$(@))\n" + rm -f $@ + sed -e 's/^/ROMSTAGE_BASE = /g' -e 's/$$/;/g' $(objcbfs)/base_xip.txt > $@.tmp + sed -e '/ROMSTAGE_BASE/d' $(objgenerated)/romstage_null.ld >> $@.tmp + mv $@.tmp $@ + +$(objcbfs)/base_xip.txt: $(obj)/coreboot.pre1 $(objcbfs)/romstage_null.bin + @printf " generating base_xip.txt\n" + rm -f $@ + $(CBFSTOOL) $(obj)/coreboot.pre1 locate -f $(objcbfs)/romstage_null.bin -n $(CONFIG_CBFS_PREFIX)/romstage -a $(CONFIG_XIP_ROM_SIZE) > $@.tmp \ + || { echo "The romstage is larger than XIP size. Please expand the CONFIG_XIP_ROM_SIZE" ; exit 1; } + sed -e 's/^/0x/g' $@.tmp > $@.tmp2 + rm $@.tmp + mv $@.tmp2 $@ + +$(objgenerated)/crt0.romstage.S: $$(crt0s) + @printf " GEN $(subst $(obj)/,,$(@))\n" + printf '$(foreach crt0,$(crt0s),#include "$(crt0:$(obj)/%=%)"\n)' > $@ + +$(objgenerated)/crt0.romstage.o: $(objgenerated)/crt0.s + @printf " CC $(subst $(obj)/,,$(@))\n" + $(CC) -Wa,-acdlns -c -o $@ $< > $(basename $@).disasm + +$(objgenerated)/crt0.s: $(objgenerated)/crt0.romstage.S $(obj)/config.h $(obj)/build.h + @printf " CC $(subst $(obj)/,,$(@))\n" + $(CC) -MMD -x assembler-with-cpp -E -I$(src)/include -I$(src)/arch/armv7/include -I$(obj) -include $(obj)/config.h -include $(obj)/build.h -I. -I$(src) $< -o $@ + diff --git a/src/arch/armv7/boot/Makefile.inc b/src/arch/armv7/boot/Makefile.inc new file mode 100644 index 0000000..a0752d6 --- /dev/null +++ b/src/arch/armv7/boot/Makefile.inc @@ -0,0 +1,13 @@ +ramstage-y += boot.c +ramstage-y += coreboot_table.c +#ramstage-$(CONFIG_MULTIBOOT) += multiboot.c +ramstage-y += tables.c +#ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c +#ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpigen.c +#ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S + +#FIXME(dhendrix): is there anything preventing multiboot from +#working on ARM? + +$(obj)/arch/armv7/boot/coreboot_table.ramstage.o : $(OPTION_TABLE_H) +#$(obj)/arch/x86/boot/smbios.ramstage.o: $(obj)/build.h diff --git a/src/arch/armv7/boot/acpi.c b/src/arch/armv7/boot/acpi.c new file mode 100644 index 0000000..4d405d9 --- /dev/null +++ b/src/arch/armv7/boot/acpi.c @@ -0,0 +1,791 @@ +/* + * This file is part of the coreboot project. + * + * coreboot ACPI Table support + * written by Stefan Reinauer stepan@openbios.org + * + * Copyright (C) 2004 SUSE LINUX AG + * Copyright (C) 2005-2009 coresystems GmbH + * + * ACPI FADT, FACS, and DSDT table support added by + * Nick Barker nick.barker9@btinternet.com, and those portions + * Copyright (C) 2004 Nick Barker + * + * Copyright (C) 2005 ADVANCED MICRO DEVICES, INC. All Rights Reserved. + * 2005.9 yhlu add SRAT table generation + */ + +/* + * Each system port implementing ACPI has to provide two functions: + * + * write_acpi_tables() + * acpi_dump_apics() + * + * See Kontron 986LCD-M port for a good example of an ACPI implementation + * in coreboot. + */ + +#include <console/console.h> +#include <string.h> +#include <arch/acpi.h> +#include <arch/acpigen.h> +#include <device/pci.h> +#include <cbmem.h> +#include <cpu/x86/lapic_def.h> +#include <cpu/cpu.h> +#if CONFIG_COLLECT_TIMESTAMPS +#include <timestamp.h> +#endif + +/* FIXME: Kconfig doesn't support overridable defaults :-( */ +#ifndef CONFIG_HPET_MIN_TICKS +#define CONFIG_HPET_MIN_TICKS 0x1000 +#endif + +u8 acpi_checksum(u8 *table, u32 length) +{ + u8 ret = 0; + while (length--) { + ret += *table; + table++; + } + return -ret; +} + +/** + * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length + * and checksum. + */ +void acpi_add_table(acpi_rsdp_t *rsdp, void *table) +{ + int i, entries_num; + acpi_rsdt_t *rsdt; + acpi_xsdt_t *xsdt = NULL; + + /* The RSDT is mandatory... */ + rsdt = (acpi_rsdt_t *)rsdp->rsdt_address; + + /* ...while the XSDT is not. */ + if (rsdp->xsdt_address) + xsdt = (acpi_xsdt_t *)((u32)rsdp->xsdt_address); + + /* This should always be MAX_ACPI_TABLES. */ + entries_num = ARRAY_SIZE(rsdt->entry); + + for (i = 0; i < entries_num; i++) { + if (rsdt->entry[i] == 0) + break; + } + + if (i >= entries_num) { + printk(BIOS_ERR, "ACPI: Error: Could not add ACPI table, " + "too many tables.\n"); + return; + } + + /* Add table to the RSDT. */ + rsdt->entry[i] = (u32)table; + + /* Fix RSDT length or the kernel will assume invalid entries. */ + rsdt->header.length = sizeof(acpi_header_t) + (sizeof(u32) * (i + 1)); + + /* Re-calculate checksum. */ + rsdt->header.checksum = 0; /* Hope this won't get optimized away */ + rsdt->header.checksum = acpi_checksum((u8 *)rsdt, rsdt->header.length); + + /* + * And now the same thing for the XSDT. We use the same index as for + * now we want the XSDT and RSDT to always be in sync in coreboot. + */ + if (xsdt) { + /* Add table to the XSDT. */ + xsdt->entry[i] = (u64)(u32)table; + + /* Fix XSDT length. */ + xsdt->header.length = sizeof(acpi_header_t) + + (sizeof(u64) * (i + 1)); + + /* Re-calculate checksum. */ + xsdt->header.checksum = 0; + xsdt->header.checksum = acpi_checksum((u8 *)xsdt, + xsdt->header.length); + } + + printk(BIOS_DEBUG, "ACPI: added table %d/%d, length now %d\n", + i + 1, entries_num, rsdt->header.length); +} + +int acpi_create_mcfg_mmconfig(acpi_mcfg_mmconfig_t *mmconfig, u32 base, + u16 seg_nr, u8 start, u8 end) +{ + mmconfig->base_address = base; + mmconfig->base_reserved = 0; + mmconfig->pci_segment_group_number = seg_nr; + mmconfig->start_bus_number = start; + mmconfig->end_bus_number = end; + + return sizeof(acpi_mcfg_mmconfig_t); +} + +int acpi_create_madt_lapic(acpi_madt_lapic_t *lapic, u8 cpu, u8 apic) +{ + lapic->type = 0; /* Local APIC structure */ + lapic->length = sizeof(acpi_madt_lapic_t); + lapic->flags = (1 << 0); /* Processor/LAPIC enabled */ + lapic->processor_id = cpu; + lapic->apic_id = apic; + + return lapic->length; +} + +unsigned long acpi_create_madt_lapics(unsigned long current) +{ + device_t cpu; + int index = 0; + + for (cpu = all_devices; cpu; cpu = cpu->next) { + if ((cpu->path.type != DEVICE_PATH_APIC) || + (cpu->bus->dev->path.type != DEVICE_PATH_APIC_CLUSTER)) { + continue; + } + if (!cpu->enabled) + continue; + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, + index, cpu->path.apic.apic_id); + index++; + } + + return current; +} + +int acpi_create_madt_ioapic(acpi_madt_ioapic_t *ioapic, u8 id, u32 addr, + u32 gsi_base) +{ + ioapic->type = 1; /* I/O APIC structure */ + ioapic->length = sizeof(acpi_madt_ioapic_t); + ioapic->reserved = 0x00; + ioapic->gsi_base = gsi_base; + ioapic->ioapic_id = id; + ioapic->ioapic_addr = addr; + + return ioapic->length; +} + +int acpi_create_madt_irqoverride(acpi_madt_irqoverride_t *irqoverride, + u8 bus, u8 source, u32 gsirq, u16 flags) +{ + irqoverride->type = 2; /* Interrupt source override */ + irqoverride->length = sizeof(acpi_madt_irqoverride_t); + irqoverride->bus = bus; + irqoverride->source = source; + irqoverride->gsirq = gsirq; + irqoverride->flags = flags; + + return irqoverride->length; +} + +int acpi_create_madt_lapic_nmi(acpi_madt_lapic_nmi_t *lapic_nmi, u8 cpu, + u16 flags, u8 lint) +{ + lapic_nmi->type = 4; /* Local APIC NMI structure */ + lapic_nmi->length = sizeof(acpi_madt_lapic_nmi_t); + lapic_nmi->flags = flags; + lapic_nmi->processor_id = cpu; + lapic_nmi->lint = lint; + + return lapic_nmi->length; +} + +void acpi_create_madt(acpi_madt_t *madt) +{ + acpi_header_t *header = &(madt->header); + unsigned long current = (unsigned long)madt + sizeof(acpi_madt_t); + + memset((void *)madt, 0, sizeof(acpi_madt_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "APIC", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_madt_t); + header->revision = 1; /* ACPI 1.0/2.0: 1, ACPI 3.0: 2, ACPI 4.0: 3 */ + + madt->lapic_addr = LOCAL_APIC_ADDR; + madt->flags = 0x1; /* PCAT_COMPAT */ + + current = acpi_fill_madt(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)madt; + + header->checksum = acpi_checksum((void *)madt, header->length); +} + +/* MCFG is defined in the PCI Firmware Specification 3.0. */ +void acpi_create_mcfg(acpi_mcfg_t *mcfg) +{ + acpi_header_t *header = &(mcfg->header); + unsigned long current = (unsigned long)mcfg + sizeof(acpi_mcfg_t); + + memset((void *)mcfg, 0, sizeof(acpi_mcfg_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "MCFG", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_mcfg_t); + header->revision = 1; + + current = acpi_fill_mcfg(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)mcfg; + header->checksum = acpi_checksum((void *)mcfg, header->length); +} + +/* + * This can be overriden by platform ACPI setup code, if it calls + * acpi_create_ssdt_generator(). + */ +unsigned long __attribute__((weak)) acpi_fill_ssdt_generator( + unsigned long current, const char *oem_table_id) +{ + return current; +} + +void acpi_create_ssdt_generator(acpi_header_t *ssdt, const char *oem_table_id) +{ + unsigned long current = (unsigned long)ssdt + sizeof(acpi_header_t); + + memset((void *)ssdt, 0, sizeof(acpi_header_t)); + + memcpy(&ssdt->signature, "SSDT", 4); + ssdt->revision = 2; /* ACPI 1.0/2.0: ?, ACPI 3.0/4.0: 2 */ + memcpy(&ssdt->oem_id, OEM_ID, 6); + memcpy(&ssdt->oem_table_id, oem_table_id, 8); + ssdt->oem_revision = 42; + memcpy(&ssdt->asl_compiler_id, ASLC, 4); + ssdt->asl_compiler_revision = 42; + ssdt->length = sizeof(acpi_header_t); + + acpigen_set_current((char *) current); + current = acpi_fill_ssdt_generator(current, oem_table_id); + + /* (Re)calculate length and checksum. */ + ssdt->length = current - (unsigned long)ssdt; + ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length); +} + +int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic) +{ + memset((void *)lapic, 0, sizeof(acpi_srat_lapic_t)); + + lapic->type = 0; /* Processor local APIC/SAPIC affinity structure */ + lapic->length = sizeof(acpi_srat_lapic_t); + lapic->flags = (1 << 0); /* Enabled (the use of this structure). */ + lapic->proximity_domain_7_0 = node; + /* TODO: proximity_domain_31_8, local SAPIC EID, clock domain. */ + lapic->apic_id = apic; + + return lapic->length; +} + +int acpi_create_srat_mem(acpi_srat_mem_t *mem, u8 node, u32 basek, u32 sizek, + u32 flags) +{ + mem->type = 1; /* Memory affinity structure */ + mem->length = sizeof(acpi_srat_mem_t); + mem->base_address_low = (basek << 10); + mem->base_address_high = (basek >> (32 - 10)); + mem->length_low = (sizek << 10); + mem->length_high = (sizek >> (32 - 10)); + mem->proximity_domain = node; + mem->flags = flags; + + return mem->length; +} + +/* http://www.microsoft.com/whdc/system/sysinternals/sratdwn.mspx */ +void acpi_create_srat(acpi_srat_t *srat) +{ + acpi_header_t *header = &(srat->header); + unsigned long current = (unsigned long)srat + sizeof(acpi_srat_t); + + memset((void *)srat, 0, sizeof(acpi_srat_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "SRAT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_srat_t); + header->revision = 1; /* ACPI 1.0: N/A, 2.0: 1, 3.0: 2, 4.0: 3 */ + + srat->resv = 1; /* Spec: Reserved to 1 for backwards compatibility. */ + + current = acpi_fill_srat(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)srat; + header->checksum = acpi_checksum((void *)srat, header->length); +} + +unsigned long __attribute__((weak)) acpi_fill_dmar(unsigned long current) +{ + return current; +} + +void acpi_create_dmar(acpi_dmar_t *dmar) +{ + acpi_header_t *header = &(dmar->header); + unsigned long current = (unsigned long)dmar + sizeof(acpi_dmar_t); + + memset((void *)dmar, 0, sizeof(acpi_dmar_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "DMAR", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_dmar_t); + header->revision = 1; + + dmar->host_address_width = 40 - 1; /* FIXME: == MTRR size? */ + dmar->flags = 0; + + current = acpi_fill_dmar(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)dmar; + header->checksum = acpi_checksum((void *)dmar, header->length); +} + +unsigned long acpi_create_dmar_drhd(unsigned long current, u8 flags, + u16 segment, u32 bar) +{ + dmar_entry_t *drhd = (dmar_entry_t *)current; + memset(drhd, 0, sizeof(*drhd)); + drhd->type = DMAR_DRHD; + drhd->length = sizeof(*drhd); /* will be fixed up later */ + drhd->flags = flags; + drhd->segment = segment; + drhd->bar = bar; + + return drhd->length; +} + +void acpi_dmar_drhd_fixup(unsigned long base, unsigned long current) +{ + dmar_entry_t *drhd = (dmar_entry_t *)base; + drhd->length = current - base; +} + +unsigned long acpi_create_dmar_drhd_ds_pci(unsigned long current, u8 segment, + u8 dev, u8 fn) +{ + dev_scope_t *ds = (dev_scope_t *)current; + memset(ds, 0, sizeof(*ds)); + ds->type = SCOPE_PCI_ENDPOINT; + ds->length = sizeof(*ds) + 2; /* we don't support longer paths yet */ + ds->start_bus = segment; + ds->path[0].dev = dev; + ds->path[0].fn = fn; + + return ds->length; +} + +/* http://h21007.www2.hp.com/portal/download/files/unprot/Itanium/slit.pdf */ +void acpi_create_slit(acpi_slit_t *slit) +{ + acpi_header_t *header = &(slit->header); + unsigned long current = (unsigned long)slit + sizeof(acpi_slit_t); + + memset((void *)slit, 0, sizeof(acpi_slit_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "SLIT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_slit_t); + header->revision = 1; /* ACPI 1.0: N/A, ACPI 2.0/3.0/4.0: 1 */ + + current = acpi_fill_slit(current); + + /* (Re)calculate length and checksum. */ + header->length = current - (unsigned long)slit; + header->checksum = acpi_checksum((void *)slit, header->length); +} + +/* http://www.intel.com/hardwaredesign/hpetspec_1.pdf */ +void acpi_create_hpet(acpi_hpet_t *hpet) +{ + acpi_header_t *header = &(hpet->header); + acpi_addr_t *addr = &(hpet->addr); + + memset((void *)hpet, 0, sizeof(acpi_hpet_t)); + + /* Fill out header fields. */ + memcpy(header->signature, "HPET", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_hpet_t); + header->revision = 1; /* Currently 1. Table added in ACPI 2.0. */ + + /* Fill out HPET address. */ + addr->space_id = 0; /* Memory */ + addr->bit_width = 64; + addr->bit_offset = 0; + addr->addrl = CONFIG_HPET_ADDRESS & 0xffffffff; + addr->addrh = ((unsigned long long)CONFIG_HPET_ADDRESS) >> 32; + + hpet->id = *(unsigned int*)CONFIG_HPET_ADDRESS; + hpet->number = 0; + hpet->min_tick = CONFIG_HPET_MIN_TICKS; + + header->checksum = acpi_checksum((void *)hpet, sizeof(acpi_hpet_t)); +} + +void acpi_create_facs(acpi_facs_t *facs) +{ + memset((void *)facs, 0, sizeof(acpi_facs_t)); + + memcpy(facs->signature, "FACS", 4); + facs->length = sizeof(acpi_facs_t); + facs->hardware_signature = 0; + facs->firmware_waking_vector = 0; + facs->global_lock = 0; + facs->flags = 0; + facs->x_firmware_waking_vector_l = 0; + facs->x_firmware_waking_vector_h = 0; + facs->version = 1; /* ACPI 1.0: 0, ACPI 2.0/3.0: 1, ACPI 4.0: 2 */ +} + +void acpi_write_rsdt(acpi_rsdt_t *rsdt) +{ + acpi_header_t *header = &(rsdt->header); + + /* Fill out header fields. */ + memcpy(header->signature, "RSDT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_rsdt_t); + header->revision = 1; /* ACPI 1.0/2.0/3.0/4.0: 1 */ + + /* Entries are filled in later, we come with an empty set. */ + + /* Fix checksum. */ + header->checksum = acpi_checksum((void *)rsdt, sizeof(acpi_rsdt_t)); +} + +void acpi_write_xsdt(acpi_xsdt_t *xsdt) +{ + acpi_header_t *header = &(xsdt->header); + + /* Fill out header fields. */ + memcpy(header->signature, "XSDT", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + + header->length = sizeof(acpi_xsdt_t); + header->revision = 1; /* ACPI 1.0: N/A, 2.0/3.0/4.0: 1 */ + + /* Entries are filled in later, we come with an empty set. */ + + /* Fix checksum. */ + header->checksum = acpi_checksum((void *)xsdt, sizeof(acpi_xsdt_t)); +} + +void acpi_write_rsdp(acpi_rsdp_t *rsdp, acpi_rsdt_t *rsdt, acpi_xsdt_t *xsdt) +{ + memset(rsdp, 0, sizeof(acpi_rsdp_t)); + + memcpy(rsdp->signature, RSDP_SIG, 8); + memcpy(rsdp->oem_id, OEM_ID, 6); + + rsdp->length = sizeof(acpi_rsdp_t); + rsdp->rsdt_address = (u32)rsdt; + + /* + * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2. + * + * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2. + * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR + * revision 0). + */ + if (xsdt == NULL) { + rsdp->revision = 0; + } else { + rsdp->xsdt_address = (u64)(u32)xsdt; + rsdp->revision = 2; + } + + /* Calculate checksums. */ + rsdp->checksum = acpi_checksum((void *)rsdp, 20); + rsdp->ext_checksum = acpi_checksum((void *)rsdp, sizeof(acpi_rsdp_t)); +} + +unsigned long __attribute__((weak)) acpi_fill_hest(acpi_hest_t *hest) +{ + return (unsigned long)hest; +} + +unsigned long acpi_create_hest_error_source(acpi_hest_t *hest, acpi_hest_esd_t *esd, u16 type, void *data, u16 data_len) +{ + acpi_header_t *header = &(hest->header); + acpi_hest_hen_t *hen; + void *pos; + u16 len; + + pos = esd; + memset(pos, 0, sizeof(acpi_hest_esd_t)); + len = 0; + esd->type = type; /* MCE */ + esd->source_id = hest->error_source_count; + esd->flags = 0; /* FIRMWARE_FIRST */ + esd->enabled = 1; + esd->prealloc_erecords = 1; + esd->max_section_per_record = 0x1; + + len += sizeof(acpi_hest_esd_t); + pos = esd + 1; + + switch (type) { + case 0: /* MCE */ + break; + case 1: /* CMC */ + hen = (acpi_hest_hen_t *) (pos); + memset(pos, 0, sizeof(acpi_hest_hen_t)); + hen->type = 3; /* SCI? */ + hen->length = sizeof(acpi_hest_hen_t); + hen->conf_we = 0; /* Configuration Write Enable. */ + hen->poll_interval = 0; + hen->vector = 0; + hen->sw2poll_threshold_val = 0; + hen->sw2poll_threshold_win = 0; + hen->error_threshold_val = 0; + hen->error_threshold_win = 0; + len += sizeof(acpi_hest_hen_t); + pos = hen + 1; + break; + case 2: /* NMI */ + case 6: /* AER Root Port */ + case 7: /* AER Endpoint */ + case 8: /* AER Bridge */ + case 9: /* Generic Hardware Error Source. */ + /* TODO: */ + break; + default: + printk(BIOS_DEBUG, "Invalid type of Error Source."); + break; + } + hest->error_source_count ++; + + memcpy(pos, data, data_len); + len += data_len; + header->length += len; + + return len; +} + +/* ACPI 4.0 */ +void acpi_write_hest(acpi_hest_t *hest) +{ + acpi_header_t *header = &(hest->header); + + memset(hest, 0, sizeof(acpi_hest_t)); + + memcpy(header->signature, "HEST", 4); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + header->length += sizeof(acpi_hest_t); + header->revision = 1; + + acpi_fill_hest(hest); + + /* Calculate checksums. */ + header->checksum = acpi_checksum((void *)hest, header->length); +} + +#if CONFIG_HAVE_ACPI_RESUME +void suspend_resume(void) +{ + void *wake_vec; + + /* If we happen to be resuming find wakeup vector and jump to OS. */ + wake_vec = acpi_find_wakeup_vector(); + if (wake_vec) { +#if CONFIG_HAVE_SMI_HANDLER + u32 *gnvs_address = cbmem_find(CBMEM_ID_ACPI_GNVS); + + /* Restore GNVS pointer in SMM if found */ + if (gnvs_address && *gnvs_address) { + printk(BIOS_DEBUG, "Restore GNVS pointer to 0x%08x\n", + *gnvs_address); + smm_setup_structures((void *)*gnvs_address, NULL, NULL); + } +#endif + + /* Call mainboard resume handler first, if defined. */ + if (mainboard_suspend_resume) + mainboard_suspend_resume(); + post_code(POST_OS_RESUME); + acpi_jump_to_wakeup(wake_vec); + } +} + +/* This is to be filled by SB code - startup value what was found. */ +u8 acpi_slp_type = 0; + +static int acpi_is_wakeup(void) +{ + /* Both resume from S2 and resume from S3 restart at CPU reset */ + return (acpi_slp_type == 3 || acpi_slp_type == 2); +} + +static acpi_rsdp_t *valid_rsdp(acpi_rsdp_t *rsdp) +{ + if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0) + return NULL; + + printk(BIOS_DEBUG, "Looking on %p for valid checksum\n", rsdp); + + if (acpi_checksum((void *)rsdp, 20) != 0) + return NULL; + printk(BIOS_DEBUG, "Checksum 1 passed\n"); + + if ((rsdp->revision > 1) && (acpi_checksum((void *)rsdp, + rsdp->length) != 0)) + return NULL; + printk(BIOS_DEBUG, "Checksum 2 passed all OK\n"); + + return rsdp; +} + +static acpi_rsdp_t *rsdp; + +void *acpi_get_wakeup_rsdp(void) +{ + return rsdp; +} + +void *acpi_find_wakeup_vector(void) +{ + char *p, *end; + acpi_rsdt_t *rsdt; + acpi_facs_t *facs; + acpi_fadt_t *fadt; + void *wake_vec; + int i; + + rsdp = NULL; + + if (!acpi_is_wakeup()) + return NULL; + + printk(BIOS_DEBUG, "Trying to find the wakeup vector...\n"); + + /* Find RSDP. */ + for (p = (char *)0xe0000; p < (char *)0xfffff; p += 16) { + if ((rsdp = valid_rsdp((acpi_rsdp_t *)p))) + break; + } + + if (rsdp == NULL) + return NULL; + + printk(BIOS_DEBUG, "RSDP found at %p\n", rsdp); + rsdt = (acpi_rsdt_t *) rsdp->rsdt_address; + + end = (char *)rsdt + rsdt->header.length; + printk(BIOS_DEBUG, "RSDT found at %p ends at %p\n", rsdt, end); + + for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) { + fadt = (acpi_fadt_t *)rsdt->entry[i]; + if (strncmp((char *)fadt, "FACP", 4) == 0) + break; + fadt = NULL; + } + + if (fadt == NULL) + return NULL; + + printk(BIOS_DEBUG, "FADT found at %p\n", fadt); + facs = (acpi_facs_t *)fadt->firmware_ctrl; + + if (facs == NULL) { + printk(BIOS_DEBUG, "No FACS found, wake up from S3 not " + "possible.\n"); + return NULL; + } + + printk(BIOS_DEBUG, "FACS found at %p\n", facs); + wake_vec = (void *)facs->firmware_waking_vector; + printk(BIOS_DEBUG, "OS waking vector is %p\n", wake_vec); + + return wake_vec; +} + +#if CONFIG_SMP +extern char *lowmem_backup; +extern char *lowmem_backup_ptr; +extern int lowmem_backup_size; +#endif + +#define WAKEUP_BASE 0x600 + +void (*acpi_do_wakeup)(u32 vector, u32 backup_source, u32 backup_target, + u32 backup_size) __attribute__((regparm(0))) = (void *)WAKEUP_BASE; + +extern unsigned char __wakeup, __wakeup_size; + +void acpi_jump_to_wakeup(void *vector) +{ + u32 acpi_backup_memory = (u32)cbmem_find(CBMEM_ID_RESUME); + + if (!acpi_backup_memory) { + printk(BIOS_WARNING, "ACPI: Backup memory missing. " + "No S3 resume.\n"); + return; + } + +#if CONFIG_SMP + // FIXME: This should go into the ACPI backup memory, too. No pork saussages. + /* + * Just restore the SMP trampoline and continue with wakeup on + * assembly level. + */ + memcpy(lowmem_backup_ptr, lowmem_backup, lowmem_backup_size); +#endif + + /* Copy wakeup trampoline in place. */ + memcpy((void *)WAKEUP_BASE, &__wakeup, (size_t)&__wakeup_size); + +#if CONFIG_COLLECT_TIMESTAMPS + timestamp_add_now(TS_ACPI_WAKE_JUMP); +#endif + + acpi_do_wakeup((u32)vector, acpi_backup_memory, CONFIG_RAMBASE, + HIGH_MEMORY_SAVE); +} +#endif + +void acpi_save_gnvs(u32 gnvs_address) +{ + u32 *gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(*gnvs)); + if (gnvs) + *gnvs = gnvs_address; +} diff --git a/src/arch/armv7/boot/acpigen.c b/src/arch/armv7/boot/acpigen.c new file mode 100644 index 0000000..47845a0 --- /dev/null +++ b/src/arch/armv7/boot/acpigen.c @@ -0,0 +1,729 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Rudolf Marek r.marek@assembler.cz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* how many nesting we support */ +#define ACPIGEN_LENSTACK_SIZE 10 + +/* if you need to change this, change the acpigen_write_f and + acpigen_patch_len */ + +#define ACPIGEN_MAXLEN 0xfff + +#include <string.h> +#include <arch/acpigen.h> +#include <console/console.h> +#include <device/device.h> + +static char *gencurrent; + +char *len_stack[ACPIGEN_LENSTACK_SIZE]; +int ltop = 0; + +int acpigen_write_len_f(void) +{ + ASSERT(ltop < (ACPIGEN_LENSTACK_SIZE - 1)) + len_stack[ltop++] = gencurrent; + acpigen_emit_byte(0); + acpigen_emit_byte(0); + return 2; +} + +void acpigen_patch_len(int len) +{ + ASSERT(len <= ACPIGEN_MAXLEN) + ASSERT(ltop > 0) + char *p = len_stack[--ltop]; + /* generate store length for 0xfff max */ + p[0] = (0x40 | (len & 0xf)); + p[1] = (len >> 4 & 0xff); + +} + +void acpigen_set_current(char *curr) +{ + gencurrent = curr; +} + +char *acpigen_get_current(void) +{ + return gencurrent; +} + +int acpigen_emit_byte(unsigned char b) +{ + (*gencurrent++) = b; + return 1; +} + +int acpigen_write_package(int nr_el) +{ + int len; + /* package op */ + acpigen_emit_byte(0x12); + len = acpigen_write_len_f(); + acpigen_emit_byte(nr_el); + return len + 2; +} + +int acpigen_write_byte(unsigned int data) +{ + /* byte op */ + acpigen_emit_byte(0xa); + acpigen_emit_byte(data & 0xff); + return 2; +} + +int acpigen_write_dword(unsigned int data) +{ + /* dword op */ + acpigen_emit_byte(0xc); + acpigen_emit_byte(data & 0xff); + acpigen_emit_byte((data >> 8) & 0xff); + acpigen_emit_byte((data >> 16) & 0xff); + acpigen_emit_byte((data >> 24) & 0xff); + return 5; +} + +int acpigen_write_qword(uint64_t data) +{ + /* qword op */ + acpigen_emit_byte(0xe); + acpigen_emit_byte(data & 0xff); + acpigen_emit_byte((data >> 8) & 0xff); + acpigen_emit_byte((data >> 16) & 0xff); + acpigen_emit_byte((data >> 24) & 0xff); + acpigen_emit_byte((data >> 32) & 0xff); + acpigen_emit_byte((data >> 40) & 0xff); + acpigen_emit_byte((data >> 48) & 0xff); + acpigen_emit_byte((data >> 56) & 0xff); + return 9; +} + +int acpigen_write_name_byte(const char *name, uint8_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_byte(val); + return len; +} + +int acpigen_write_name_dword(const char *name, uint32_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_dword(val); + return len; +} + +int acpigen_write_name_qword(const char *name, uint64_t val) +{ + int len; + len = acpigen_write_name(name); + len += acpigen_write_qword(val); + return len; +} + +int acpigen_emit_stream(const char *data, int size) +{ + int i; + for (i = 0; i < size; i++) { + acpigen_emit_byte(data[i]); + } + return size; +} + +/* The NameString are bit tricky, each element can be 4 chars, if + less its padded with underscore. Check 18.2.2 and 18.4 + and 5.3 of ACPI specs 3.0 for details +*/ + +static int acpigen_emit_simple_namestring(const char *name) { + int i, len = 0; + char ud[] = "____"; + for (i = 0; i < 4; i++) { + if ((name[i] == '\0') || (name[i] == '.')) { + len += acpigen_emit_stream(ud, 4 - i); + break; + } else { + len += acpigen_emit_byte(name[i]); + } + } + return len; +} + +static int acpigen_emit_double_namestring(const char *name, int dotpos) { + int len = 0; + /* mark dual name prefix */ + len += acpigen_emit_byte(0x2e); + len += acpigen_emit_simple_namestring(name); + len += acpigen_emit_simple_namestring(&name[dotpos + 1]); + return len; +} + +static int acpigen_emit_multi_namestring(const char *name) { + int len = 0, count = 0; + unsigned char *pathlen; + /* mark multi name prefix */ + len += acpigen_emit_byte(0x2f); + len += acpigen_emit_byte(0x0); + pathlen = ((unsigned char *) acpigen_get_current()) - 1; + + while (name[0] != '\0') { + len += acpigen_emit_simple_namestring(name); + /* find end or next entity */ + while ((name[0] != '.') && (name[0] != '\0')) + name++; + /* forward to next */ + if (name[0] == '.') + name++; + count++; + } + + pathlen[0] = count; + return len; +} + + +int acpigen_emit_namestring(const char *namepath) { + int dotcount = 0, i; + int dotpos = 0; + int len = 0; + + /* we can start with a \ */ + if (namepath[0] == '\') { + len += acpigen_emit_byte('\'); + namepath++; + } + + /* and there can be any number of ^ */ + while (namepath[0] == '^') { + len += acpigen_emit_byte('^'); + namepath++; + } + + ASSERT(namepath[0] != '\0'); + + i = 0; + while (namepath[i] != '\0') { + if (namepath[i] == '.') { + dotcount++; + dotpos = i; + } + i++; + } + + if (dotcount == 0) { + len += acpigen_emit_simple_namestring(namepath); + } else if (dotcount == 1) { + len += acpigen_emit_double_namestring(namepath, dotpos); + } else { + len += acpigen_emit_multi_namestring(namepath); + } + return len; +} + +int acpigen_write_name(const char *name) +{ + int len; + /* name op */ + len = acpigen_emit_byte(0x8); + return len + acpigen_emit_namestring(name); +} + +int acpigen_write_scope(const char *name) +{ + int len; + /* scope op */ + len = acpigen_emit_byte(0x10); + len += acpigen_write_len_f(); + return len + acpigen_emit_namestring(name); +} + +int acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len) +{ +/* + Processor (_PR.CPUcpuindex, cpuindex, pblock_addr, pblock_len) + { +*/ + char pscope[16]; + int len; + /* processor op */ + acpigen_emit_byte(0x5b); + acpigen_emit_byte(0x83); + len = acpigen_write_len_f(); + + sprintf(pscope, "\_PR.CPU%x", (unsigned int) cpuindex); + len += acpigen_emit_namestring(pscope); + acpigen_emit_byte(cpuindex); + acpigen_emit_byte(pblock_addr & 0xff); + acpigen_emit_byte((pblock_addr >> 8) & 0xff); + acpigen_emit_byte((pblock_addr >> 16) & 0xff); + acpigen_emit_byte((pblock_addr >> 24) & 0xff); + acpigen_emit_byte(pblock_len); + return 6 + 2 + len; +} + +int acpigen_write_empty_PCT(void) +{ +/* + Name (_PCT, Package (0x02) + { + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + }, + + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + } + }) +*/ + static char stream[] = { + 0x08, 0x5F, 0x50, 0x43, 0x54, 0x12, 0x2C, /* 00000030 "0._PCT.," */ + 0x02, 0x11, 0x14, 0x0A, 0x11, 0x82, 0x0C, 0x00, /* 00000038 "........" */ + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000040 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14, /* 00000048 "....y..." */ + 0x0A, 0x11, 0x82, 0x0C, 0x00, 0x7F, 0x00, 0x00, /* 00000050 "........" */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000058 "........" */ + 0x00, 0x79, 0x00 + }; + return acpigen_emit_stream(stream, ARRAY_SIZE(stream)); +} + +int acpigen_write_empty_PTC(void) +{ +/* + Name (_PTC, Package (0x02) + { + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + }, + + ResourceTemplate () + { + Register (FFixedHW, + 0x00, // Bit Width + 0x00, // Bit Offset + 0x0000000000000000, // Address + ,) + } + }) +*/ + int len, nlen, rlen; + acpi_addr_t addr = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + .bit_width = 0, + .bit_offset = 0, + { + .resv = 0 + }, + .addrl = 0, + .addrh = 0, + }; + + nlen = acpigen_write_name("_PTC"); + len = acpigen_write_package(2); + + /* ControlRegister */ + rlen = acpigen_write_resourcetemplate_header(); + rlen += acpigen_write_register(&addr); + len += acpigen_write_resourcetemplate_footer(rlen); + len += rlen; + + /* StatusRegister */ + rlen = acpigen_write_resourcetemplate_header(); + rlen += acpigen_write_register(&addr); + len += acpigen_write_resourcetemplate_footer(rlen); + len += rlen; + + acpigen_patch_len(len - 1); + return len + nlen; +} + +/* generates a func with max supported P states */ +int acpigen_write_PPC(u8 nr) +{ +/* + Method (_PPC, 0, NotSerialized) + { + Return (nr) + } +*/ + int len; + /* method op */ + acpigen_emit_byte(0x14); + len = acpigen_write_len_f(); + len += acpigen_emit_namestring("_PPC"); + /* no fnarg */ + acpigen_emit_byte(0x00); + /* return */ + acpigen_emit_byte(0xa4); + /* arg */ + len += acpigen_write_byte(nr); + /* add all single bytes */ + len += 3; + acpigen_patch_len(len - 1); + return len; +} + +/* generates a func with max supported P states */ +int acpigen_write_PPC_NVS(void) +{ +/* + Method (_PPC, 0, NotSerialized) + { + Return (PPCM) + } +*/ + int len; + /* method op */ + acpigen_emit_byte(0x14); + len = acpigen_write_len_f(); + len += acpigen_emit_namestring("_PPC"); + /* no fnarg */ + acpigen_emit_byte(0x00); + /* return */ + acpigen_emit_byte(0xa4); + /* arg */ + len += acpigen_emit_namestring("PPCM"); + /* add all single bytes */ + len += 3; + acpigen_patch_len(len - 1); + return len; +} + +int acpigen_write_TPC(const char *gnvs_tpc_limit) +{ +/* + // Sample _TPC method + Method (_TPC, 0, NotSerialized) + { + Return (\TLVL) + } + */ + int len; + + len = acpigen_emit_byte(0x14); /* MethodOp */ + len += acpigen_write_len_f(); /* PkgLength */ + len += acpigen_emit_namestring("_TPC"); + len += acpigen_emit_byte(0x00); /* No Arguments */ + len += acpigen_emit_byte(0xa4); /* ReturnOp */ + len += acpigen_emit_namestring(gnvs_tpc_limit); + acpigen_patch_len(len - 1); + return len; +} + +int acpigen_write_PSS_package(u32 coreFreq, u32 power, u32 transLat, + u32 busmLat, u32 control, u32 status) +{ + int len; + len = acpigen_write_package(6); + len += acpigen_write_dword(coreFreq); + len += acpigen_write_dword(power); + len += acpigen_write_dword(transLat); + len += acpigen_write_dword(busmLat); + len += acpigen_write_dword(control); + len += acpigen_write_dword(status); + // pkglen without the len opcode + acpigen_patch_len(len - 1); + + printk(BIOS_DEBUG, "PSS: %uMHz power %u control 0x%x status 0x%x\n", + coreFreq, power, control, status); + + return len; +} + +int acpigen_write_PSD_package(u32 domain, u32 numprocs, PSD_coord coordtype) +{ + int len, lenh, lenp; + lenh = acpigen_write_name("_PSD"); + lenp = acpigen_write_package(1); + len = acpigen_write_package(5); + len += acpigen_write_byte(5); // 5 values + len += acpigen_write_byte(0); // revision 0 + len += acpigen_write_dword(domain); + len += acpigen_write_dword(coordtype); + len += acpigen_write_dword(numprocs); + acpigen_patch_len(len - 1); + len += lenp; + acpigen_patch_len(len - 1); + return len + lenh; +} + +int acpigen_write_CST_package_entry(acpi_cstate_t *cstate) +{ + int len, len0; + char *start, *end; + + len0 = acpigen_write_package(4); + len = acpigen_write_resourcetemplate_header(); + start = acpigen_get_current(); + acpigen_write_register(&cstate->resource); + end = acpigen_get_current(); + len += end - start; + len += acpigen_write_resourcetemplate_footer(len); + len += len0; + len += acpigen_write_dword(cstate->ctype); + len += acpigen_write_dword(cstate->latency); + len += acpigen_write_dword(cstate->power); + acpigen_patch_len(len - 1); + return len; +} + +int acpigen_write_CST_package(acpi_cstate_t *cstate, int nentries) +{ + int len, lenh, lenp, i; + lenh = acpigen_write_name("_CST"); + lenp = acpigen_write_package(nentries+1); + len = acpigen_write_dword(nentries); + + for (i = 0; i < nentries; i++) + len += acpigen_write_CST_package_entry(cstate + i); + + len += lenp; + acpigen_patch_len(len - 1); + return len + lenh; +} + +int acpigen_write_TSS_package(int entries, acpi_tstate_t *tstate_list) +{ +/* + Sample _TSS package with 100% and 50% duty cycles + Name (_TSS, Package (0x02) + { + Package(){100, 1000, 0, 0x00, 0) + Package(){50, 520, 0, 0x18, 0) + }) + */ + int i, len, plen, nlen; + acpi_tstate_t *tstate = tstate_list; + + nlen = acpigen_write_name("_TSS"); + plen = acpigen_write_package(entries); + + for (i = 0; i < entries; i++) { + len = acpigen_write_package(5); + len += acpigen_write_dword(tstate->percent); + len += acpigen_write_dword(tstate->power); + len += acpigen_write_dword(tstate->latency); + len += acpigen_write_dword(tstate->control); + len += acpigen_write_dword(tstate->status); + acpigen_patch_len(len - 1); + tstate++; + plen += len; + } + + acpigen_patch_len(plen - 1); + return plen + nlen; +} + +int acpigen_write_TSD_package(u32 domain, u32 numprocs, PSD_coord coordtype) +{ + int len, lenh, lenp; + lenh = acpigen_write_name("_TSD"); + lenp = acpigen_write_package(1); + len = acpigen_write_package(5); + len += acpigen_write_byte(5); // 5 values + len += acpigen_write_byte(0); // revision 0 + len += acpigen_write_dword(domain); + len += acpigen_write_dword(coordtype); + len += acpigen_write_dword(numprocs); + acpigen_patch_len(len - 1); + len += lenp; + acpigen_patch_len(len - 1); + return len + lenh; +} + + + +int acpigen_write_mem32fixed(int readwrite, u32 base, u32 size) +{ + /* + * acpi 4.0 section 6.4.3.4: 32-Bit Fixed Memory Range Descriptor + * Byte 0: + * Bit7 : 1 => big item + * Bit6-0: 0000110 (0x6) => 32-bit fixed memory + */ + acpigen_emit_byte(0x86); + /* Byte 1+2: length (0x0009) */ + acpigen_emit_byte(0x09); + acpigen_emit_byte(0x00); + /* bit1-7 are ignored */ + acpigen_emit_byte(readwrite ? 0x01 : 0x00); + acpigen_emit_byte(base & 0xff); + acpigen_emit_byte((base >> 8) & 0xff); + acpigen_emit_byte((base >> 16) & 0xff); + acpigen_emit_byte((base >> 24) & 0xff); + acpigen_emit_byte(size & 0xff); + acpigen_emit_byte((size >> 8) & 0xff); + acpigen_emit_byte((size >> 16) & 0xff); + acpigen_emit_byte((size >> 24) & 0xff); + return 12; +} + +int acpigen_write_register(acpi_addr_t *addr) +{ + acpigen_emit_byte(0x82); /* Register Descriptor */ + acpigen_emit_byte(0x0c); /* Register Length 7:0 */ + acpigen_emit_byte(0x00); /* Register Length 15:8 */ + acpigen_emit_byte(addr->space_id); /* Address Space ID */ + acpigen_emit_byte(addr->bit_width); /* Register Bit Width */ + acpigen_emit_byte(addr->bit_offset); /* Register Bit Offset */ + acpigen_emit_byte(addr->resv); /* Register Access Size */ + acpigen_emit_byte(addr->addrl & 0xff); /* Register Address Low */ + acpigen_emit_byte((addr->addrl >> 8) & 0xff); + acpigen_emit_byte((addr->addrl >> 16) & 0xff); + acpigen_emit_byte((addr->addrl >> 24) & 0xff); + acpigen_emit_byte(addr->addrh & 0xff); /* Register Address High */ + acpigen_emit_byte((addr->addrh >> 8) & 0xff); + acpigen_emit_byte((addr->addrh >> 16) & 0xff); + acpigen_emit_byte((addr->addrh >> 24) & 0xff); + return 15; +} + +int acpigen_write_io16(u16 min, u16 max, u8 align, u8 len, u8 decode16) +{ + /* + * acpi 4.0 section 6.4.2.6: I/O Port Descriptor + * Byte 0: + * Bit7 : 0 => small item + * Bit6-3: 1000 (0x8) => I/O port descriptor + * Bit2-0: 111 (0x7) => 7 Bytes long + */ + acpigen_emit_byte(0x47); + /* does the device decode all 16 or just 10 bits? */ + /* bit1-7 are ignored */ + acpigen_emit_byte(decode16 ? 0x01 : 0x00); + /* minimum base address the device may be configured for */ + acpigen_emit_byte(min & 0xff); + acpigen_emit_byte((min >> 8) & 0xff); + /* maximum base address the device may be configured for */ + acpigen_emit_byte(max & 0xff); + acpigen_emit_byte((max >> 8) & 0xff); + /* alignment for min base */ + acpigen_emit_byte(align & 0xff); + acpigen_emit_byte(len & 0xff); + return 8; +} + +int acpigen_write_resourcetemplate_header(void) +{ + int len; + /* + * A ResourceTemplate() is a Buffer() with a + * (Byte|Word|DWord) containing the length, followed by one or more + * resource items, terminated by the end tag + * (small item 0xf, len 1) + */ + len = acpigen_emit_byte(0x11); /* Buffer opcode */ + len += acpigen_write_len_f(); + len += acpigen_emit_byte(0x0b); /* Word opcode */ + len_stack[ltop++] = acpigen_get_current(); + len += acpigen_emit_byte(0x00); + len += acpigen_emit_byte(0x00); + return len; +} + +int acpigen_write_resourcetemplate_footer(int len) +{ + char *p = len_stack[--ltop]; + /* + * end tag (acpi 4.0 Section 6.4.2.8) + * 0x79 <checksum> + * 0x00 is treated as a good checksum according to the spec + * and is what iasl generates. + */ + len += acpigen_emit_byte(0x79); + len += acpigen_emit_byte(0x00); + /* patch len word */ + p[0] = (len-6) & 0xff; + p[1] = ((len-6) >> 8) & 0xff; + /* patch len field */ + acpigen_patch_len(len-1); + return 2; +} + +static void acpigen_add_mainboard_rsvd_mem32(void *gp, struct device *dev, + struct resource *res) +{ + acpigen_write_mem32fixed(0, res->base, res->size); +} + +static void acpigen_add_mainboard_rsvd_io(void *gp, struct device *dev, + struct resource *res) +{ + resource_t base = res->base; + resource_t size = res->size; + while (size > 0) { + resource_t sz = size > 255 ? 255 : size; + acpigen_write_io16(base, base, 0, sz, 1); + size -= sz; + base += sz; + } +} + +int acpigen_write_mainboard_resource_template(void) +{ + int len; + char *start; + char *end; + len = acpigen_write_resourcetemplate_header(); + start = acpigen_get_current(); + + /* Add reserved memory ranges */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_RESERVE, + IORESOURCE_MEM | IORESOURCE_RESERVE, + acpigen_add_mainboard_rsvd_mem32, 0); + + /* Add reserved io ranges */ + search_global_resources( + IORESOURCE_IO | IORESOURCE_RESERVE, + IORESOURCE_IO | IORESOURCE_RESERVE, + acpigen_add_mainboard_rsvd_io, 0); + + end = acpigen_get_current(); + len += end-start; + len += acpigen_write_resourcetemplate_footer(len); + return len; +} + +int acpigen_write_mainboard_resources(const char *scope, const char *name) +{ + int len; + len = acpigen_write_scope(scope); + len += acpigen_write_name(name); + len += acpigen_write_mainboard_resource_template(); + acpigen_patch_len(len - 1); + return len; +} diff --git a/src/arch/armv7/boot/boot.c b/src/arch/armv7/boot/boot.c new file mode 100644 index 0000000..722fca5 --- /dev/null +++ b/src/arch/armv7/boot/boot.c @@ -0,0 +1,189 @@ +#include <console/console.h> +#include <ip_checksum.h> +#include <boot/elf.h> +#include <boot/elf_boot.h> +#include <string.h> +#include <cpu/x86/multiboot.h> + + +#ifndef CMD_LINE +#define CMD_LINE "" +#endif + + + +#define UPSZ(X) ((sizeof(X) + 3) &~3) + +static struct { + Elf_Bhdr hdr; + Elf_Nhdr ft_hdr; + unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)]; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char cmd_desc[UPSZ(CMD_LINE)]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 4, + }, + .ft_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(FIRMWARE_TYPE), + .n_type = EBN_FIRMWARE_TYPE, + }, + .ft_desc = FIRMWARE_TYPE, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(CMD_LINE), + .n_type = EBN_COMMAND_LINE, + }, + .cmd_desc = CMD_LINE, +}; + + +int elf_check_arch(Elf_ehdr *ehdr) +{ + return ( + ((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) && + (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + ); + +} + +void jmp_to_elf_entry(void *entry, unsigned long buffer, unsigned long size) +{ + extern unsigned char _ram_seg, _eram_seg; + unsigned long lb_start, lb_size; + unsigned long adjust, adjusted_boot_notes; + + elf_boot_notes.hdr.b_checksum = + compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes)); + + lb_start = (unsigned long)&_ram_seg; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + adjust = buffer + size - lb_start; + + adjusted_boot_notes = (unsigned long)&elf_boot_notes; + adjusted_boot_notes += adjust; + + printk(BIOS_SPEW, "entry = 0x%08lx\n", (unsigned long)entry); + printk(BIOS_SPEW, "lb_start = 0x%08lx\n", lb_start); + printk(BIOS_SPEW, "lb_size = 0x%08lx\n", lb_size); + printk(BIOS_SPEW, "adjust = 0x%08lx\n", adjust); + printk(BIOS_SPEW, "buffer = 0x%08lx\n", buffer); + printk(BIOS_SPEW, " elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes); + printk(BIOS_SPEW, "adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes); + + /* FIXME(dhendrix): port code to jump to kernel here... */ +#if 0 + /* Jump to kernel */ + __asm__ __volatile__( + " cld \n\t" + /* Save the callee save registers... */ + " pushl %%esi\n\t" + " pushl %%edi\n\t" + " pushl %%ebx\n\t" + /* Save the parameters I was passed */ + " pushl $0\n\t" /* 20 adjust */ + " pushl %0\n\t" /* 16 lb_start */ + " pushl %1\n\t" /* 12 buffer */ + " pushl %2\n\t" /* 8 lb_size */ + " pushl %3\n\t" /* 4 entry */ + " pushl %4\n\t" /* 0 elf_boot_notes */ + /* Compute the adjustment */ + " xorl %%eax, %%eax\n\t" + " subl 16(%%esp), %%eax\n\t" + " addl 12(%%esp), %%eax\n\t" + " addl 8(%%esp), %%eax\n\t" + " movl %%eax, 20(%%esp)\n\t" + /* Place a copy of coreboot in its new location */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 12(%%esp), %%edi\n\t" + " addl 8(%%esp), %%edi\n\t" + " movl 16(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\n" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the new coreboot image */ + " addl 20(%%esp), %%esp\n\t" + /* Adjust the instruction pointer to point into the new coreboot image */ + " movl $1f, %%eax\n\t" + " addl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Copy the coreboot bounce buffer over coreboot */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Now jump to the loaded image */ + " movl %5, %%eax\n\t" + " movl 0(%%esp), %%ebx\n\t" + " call *4(%%esp)\n\t" + + /* The loaded image returned? */ + " cli \n\t" + " cld \n\t" + + /* Copy the saved copy of coreboot where coreboot runs */ + /* Move ``longs'' the coreboot size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " addl 8(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the old coreboot image */ + " subl 20(%%esp), %%esp\n\t" + + /* Adjust the instruction pointer to point into the old coreboot image */ + " movl $1f, %%eax\n\t" + " subl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Drop the parameters I was passed */ + " addl $24, %%esp\n\t" + + /* Restore the callee save registers */ + " popl %%ebx\n\t" + " popl %%edi\n\t" + " popl %%esi\n\t" + + :: + "ri" (lb_start), "ri" (buffer), "ri" (lb_size), + "ri" (entry), +#if CONFIG_MULTIBOOT + "ri"(mbi), "ri" (MB_MAGIC2) +#else + "ri"(adjusted_boot_notes), "ri" (0x0E1FB007) +#endif + ); +#endif +} + + diff --git a/src/arch/armv7/boot/coreboot_table.c b/src/arch/armv7/boot/coreboot_table.c new file mode 100644 index 0000000..9f67da0 --- /dev/null +++ b/src/arch/armv7/boot/coreboot_table.c @@ -0,0 +1,711 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2003-2004 Eric Biederman + * Copyright (C) 2005-2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <console/console.h> +#include <ip_checksum.h> +#include <boot/tables.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> +#include <string.h> +#include <version.h> +#include <device/device.h> +#include <stdlib.h> +#include <cbfs.h> +#include <cbmem.h> +#if CONFIG_USE_OPTION_TABLE +#include <option_table.h> +#endif +#if CONFIG_CHROMEOS +#include <arch/acpi.h> +#include <vendorcode/google/chromeos/gnvs.h> +#endif + +static struct lb_header *lb_table_init(unsigned long addr) +{ + struct lb_header *header; + + /* 16 byte align the address */ + addr += 15; + addr &= ~15; + + header = (void *)addr; + header->signature[0] = 'L'; + header->signature[1] = 'B'; + header->signature[2] = 'I'; + header->signature[3] = 'O'; + header->header_bytes = sizeof(*header); + header->header_checksum = 0; + header->table_bytes = 0; + header->table_checksum = 0; + header->table_entries = 0; + return header; +} + +static struct lb_record *lb_first_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header)); + return rec; +} + +static struct lb_record *lb_last_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = (void *)(((char *)header) + sizeof(*header) + header->table_bytes); + return rec; +} + +#if 0 +static struct lb_record *lb_next_record(struct lb_record *rec) +{ + rec = (void *)(((char *)rec) + rec->size); + return rec; +} +#endif + +static struct lb_record *lb_new_record(struct lb_header *header) +{ + struct lb_record *rec; + rec = lb_last_record(header); + if (header->table_entries) { + header->table_bytes += rec->size; + } + rec = lb_last_record(header); + header->table_entries++; + rec->tag = LB_TAG_UNUSED; + rec->size = sizeof(*rec); + return rec; +} + + +static struct lb_memory *lb_memory(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_memory *mem; + rec = lb_new_record(header); + mem = (struct lb_memory *)rec; + mem->tag = LB_TAG_MEMORY; + mem->size = sizeof(*mem); + return mem; +} + +static struct lb_serial *lb_serial(struct lb_header *header) +{ +#if CONFIG_CONSOLE_SERIAL8250 + struct lb_record *rec; + struct lb_serial *serial; + rec = lb_new_record(header); + serial = (struct lb_serial *)rec; + serial->tag = LB_TAG_SERIAL; + serial->size = sizeof(*serial); + serial->type = LB_SERIAL_TYPE_IO_MAPPED; + serial->baseaddr = CONFIG_TTYS0_BASE; + serial->baud = CONFIG_TTYS0_BAUD; + return serial; +#elif CONFIG_CONSOLE_SERIAL8250MEM + if (uartmem_getbaseaddr()) { + struct lb_record *rec; + struct lb_serial *serial; + rec = lb_new_record(header); + serial = (struct lb_serial *)rec; + serial->tag = LB_TAG_SERIAL; + serial->size = sizeof(*serial); + serial->type = LB_SERIAL_TYPE_MEMORY_MAPPED; + serial->baseaddr = uartmem_getbaseaddr(); + serial->baud = CONFIG_TTYS0_BAUD; + return serial; + } else { + return NULL; + } +#else + return NULL; +#endif +} + +#if CONFIG_CONSOLE_SERIAL8250 || CONFIG_CONSOLE_SERIAL8250MEM || \ + CONFIG_CONSOLE_LOGBUF || CONFIG_USBDEBUG +static void add_console(struct lb_header *header, u16 consoletype) +{ + struct lb_console *console; + + console = (struct lb_console *)lb_new_record(header); + console->tag = LB_TAG_CONSOLE; + console->size = sizeof(*console); + console->type = consoletype; +} +#endif + +static void lb_console(struct lb_header *header) +{ +#if CONFIG_CONSOLE_SERIAL8250 + add_console(header, LB_TAG_CONSOLE_SERIAL8250); +#endif +#if CONFIG_CONSOLE_SERIAL8250MEM + add_console(header, LB_TAG_CONSOLE_SERIAL8250MEM); +#endif +#if CONFIG_CONSOLE_LOGBUF + add_console(header, LB_TAG_CONSOLE_LOGBUF); +#endif +#if CONFIG_USBDEBUG + add_console(header, LB_TAG_CONSOLE_EHCI); +#endif +} + +static void lb_framebuffer(struct lb_header *header) +{ +#if CONFIG_FRAMEBUFFER_KEEP_VESA_MODE + void fill_lb_framebuffer(struct lb_framebuffer *framebuffer); + int vbe_mode_info_valid(void); + + // If there isn't any mode info to put in the table, don't ask for it + // to be filled with junk. + if (!vbe_mode_info_valid()) + return; + struct lb_framebuffer *framebuffer; + framebuffer = (struct lb_framebuffer *)lb_new_record(header); + framebuffer->tag = LB_TAG_FRAMEBUFFER; + framebuffer->size = sizeof(*framebuffer); + fill_lb_framebuffer(framebuffer); +#endif +} + +#if CONFIG_CHROMEOS +static void lb_gpios(struct lb_header *header) +{ + struct lb_gpios *gpios; + gpios = (struct lb_gpios *)lb_new_record(header); + gpios->tag = LB_TAG_GPIO; + gpios->size = sizeof(*gpios); + gpios->count = 0; + fill_lb_gpios(gpios); +} + +static void lb_vdat(struct lb_header *header) +{ + struct lb_vdat* vdat; + + vdat = (struct lb_vdat *)lb_new_record(header); + vdat->tag = LB_TAG_VDAT; + vdat->size = sizeof(*vdat); + acpi_get_vdat_info(&vdat->vdat_addr, &vdat->vdat_size); +} + +static void lb_vbnv(struct lb_header *header) +{ + struct lb_vbnv* vbnv; + + vbnv = (struct lb_vbnv *)lb_new_record(header); + vbnv->tag = LB_TAG_VBNV; + vbnv->size = sizeof(*vbnv); + vbnv->vbnv_start = CONFIG_VBNV_OFFSET + 14; + vbnv->vbnv_size = CONFIG_VBNV_SIZE; +} +#endif + +static void add_cbmem_pointers(struct lb_header *header) +{ + /* + * These CBMEM sections' addresses are included in the coreboot table + * with the appropriate tags. + */ + const struct section_id { + int cbmem_id; + int table_tag; + } section_ids[] = { + {CBMEM_ID_TIMESTAMP, LB_TAG_TIMESTAMPS}, + {CBMEM_ID_CONSOLE, LB_TAG_CBMEM_CONSOLE} + }; + int i; + + for (i = 0; i < ARRAY_SIZE(section_ids); i++) { + const struct section_id *sid = section_ids + i; + struct lb_cbmem_ref *cbmem_ref; + void *cbmem_addr = cbmem_find(sid->cbmem_id); + + if (!cbmem_addr) + continue; /* This section is not present */ + + cbmem_ref = (struct lb_cbmem_ref *)lb_new_record(header); + if (!cbmem_ref) { + printk(BIOS_ERR, "No more room in coreboot table!\n"); + break; + } + cbmem_ref->tag = sid->table_tag; + cbmem_ref->size = sizeof(*cbmem_ref); + cbmem_ref->cbmem_addr = cbmem_addr; + } +} + +static struct lb_mainboard *lb_mainboard(struct lb_header *header) +{ + struct lb_record *rec; + struct lb_mainboard *mainboard; + rec = lb_new_record(header); + mainboard = (struct lb_mainboard *)rec; + mainboard->tag = LB_TAG_MAINBOARD; + + mainboard->size = (sizeof(*mainboard) + + strlen(mainboard_vendor) + 1 + + strlen(mainboard_part_number) + 1 + + 3) & ~3; + + mainboard->vendor_idx = 0; + mainboard->part_number_idx = strlen(mainboard_vendor) + 1; + + memcpy(mainboard->strings + mainboard->vendor_idx, + mainboard_vendor, strlen(mainboard_vendor) + 1); + memcpy(mainboard->strings + mainboard->part_number_idx, + mainboard_part_number, strlen(mainboard_part_number) + 1); + + return mainboard; +} + +#if CONFIG_USE_OPTION_TABLE +static struct cmos_checksum *lb_cmos_checksum(struct lb_header *header) +{ + struct lb_record *rec; + struct cmos_checksum *cmos_checksum; + rec = lb_new_record(header); + cmos_checksum = (struct cmos_checksum *)rec; + cmos_checksum->tag = LB_TAG_OPTION_CHECKSUM; + + cmos_checksum->size = (sizeof(*cmos_checksum)); + + cmos_checksum->range_start = LB_CKS_RANGE_START * 8; + cmos_checksum->range_end = ( LB_CKS_RANGE_END * 8 ) + 7; + cmos_checksum->location = LB_CKS_LOC * 8; + cmos_checksum->type = CHECKSUM_PCBIOS; + + return cmos_checksum; +} +#endif + +static void lb_strings(struct lb_header *header) +{ + static const struct { + uint32_t tag; + const char *string; + } strings[] = { + { LB_TAG_VERSION, coreboot_version, }, + { LB_TAG_EXTRA_VERSION, coreboot_extra_version, }, + { LB_TAG_BUILD, coreboot_build, }, + { LB_TAG_COMPILE_TIME, coreboot_compile_time, }, + { LB_TAG_COMPILE_BY, coreboot_compile_by, }, + { LB_TAG_COMPILE_HOST, coreboot_compile_host, }, + { LB_TAG_COMPILE_DOMAIN, coreboot_compile_domain, }, + { LB_TAG_COMPILER, coreboot_compiler, }, + { LB_TAG_LINKER, coreboot_linker, }, + { LB_TAG_ASSEMBLER, coreboot_assembler, }, + }; + unsigned int i; + for(i = 0; i < ARRAY_SIZE(strings); i++) { + struct lb_string *rec; + size_t len; + rec = (struct lb_string *)lb_new_record(header); + len = strlen(strings[i].string); + rec->tag = strings[i].tag; + rec->size = (sizeof(*rec) + len + 1 + 3) & ~3; + memcpy(rec->string, strings[i].string, len+1); + } + +} + +#if CONFIG_WRITE_HIGH_TABLES +static struct lb_forward *lb_forward(struct lb_header *header, struct lb_header *next_header) +{ + struct lb_record *rec; + struct lb_forward *forward; + rec = lb_new_record(header); + forward = (struct lb_forward *)rec; + forward->tag = LB_TAG_FORWARD; + forward->size = sizeof(*forward); + forward->forward = (uint64_t)(unsigned long)next_header; + return forward; +} +#endif + +/* FIXME(dhendrix): used to be static void lb_memory_range(), but compiler + started complaining since it shares a name with a non-static struct. ugh. */ +static void new_lb_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size) +{ + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + mem->map[entries].start = pack_lb64(start); + mem->map[entries].size = pack_lb64(size); + mem->map[entries].type = type; + mem->size += sizeof(mem->map[0]); +} + +static void lb_reserve_table_memory(struct lb_header *head) +{ + struct lb_record *last_rec; + struct lb_memory *mem; + uint64_t start; + uint64_t end; + int i, entries; + + last_rec = lb_last_record(head); + mem = get_lb_mem(); + start = (unsigned long)head; + end = (unsigned long)last_rec; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + /* Resize the right two memory areas so this table is in + * a reserved area of memory. Everything has been carefully + * setup so that is all we need to do. + */ + for(i = 0; i < entries; i++ ) { + uint64_t map_start = unpack_lb64(mem->map[i].start); + uint64_t map_end = map_start + unpack_lb64(mem->map[i].size); + /* Does this area need to be expanded? */ + if (map_end == start) { + mem->map[i].size = pack_lb64(end - map_start); + } + /* Does this area need to be contracted? */ + else if (map_start == start) { + mem->map[i].start = pack_lb64(end); + mem->map[i].size = pack_lb64(map_end - end); + } + } +} + +static unsigned long lb_table_fini(struct lb_header *head, int fixup) +{ + struct lb_record *rec, *first_rec; + rec = lb_last_record(head); + if (head->table_entries) { + head->table_bytes += rec->size; + } + + if (fixup) + lb_reserve_table_memory(head); + + first_rec = lb_first_record(head); + head->table_checksum = compute_ip_checksum(first_rec, head->table_bytes); + head->header_checksum = 0; + head->header_checksum = compute_ip_checksum(head, sizeof(*head)); + printk(BIOS_DEBUG, + "Wrote coreboot table at: %p, 0x%x bytes, checksum %x\n", + head, head->table_bytes, head->table_checksum); + return (unsigned long)rec + rec->size; +} + +static void lb_cleanup_memory_ranges(struct lb_memory *mem) +{ + int entries; + int i, j; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* Sort the lb memory ranges */ + for(i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(mem->map[i].start); + for(j = i; j < entries; j++) { + uint64_t temp_start = unpack_lb64(mem->map[j].start); + if (temp_start < entry_start) { + struct lb_memory_range tmp; + tmp = mem->map[i]; + mem->map[i] = mem->map[j]; + mem->map[j] = tmp; + } + } + } + + /* Merge adjacent entries */ + for(i = 0; (i + 1) < entries; i++) { + uint64_t start, end, nstart, nend; + if (mem->map[i].type != mem->map[i + 1].type) { + continue; + } + start = unpack_lb64(mem->map[i].start); + end = start + unpack_lb64(mem->map[i].size); + nstart = unpack_lb64(mem->map[i + 1].start); + nend = nstart + unpack_lb64(mem->map[i + 1].size); + if ((start <= nstart) && (end > nstart)) { + if (start > nstart) { + start = nstart; + } + if (end < nend) { + end = nend; + } + /* Record the new region size */ + mem->map[i].start = pack_lb64(start); + mem->map[i].size = pack_lb64(end - start); + + /* Delete the entry I have merged with */ + memmove(&mem->map[i + 1], &mem->map[i + 2], + ((entries - i - 2) * sizeof(mem->map[0]))); + mem->size -= sizeof(mem->map[0]); + entries -= 1; + /* See if I can merge with the next entry as well */ + i -= 1; + } + } +} + +static void lb_remove_memory_range(struct lb_memory *mem, + uint64_t start, uint64_t size) +{ + uint64_t end; + int entries; + int i; + + end = start + size; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* Remove a reserved area from the memory map */ + for(i = 0; i < entries; i++) { + uint64_t map_start = unpack_lb64(mem->map[i].start); + uint64_t map_end = map_start + unpack_lb64(mem->map[i].size); + if ((start <= map_start) && (end >= map_end)) { + /* Remove the completely covered range */ + memmove(&mem->map[i], &mem->map[i + 1], + ((entries - i - 1) * sizeof(mem->map[0]))); + mem->size -= sizeof(mem->map[0]); + entries -= 1; + /* Since the index will disappear revisit what will appear here */ + i -= 1; + } + else if ((start > map_start) && (end < map_end)) { + /* Split the memory range */ + memmove(&mem->map[i + 1], &mem->map[i], + ((entries - i) * sizeof(mem->map[0]))); + mem->size += sizeof(mem->map[0]); + entries += 1; + /* Update the first map entry */ + mem->map[i].size = pack_lb64(start - map_start); + /* Update the second map entry */ + mem->map[i + 1].start = pack_lb64(end); + mem->map[i + 1].size = pack_lb64(map_end - end); + /* Don't bother with this map entry again */ + i += 1; + } + else if ((start <= map_start) && (end > map_start)) { + /* Shrink the start of the memory range */ + mem->map[i].start = pack_lb64(end); + mem->map[i].size = pack_lb64(map_end - end); + } + else if ((start < map_end) && (start > map_start)) { + /* Shrink the end of the memory range */ + mem->map[i].size = pack_lb64(start - map_start); + } + } +} + +static void lb_add_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size) +{ + lb_remove_memory_range(mem, start, size); + new_lb_memory_range(mem, type, start, size); + lb_cleanup_memory_ranges(mem); +} + +static void lb_dump_memory_ranges(struct lb_memory *mem) +{ + int entries; + int i; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + printk(BIOS_DEBUG, "coreboot memory table:\n"); + for(i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(mem->map[i].start); + uint64_t entry_size = unpack_lb64(mem->map[i].size); + const char *entry_type; + + switch (mem->map[i].type) { + case LB_MEM_RAM: entry_type="RAM"; break; + case LB_MEM_RESERVED: entry_type="RESERVED"; break; + case LB_MEM_ACPI: entry_type="ACPI"; break; + case LB_MEM_NVS: entry_type="NVS"; break; + case LB_MEM_UNUSABLE: entry_type="UNUSABLE"; break; + case LB_MEM_VENDOR_RSVD: entry_type="VENDOR RESERVED"; break; + case LB_MEM_TABLE: entry_type="CONFIGURATION TABLES"; break; + default: entry_type="UNKNOWN!"; break; + } + + printk(BIOS_DEBUG, "%2d. %016llx-%016llx: %s\n", + i, entry_start, entry_start+entry_size-1, entry_type); + + } +} + + +/* Routines to extract part so the coreboot table or + * information from the coreboot table after we have written it. + * Currently get_lb_mem relies on a global we can change the + * implementaiton. + */ +static struct lb_memory *mem_ranges = 0; +struct lb_memory *get_lb_mem(void) +{ + return mem_ranges; +} + +static void build_lb_mem_range(void *gp, struct device *dev, struct resource *res) +{ + struct lb_memory *mem = gp; + new_lb_memory_range(mem, LB_MEM_RAM, res->base, res->size); +} + +static struct lb_memory *build_lb_mem(struct lb_header *head) +{ + struct lb_memory *mem; + + /* Record where the lb memory ranges will live */ + mem = lb_memory(head); + mem_ranges = mem; + + /* Build the raw table of memory */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_CACHEABLE, IORESOURCE_MEM | IORESOURCE_CACHEABLE, + build_lb_mem_range, mem); + lb_cleanup_memory_ranges(mem); + return mem; +} + +static void lb_add_rsvd_range(void *gp, struct device *dev, struct resource *res) +{ + struct lb_memory *mem = gp; + lb_add_memory_range(mem, LB_MEM_RESERVED, res->base, res->size); +} + +static void add_lb_reserved(struct lb_memory *mem) +{ + /* Add reserved ranges */ + search_global_resources( + IORESOURCE_MEM | IORESOURCE_RESERVE, IORESOURCE_MEM | IORESOURCE_RESERVE, + lb_add_rsvd_range, mem); +} + +unsigned long write_coreboot_table( + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_start, unsigned long rom_table_end) +{ + struct lb_header *head; + struct lb_memory *mem; + +#if CONFIG_WRITE_HIGH_TABLES + printk(BIOS_DEBUG, "Writing high table forward entry at 0x%08lx\n", + low_table_end); + head = lb_table_init(low_table_end); + lb_forward(head, (struct lb_header*)rom_table_end); + + low_table_end = (unsigned long) lb_table_fini(head, 0); + printk(BIOS_DEBUG, "New low_table_end: 0x%08lx\n", low_table_end); + printk(BIOS_DEBUG, "Now going to write high coreboot table at 0x%08lx\n", + rom_table_end); + + head = lb_table_init(rom_table_end); + rom_table_end = (unsigned long)head; + printk(BIOS_DEBUG, "rom_table_end = 0x%08lx\n", rom_table_end); +#else + if(low_table_end > (0x1000 - sizeof(struct lb_header))) { /* after 4K */ + /* We need to put lbtable on to [0xf0000,0x100000) */ + head = lb_table_init(rom_table_end); + rom_table_end = (unsigned long)head; + } else { + head = lb_table_init(low_table_end); + low_table_end = (unsigned long)head; + } +#endif + + printk(BIOS_DEBUG, "Adjust low_table_end from 0x%08lx to ", low_table_end); + low_table_end += 0xfff; // 4K aligned + low_table_end &= ~0xfff; + printk(BIOS_DEBUG, "0x%08lx \n", low_table_end); + + /* The Linux kernel assumes this region is reserved */ + printk(BIOS_DEBUG, "Adjust rom_table_end from 0x%08lx to ", rom_table_end); + rom_table_end += 0xffff; // 64K align + rom_table_end &= ~0xffff; + printk(BIOS_DEBUG, "0x%08lx \n", rom_table_end); + +#if CONFIG_USE_OPTION_TABLE + { + struct cmos_option_table *option_table = cbfs_find_file("cmos_layout.bin", 0x1aa); + if (option_table) { + struct lb_record *rec_dest = lb_new_record(head); + /* Copy the option config table, it's already a lb_record... */ + memcpy(rec_dest, option_table, option_table->size); + /* Create cmos checksum entry in coreboot table */ + lb_cmos_checksum(head); + } else { + printk(BIOS_ERR, "cmos_layout.bin could not be found!\n"); + } + } +#endif + /* Record where RAM is located */ + mem = build_lb_mem(head); + + /* Record the mptable and the the lb_table (This will be adjusted later) */ + lb_add_memory_range(mem, LB_MEM_TABLE, + low_table_start, low_table_end - low_table_start); + + /* Record the pirq table, acpi tables, and maybe the mptable */ + lb_add_memory_range(mem, LB_MEM_TABLE, + rom_table_start, rom_table_end-rom_table_start); + +#if CONFIG_WRITE_HIGH_TABLES + printk(BIOS_DEBUG, "Adding high table area\n"); + // should this be LB_MEM_ACPI? + lb_add_memory_range(mem, LB_MEM_TABLE, + high_tables_base, high_tables_size); +#endif + + /* Add reserved regions */ + add_lb_reserved(mem); + + lb_dump_memory_ranges(mem); + + /* Note: + * I assume that there is always memory at immediately after + * the low_table_end. This means that after I setup the coreboot table. + * I can trivially fixup the reserved memory ranges to hold the correct + * size of the coreboot table. + */ + + /* Record our motherboard */ + lb_mainboard(head); + /* Record the serial port, if present */ + lb_serial(head); + /* Record our console setup */ + lb_console(head); + /* Record our various random string information */ + lb_strings(head); + /* Record our framebuffer */ + lb_framebuffer(head); + +#if CONFIG_CHROMEOS + /* Record our GPIO settings (ChromeOS specific) */ + lb_gpios(head); + + /* pass along the VDAT buffer adress */ + lb_vdat(head); + + /* pass along VBNV offsets in CMOS */ + lb_vbnv(head); +#endif + add_cbmem_pointers(head); + + /* Remember where my valid memory ranges are */ + return lb_table_fini(head, 1); + +} diff --git a/src/arch/armv7/boot/multiboot.c b/src/arch/armv7/boot/multiboot.c new file mode 100644 index 0000000..4059f27 --- /dev/null +++ b/src/arch/armv7/boot/multiboot.c @@ -0,0 +1,77 @@ +/* + * support for Multiboot payloads + * + * Copyright (C) 2008 Robert Millan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + * + */ + +#include <cpu/x86/multiboot.h> +#include <string.h> +#include <device/resource.h> +#include <console/console.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> + +struct multiboot_info *mbi = NULL; + +unsigned long write_multiboot_info(unsigned long rom_table_end) +{ + static struct multiboot_mmap_entry *mb_mem; + struct lb_memory* coreboot_table; + int entries; + int i; + + mbi = (struct multiboot_info *)rom_table_end; + + memset(mbi, 0, sizeof(*mbi)); + rom_table_end += sizeof(*mbi); + + mbi->mmap_addr = (u32) rom_table_end; + mb_mem = (struct multiboot_mmap_entry *)rom_table_end; + + /* copy regions from coreboot tables */ + coreboot_table = get_lb_mem(); + entries = (coreboot_table->size - sizeof(*coreboot_table))/sizeof(coreboot_table->map[0]); + + if (coreboot_table == NULL || entries < 1) { + printk(BIOS_INFO, "%s: Cannot find coreboot table.\n", __func__); + return (unsigned long) mb_mem; + } + + for (i = 0; i < entries; i++) { + uint64_t entry_start = unpack_lb64(coreboot_table->map[i].start); + uint64_t entry_size = unpack_lb64(coreboot_table->map[i].size); + mb_mem->addr = entry_start; + mb_mem->len = entry_size; + switch (coreboot_table->map[i].type) { + case LB_MEM_RAM: + mb_mem->type = MULTIBOOT_MEMORY_AVAILABLE; + break; + default: // anything other than usable RAM + mb_mem->type = MULTIBOOT_MEMORY_RESERVED; + break; + } + mb_mem->size = sizeof(*mb_mem) - sizeof(mb_mem->size); + mb_mem++; + } + + mbi->mmap_length = ((u32) mb_mem) - mbi->mmap_addr; + mbi->flags |= MB_INFO_MEM_MAP; + + printk(BIOS_INFO, "Multiboot Information structure has been written.\n"); + + return (unsigned long)mb_mem; +} diff --git a/src/arch/armv7/boot/tables.c b/src/arch/armv7/boot/tables.c new file mode 100644 index 0000000..07c5635 --- /dev/null +++ b/src/arch/armv7/boot/tables.c @@ -0,0 +1,100 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2003 Eric Biederman + * Copyright (C) 2005 Steve Magnani + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <cpu/cpu.h> +#include <boot/tables.h> +#include <boot/coreboot_tables.h> +#include <arch/coreboot_tables.h> +#include <string.h> +#include <cbmem.h> +#include <lib.h> + +uint64_t high_tables_base = 0; +uint64_t high_tables_size; + +void cbmem_arch_init(void) +{ +} + +struct lb_memory *write_tables(void) +{ + unsigned long low_table_start, low_table_end; + unsigned long rom_table_start, rom_table_end; + + /* Even if high tables are configured, some tables are copied both to + * the low and the high area, so payloads and OSes don't need to know + * about the high tables. + */ + unsigned long high_table_pointer; + + if (!high_tables_base) { + printk(BIOS_ERR, "ERROR: High Tables Base is not set.\n"); + // Are there any boards without? + // Stepan thinks we should die() here! + } + + printk(BIOS_DEBUG, "High Tables Base is %llx.\n", high_tables_base); + + rom_table_start = 0xf0000; + rom_table_end = 0xf0000; + + /* Start low addr at 0x500, so we don't run into conflicts with the BDA + * in case our data structures grow beyound 0x400. Only multiboot, GDT + * and the coreboot table use low_tables. + */ + low_table_start = 0; + low_table_end = 0x500; + +#define MAX_COREBOOT_TABLE_SIZE (8 * 1024) + post_code(0x9d); + + high_table_pointer = (unsigned long)cbmem_add(CBMEM_ID_CBTABLE, MAX_COREBOOT_TABLE_SIZE); + + if (high_table_pointer) { + unsigned long new_high_table_pointer; + + /* Also put a forwarder entry into 0-4K */ + new_high_table_pointer = write_coreboot_table(low_table_start, low_table_end, + high_tables_base, high_table_pointer); + + if (new_high_table_pointer > (high_table_pointer + + MAX_COREBOOT_TABLE_SIZE)) + printk(BIOS_ERR, "%s: coreboot table didn't fit (%lx)\n", + __func__, new_high_table_pointer - + high_table_pointer); + + printk(BIOS_DEBUG, "coreboot table: %ld bytes.\n", + new_high_table_pointer - high_table_pointer); + } else { + /* The coreboot table must be in 0-4K or 960K-1M */ + rom_table_end = write_coreboot_table( + low_table_start, low_table_end, + rom_table_start, rom_table_end); + } + + post_code(0x9e); + + // Remove before sending upstream + cbmem_list(); + + return get_lb_mem(); +} diff --git a/src/arch/armv7/boot/wakeup.S b/src/arch/armv7/boot/wakeup.S new file mode 100644 index 0000000..8ae337c --- /dev/null +++ b/src/arch/armv7/boot/wakeup.S @@ -0,0 +1,94 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 Rudolf Marek r.marek@assembler.cz + * Copyright (C) 2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define WAKEUP_BASE 0x600 +#define RELOCATED(x) (x - __wakeup + WAKEUP_BASE) + +/* CR0 bits */ +#define PE (1 << 0) + + .code32 + .globl __wakeup +__wakeup: + /* First prepare the jmp to the resume vector */ + mov 0x4(%esp), %eax /* vector */ + /* last 4 bits of linear addr are taken as offset */ + andw $0x0f, %ax + movw %ax, (__wakeup_offset) + mov 0x4(%esp), %eax + /* the rest is taken as segment */ + shr $4, %eax + movw %ax, (__wakeup_segment) + + /* Then overwrite coreboot with our backed up memory */ + cld + movl 8(%esp), %esi + movl 12(%esp), %edi + movl 16(%esp), %ecx + shrl $2, %ecx + rep movsl + + /* Activate the right segment descriptor real mode. */ + ljmp $0x28, $RELOCATED(1f) +1: +.code16 + /* 16 bit code from here on... */ + + /* Load the segment registers w/ properly configured + * segment descriptors. They will retain these + * configurations (limits, writability, etc.) once + * protected mode is turned off. + */ + mov $0x30, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + /* Turn off protection */ + movl %cr0, %eax + andl $~PE, %eax + movl %eax, %cr0 + + /* Now really going into real mode */ + ljmp $0, $RELOCATED(1f) +1: + movw $0x0, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* This is a FAR JMP to the OS waking vector. The C code changed + * the address to be correct. + */ + .byte 0xea + +__wakeup_offset = RELOCATED(.) + .word 0x0000 + +__wakeup_segment = RELOCATED(.) + .word 0x0000 + + .globl __wakeup_size +__wakeup_size = ( . - __wakeup) + diff --git a/src/arch/armv7/bootblock_simple.c b/src/arch/armv7/bootblock_simple.c new file mode 100644 index 0000000..f447a29 --- /dev/null +++ b/src/arch/armv7/bootblock_simple.c @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + + + +#include <bootblock_common.h> + + +#include "../../lib/uart8250.c" +#include "lib/div.c" + +struct uart8250 uart = { + 115200 +}; + +void main(unsigned long bist) +{ + init_uart8250(CONFIG_TTYS0_BASE, &uart); + uart8250_tx_byte(CONFIG_TTYS0_BASE, '@'); + + if (boot_cpu()) { + bootblock_cpu_init(); + bootblock_northbridge_init(); + bootblock_southbridge_init(); + } + const char* target1 = "fallback/romstage"; + unsigned long entry; + entry = findstage(target1); + if (entry) call(entry, bist); + + hlt(); +} + diff --git a/src/arch/armv7/coreboot_ram.ld b/src/arch/armv7/coreboot_ram.ld new file mode 100644 index 0000000..57ddd03 --- /dev/null +++ b/src/arch/armv7/coreboot_ram.ld @@ -0,0 +1,128 @@ +/* + * Memory map: + * + * CONFIG_RAMBASE : text segment + * : rodata segment + * : data segment + * : bss segment + * : stack + * : heap + */ +/* + * Bootstrap code for the STPC Consumer + * Copyright (c) 1999 by Net Insight AB. All Rights Reserved. + */ + +/* + * Written by Johan Rydberg, based on work by Daniel Kahlin. + * Rewritten by Eric Biederman + * 2005.12 yhlu add coreboot_ram cross the vga font buffer handling + */ + +/* We use ELF as output format. So that we can debug the code in some form. */ +INCLUDE ldoptions + +ENTRY(_start) + +SECTIONS +{ + . = CONFIG_RAMBASE; + /* First we place the code and read only data (typically const declared). + * This could theoretically be placed in rom. + */ + .text : { + _text = .; + *(.text); + *(.text.*); + . = ALIGN(16); + _etext = .; + } + + .rodata : { + _rodata = .; + . = ALIGN(4); + console_drivers = .; + *(.rodata.console_drivers) + econsole_drivers = . ; + . = ALIGN(4); + pci_drivers = . ; + *(.rodata.pci_driver) + epci_drivers = . ; + cpu_drivers = . ; + *(.rodata.cpu_driver) + ecpu_drivers = . ; + *(.rodata) + *(.rodata.*) + /* kevinh/Ispiri - Added an align, because the objcopy tool + * incorrectly converts sections that are not long word aligned. + */ + . = ALIGN(4); + + _erodata = .; + } + /* After the code we place initialized data (typically initialized + * global variables). This gets copied into ram by startup code. + * __data_start and __data_end shows where in ram this should be placed, + * whereas __data_loadstart and __data_loadend shows where in rom to + * copy from. + */ + .data : { + _data = .; + *(.data) + _edata = .; + } + + /* bss does not contain data, it is just a space that should be zero + * initialized on startup. (typically uninitialized global variables) + * crt0.S fills between _bss and _ebss with zeroes. + */ + _bss = .; + .bss . : { + *(.bss) + *(.sbss) + *(COMMON) + } + _ebss = .; + _end = .; + + /* coreboot really "ends" here. Only heap and stack are placed after + * this line. + */ + + . = ALIGN(CONFIG_STACK_SIZE); + + _stack = .; + .stack . : { + /* Reserve a stack for each possible cpu */ + . += CONFIG_MAX_CPUS*CONFIG_STACK_SIZE; + } + _estack = .; + + _heap = .; + .heap . : { + /* Reserve CONFIG_HEAP_SIZE bytes for the heap */ + . = CONFIG_HEAP_SIZE ; + . = ALIGN(4); + } + _eheap = .; + + /* The ram segment. This includes all memory used by the memory + * resident copy of coreboot, except the tables that are produced on + * the fly, but including stack and heap. + */ + _ram_seg = _text; + _eram_seg = _eheap; + + /* CONFIG_RAMTOP is the upper address of cached memory (among other + * things). We must not exceed beyond that address, there be dragons. + */ + _bogus = ASSERT( ( _eram_seg < (CONFIG_RAMTOP)) , "Please increase CONFIG_RAMTOP"); + + /* Discard the sections we don't need/want */ + + /DISCARD/ : { + *(.comment) + *(.note) + *(.note.*) + } +} diff --git a/src/arch/armv7/cpu.c b/src/arch/armv7/cpu.c new file mode 100644 index 0000000..622e7ed --- /dev/null +++ b/src/arch/armv7/cpu.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2008 Texas Insturments + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, garyj@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * CPU specific code + */ + +#if 0 +#include <common.h> +#include <command.h> +#include <asm/system.h> +#include <asm/cache.h> +#include <asm/armv7.h> +#ifdef CONFIG_EXYNOS_LCD +#include <exynos-fb.h> +#endif +#endif + +void save_boot_params_default(u32 r0, u32 r1, u32 r2, u32 r3) +{ +} + +void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) + __attribute__((weak, alias("save_boot_params_default"))); + +#if 0 +int cleanup_before_linux(void) +{ +#ifdef CONFIG_BOOTSTAGE_REPORT + bootstage_report(); +#endif +#ifdef CONFIG_BOOTSTAGE_STASH + bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH, + CONFIG_BOOTSTAGE_STASH_SIZE); +#endif + /* + * this function is called just before we call linux + * it prepares the processor for linux + * + * we turn off caches etc ... + */ + disable_interrupts(); + +#ifdef CONFIG_EXYNOS_LCD + exynos_fimd_disable(); +#endif + + /* + * Turn off I-cache and invalidate it + */ + icache_disable(); + invalidate_icache_all(); + + /* + * turn off D-cache + * dcache_disable() in turn flushes the d-cache and disables MMU + */ + dcache_disable(); + v7_outer_cache_disable(); + + /* + * After D-cache is flushed and before it is disabled there may + * be some new valid entries brought into the cache. We are sure + * that these lines are not dirty and will not affect our execution. + * (because unwinding the call-stack and setting a bit in CP15 SCTRL + * is all we did during this. We have not pushed anything on to the + * stack. Neither have we affected any static data) + * So just invalidate the entire d-cache again to avoid coherency + * problems for kernel + */ + invalidate_dcache_all(); + + return 0; +} +#endif diff --git a/src/arch/armv7/include/arch/atomic.h b/src/arch/armv7/include/arch/atomic.h new file mode 100644 index 0000000..5a8954f --- /dev/null +++ b/src/arch/armv7/include/arch/atomic.h @@ -0,0 +1,111 @@ +/* + * linux/include/asm-arm/atomic.h + * + * Copyright (c) 1996 Russell King. + * + * 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 + * 13-04-1997 RMK Made functions atomic! + * 07-12-1997 RMK Upgraded for v2.1. + * 26-08-1998 PJB Added #ifdef __KERNEL__ + */ +#ifndef __ASM_ARM_ATOMIC_H +#define __ASM_ARM_ATOMIC_H + +#include <linux/config.h> + +#ifdef CONFIG_SMP +#error SMP not supported +#endif + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#include <asm/proc/system.h> + +#define atomic_read(v) ((v)->counter) +#define atomic_set(v,i) (((v)->counter) = (i)) + +static inline void atomic_add(int i, volatile atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + v->counter += i; + local_irq_restore(flags); +} + +static inline void atomic_sub(int i, volatile atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + v->counter -= i; + local_irq_restore(flags); +} + +static inline void atomic_inc(volatile atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + v->counter += 1; + local_irq_restore(flags); +} + +static inline void atomic_dec(volatile atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + v->counter -= 1; + local_irq_restore(flags); +} + +static inline int atomic_dec_and_test(volatile atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val -= 1; + local_irq_restore(flags); + + return val == 0; +} + +static inline int atomic_add_negative(int i, volatile atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val += i; + local_irq_restore(flags); + + return val < 0; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + local_irq_save(flags); + *addr &= ~mask; + local_irq_restore(flags); +} + +/* Atomic operations are already serializing on ARM */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff --git a/src/arch/armv7/include/arch/boot/boot.h b/src/arch/armv7/include/arch/boot/boot.h new file mode 100644 index 0000000..08651cd --- /dev/null +++ b/src/arch/armv7/include/arch/boot/boot.h @@ -0,0 +1,8 @@ +#ifndef ASM_ARM_BOOT_H +#define ASM_ARM_BOOT_H + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_ARM + +#endif /* ASM_ARM_BOOT_H */ diff --git a/src/arch/armv7/include/arch/byteorder.h b/src/arch/armv7/include/arch/byteorder.h new file mode 100644 index 0000000..8dc069f --- /dev/null +++ b/src/arch/armv7/include/arch/byteorder.h @@ -0,0 +1,27 @@ +#ifndef _BYTEORDER_H +#define _BYTEORDER_H + +#define __LITTLE_ENDIAN 1234 + +#include <stdint.h> +#include <swab.h> + +#define cpu_to_le64(x) ((uint64_t)(x)) +#define le64_to_cpu(x) ((uint64_t)(x)) +#define cpu_to_le32(x) ((uint32_t)(x)) +#define le32_to_cpu(x) ((uint32_t)(x)) +#define cpu_to_le16(x) ((uint16_t)(x)) +#define le16_to_cpu(x) ((uint16_t)(x)) +#define cpu_to_be64(x) swab64(x) +#define be64_to_cpu(x) swab64(x) +#define cpu_to_be32(x) swab32((x)) +#define be32_to_cpu(x) swab32((x)) +#define cpu_to_be16(x) swab16((x)) +#define be16_to_cpu(x) swab16((x)) + +#define ntohll(x) be64_to_cpu(x) +#define htonll(x) cpu_to_be64(x) +#define ntohl(x) be32_to_cpu(x) +#define htonl(x) cpu_to_be32(x) + +#endif /* _BYTEORDER_H */ diff --git a/src/arch/armv7/include/arch/coreboot_tables.h b/src/arch/armv7/include/arch/coreboot_tables.h new file mode 100644 index 0000000..3c9bf98 --- /dev/null +++ b/src/arch/armv7/include/arch/coreboot_tables.h @@ -0,0 +1,25 @@ +#ifndef COREBOOT_TABLE_H +#define COREBOOT_TABLE_H + +#include <boot/coreboot_tables.h> + +/* This file holds function prototypes for building the coreboot table. */ +unsigned long write_coreboot_table( + unsigned long low_table_start, unsigned long low_table_end, + unsigned long rom_table_start, unsigned long rom_table_end); + +void lb_memory_range(struct lb_memory *mem, + uint32_t type, uint64_t start, uint64_t size); + +/* Routines to extract part so the coreboot table or information + * from the coreboot table. + */ +struct lb_memory *get_lb_mem(void); + +extern struct cmos_option_table option_table; + +/* defined by mainboard.c if the mainboard requires extra resources */ +int add_mainboard_resources(struct lb_memory *mem); +int add_northbridge_resources(struct lb_memory *mem); + +#endif /* COREBOOT_TABLE_H */ diff --git a/src/arch/armv7/include/arch/cpu.h b/src/arch/armv7/include/arch/cpu.h index b68004a..20a12c9 100644 --- a/src/arch/armv7/include/arch/cpu.h +++ b/src/arch/armv7/include/arch/cpu.h @@ -22,4 +22,25 @@
#define asmlinkage
+#if !defined(__PRE_RAM__) +#include <device/device.h> + +struct cpu_driver { + struct device_operations *ops; + struct cpu_device_id *id_table; +}; + +struct cpu_info { + device_t cpu; + unsigned long index; +}; + +struct cpuinfo_arm { + uint8_t arm; /* CPU family */ + uint8_t arm_vendor; /* CPU vendor */ + uint8_t arm_model; +}; + #endif + +#endif /* __ARCH_CPU_H__ */ diff --git a/src/arch/armv7/include/arch/gpio.h b/src/arch/armv7/include/arch/gpio.h new file mode 100644 index 0000000..b5dd9b7 --- /dev/null +++ b/src/arch/armv7/include/arch/gpio.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2011, NVIDIA Corp. All rights reserved. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _ASM_GENERIC_GPIO_H_ +#define _ASM_GENERIC_GPIO_H_ + +/* + * Generic GPIO API for U-Boot + * + * GPIOs are numbered from 0 to GPIO_COUNT-1 which value is defined + * by the SOC/architecture. + * + * Each GPIO can be an input or output. If an input then its value can + * be read as 0 or 1. If an output then its value can be set to 0 or 1. + * If you try to write an input then the value is undefined. If you try + * to read an output, barring something very unusual, you will get + * back the value of the output that you previously set. + * + * In some cases the operation may fail, for example if the GPIO number + * is out of range, or the GPIO is not available because its pin is + * being used by another function. In that case, functions may return + * an error value of -1. + */ + +/** + * Stop using the GPIO. This function should not alter pin configuration. + * + * @param gpio GPIO number + * @return 0 if ok, -1 on error + */ +int gpio_free(unsigned gpio); + +/** + * Make a GPIO an input. + * + * @param gpio GPIO number + * @return 0 if ok, -1 on error + */ +int gpio_direction_input(unsigned gpio); + +/** + * Make a GPIO an output, and set its value. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_direction_output(unsigned gpio, int value); + +/** + * Get a GPIO's value. This will work whether the GPIO is an input + * or an output. + * + * @param gpio GPIO number + * @return 0 if low, 1 if high, -1 on error + */ +int gpio_get_value(unsigned gpio); + +/** + * Set an output GPIO's value. The GPIO must already be an output or + * this function may have no effect. + * + * @param gpio GPIO number + * @param value GPIO value (0 for low or 1 for high) + * @return 0 if ok, -1 on error + */ +int gpio_set_value(unsigned gpio, int value); + +/** + * Request ownership of a gpio. This should be called before any of the other + * functions are used on this gpio. + * + * @param gp GPIO number + * @param label User label for this GPIO + * @return 0 if ok, -1 on error + */ +int gpio_request(unsigned gpio, const char *label); + +#endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/src/arch/armv7/include/arch/hlt.h b/src/arch/armv7/include/arch/hlt.h new file mode 100644 index 0000000..535508a --- /dev/null +++ b/src/arch/armv7/include/arch/hlt.h @@ -0,0 +1,10 @@ +#ifndef ARCH_HLT_H +#define ARCH_HLT_H + +static inline __attribute__((always_inline)) void hlt(void) +{ + for (;;) ; + //asm("hlt"); +} + +#endif /* ARCH_HLT_H */ diff --git a/src/arch/armv7/include/arch/io.h b/src/arch/armv7/include/arch/io.h new file mode 100644 index 0000000..7675a88 --- /dev/null +++ b/src/arch/armv7/include/arch/io.h @@ -0,0 +1,427 @@ +/* + * linux/include/asm-arm/io.h + * + * Copyright (C) 1996-2000 Russell King + * + * 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. + * + * Modifications: + * 16-Sep-1996 RMK Inlined the inx/outx functions & optimised for both + * constant addresses and variable addresses. + * 04-Dec-1997 RMK Moved a lot of this stuff to the new architecture + * specific IO header files. + * 27-Mar-1999 PJB Second parameter of memcpy_toio is const.. + * 04-Apr-1999 PJB Added check_signature. + * 12-Dec-1999 RMK More cleanups + * 18-Jun-2000 RMK Removed virt_to_* and friends definitions + */ +#ifndef __ASM_ARM_IO_H +#define __ASM_ARM_IO_H + +#ifdef __KERNEL__ + +#include <types.h> +#include <arch/byteorder.h> +#if 0 /* XXX###XXX */ +#include <asm/arch/hardware.h> +#endif /* XXX###XXX */ + +static inline void sync(void) +{ +} + +/* + * 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) (*(volatile unsigned char *)(a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) + +#if 0 +extern inline void __raw_writesb(unsigned int addr, const void *data, int bytelen) +{ + uint8_t *buf = (uint8_t *)data; + while(bytelen--) + __arch_putb(*buf++, addr); +} + +extern inline void __raw_writesw(unsigned int addr, const void *data, int wordlen) +{ + uint16_t *buf = (uint16_t *)data; + while(wordlen--) + __arch_putw(*buf++, addr); +} + +extern inline void __raw_writesl(unsigned int addr, const void *data, int longlen) +{ + uint32_t *buf = (uint32_t *)data; + while(longlen--) + __arch_putl(*buf++, addr); +} + +extern inline void __raw_readsb(unsigned int addr, void *data, int bytelen) +{ + uint8_t *buf = (uint8_t *)data; + while(bytelen--) + *buf++ = __arch_getb(addr); +} + +extern inline void __raw_readsw(unsigned int addr, void *data, int wordlen) +{ + uint16_t *buf = (uint16_t *)data; + while(wordlen--) + *buf++ = __arch_getw(addr); +} + +extern inline void __raw_readsl(unsigned int addr, void *data, int longlen) +{ + uint32_t *buf = (uint32_t *)data; + while(longlen--) + *buf++ = __arch_getl(addr); +} +#endif + +#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_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(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() + +#define writeb(v,c) ({ u8 __v = v; __iowmb(); __arch_putb(__v,c); __v; }) +#define writew(v,c) ({ u16 __v = v; __iowmb(); __arch_putw(__v,c); __v; }) +#define writel(v,c) ({ u32 __v = v; __iowmb(); __arch_putl(__v,c); __v; }) + +#define readb(c) ({ u8 __v = __arch_getb(c); __iormb(); __v; }) +#define readw(c) ({ u16 __v = __arch_getw(c); __iormb(); __v; }) +#define readl(c) ({ u32 __v = __arch_getl(c); __iormb(); __v; }) + +/* + * 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) + +/* + * 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 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) + +#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 + */ +#if 0 /* XXX###XXX */ +#include <asm/arch/io.h> +#endif /* XXX###XXX */ + +/* + * IO port access primitives + * ------------------------- + * + * The ARM doesn't have special IO access instructions; 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 numerous + * 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) __raw_writesb(__io(p),d,l) +#define outsw(p,d,l) __raw_writesw(__io(p),d,l) +#define outsl(p,d,l) __raw_writesl(__io(p),d,l) + +#define insb(p,d,l) __raw_readsb(__io(p),d,l) +#define insw(p,d,l) __raw_readsw(__io(p),d,l) +#define insl(p,d,l) __raw_readsl(__io(p),d,l) +#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) + +/* + * ioremap and friends. + * + * ioremap takes a PCI memory address, as specified in + * linux/Documentation/IO-mapping.txt. If you want a + * physical address, use __ioremap instead. + */ +extern void * __ioremap(unsigned long offset, size_t size, unsigned long flags); +extern void __iounmap(void *addr); + +/* + * Generic ioremap support. + * + * Define: + * iomem_valid_addr(off,size) + * iomem_to_phys(off) + */ +#ifdef iomem_valid_addr +#define __arch_ioremap(off,sz,nocache) \ + ({ \ + unsigned long _off = (off), _size = (sz); \ + void *_ret = (void *)0; \ + if (iomem_valid_addr(_off, _size)) \ + _ret = __ioremap(iomem_to_phys(_off),_size,nocache); \ + _ret; \ + }) + +#define __arch_iounmap __iounmap +#endif + +/* + * String version of IO memory access ops: + */ +extern void _memcpy_fromio(void *, unsigned long, size_t); +extern void _memcpy_toio(unsigned long, const void *, size_t); +extern void _memset_io(unsigned long, int, size_t); + +extern void __readwrite_bug(const char *fn); + +/* + * 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; +} + +#elif !defined(readb) + +#define readb(addr) (__readwrite_bug("readb"),0) +#define readw(addr) (__readwrite_bug("readw"),0) +#define readl(addr) (__readwrite_bug("readl"),0) +#define writeb(v,addr) __readwrite_bug("writeb") +#define writew(v,addr) __readwrite_bug("writew") +#define writel(v,addr) __readwrite_bug("writel") + +#define eth_io_copy_and_sum(a,b,c,d) __readwrite_bug("eth_io_copy_and_sum") + +#define check_signature(io,sig,len) (0) + +#endif /* __mem_pci */ + +/* FIXME(dhendrix): added to make uart8250_mem code happy. Note: lL */ +static inline __attribute__((always_inline)) uint8_t read8(unsigned long addr) +{ + return readb(addr); +} + +static inline __attribute__((always_inline)) uint16_t read16(unsigned long addr) +{ + return readw(addr); +} + +static inline __attribute__((always_inline)) uint32_t read32(unsigned long addr) +{ + return readl(addr); +} + +static inline __attribute__((always_inline)) void write8(unsigned long addr, uint8_t value) +{ + writeb(value, addr); +} + +static inline __attribute__((always_inline)) void write16(unsigned long addr, uint16_t value) +{ + writew(value, addr); +} + +static inline __attribute__((always_inline)) void write32(unsigned long addr, uint32_t value) +{ + writel(value, addr); +} + + +/* + * 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_ARM_IO_H */ diff --git a/src/arch/armv7/include/arch/pci_ops.h b/src/arch/armv7/include/arch/pci_ops.h new file mode 100644 index 0000000..eca9390 --- /dev/null +++ b/src/arch/armv7/include/arch/pci_ops.h @@ -0,0 +1,19 @@ +#ifndef ARCH_I386_PCI_OPS_H +#define ARCH_I386_PCI_OPS_H + +extern const struct pci_bus_operations pci_cf8_conf1; + +#if CONFIG_MMCONF_SUPPORT +extern const struct pci_bus_operations pci_ops_mmconf; +#endif + +static inline const struct pci_bus_operations *pci_config_default(void) +{ + return &pci_cf8_conf1; +} + +static inline void pci_set_method(device_t dev) +{ + dev->ops->ops_pci_bus = pci_config_default(); +} +#endif /* ARCH_I386_PCI_OPS_H */ diff --git a/src/arch/armv7/include/arch/types.h b/src/arch/armv7/include/arch/types.h new file mode 100644 index 0000000..71dc049 --- /dev/null +++ b/src/arch/armv7/include/arch/types.h @@ -0,0 +1,53 @@ +#ifndef __ASM_ARM_TYPES_H +#define __ASM_ARM_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__) +__extension__ typedef __signed__ long long __s64; +__extension__ 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 + +/* Dma addresses are 32-bits wide. */ + +typedef u32 dma_addr_t; + +typedef unsigned long phys_addr_t; +typedef unsigned long phys_size_t; + +#endif /* __KERNEL__ */ + +#endif diff --git a/src/arch/armv7/include/armv7.h b/src/arch/armv7/include/armv7.h new file mode 100644 index 0000000..dc111c1 --- /dev/null +++ b/src/arch/armv7/include/armv7.h @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V aneesh@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef ARMV7_H +#define ARMV7_H +#include <types.h> + +/* Cortex-A9 revisions */ +#define MIDR_CORTEX_A9_R0P1 0x410FC091 +#define MIDR_CORTEX_A9_R1P2 0x411FC092 +#define MIDR_CORTEX_A9_R1P3 0x411FC093 +#define MIDR_CORTEX_A9_R2P10 0x412FC09A + +/* Cortex-A15 revisions */ +#define MIDR_CORTEX_A15_R0P0 0x410FC0F0 + +/* CCSIDR */ +#define CCSIDR_LINE_SIZE_OFFSET 0 +#define CCSIDR_LINE_SIZE_MASK 0x7 +#define CCSIDR_ASSOCIATIVITY_OFFSET 3 +#define CCSIDR_ASSOCIATIVITY_MASK (0x3FF << 3) +#define CCSIDR_NUM_SETS_OFFSET 13 +#define CCSIDR_NUM_SETS_MASK (0x7FFF << 13) + +/* + * Values for InD field in CSSELR + * Selects the type of cache + */ +#define ARMV7_CSSELR_IND_DATA_UNIFIED 0 +#define ARMV7_CSSELR_IND_INSTRUCTION 1 + +/* Values for Ctype fields in CLIDR */ +#define ARMV7_CLIDR_CTYPE_NO_CACHE 0 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_ONLY 1 +#define ARMV7_CLIDR_CTYPE_DATA_ONLY 2 +#define ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA 3 +#define ARMV7_CLIDR_CTYPE_UNIFIED 4 + +/* + * CP15 Barrier instructions + * Please note that we have separate barrier instructions in ARMv7 + * However, we use the CP15 based instructtions because we use + * -march=armv5 in U-Boot + */ +#define CP15ISB asm volatile ("mcr p15, 0, %0, c7, c5, 4" : : "r" (0)) +#define CP15DSB asm volatile ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)) +#define CP15DMB asm volatile ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0)) + +void v7_outer_cache_enable(void); +void v7_outer_cache_disable(void); +void v7_outer_cache_flush_all(void); +void v7_outer_cache_inval_all(void); +void v7_outer_cache_flush_range(u32 start, u32 end); +void v7_outer_cache_inval_range(u32 start, u32 end); + +#endif diff --git a/src/arch/armv7/include/assembler.h b/src/arch/armv7/include/assembler.h new file mode 100644 index 0000000..5e4789b --- /dev/null +++ b/src/arch/armv7/include/assembler.h @@ -0,0 +1,60 @@ +/* + * arch/arm/include/asm/assembler.h + * + * Copyright (C) 1996-2000 Russell King + * + * 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. + * + * This file contains arm architecture specific defines + * for the different processors. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ + +/* + * Endian independent macros for shifting bytes within registers. + */ +#ifndef __ARMEB__ +#define pull lsr +#define push lsl +#define get_byte_0 lsl #0 +#define get_byte_1 lsr #8 +#define get_byte_2 lsr #16 +#define get_byte_3 lsr #24 +#define put_byte_0 lsl #0 +#define put_byte_1 lsl #8 +#define put_byte_2 lsl #16 +#define put_byte_3 lsl #24 +#else +#define pull lsl +#define push lsr +#define get_byte_0 lsr #24 +#define get_byte_1 lsr #16 +#define get_byte_2 lsr #8 +#define get_byte_3 lsl #0 +#define put_byte_0 lsl #24 +#define put_byte_1 lsl #16 +#define put_byte_2 lsl #8 +#define put_byte_3 lsl #0 +#endif + +/* + * Data preload for architectures that support it + */ +#if defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7R__) +#define PLD(code...) code +#else +#define PLD(code...) +#endif + +/* + * Cache alligned + */ +#define CALGN(code...) code diff --git a/src/arch/armv7/include/bootblock_common.h b/src/arch/armv7/include/bootblock_common.h new file mode 100644 index 0000000..f5c7129 --- /dev/null +++ b/src/arch/armv7/include/bootblock_common.h @@ -0,0 +1,69 @@ +#include <types.h> +#include <cbfs.h> +#include <string.h> +#include <arch/byteorder.h> + + +#define boot_cpu(x) 1 + +#ifdef CONFIG_BOOTBLOCK_CPU_INIT +#include CONFIG_BOOTBLOCK_CPU_INIT +#else +static void bootblock_cpu_init(void) { } +#endif +#ifdef CONFIG_BOOTBLOCK_NORTHBRIDGE_INIT +#include CONFIG_BOOTBLOCK_NORTHBRIDGE_INIT +#else +static void bootblock_northbridge_init(void) { } +#endif +#ifdef CONFIG_BOOTBLOCK_SOUTHBRIDGE_INIT +#include CONFIG_BOOTBLOCK_SOUTHBRIDGE_INIT +#else +static void bootblock_southbridge_init(void) { } +#endif + +static int cbfs_check_magic(struct cbfs_file *file) +{ + return !strcmp(file->magic, CBFS_FILE_MAGIC) ? 1 : 0; +} + +static unsigned long findstage(const char* target) +{ + unsigned long offset; + + void *ptr = (void *)*((unsigned long *) CBFS_HEADPTR_ADDR); + struct cbfs_header *header = (struct cbfs_header *) ptr; + // if (ntohl(header->magic) != CBFS_HEADER_MAGIC) + // printk(BIOS_ERR, "ERROR: No valid CBFS header found!\n"); + + offset = 0 - ntohl(header->romsize) + ntohl(header->offset); + int align = ntohl(header->align); + while(1) { + struct cbfs_file *file = (struct cbfs_file *) offset; + if (!cbfs_check_magic(file)) + return 0; + if (!strcmp(CBFS_NAME(file), target)) + return (unsigned long)CBFS_SUBHEADER(file); + int flen = ntohl(file->len); + int foffset = ntohl(file->offset); + unsigned long oldoffset = offset; + offset = ALIGN(offset + foffset + flen, align); + if (offset <= oldoffset) + return 0; + if (offset < 0xFFFFFFFF - ntohl(header->romsize)) + return 0; + } +} + + +static void call(unsigned long addr, unsigned long bist) +{ + asm volatile ("mov r0, %1\nbx %0\n" : : "r" (addr), "r" (bist)); +} + +static void hlt(void) +{ + /* is there such a thing as hlt on ARM? */ + // asm volatile ("1:\n\thlt\n\tjmp 1b\n\t"); + asm volatile ("1:\nb 1b\n\t"); +} diff --git a/src/arch/armv7/include/cache.h b/src/arch/armv7/include/cache.h new file mode 100644 index 0000000..cf8fb5a --- /dev/null +++ b/src/arch/armv7/include/cache.h @@ -0,0 +1,56 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar prafulla@marvell.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _ASM_CACHE_H +#define _ASM_CACHE_H + +/* + * Invalidate L2 Cache using co-proc instruction + */ +static inline void invalidate_l2_cache(void) +{ + unsigned int val=0; + + asm volatile("mcr p15, 1, %0, c15, c11, 0 @ invl l2 cache" + : : "r" (val) : "cc"); + isb(); +} + +void l2_cache_enable(void); +void l2_cache_disable(void); + +/* + * The current upper bound for ARM L1 data cache line sizes is 64 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 64 +#endif + +inline void dram_bank_mmu_setup(unsigned long start, unsigned long size); + +#endif /* _ASM_CACHE_H */ diff --git a/src/arch/armv7/include/clocks.h b/src/arch/armv7/include/clocks.h new file mode 100644 index 0000000..8f35303 --- /dev/null +++ b/src/arch/armv7/include/clocks.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* Standard clock speeds */ + +/* + * We define some commonly-used clock speeds to avoid error since long + * numbers are hard to read. + * + * The format of the label is + * CLK_x_yU where: + * x is the integer speed + * y is the fractional part which can be omitted if 0 + * U is the units (blank for Hz, K or M for KHz and MHz) + * + * Please order the items by increasing Hz + */ +enum { + CLK_32768 = 32768, + CLK_20M = 20000000, + CLK_24M = 24000000, + CLK_144M = 144000000, + CLK_216M = 216000000, + CLK_300M = 300000000, +}; + diff --git a/src/arch/armv7/include/common.h b/src/arch/armv7/include/common.h new file mode 100644 index 0000000..e21cfb0 --- /dev/null +++ b/src/arch/armv7/include/common.h @@ -0,0 +1,494 @@ +/* + * (C) Copyright 2000-2009 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __COMMON_H_ +#define __COMMON_H_ 1 + +#undef _LINUX_CONFIG_H +#define _LINUX_CONFIG_H 1 /* avoid reading Linux autoconf.h file */ + +#ifndef __ASSEMBLER__ /* put C only stuff in this section */ + +typedef unsigned char uchar; +typedef volatile unsigned long vu_long; +typedef volatile unsigned short vu_short; +typedef volatile unsigned char vu_char; +typedef unsigned long ulong; +typedef unsigned int uint; + +//#include <config.h> +//#include <asm-offsets.h> +//#include <linux/bitops.h> +//#include <linux/string.h> +//#include <asm/ptrace.h> +#include <types.h> +//#include <stdarg.h> + +//#include <part.h> +//#include <flash.h> +//#include <image.h> + +#ifdef DEBUG +#define debug(fmt,args...) printf (fmt ,##args) +#define debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args); +#else +#define debug(fmt,args...) +#define debugX(level,fmt,args...) +#endif /* DEBUG */ + +#ifdef DEBUG +# define _DEBUG 1 +#else +# define _DEBUG 0 +#endif + +/* + * An assertion is run-time check done in debug mode only. If DEBUG is not + * defined then it is skipped. If DEBUG is defined and the assertion fails, + * then it calls panic*( which may or may not reset/halt U-Boot (see + * CONFIG_PANIC_HANG), It is hoped that all failing assertions are found + * before release, and after release it is hoped that they don't matter. But + * in any case these failing assertions cannot be fixed with a reset (which + * may just do the same assertion again). + */ +void __assert_fail(const char *assertion, const char *file, unsigned line, + const char *function); +#define assert(x) \ + ({ if (!(x) && _DEBUG) \ + __assert_fail(#x, __FILE__, __LINE__, __func__); }) + +#define error(fmt, args...) do { \ + printf("ERROR: " fmt "\nat %s:%d/%s()\n", \ + ##args, __FILE__, __LINE__, __func__); \ +} while (0) + +#ifndef BUG +#define BUG() do { \ + printf("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \ + panic("BUG!"); \ +} while (0) +#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) +#endif /* BUG */ + +/* Force a compilation error if condition is true */ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +typedef void (interrupt_handler_t)(void *); + +//#include <asm/u-boot.h> /* boot information for Linux kernel */ +#include <global_data.h> /* global data used for startup functions */ + +/* + * Return the time since boot in microseconds, This is needed for bootstage + * and should be defined in CPU- or board-specific code. If undefined then + * millisecond resolution will be used (the standard get_timer()). + */ +ulong timer_get_boot_us(void); + +/* + * Return the current value of a monotonically increasing microsecond timer. + * Granularity may be larger than 1us if hardware does not support this. + */ +ulong timer_get_us(void); + +/* + * General Purpose Utilities + */ +#if 0 +#define min(X, Y) \ + ({ typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x < __y) ? __x : __y; }) + +#define max(X, Y) \ + ({ typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x > __y) ? __x : __y; }) +#define MIN(x, y) min(x, y) +#define MAX(x, y) max(x, y) +#endif + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * Function Prototypes + */ + +void hang (void) __attribute__ ((noreturn)); + +int init_timer(void); /* FIXME(dhendrix): used to be timer_init() */ +int cpu_init(void); + +/* */ +unsigned long long initdram (int); +int display_options (void); +void print_size(unsigned long long, const char *); +int print_buffer (ulong addr, void* data, uint width, uint count, uint linelen); + +/* common/main.c */ +void main_loop (void); +int run_command (const char *cmd, int flag); +int readline (const char *const prompt); +int readline_into_buffer (const char *const prompt, char * buffer); +int parse_line (char *, char *[]); +void init_cmd_timeout(void); +void reset_cmd_timeout(void); + +/* arch/$(ARCH)/lib/board.c */ +void board_init_f (ulong); +//void board_init_f (ulong) __attribute__ ((noreturn)); +void board_init_r (gd_t *, ulong) __attribute__ ((noreturn)); +int checkboard (void); +int checkflash (void); +int checkdram (void); +int last_stage_init(void); +extern ulong monitor_flash_len; +int mac_read_from_eeprom(void); + +#ifdef CONFIG_ARM +# include <asm/mach-types.h> +# include <asm/setup.h> +# include <asm/u-boot-arm.h> /* ARM version to be fixed! */ +#endif /* CONFIG_ARM */ + +int misc_init_f (void); +int misc_init_r (void); + +/* common/exports.c */ +void jumptable_init(void); + +/* common/kallsysm.c */ +const char *symbol_lookup(unsigned long addr, unsigned long *caddr); + +/* api/api.c */ +void api_init (void); + +/* common/memsize.c */ +long get_ram_size (long *, long); + +/* $(BOARD)/$(BOARD).c */ +void reset_phy (void); +void fdc_hw_init (void); + +/* $(BOARD)/eeprom.c */ +void eeprom_init (void); +#ifndef CONFIG_SPI +int eeprom_probe (unsigned dev_addr, unsigned offset); +#endif +int eeprom_read (unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt); +int eeprom_write (unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt); + +/* + * Set this up regardless of board + * type, to prevent errors. + */ +#if defined(CONFIG_SPI) || !defined(CONFIG_SYS_I2C_EEPROM_ADDR) +# define CONFIG_SYS_DEF_EEPROM_ADDR 0 +#else +#if !defined(CONFIG_ENV_EEPROM_IS_ON_I2C) +# define CONFIG_SYS_DEF_EEPROM_ADDR CONFIG_SYS_I2C_EEPROM_ADDR +#endif +#endif /* CONFIG_SPI || !defined(CONFIG_SYS_I2C_EEPROM_ADDR) */ + +#if defined(CONFIG_SPI) +extern void spi_init_f (void); +extern void spi_init_r (void); +extern ssize_t spi_read (uchar *, int, uchar *, int); +extern ssize_t spi_write (uchar *, int, uchar *, int); +#endif + +/* $(BOARD)/$(BOARD).c */ +int board_early_init_f (void); +int board_late_init (void); +int board_postclk_init (void); /* after clocks/timebase, before env/serial */ +int board_early_init_r (void); +void board_poweroff (void); + +#if defined(CONFIG_SYS_DRAM_TEST) +int testdram(void); +#endif /* CONFIG_SYS_DRAM_TEST */ + +/* $(CPU)/start.S */ +#if defined(CONFIG_5xx) || \ + defined(CONFIG_8xx) +uint get_immr (uint); +#endif +uint get_pir (void); +#if defined(CONFIG_MPC5xxx) +uint get_svr (void); +#endif +uint get_pvr (void); +uint get_svr (void); +uint rd_ic_cst (void); +void wr_ic_cst (uint); +void wr_ic_adr (uint); +uint rd_dc_cst (void); +void wr_dc_cst (uint); +void wr_dc_adr (uint); +int icache_status (void); +void icache_enable (void); +void icache_disable(void); +int dcache_status (void); +void dcache_enable (void); +void dcache_disable(void); +void mmu_disable(void); +void relocate_code (ulong, gd_t *, ulong) __attribute__ ((noreturn)); +ulong get_endaddr (void); +void trap_init (ulong); +#if defined (CONFIG_4xx) || \ + defined (CONFIG_MPC5xxx) || \ + defined (CONFIG_74xx_7xx) || \ + defined (CONFIG_74x) || \ + defined (CONFIG_75x) || \ + defined (CONFIG_74xx) || \ + defined (CONFIG_MPC8220) || \ + defined (CONFIG_MPC85xx) || \ + defined (CONFIG_MPC86xx) || \ + defined (CONFIG_MPC83xx) +unsigned char in8(unsigned int); +void out8(unsigned int, unsigned char); +unsigned short in16(unsigned int); +unsigned short in16r(unsigned int); +void out16(unsigned int, unsigned short value); +void out16r(unsigned int, unsigned short value); +unsigned long in32(unsigned int); +unsigned long in32r(unsigned int); +void out32(unsigned int, unsigned long value); +void out32r(unsigned int, unsigned long value); +void ppcDcbf(unsigned long value); +void ppcDcbi(unsigned long value); +void ppcSync(void); +void ppcDcbz(unsigned long value); +#endif + +/* $(CPU)/cpu.c */ +static inline int cpumask_next(int cpu, unsigned int mask) +{ + for (cpu++; !((1 << cpu) & mask); cpu++) + ; + + return cpu; +} + +#define for_each_cpu(iter, cpu, num_cpus, mask) \ + for (iter = 0, cpu = cpumask_next(-1, mask); \ + iter < num_cpus; \ + iter++, cpu = cpumask_next(cpu, mask)) \ + +int cpu_numcores (void); +u32 cpu_mask (void); +int is_core_valid (unsigned int); +int probecpu (void); +int checkcpu (void); +int checkicache (void); +int checkdcache (void); +void upmconfig (unsigned int, unsigned int *, unsigned int); +ulong get_tbclk (void); +void reset_cpu (ulong addr); +#if defined (CONFIG_OF_LIBFDT) && defined (CONFIG_OF_BOARD_SETUP) +void ft_cpu_setup(void *blob, bd_t *bd); +#endif + + +/* $(CPU)/serial.c */ +int serial_init (void); +void serial_setbrg (void); +void serial_putc (const char); +void serial_putc_raw(const char); +void serial_puts (const char *); +int serial_getc (void); +int serial_tstc (void); + +void _serial_setbrg (const int); +void _serial_putc (const char, const int); +void _serial_putc_raw(const char, const int); +void _serial_puts (const char *, const int); +int _serial_getc (const int); +int _serial_tstc (const int); + +/* $(CPU)/speed.c */ +int get_clocks (void); +int get_clocks_866 (void); +int sdram_adjust_866 (void); +int adjust_sdram_tbs_8xx (void); +#if defined(CONFIG_8260) +int prt_8260_clks (void); +#elif defined(CONFIG_MPC5xxx) +int prt_mpc5xxx_clks (void); +#endif +#if defined(CONFIG_MPC512X) +int prt_mpc512xxx_clks (void); +#endif +#if defined(CONFIG_MPC8220) +int prt_mpc8220_clks (void); +#endif +#ifdef CONFIG_4xx +ulong get_OPB_freq (void); +ulong get_PCI_freq (void); +#endif +#if defined(CONFIG_S3C24X0) || \ + defined(CONFIG_LH7A40X) || \ + defined(CONFIG_S3C6400) || \ + defined(CONFIG_EP93XX) +ulong get_FCLK (void); +ulong get_HCLK (void); +ulong get_PCLK (void); +ulong get_UCLK (void); +#endif +#if defined(CONFIG_LH7A40X) +ulong get_PLLCLK (void); +#endif +#if defined CONFIG_INCA_IP +uint incaip_get_cpuclk (void); +#endif +#if defined(CONFIG_IMX) +ulong get_systemPLLCLK(void); +ulong get_FCLK(void); +ulong get_HCLK(void); +ulong get_BCLK(void); +ulong get_PERCLK1(void); +ulong get_PERCLK2(void); +ulong get_PERCLK3(void); +#endif +ulong get_bus_freq (ulong); +int get_serial_clock(void); + +struct pt_regs; +/* $(CPU)/interrupts.c */ +int interrupt_init (void); +void timer_interrupt (struct pt_regs *); +void external_interrupt (struct pt_regs *); +void irq_install_handler(int, interrupt_handler_t *, void *); +void irq_free_handler (int); +void reset_timer (void); +ulong get_timer (ulong base); +void enable_interrupts (void); +int disable_interrupts (void); + +/* $(CPU)/.../commproc.c */ +int dpram_init (void); +uint dpram_base(void); +uint dpram_base_align(uint align); +uint dpram_alloc(uint size); +uint dpram_alloc_align(uint size,uint align); +void bootcount_store (ulong); +ulong bootcount_load (void); +#define BOOTCOUNT_MAGIC 0xB001C041 + +/* $(CPU)/.../<eth> */ +void mii_init (void); + +/* $(CPU)/.../lcd.c */ +ulong lcd_setmem (ulong); + +/* $(CPU)/.../video.c */ +ulong video_setmem (ulong); + +/* arch/$(ARCH)/lib/cache.c */ +ulong dcache_get_line_size(void); +void enable_caches(void); +void flush_cache (unsigned long, unsigned long); +void flush_dcache_all(void); +void flush_dcache_range(unsigned long start, unsigned long stop); +void invalidate_dcache_range(unsigned long start, unsigned long stop); +void invalidate_dcache_all(void); +void invalidate_icache_all(void); + +/* arch/$(ARCH)/lib/ticks.S */ +unsigned long long get_ticks(void); +void wait_ticks (unsigned long); + +/* arch/$(ARCH)/lib/time.c */ +void __udelay (unsigned long); +ulong usec2ticks (unsigned long usec); +ulong ticks2usec (unsigned long ticks); +int init_timebase (void); + +/* lib/qsort.c */ +void qsort(void *base, size_t nmemb, size_t size, + int(*compar)(const void *, const void *)); +int strcmp_compar(const void *, const void *); + +/* lib/time.c */ +void udelay (unsigned long); + +#if 0 +/* lib/vsprintf.c */ +ulong simple_strtoul(const char *cp,char **endp,unsigned int base); +int strict_strtoul(const char *cp, unsigned int base, unsigned long *res); +unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base); +long simple_strtol(const char *cp,char **endp,unsigned int base); +void panic(const char *fmt, ...) + __attribute__ ((format (__printf__, 1, 2), noreturn)); +int sprintf(char * buf, const char *fmt, ...) + __attribute__ ((format (__printf__, 2, 3))); +int vsprintf(char *buf, const char *fmt, va_list args); +#endif + +/* lib/strmhz.c */ +char * strmhz(char *buf, unsigned long hz); + +/* Multicore arch functions */ +#ifdef CONFIG_MP +int cpu_status(int nr); +int cpu_reset(int nr); +int cpu_disable(int nr); +int cpu_release(int nr, int argc, char * const argv[]); +#endif + +#endif /* __ASSEMBLER__ */ + +/* Put only stuff here that the assembler can digest */ + +#ifdef CONFIG_POST +#define CONFIG_HAS_POST +#ifndef CONFIG_POST_ALT_LIST +#define CONFIG_POST_STD_LIST +#endif +#endif + +#ifdef CONFIG_INIT_CRITICAL +#error CONFIG_INIT_CRITICAL is deprecated! +#error Read section CONFIG_SKIP_LOWLEVEL_INIT in README. +#endif + +//#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define ROUND(a,b) (((a) + (b) - 1) & ~((b) - 1)) +#define DIV_ROUND(n,d) (((n) + ((d)/2)) / (d)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + +//#define ALIGN(x,a) __ALIGN_MASK((x),(typeof(x))(a)-1) +#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) + +#endif /* __COMMON_H_ */ diff --git a/src/arch/armv7/include/div64.h b/src/arch/armv7/include/div64.h new file mode 100644 index 0000000..5baed2b --- /dev/null +++ b/src/arch/armv7/include/div64.h @@ -0,0 +1,233 @@ +/* taken from linux 2.6.31.14 */ + +#ifndef __ASM_ARM_DIV64 +#define __ASM_ARM_DIV64 + +//#include <asm/system.h> +//#include <linux/types.h> +// FIXME + +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#define __LINUX_ARM_ARCH__ 7 + +/* + * The semantics of do_div() are: + * + * uint32_t do_div(uint64_t *n, uint32_t base) + * { + * uint32_t remainder = *n % base; + * *n = *n / base; + * return remainder; + * } + * + * In other words, a 64-bit dividend with a 32-bit divisor producing + * a 64-bit result and a 32-bit remainder. To accomplish this optimally + * we call a special __do_div64 helper with completely non standard + * calling convention for arguments and results (beware). + */ + +#ifdef __ARMEB__ +#define __xh "r0" +#define __xl "r1" +#else +#define __xl "r0" +#define __xh "r1" +#endif + +#define __do_div_asm(n, base) \ +({ \ + register unsigned int __base asm("r4") = base; \ + register unsigned long long __n asm("r0") = n; \ + register unsigned long long __res asm("r2"); \ + register unsigned int __rem asm(__xh); \ + asm( __asmeq("%0", __xh) \ + __asmeq("%1", "r2") \ + __asmeq("%2", "r0") \ + __asmeq("%3", "r4") \ + "bl __do_div64" \ + : "=r" (__rem), "=r" (__res) \ + : "r" (__n), "r" (__base) \ + : "ip", "lr", "cc"); \ + n = __res; \ + __rem; \ +}) + +#if __GNUC__ < 4 + +/* + * gcc versions earlier than 4.0 are simply too problematic for the + * optimized implementation below. First there is gcc PR 15089 that + * tend to trig on more complex constructs, spurious .global __udivsi3 + * are inserted even if none of those symbols are referenced in the + * generated code, and those gcc versions are not able to do constant + * propagation on long long values anyway. + */ +#define do_div(n, base) __do_div_asm(n, base) + +#elif __GNUC__ >= 4 + +//#include <asm/bug.h> + +/* + * If the divisor happens to be constant, we determine the appropriate + * inverse at compile time to turn the division into a few inline + * multiplications instead which is much faster. And yet only if compiling + * for ARMv4 or higher (we need umull/umlal) and if the gcc version is + * sufficiently recent to perform proper long long constant propagation. + * (It is unfortunate that gcc doesn't perform all this internally.) + */ +#define do_div(n, base) \ +({ \ + unsigned int __r, __b = (base); \ + if (!__builtin_constant_p(__b) || __b == 0 || \ + (__LINUX_ARM_ARCH__ < 4 && (__b & (__b - 1)) != 0)) { \ + /* non-constant divisor (or zero): slow path */ \ + __r = __do_div_asm(n, __b); \ + } else if ((__b & (__b - 1)) == 0) { \ + /* Trivial: __b is constant and a power of 2 */ \ + /* gcc does the right thing with this code. */ \ + __r = n; \ + __r &= (__b - 1); \ + n /= __b; \ + } else { \ + /* Multiply by inverse of __b: n/b = n*(p/b)/p */ \ + /* We rely on the fact that most of this code gets */ \ + /* optimized away at compile time due to constant */ \ + /* propagation and only a couple inline assembly */ \ + /* instructions should remain. Better avoid any */ \ + /* code construct that might prevent that. */ \ + unsigned long long __res, __x, __t, __m, __n = n; \ + unsigned int __c, __p, __z = 0; \ + /* preserve low part of n for reminder computation */ \ + __r = __n; \ + /* determine number of bits to represent __b */ \ + __p = 1 << __div64_fls(__b); \ + /* compute __m = ((__p << 64) + __b - 1) / __b */ \ + __m = (~0ULL / __b) * __p; \ + __m += (((~0ULL % __b + 1) * __p) + __b - 1) / __b; \ + /* compute __res = __m*(~0ULL/__b*__b-1)/(__p << 64) */ \ + __x = ~0ULL / __b * __b - 1; \ + __res = (__m & 0xffffffff) * (__x & 0xffffffff); \ + __res >>= 32; \ + __res += (__m & 0xffffffff) * (__x >> 32); \ + __t = __res; \ + __res += (__x & 0xffffffff) * (__m >> 32); \ + __t = (__res < __t) ? (1ULL << 32) : 0; \ + __res = (__res >> 32) + __t; \ + __res += (__m >> 32) * (__x >> 32); \ + __res /= __p; \ + /* Now sanitize and optimize what we've got. */ \ + if (~0ULL % (__b / (__b & -__b)) == 0) { \ + /* those cases can be simplified with: */ \ + __n /= (__b & -__b); \ + __m = ~0ULL / (__b / (__b & -__b)); \ + __p = 1; \ + __c = 1; \ + } else if (__res != __x / __b) { \ + /* We can't get away without a correction */ \ + /* to compensate for bit truncation errors. */ \ + /* To avoid it we'd need an additional bit */ \ + /* to represent __m which would overflow it. */ \ + /* Instead we do m=p/b and n/b=(n*m+m)/p. */ \ + __c = 1; \ + /* Compute __m = (__p << 64) / __b */ \ + __m = (~0ULL / __b) * __p; \ + __m += ((~0ULL % __b + 1) * __p) / __b; \ + } else { \ + /* Reduce __m/__p, and try to clear bit 31 */ \ + /* of __m when possible otherwise that'll */ \ + /* need extra overflow handling later. */ \ + unsigned int __bits = -(__m & -__m); \ + __bits |= __m >> 32; \ + __bits = (~__bits) << 1; \ + /* If __bits == 0 then setting bit 31 is */ \ + /* unavoidable. Simply apply the maximum */ \ + /* possible reduction in that case. */ \ + /* Otherwise the MSB of __bits indicates the */ \ + /* best reduction we should apply. */ \ + if (!__bits) { \ + __p /= (__m & -__m); \ + __m /= (__m & -__m); \ + } else { \ + __p >>= __div64_fls(__bits); \ + __m >>= __div64_fls(__bits); \ + } \ + /* No correction needed. */ \ + __c = 0; \ + } \ + /* Now we have a combination of 2 conditions: */ \ + /* 1) whether or not we need a correction (__c), and */ \ + /* 2) whether or not there might be an overflow in */ \ + /* the cross product (__m & ((1<<63) | (1<<31))) */ \ + /* Select the best insn combination to perform the */ \ + /* actual __m * __n / (__p << 64) operation. */ \ + if (!__c) { \ + asm ( "umull %Q0, %R0, %1, %Q2\n\t" \ + "mov %Q0, #0" \ + : "=&r" (__res) \ + : "r" (__m), "r" (__n) \ + : "cc" ); \ + } else if (!(__m & ((1ULL << 63) | (1ULL << 31)))) { \ + __res = __m; \ + asm ( "umlal %Q0, %R0, %Q1, %Q2\n\t" \ + "mov %Q0, #0" \ + : "+&r" (__res) \ + : "r" (__m), "r" (__n) \ + : "cc" ); \ + } else { \ + asm ( "umull %Q0, %R0, %Q1, %Q2\n\t" \ + "cmn %Q0, %Q1\n\t" \ + "adcs %R0, %R0, %R1\n\t" \ + "adc %Q0, %3, #0" \ + : "=&r" (__res) \ + : "r" (__m), "r" (__n), "r" (__z) \ + : "cc" ); \ + } \ + if (!(__m & ((1ULL << 63) | (1ULL << 31)))) { \ + asm ( "umlal %R0, %Q0, %R1, %Q2\n\t" \ + "umlal %R0, %Q0, %Q1, %R2\n\t" \ + "mov %R0, #0\n\t" \ + "umlal %Q0, %R0, %R1, %R2" \ + : "+&r" (__res) \ + : "r" (__m), "r" (__n) \ + : "cc" ); \ + } else { \ + asm ( "umlal %R0, %Q0, %R2, %Q3\n\t" \ + "umlal %R0, %1, %Q2, %R3\n\t" \ + "mov %R0, #0\n\t" \ + "adds %Q0, %1, %Q0\n\t" \ + "adc %R0, %R0, #0\n\t" \ + "umlal %Q0, %R0, %R2, %R3" \ + : "+&r" (__res), "+&r" (__z) \ + : "r" (__m), "r" (__n) \ + : "cc" ); \ + } \ + __res /= __p; \ + /* The reminder can be computed with 32-bit regs */ \ + /* only, and gcc is good at that. */ \ + { \ + unsigned int __res0 = __res; \ + unsigned int __b0 = __b; \ + __r -= __res0 * __b0; \ + } \ + /* BUG_ON(__r >= __b || __res * __b + __r != n); */ \ + n = __res; \ + } \ + __r; \ +}) + +/* our own fls implementation to make sure constant propagation is fine */ +#define __div64_fls(bits) \ +({ \ + unsigned int __left = (bits), __nr = 0; \ + if (__left & 0xffff0000) __nr += 16, __left >>= 16; \ + if (__left & 0x0000ff00) __nr += 8, __left >>= 8; \ + if (__left & 0x000000f0) __nr += 4, __left >>= 4; \ + if (__left & 0x0000000c) __nr += 2, __left >>= 2; \ + if (__left & 0x00000002) __nr += 1; \ + __nr; \ +}) + +#endif + +#endif diff --git a/src/arch/armv7/include/global_data.h b/src/arch/armv7/include/global_data.h new file mode 100644 index 0000000..4ae86aa --- /dev/null +++ b/src/arch/armv7/include/global_data.h @@ -0,0 +1,108 @@ +/* + * (C) Copyright 2002-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __ASM_GBL_DATA_H +#define __ASM_GBL_DATA_H + +/* + * The following data structure is placed in some memory which is + * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or + * some locked parts of the data cache) to allow for a minimum set of + * global variables during system initialization (until we have set + * up the memory controller so that we can use RAM). + * + * Keep it *SMALL* and remember to set GENERATED_GBL_DATA_SIZE > sizeof(gd_t) + */ + +typedef struct global_data { +// bd_t *bd; + unsigned long flags; + unsigned long baudrate; + unsigned long have_console; /* serial_init() was called */ +#ifdef CONFIG_PRE_CONSOLE_BUFFER + unsigned long precon_buf_idx; /* Pre-Console buffer index */ +#endif + unsigned long env_addr; /* Address of Environment struct */ + unsigned long env_valid; /* Checksum of Environment valid? */ + unsigned long fb_base; /* base address of frame buffer */ +#ifdef CONFIG_FSL_ESDHC + unsigned long sdhc_clk; +#endif +#ifdef CONFIG_AT91FAMILY + /* "static data" needed by at91's clock.c */ + unsigned long cpu_clk_rate_hz; + unsigned long main_clk_rate_hz; + unsigned long mck_rate_hz; + unsigned long plla_rate_hz; + unsigned long pllb_rate_hz; + unsigned long at91_pllb_usb_init; +#endif +#ifdef CONFIG_ARM + /* "static data" needed by most of timer.c on ARM platforms */ + unsigned long timer_rate_hz; + unsigned long tbl; + unsigned long tbu; + unsigned long long timer_reset_value; + unsigned long lastinc; +#endif +#ifdef CONFIG_IXP425 + unsigned long timestamp; +#endif + unsigned long relocaddr; /* Start address of U-Boot in RAM */ + unsigned long long ram_size; /* RAM size */ + unsigned long mon_len; /* monitor len */ + unsigned long irq_sp; /* irq stack pointer */ + unsigned long start_addr_sp; /* start_addr_stackpointer */ + unsigned long reloc_off; +#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) + unsigned long tlb_addr; + unsigned long tlb_size; +#endif + const void *fdt_blob; /* Our device tree, NULL if none */ +#ifdef CONFIG_SYS_SKIP_ARM_RELOCATION + ulong malloc_end; /* End of malloc region (addr + 1) */ +#endif + void **jt; /* jump table */ + char env_buf[32]; /* buffer for getenv() before reloc. */ +#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER) + unsigned long post_log_word; /* Record POST activities */ + unsigned long post_log_res; /* success of POST test */ + unsigned long post_init_f_time; /* When post_init_f started */ +#endif +} gd_t; + +/* + * Global Data Flags + */ +#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */ +#define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */ +#define GD_FLG_SILENT 0x00004 /* Silent mode */ +#define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */ +#define GD_FLG_POSTSTOP 0x00010 /* POST seqeunce aborted */ +#define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */ +#define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out) */ +#define GD_FLG_ENV_READY 0x00080 /* Environment imported into hash table */ + +#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8") + +#endif /* __ASM_GBL_DATA_H */ diff --git a/src/arch/armv7/include/hang.h b/src/arch/armv7/include/hang.h new file mode 100644 index 0000000..f57fc28 --- /dev/null +++ b/src/arch/armv7/include/hang.h @@ -0,0 +1,20 @@ + /* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +void hang(void); diff --git a/src/arch/armv7/include/mmio_conf.h b/src/arch/armv7/include/mmio_conf.h new file mode 100644 index 0000000..143c6bd --- /dev/null +++ b/src/arch/armv7/include/mmio_conf.h @@ -0,0 +1,6 @@ +#ifndef ARCH_MMIO_H +#define ARCH_MMIO_H 1 + +// Nut'n'Bitch + +#endif /* ARCH_MMIO_H */ diff --git a/src/arch/armv7/include/ptrace.h b/src/arch/armv7/include/ptrace.h new file mode 100644 index 0000000..e981cad --- /dev/null +++ b/src/arch/armv7/include/ptrace.h @@ -0,0 +1,30 @@ +#ifndef __ASM_ARM_PTRACE_H +#define __ASM_ARM_PTRACE_H + +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 +#define PTRACE_GETFPREGS 14 +#define PTRACE_SETFPREGS 15 + +#define PTRACE_SETOPTIONS 21 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 + +#include <asm/proc/ptrace.h> + +#ifndef __ASSEMBLER__ +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + +extern void show_regs(struct pt_regs *); + +#define predicate(x) (x & 0xf0000000) +#define PREDICATE_ALWAYS 0xe0000000 + +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/src/arch/armv7/include/smp/spinlock.h b/src/arch/armv7/include/smp/spinlock.h new file mode 100644 index 0000000..1dc397c --- /dev/null +++ b/src/arch/armv7/include/smp/spinlock.h @@ -0,0 +1,52 @@ +#ifndef ARCH_SMP_SPINLOCK_H +#define ARCH_SMP_SPINLOCK_H + +/* FIXME: implement this for ARM */ +#error "implement this for ARM" +#if 0 +/* + * Your basic SMP spinlocks, allowing only a single CPU anywhere + */ + +typedef struct { + volatile unsigned int lock; +} spinlock_t; + + +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 } +#define DECLARE_SPIN_LOCK(x) static spinlock_t x = SPIN_LOCK_UNLOCKED; + +#define barrier() __asm__ __volatile__("": : :"memory") +#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) != 0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + +static inline __attribute__((always_inline)) void spin_lock(spinlock_t *lock) +{ + unsigned long tmp; + __asm__ __volatile__ ( + "1: ldrex %0, [%1]\n" + " teq %0, #0\n" + " strexeq %0, %2, [%1]\n" + " teqeq %0, #0\n" + " bne 1b\n" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc" + ); + barrier(); +} + +static inline __attribute__((always_inline)) void spin_unlock(spinlock_t *lock) +{ + __asm__ __volatile__( + " str %1, [%0]\n" + : + : "r" (&lock->lock), "r" (0) + : "cc" + ); +} + +#define cpu_relax() barrier() + +#endif +#endif /* ARCH_SMP_SPINLOCK_H */ diff --git a/src/arch/armv7/include/stdint.h b/src/arch/armv7/include/stdint.h new file mode 100644 index 0000000..a8a0230 --- /dev/null +++ b/src/arch/armv7/include/stdint.h @@ -0,0 +1,81 @@ +#ifndef ARM_STDINT_H +#define ARM_STDINT_H + +#if defined(__GNUC__) +#define __HAVE_LONG_LONG__ 1 +#else +#define __HAVE_LONG_LONG__ 0 +#endif + +/* Exact integral types */ +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef unsigned short uint16_t; +typedef signed short int16_t; + +typedef unsigned int uint32_t; +typedef signed int int32_t; + +#if __HAVE_LONG_LONG__ +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +#endif + +/* Small types */ +typedef unsigned char uint_least8_t; +typedef signed char int_least8_t; + +typedef unsigned short uint_least16_t; +typedef signed short int_least16_t; + +typedef unsigned int uint_least32_t; +typedef signed int int_least32_t; + +#if __HAVE_LONG_LONG__ +typedef unsigned long long uint_least64_t; +typedef signed long long int_least64_t; +#endif + +/* Fast Types */ +typedef unsigned char uint_fast8_t; +typedef signed char int_fast8_t; + +typedef unsigned int uint_fast16_t; +typedef signed int int_fast16_t; + +typedef unsigned int uint_fast32_t; +typedef signed int int_fast32_t; + +#if __HAVE_LONG_LONG__ +typedef unsigned long long uint_fast64_t; +typedef signed long long int_fast64_t; +#endif + +/* Types for `void *' pointers. */ +typedef int intptr_t; +typedef unsigned int uintptr_t; + +/* Largest integral types */ +#if __HAVE_LONG_LONG__ +typedef long long int intmax_t; +typedef unsigned long long uintmax_t; +#else +typedef long int intmax_t; +typedef unsigned long int uintmax_t; +#endif + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +#if __HAVE_LONG_LONG__ +typedef uint64_t u64; +#endif +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; + + +#undef __HAVE_LONG_LONG__ + +#endif /* ARM_STDINT_H */ diff --git a/src/arch/armv7/include/system.h b/src/arch/armv7/include/system.h new file mode 100644 index 0000000..3e14fdb --- /dev/null +++ b/src/arch/armv7/include/system.h @@ -0,0 +1,123 @@ +/* FIXME(dhendrix): This is split out from asm/system.h. */ +#ifndef SYSTEM_H_ +#define SYSTEM_H_ + +#define CPU_ARCH_UNKNOWN 0 +#define CPU_ARCH_ARMv3 1 +#define CPU_ARCH_ARMv4 2 +#define CPU_ARCH_ARMv4T 3 +#define CPU_ARCH_ARMv5 4 +#define CPU_ARCH_ARMv5T 5 +#define CPU_ARCH_ARMv5TE 6 +#define CPU_ARCH_ARMv5TEJ 7 +#define CPU_ARCH_ARMv6 8 +#define CPU_ARCH_ARMv7 9 + +/* + * CR1 bits (CP#15 CR1) + */ +#define CR_M (1 << 0) /* MMU enable */ +#define CR_A (1 << 1) /* Alignment abort enable */ +#define CR_C (1 << 2) /* Dcache enable */ +#define CR_W (1 << 3) /* Write buffer enable */ +#define CR_P (1 << 4) /* 32-bit exception handler */ +#define CR_D (1 << 5) /* 32-bit data address range */ +#define CR_L (1 << 6) /* Implementation defined */ +#define CR_B (1 << 7) /* Big endian */ +#define CR_S (1 << 8) /* System MMU protection */ +#define CR_R (1 << 9) /* ROM MMU protection */ +#define CR_F (1 << 10) /* Implementation defined */ +#define CR_Z (1 << 11) /* Implementation defined */ +#define CR_I (1 << 12) /* Icache enable */ +#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */ +#define CR_RR (1 << 14) /* Round Robin cache replacement */ +#define CR_L4 (1 << 15) /* LDR pc can set T bit */ +#define CR_DT (1 << 16) +#define CR_IT (1 << 18) +#define CR_ST (1 << 19) +#define CR_FI (1 << 21) /* Fast interrupt (lower latency mode) */ +#define CR_U (1 << 22) /* Unaligned access operation */ +#define CR_XP (1 << 23) /* Extended page tables */ +#define CR_VE (1 << 24) /* Vectored interrupts */ +#define CR_EE (1 << 25) /* Exception (Big) Endian */ +#define CR_TRE (1 << 28) /* TEX remap enable */ +#define CR_AFE (1 << 29) /* Access flag enable */ +#define CR_TE (1 << 30) /* Thumb exception enable */ + +/* + * This is used to ensure the compiler did actually allocate the register we + * asked it for some inline assembly sequences. Apparently we can't trust + * the compiler from one version to another so a bit of paranoia won't hurt. + * This string is meant to be concatenated with the inline asm string and + * will cause compilation to stop on mismatch. + * (for details, see gcc PR 15089) + */ +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" + +#define isb() __asm__ __volatile__ ("" : : : "memory") + +#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); + +#define arch_align_stack(x) (x) + +#ifndef __ASSEMBLY__ +static inline unsigned int get_cr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc"); + return val; +} + +static inline void set_cr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR" + : : "r" (val) : "cc"); + isb(); +} + +/* options available for data cache on each page */ +enum dcache_option { + DCACHE_OFF, + DCACHE_WRITETHROUGH, + DCACHE_WRITEBACK, +}; + +/* Size of an MMU section */ +enum { + MMU_SECTION_SHIFT = 20, + MMU_SECTION_SIZE = 1 << MMU_SECTION_SHIFT, +}; + +/** + * Change the cache settings for a region. + * + * \param start start address of memory region to change + * \param size size of memory region to change + * \param option dcache option to select + */ +void mmu_set_region_dcache(unsigned long start, int size, + enum dcache_option option); + +/** + * Register an update to the page tables, and flush the TLB + * + * \param start start address of update in page table + * \param stop stop address of update in page table + */ +void mmu_page_table_flush(unsigned long start, unsigned long stop); + +void dram_bank_mmu_setup(unsigned long start, unsigned long size); + +void arm_init_before_mmu(void); + + /* + * FIXME: sdelay, sr32, and wait_on_value originally came from + * arch/arm/cpu/armv7/exynos5/setup.h in u-boot but do not seem + * specific to exynos5... + */ +void sdelay(unsigned long loops); +void sr32(void *addr, u32 start_bit, u32 num_bits, u32 value); +u32 wait_on_value(u32 read_bit_mask, u32 match_value, void *read_addr, + u32 bound); +#endif // __ASSEMBLY__ +#endif /* SYSTEM_H_ */ diff --git a/src/arch/armv7/include/utils.h b/src/arch/armv7/include/utils.h new file mode 100644 index 0000000..828b86c --- /dev/null +++ b/src/arch/armv7/include/utils.h @@ -0,0 +1,56 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V aneesh@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +static inline s32 log_2_n_round_up(u32 n) +{ + s32 log2n = -1; + u32 temp = n; + + while (temp) { + log2n++; + temp >>= 1; + } + + if (n & (n - 1)) + return log2n + 1; /* not power of 2 - round up */ + else + return log2n; /* power of 2 */ +} + +static inline s32 log_2_n_round_down(u32 n) +{ + s32 log2n = -1; + u32 temp = n; + + while (temp) { + log2n++; + temp >>= 1; + } + + return log2n; +} + +#endif diff --git a/src/arch/armv7/ldscript_failover.lb b/src/arch/armv7/ldscript_failover.lb new file mode 100644 index 0000000..9a3b9ae --- /dev/null +++ b/src/arch/armv7/ldscript_failover.lb @@ -0,0 +1,74 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2006 Advanced Micro Devices, Inc. + * Copyright (C) 2008-2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* We use ELF as output format. So that we can debug the code in some form. */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +MEMORY { + rom : ORIGIN = 0xffff0000, LENGTH = 64K +} + +TARGET(binary) +SECTIONS +{ + /* Symbol ap_sipi_vector must be aligned to 4kB to start AP CPUs + * with Startup IPI message without RAM. Align .rom to next 4 byte + * boundary anyway, so no pad byte appears between _rom and _start. + */ + .bogus ROMLOC_MIN : { + . = CONFIG_SIPI_VECTOR_IN_ROM ? ALIGN(4096) : ALIGN(4); + ROMLOC = .; + } >rom = 0xff + + /* This section might be better named .setup */ + .rom ROMLOC : { + _rom = .; + ap_sipi_vector = .; + *(.rom.text); + *(.rom.data); + *(.rom.data.*); + *(.rodata.*); + _erom = .; + } >rom = 0xff + + /* Allocation reserves extra 16 bytes here. Alignment requirements + * may cause the total size of a section to change when the start + * address gets applied. + */ + ROMLOC_MIN = 0xffffff00 - (_erom - _rom + 16) - + (CONFIG_SIPI_VECTOR_IN_ROM ? 4096 : 0); + + /* Post-check proper SIPI vector. */ + _bogus = ASSERT(!CONFIG_SIPI_VECTOR_IN_ROM || ((ap_sipi_vector & 0x0fff) == 0x0), + "Bad SIPI vector alignment"); + _bogus = ASSERT(!CONFIG_SIPI_VECTOR_IN_ROM || (ap_sipi_vector == CONFIG_AP_SIPI_VECTOR), + "Address mismatch on AP_SIPI_VECTOR"); + + /DISCARD/ : { + *(.comment) + *(.note) + *(.comment.*) + *(.note.*) + *(.iplt) + *(.rel.*) + *(.igot.*) + } +} diff --git a/src/arch/armv7/ldscript_fallback_cbfs.lb b/src/arch/armv7/ldscript_fallback_cbfs.lb new file mode 100644 index 0000000..148f4e3 --- /dev/null +++ b/src/arch/armv7/ldscript_fallback_cbfs.lb @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* We use ELF as output format. So that we can debug the code in some form. */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +TARGET(binary) +SECTIONS +{ + . = CONFIG_ROMBASE; + + /* cut _start into last 64k*/ + _x = .; + . = (_x < (CONFIG_ROMBASE - 0x10000 + CONFIG_ROM_IMAGE_SIZE)) ? (CONFIG_ROMBASE - 0x10000 + CONFIG_ROM_IMAGE_SIZE) : _x; + + /* This section might be better named .setup */ + .rom . : { + _rom = .; + *(.text); + *(.rodata); + *(.rodata.*); + *(.data.*); + . = ALIGN(16); + _erom = .; + } + + /DISCARD/ : { + *(.comment) + *(.note) + *(.comment.*) + *(.note.*) + } + _bogus = ASSERT((SIZEOF(.bss) + SIZEOF(.data)) == 0, "Do not use global variables in romstage"); +} diff --git a/src/arch/armv7/lib/Makefile.inc b/src/arch/armv7/lib/Makefile.inc new file mode 100644 index 0000000..bdf6a64 --- /dev/null +++ b/src/arch/armv7/lib/Makefile.inc @@ -0,0 +1,26 @@ +romstage-y += cache_v7.c +romstage-y += cache-cp15.c +romstage-y += div0.c +romstage-y += div64.S +romstage-y += hang_spl.c +romstage-y += romstage_console.c +romstage-y += syslib.c + +#ramstage-y += printk_init.c +#romstage-y += walkcbfs.S + +ramstage-y += c_start.S + +#ramstage-y += div.c +ramstage-y += div0.c +ramstage-y += div64.S +ramstage-y += hang_spl.c +#ramstage-y += interrupts.c +#ramstage-y += memcpy.S +#ramstage-y += memset.S +#ramstage-y += reset.c +ramstage-y += syslib.c + +#FIXME(dhendrix): should this be a config option? +romstage-y += eabi_compat.c +ramstage-y += eabi_compat.c diff --git a/src/arch/armv7/lib/Makefile.uboot b/src/arch/armv7/lib/Makefile.uboot new file mode 100644 index 0000000..d33d08b --- /dev/null +++ b/src/arch/armv7/lib/Makefile.uboot @@ -0,0 +1,95 @@ +# +# (C) Copyright 2002-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(ARCH).o +LIBGCC = $(obj)libgcc.o + +ifndef CONFIG_SPL_BUILD +GLSOBJS += _ashldi3.o +GLSOBJS += _ashrdi3.o +GLSOBJS += _divsi3.o +GLSOBJS += _lshrdi3.o +GLSOBJS += _modsi3.o +GLSOBJS += _udivsi3.o +GLSOBJS += _umodsi3.o +ifdef CONFIG_CHROMEOS +GLSOBJS += _uldivmod.o +endif + +GLCOBJS += div0.o + +ifeq ($(CONFIG_SYS_GENERIC_BOARD),) +COBJS-y += board.o +endif +COBJS-y += bootm.o +COBJS-y += cache.o +COBJS-y += cache-cp15.o +COBJS-$(CONFIG_SYS_L2_PL310) += cache-pl310.o +SOBJS-$(CONFIG_USE_ARCH_MEMSET) += memset.o +SOBJS-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.o +else # CONFIG_SPL_BUILD +ifeq ($(CONFIG_SYS_GENERIC_BOARD),) +COBJS-y += hang_spl.o +endif +endif # CONFIG_SPL_BUILD + +COBJS-y += interrupts.o +COBJS-y += reset.o + +SRCS := $(GLSOBJS:.o=.S) $(GLCOBJS:.o=.c) \ + $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +LGOBJS := $(addprefix $(obj),$(GLSOBJS)) \ + $(addprefix $(obj),$(GLCOBJS)) + +# Always build libarm.o +TARGETS := $(LIB) + +# Build private libgcc only when asked for +ifdef USE_PRIVATE_LIBGCC +TARGETS += $(LIBGCC) +endif + +# For EABI conformant tool chains, provide eabi_compat() +ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS))) +TARGETS += $(obj)eabi_compat.o +endif + +all: $(TARGETS) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +$(LIBGCC): $(obj).depend $(LGOBJS) + $(call cmd_link_o_target, $(LGOBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/src/arch/armv7/lib/_divsi3.S b/src/arch/armv7/lib/_divsi3.S new file mode 100644 index 0000000..cfbadb2 --- /dev/null +++ b/src/arch/armv7/lib/_divsi3.S @@ -0,0 +1,142 @@ + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + .align 5 +.globl __divsi3 +.globl __aeabi_idiv +__divsi3: +__aeabi_idiv: + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-4]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #4 diff --git a/src/arch/armv7/lib/_udivsi3.S b/src/arch/armv7/lib/_udivsi3.S new file mode 100644 index 0000000..1309802 --- /dev/null +++ b/src/arch/armv7/lib/_udivsi3.S @@ -0,0 +1,93 @@ +/* # 1 "libgcc1.S" */ +@ libgcc1 routines for ARM cpu. +@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) +dividend .req r0 +divisor .req r1 +result .req r2 +curbit .req r3 +/* ip .req r12 */ +/* sp .req r13 */ +/* lr .req r14 */ +/* pc .req r15 */ + .text + .globl __udivsi3 + .type __udivsi3 ,function + .globl __aeabi_uidiv + .type __aeabi_uidiv ,function + .align 0 + __udivsi3: + __aeabi_uidiv: + cmp divisor, #0 + beq Ldiv0 + mov curbit, #1 + mov result, #0 + cmp dividend, divisor + bcc Lgot_result +Loop1: + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, #0x10000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #4 + movcc curbit, curbit, lsl #4 + bcc Loop1 +Lbignum: + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, #0x80000000 + cmpcc divisor, dividend + movcc divisor, divisor, lsl #1 + movcc curbit, curbit, lsl #1 + bcc Lbignum +Loop3: + @ Test for possible subtractions, and note which bits + @ are done in the result. On the final pass, this may subtract + @ too much from the dividend, but the result will be ok, since the + @ "bit" will have been shifted out at the bottom. + cmp dividend, divisor + subcs dividend, dividend, divisor + orrcs result, result, curbit + cmp dividend, divisor, lsr #1 + subcs dividend, dividend, divisor, lsr #1 + orrcs result, result, curbit, lsr #1 + cmp dividend, divisor, lsr #2 + subcs dividend, dividend, divisor, lsr #2 + orrcs result, result, curbit, lsr #2 + cmp dividend, divisor, lsr #3 + subcs dividend, dividend, divisor, lsr #3 + orrcs result, result, curbit, lsr #3 + cmp dividend, #0 @ Early termination? + movnes curbit, curbit, lsr #4 @ No, any more bits to do? + movne divisor, divisor, lsr #4 + bne Loop3 +Lgot_result: + mov r0, result + mov pc, lr +Ldiv0: + str lr, [sp, #-4]! + bl __div0 (PLT) + mov r0, #0 @ about as wrong as it could be + ldmia sp!, {pc} + .size __udivsi3 , . - __udivsi3 + +.globl __aeabi_uidivmod +__aeabi_uidivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +.globl __aeabi_idivmod +__aeabi_idivmod: + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr diff --git a/src/arch/armv7/lib/_uldivmod.S b/src/arch/armv7/lib/_uldivmod.S new file mode 100644 index 0000000..65e4fa8 --- /dev/null +++ b/src/arch/armv7/lib/_uldivmod.S @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +/* + * A, Q = r0 + (r1 << 32) + * B, R = r2 + (r3 << 32) + * A / B = Q ... R + */ + + .text + .global __aeabi_uldivmod + .type __aeabi_uldivmod, function + .align 0 + +A_0 .req r0 +A_1 .req r1 +B_0 .req r2 +B_1 .req r3 +C_0 .req r4 +C_1 .req r5 +D_0 .req r6 +D_1 .req r7 + +Q_0 .req r0 +Q_1 .req r1 +R_0 .req r2 +R_1 .req r3 + +__aeabi_uldivmod: + stmfd sp!, {r4, r5, r6, r7, lr} + @ Test if B == 0 + orrs ip, B_0, B_1 @ Z set -> B == 0 + beq L_div_by_0 + @ Test if B is power of 2: (B & (B - 1)) == 0 + subs C_0, B_0, #1 + sbc C_1, B_1, #0 + tst C_0, B_0 + tsteq B_1, C_1 + beq L_pow2 + @ Test if A_1 == B_1 == 0 + orrs ip, A_1, B_1 + beq L_div_32_32 + +L_div_64_64: + mov C_0, #1 + mov C_1, #0 + @ D_0 = clz A + teq A_1, #0 + clz D_0, A_1 + clzeq ip, A_0 + addeq D_0, D_0, ip + @ D_1 = clz B + teq B_1, #0 + clz D_1, B_1 + clzeq ip, B_0 + addeq D_1, D_1, ip + @ if clz B - clz A > 0 + subs D_0, D_1, D_0 + bls L_done_shift + @ B <<= (clz B - clz A) + subs D_1, D_0, #32 + rsb ip, D_0, #32 + movmi B_1, B_1, lsl D_0 + orrmi B_1, B_1, B_0, lsr ip + movpl B_1, B_0, lsl D_1 + mov B_0, B_0, lsl D_0 + @ C = 1 << (clz B - clz A) + movmi C_1, C_1, lsl D_0 + orrmi C_1, C_1, C_0, lsr ip + movpl C_1, C_0, lsl D_1 + mov C_0, C_0, lsl D_0 +L_done_shift: + mov D_0, #0 + mov D_1, #0 + @ C: current bit; D: result +L_subtract: + @ if A >= B + cmp A_1, B_1 + cmpeq A_0, B_0 + bcc L_update + @ A -= B + subs A_0, A_0, B_0 + sbc A_1, A_1, B_1 + @ D |= C + orr D_0, D_0, C_0 + orr D_1, D_1, C_1 +L_update: + @ if A == 0: break + orrs ip, A_1, A_0 + beq L_exit + @ C >>= 1 + movs C_1, C_1, lsr #1 + movs C_0, C_0, rrx + @ if C == 0: break + orrs ip, C_1, C_0 + beq L_exit + @ B >>= 1 + movs B_1, B_1, lsr #1 + mov B_0, B_0, rrx + b L_subtract +L_exit: + @ Note: A, B & Q, R are aliases + mov R_0, A_0 + mov R_1, A_1 + mov Q_0, D_0 + mov Q_1, D_1 + ldmfd sp!, {r4, r5, r6, r7, pc} + +L_div_32_32: + @ Note: A_0 & r0 are aliases + @ Q_1 r1 + mov r1, B_0 + bl __aeabi_uidivmod + mov R_0, r1 + mov R_1, #0 + mov Q_1, #0 + ldmfd sp!, {r4, r5, r6, r7, pc} + +L_pow2: + @ Note: A, B and Q, R are aliases + @ R = A & (B - 1) + and C_0, A_0, C_0 + and C_1, A_1, C_1 + @ Q = A >> log2(B) + @ Note: B must not be 0 here! + clz D_0, B_0 + add D_1, D_0, #1 + rsbs D_0, D_0, #31 + bpl L_1 + clz D_0, B_1 + rsb D_0, D_0, #31 + mov A_0, A_1, lsr D_0 + add D_0, D_0, #32 +L_1: + movpl A_0, A_0, lsr D_0 + orrpl A_0, A_0, A_1, lsl D_1 + mov A_1, A_1, lsr D_0 + @ Mov back C to R + mov R_0, C_0 + mov R_1, C_1 + ldmfd sp!, {r4, r5, r6, r7, pc} + +L_div_by_0: + bl __div0 + @ As wrong as it could be + mov Q_0, #0 + mov Q_1, #0 + mov R_0, #0 + mov R_1, #0 + ldmfd sp!, {r4, r5, r6, r7, pc} diff --git a/src/arch/armv7/lib/bootm.c b/src/arch/armv7/lib/bootm.c new file mode 100644 index 0000000..afe1b70 --- /dev/null +++ b/src/arch/armv7/lib/bootm.c @@ -0,0 +1,405 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger mgroeger@sysgo.de + * + * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <command.h> +#include <image.h> +#include <u-boot/zlib.h> +#include <arch/byteorder.h> +#include <fdt.h> +#include <libfdt.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +#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); + +# if defined (CONFIG_VFD) || defined (CONFIG_LCD) +# if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) +static void setup_videolfb_tag (gd_t *gd); +# endif +# endif + +static struct tag *params; +#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ + +static ulong get_sp(void); +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) +static int bootm_linux_fdt(int machid, bootm_headers_t *images); +#endif + +void arch_lmb_reserve(struct lmb *lmb) +{ + ulong sp; + + /* + * Booting a (Linux) kernel image + * + * Allocate space for command line and board info - the + * address should be as high as possible within the reach of + * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused + * memory, which means far enough below the current stack + * pointer. + */ + sp = get_sp(); + debug("## Current stack ends at 0x%08lx ", sp); + + /* adjust sp by 1K to be safe */ + sp -= 1024; + lmb_reserve(lmb, sp, + gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); +} + +static void announce_and_cleanup(void) +{ + printf("\nStarting kernel ...\n\n"); + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); + +#ifdef CONFIG_USB_DEVICE + { + extern void udc_disconnect(void); + udc_disconnect(); + } +#endif + cleanup_before_linux(); +} + +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 (*kernel_entry)(int zero, int arch, uint params); + +#ifdef CONFIG_CMDLINE_TAG + char *commandline = getenv ("bootargs"); +#endif + + if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) + return 1; + + s = getenv ("machid"); + if (s) { + machid = simple_strtoul (s, NULL, 16); + printf ("Using machid 0x%x from environment\n", machid); + } + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) + if (images->ft_len) + return bootm_linux_fdt(machid, images); +#endif + + kernel_entry = (void (*)(int, int, uint))images->ep; + + debug ("## Transferring control to Linux (at address %08lx) ...\n", + (ulong) kernel_entry); + +#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 +#if defined (CONFIG_VFD) || defined (CONFIG_LCD) +#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) + setup_videolfb_tag ((gd_t *) gd); +#endif +#endif + setup_end_tag (bd); +#endif + + announce_and_cleanup(); + + kernel_entry(0, machid, bd->bi_boot_params); + /* does not return */ + + return 1; +} + +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) +static int fixup_memory_node(void *blob) +{ + bd_t *bd = gd->bd; + int bank; + u64 start[CONFIG_NR_DRAM_BANKS]; + u64 size[CONFIG_NR_DRAM_BANKS]; + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + start[bank] = bd->bi_dram[bank].start; + size[bank] = bd->bi_dram[bank].size; + } + + return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); +} + +static int bootm_linux_fdt(int machid, bootm_headers_t *images) +{ + ulong rd_len; + void (*kernel_entry)(int zero, int dt_machid, void *dtblob); + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + struct lmb *lmb = &images->lmb; + int ret; + + kernel_entry = (void (*)(int, int, void *))images->ep; + + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + rd_len = images->rd_end - images->rd_start; + ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, + initrd_start, initrd_end); + if (ret) + return ret; + +#ifdef CONFIG_OF_BOARD_SETUP + /* Try to reserve 1024 bytes for board fixups */ + if (!fdt_open_into(*of_flat_tree, *of_flat_tree, of_size + 1024)) + of_size += 1024; + /* Call the board-specific fixup routine */ + ft_board_setup(*of_flat_tree, gd->bd); +#endif +#ifdef CONFIG_OF_UPDATE_FDT_BEFORE_BOOT + /* this must be earlier than boot_relocate_fdt */ + ret = fit_update_fdt_before_boot(*of_flat_tree, &of_size); + if (ret) + return ret; +#endif + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return ret; + + debug("## Transferring control to Linux (at address %08lx) ...\n", + (ulong) kernel_entry); + + fdt_chosen(*of_flat_tree, 1); + + fixup_memory_node(*of_flat_tree); + + fdt_fixup_ethernet(*of_flat_tree); + + fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); + + announce_and_cleanup(); + + kernel_entry(0, machid, *of_flat_tree); + /* does not return */ + + return 1; +} +#endif + +#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); + +#if defined (ATAG_CORE_FLAGS) && \ + defined (ATAG_PAGE_SIZE) && \ + defined (ATAG_CORE_RDEV) + params->u.core.flags = ATAG_CORE_FLAGS; + params->u.core.pagesize = ATAG_PAGE_SIZE; + params->u.core.rootdev = ATAG_CORE_RDEV; +#else + params->u.core.flags = 0; + params->u.core.pagesize = 0; + params->u.core.rootdev = 0; +#endif + + 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 */ + +#if defined (CONFIG_VFD) || defined (CONFIG_LCD) +#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) +extern ulong calc_fbsize (void); +static void setup_videolfb_tag (gd_t *gd) +{ + /* An ATAG_VIDEOLFB node tells the kernel where and how large + * the framebuffer for video was allocated (among other things). + * Note that a _physical_ address is passed ! + * + * We only use it to pass the address and size, the other entries + * in the tag_videolfb are not of interest. + */ + params->hdr.tag = ATAG_VIDEOLFB; + params->hdr.size = tag_size (tag_videolfb); + + params->u.videolfb.lfb_base = (u32) gd->fb_base; + /* Fb size is calculated according to parameters for our panel + */ + params->u.videolfb.lfb_size = calc_fbsize(); + + params = tag_next (params); +} +#endif /* CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED */ +#endif /* CONFIG_VFD || CONFIG_LCD */ + +#ifdef CONFIG_SERIAL_TAG +void setup_serial_tag (struct tag **tmp) +{ + struct tag *params = *tmp; + struct tag_serialnr serialnr; + void get_board_serial(struct tag_serialnr *serialnr); + + 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 = 0; + 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 */ + +static ulong get_sp(void) +{ + ulong ret; + + asm("mov %0, sp" : "=r"(ret) : ); + return ret; +} diff --git a/src/arch/armv7/lib/c_start.S b/src/arch/armv7/lib/c_start.S new file mode 100644 index 0000000..b6e7462 --- /dev/null +++ b/src/arch/armv7/lib/c_start.S @@ -0,0 +1,9 @@ +.section ".text" +.globl _start +_start: + bl _hardwaremain + +_hardwaremain: .word hardwaremain +@ .word hardwaremain + + diff --git a/src/arch/armv7/lib/cache-cp15.c b/src/arch/armv7/lib/cache-cp15.c new file mode 100644 index 0000000..1786725 --- /dev/null +++ b/src/arch/armv7/lib/cache-cp15.c @@ -0,0 +1,304 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* FIXME(dhendrix): clean-up weak symbols if it looks unlikely we'll + want to override them with anything other than what's in cache_v7. */ +#include <common.h> +#include <stdlib.h> +#include <system.h> +#include <global_data.h> + +#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) + +DECLARE_GLOBAL_DATA_PTR; + +#if 0 +void __arm_init_before_mmu(void) +{ +} +void arm_init_before_mmu(void) + __attribute__((weak, alias("__arm_init_before_mmu"))); +#endif + +static void cp_delay (void) +{ + volatile int i; + + /* copro seems to need some delay between reading and writing */ + for (i = 0; i < 100; i++) + nop(); + asm volatile("" : : : "memory"); +} + +static void set_section_dcache(int section, enum dcache_option option) +{ + u32 value = section << MMU_SECTION_SHIFT | (3 << 10); +// u32 *page_table = (u32 *)gd->tlb_addr; + u32 *page_table; + unsigned int tlb_addr; + unsigned int tlb_size = 4096 * 4; + + /* + * FIXME(dhendrix): This calculation is from arch/arm/lib/board.c + * in u-boot. We may need to subtract more due to logging. + */ + tlb_addr = (CONFIG_SYS_SDRAM_BASE + (CONFIG_DRAM_SIZE_MB << 20UL)); + tlb_addr -= tlb_size; + /* round down to next 64KB limit */ + tlb_addr &= ~(0x10000 - 1); + page_table = (u32 *)tlb_addr; + + switch (option) { + case DCACHE_WRITETHROUGH: + value |= 0x1a; + break; + + case DCACHE_WRITEBACK: + value |= 0x1e; + break; + + case DCACHE_OFF: + value |= 0x12; + break; + } + + page_table[section] = value; +} + +#if 0 +void __mmu_page_table_flush(unsigned long start, unsigned long stop) +{ + debug("%s: Warning: not implemented\n", __func__); +} +#endif + +#if 0 +void mmu_page_table_flush(unsigned long start, unsigned long stop) + __attribute__((weak, alias("__mmu_page_table_flush"))); +#endif + +void mmu_set_region_dcache(unsigned long start, int size, enum dcache_option option) +{ + u32 *page_table = (u32 *)gd->tlb_addr; + u32 upto, end; + + end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT; + start = start >> MMU_SECTION_SHIFT; + debug("mmu_set_region_dcache start=%x, size=%x, option=%d\n", + start, size, option); + for (upto = start; upto < end; upto++) + set_section_dcache(upto, option); + mmu_page_table_flush((u32)&page_table[start], (u32)&page_table[end]); +} + +#if 0 +static inline void dram_bank_mmu_setup(int bank) +{ +// bd_t *bd = gd->bd; + int i; + + debug("%s: bank: %d\n", __func__, bank); + for (i = bd->bi_dram[bank].start >> 20; + i < (bd->bi_dram[bank].start + bd->bi_dram[bank].size) >> 20; + i++) { +#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH) + set_section_dcache(i, DCACHE_WRITETHROUGH); +#else + set_section_dcache(i, DCACHE_WRITEBACK); +#endif + } +} +#endif + +/* FIXME(dhendrix): modified to take arguments from the caller (mainboard's + romstage.c) so it doesn't rely on global data struct */ +/** + * dram_bank_mmu_set - set up the data cache policy for a given dram bank + * + * @start: virtual address start of bank + * @size: size of bank (in bytes) + */ +inline void dram_bank_mmu_setup(unsigned long start, unsigned long size) +{ + int i; + + debug("%s: bank: %d\n", __func__, bank); + for (i = start >> 20; i < (start + size) >> 20; i++) { +#if defined(CONFIG_ARM_DCACHE_POLICY_WRITEBACK) + set_section_dcache(i, DCACHE_WRITEBACK); +#elif defined(CONFIG_ARM_DCACHE_POLICY_WRITETHROUGH) + set_section_dcache(i, DCACHE_WRITETHROUGH); +#else +#error "Must define dcache policy." +#endif + } +} + +/* to activate the MMU we need to set up virtual memory: use 1M areas */ +static inline void mmu_setup(void) +{ + int i; + u32 reg; + + arm_init_before_mmu(); + /* Set up an identity-mapping for all 4GB, rw for everyone */ + for (i = 0; i < 4096; i++) + set_section_dcache(i, DCACHE_OFF); + + /* FIXME(dhendrix): u-boot's global data struct was used here... */ +#if 0 + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + dram_bank_mmu_setup(i); + } +#endif +#if 0 + /* comes from board's romstage.c, since we need to know which + ranges to setup */ + mainboard_setup_mmu(); +#endif + dram_bank_mmu_setup(CONFIG_SYS_SDRAM_BASE, CONFIG_DRAM_SIZE_MB << 20); + + /* Copy the page table address to cp15 */ + asm volatile("mcr p15, 0, %0, c2, c0, 0" + : : "r" (gd->tlb_addr) : "memory"); + /* Set the access control to all-supervisor */ + asm volatile("mcr p15, 0, %0, c3, c0, 0" + : : "r" (~0)); + /* and enable the mmu */ + reg = get_cr(); /* get control reg. */ + cp_delay(); + set_cr(reg | CR_M); +} + +static int mmu_enabled(void) +{ + return get_cr() & CR_M; +} + +/* cache_bit must be either CR_I or CR_C */ +static void cache_enable(uint32_t cache_bit) +{ + uint32_t reg; + + /* The data cache is not active unless the mmu is enabled too */ + if ((cache_bit == CR_C) && !mmu_enabled()) + mmu_setup(); + reg = get_cr(); /* get control reg. */ + cp_delay(); + set_cr(reg | cache_bit); +} + +/* + * Big hack warning! + * + * Devs like to compile with -O0 to get a nice debugging illusion. But this + * function does not survive that since -O0 causes the compiler to read the + * PC back from the stack after the dcache flush. Might it be possible to fix + * this by flushing the write buffer? + */ +static void cache_disable(uint32_t cache_bit) __attribute__ ((optimize(2))); + +/* cache_bit must be either CR_I or CR_C */ +static void cache_disable(uint32_t cache_bit) +{ + uint32_t reg; + + if (cache_bit == CR_C) { + /* if cache isn;t enabled no need to disable */ + reg = get_cr(); + if ((reg & CR_C) != CR_C) + return; + /* if disabling data cache, disable mmu too */ + cache_bit |= CR_M; + } + reg = get_cr(); + cp_delay(); + if (cache_bit == (CR_C | CR_M)) + flush_dcache_all(); + set_cr(reg & ~cache_bit); +} +#endif + +#ifdef CONFIG_SYS_ICACHE_OFF +void icache_enable (void) +{ + return; +} + +void icache_disable (void) +{ + return; +} + +int icache_status (void) +{ + return 0; /* always off */ +} +#else +void icache_enable(void) +{ + cache_enable(CR_I); +} + +void icache_disable(void) +{ + cache_disable(CR_I); +} + +int icache_status(void) +{ + return (get_cr() & CR_I) != 0; +} +#endif + +#ifdef CONFIG_SYS_DCACHE_OFF +void dcache_enable (void) +{ + return; +} + +void dcache_disable (void) +{ + return; +} + +int dcache_status (void) +{ + return 0; /* always off */ +} +#else +void dcache_enable(void) +{ + cache_enable(CR_C); +} + +void dcache_disable(void) +{ + cache_disable(CR_C); +} + +int dcache_status(void) +{ + return (get_cr() & CR_C) != 0; +} +#endif diff --git a/src/arch/armv7/lib/cache_v7.c b/src/arch/armv7/lib/cache_v7.c new file mode 100644 index 0000000..13bb59f --- /dev/null +++ b/src/arch/armv7/lib/cache_v7.c @@ -0,0 +1,427 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * Aneesh V aneesh@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <armv7.h> +#include <common.h> +#include <system.h> +#include <utils.h> +#include <console/console.h> + +#define ARMV7_DCACHE_INVAL_ALL 1 +#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2 +#define ARMV7_DCACHE_INVAL_RANGE 3 +#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4 + +#ifndef CONFIG_SYS_DCACHE_OFF +/* + * Write the level and type you want to Cache Size Selection Register(CSSELR) + * to get size details from Current Cache Size ID Register(CCSIDR) + */ +static void set_csselr(u32 level, u32 type) +{ u32 csselr = level << 1 | type; + + /* Write to Cache Size Selection Register(CSSELR) */ + asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr)); +} + +static u32 get_ccsidr(void) +{ + u32 ccsidr; + + /* Read current CP15 Cache Size ID Register */ + asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr)); + return ccsidr; +} + +static u32 get_clidr(void) +{ + u32 clidr; + + /* Read current CP15 Cache Level ID Register */ + asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr)); + return clidr; +} + +static void v7_inval_dcache_level_setway(u32 level, u32 num_sets, + u32 num_ways, u32 way_shift, + u32 log2_line_len) +{ + int way, set, setway; + + /* + * For optimal assembly code: + * a. count down + * b. have bigger loop inside + */ + for (way = num_ways - 1; way >= 0 ; way--) { + for (set = num_sets - 1; set >= 0; set--) { + setway = (level << 1) | (set << log2_line_len) | + (way << way_shift); + /* Invalidate data/unified cache line by set/way */ + asm volatile (" mcr p15, 0, %0, c7, c6, 2" + : : "r" (setway)); + } + } + /* DSB to make sure the operation is complete */ + CP15DSB; +} + +static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets, + u32 num_ways, u32 way_shift, + u32 log2_line_len) +{ + int way, set, setway; + + /* + * For optimal assembly code: + * a. count down + * b. have bigger loop inside + */ + for (way = num_ways - 1; way >= 0 ; way--) { + for (set = num_sets - 1; set >= 0; set--) { + setway = (level << 1) | (set << log2_line_len) | + (way << way_shift); + /* + * Clean & Invalidate data/unified + * cache line by set/way + */ + asm volatile (" mcr p15, 0, %0, c7, c14, 2" + : : "r" (setway)); + } + } + /* DSB to make sure the operation is complete */ + CP15DSB; +} + +static void v7_maint_dcache_level_setway(u32 level, u32 operation) +{ + u32 ccsidr; + u32 num_sets, num_ways, log2_line_len, log2_num_ways; + u32 way_shift; + + set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED); + + ccsidr = get_ccsidr(); + + log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> + CCSIDR_LINE_SIZE_OFFSET) + 2; + /* Converting from words to bytes */ + log2_line_len += 2; + + num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >> + CCSIDR_ASSOCIATIVITY_OFFSET) + 1; + num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >> + CCSIDR_NUM_SETS_OFFSET) + 1; + /* + * According to ARMv7 ARM number of sets and number of ways need + * not be a power of 2 + */ + log2_num_ways = log_2_n_round_up(num_ways); + + way_shift = (32 - log2_num_ways); + if (operation == ARMV7_DCACHE_INVAL_ALL) { + v7_inval_dcache_level_setway(level, num_sets, num_ways, + way_shift, log2_line_len); + } else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) { + v7_clean_inval_dcache_level_setway(level, num_sets, num_ways, + way_shift, log2_line_len); + } +} + +static void v7_maint_dcache_all(u32 operation) +{ + u32 level, cache_type, level_start_bit = 0; + + u32 clidr = get_clidr(); + + for (level = 0; level < 7; level++) { + cache_type = (clidr >> level_start_bit) & 0x7; + if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) || + (cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) || + (cache_type == ARMV7_CLIDR_CTYPE_UNIFIED)) + v7_maint_dcache_level_setway(level, operation); + level_start_bit += 3; + } +} + +static void v7_dcache_clean_inval_range(u32 start, + u32 stop, u32 line_len) +{ + u32 mva; + + /* Align start to cache line boundary */ + start &= ~(line_len - 1); + for (mva = start; mva < stop; mva = mva + line_len) { + /* DCCIMVAC - Clean & Invalidate data cache by MVA to PoC */ + asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva)); + } +} + +static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len) +{ + u32 mva; + + /* + * If start address is not aligned to cache-line do not + * invalidate the first cache-line + */ + if (start & (line_len - 1)) { + printk(BIOS_ERR, "%s - start address is not aligned - 0x%08x\n", + __func__, start); + /* move to next cache line */ + start = (start + line_len - 1) & ~(line_len - 1); + } + + /* + * If stop address is not aligned to cache-line do not + * invalidate the last cache-line + */ + if (stop & (line_len - 1)) { + printk(BIOS_ERR, "%s - stop address is not aligned - 0x%08x\n", + __func__, stop); + /* align to the beginning of this cache line */ + stop &= ~(line_len - 1); + } + + for (mva = start; mva < stop; mva = mva + line_len) { + /* DCIMVAC - Invalidate data cache by MVA to PoC */ + asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva)); + } +} + +static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op) +{ + u32 line_len = dcache_get_line_size(); + + switch (range_op) { + case ARMV7_DCACHE_CLEAN_INVAL_RANGE: + v7_dcache_clean_inval_range(start, stop, line_len); + break; + case ARMV7_DCACHE_INVAL_RANGE: + v7_dcache_inval_range(start, stop, line_len); + break; + } + + /* DSB to make sure the operation is complete */ + CP15DSB; +} + +/* Invalidate TLB */ +static void v7_inval_tlb(void) +{ + /* Invalidate entire unified TLB */ + asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0)); + /* Invalidate entire data TLB */ + asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0)); + /* Invalidate entire instruction TLB */ + asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0)); + /* Full system DSB - make sure that the invalidation is complete */ + CP15DSB; + /* Full system ISB - make sure the instruction stream sees it */ + CP15ISB; +} + +ulong dcache_get_line_size(void) +{ + u32 line_len, ccsidr; + + ccsidr = get_ccsidr(); + line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >> + CCSIDR_LINE_SIZE_OFFSET) + 2; + /* Converting from words to bytes */ + line_len += 2; + /* converting from log2(linelen) to linelen */ + line_len = 1 << line_len; + + return line_len; +} + +void invalidate_dcache_all(void) +{ + v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL); + + v7_outer_cache_inval_all(); +} + +/* + * Performs a clean & invalidation of the entire data cache + * at all levels + */ +void flush_dcache_all(void) +{ + v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL); + + v7_outer_cache_flush_all(); +} + +/* + * Invalidates range in all levels of D-cache/unified cache used: + * Affects the range [start, stop - 1] + */ +void invalidate_dcache_range(unsigned long start, unsigned long stop) +{ + + v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE); + + v7_outer_cache_inval_range(start, stop); +} + +/* + * Flush range(clean & invalidate) from all levels of D-cache/unified + * cache used: + * Affects the range [start, stop - 1] + */ +void flush_dcache_range(unsigned long start, unsigned long stop) +{ + v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE); + + v7_outer_cache_flush_range(start, stop); +} + +void arm_init_before_mmu(void) +{ + v7_outer_cache_enable(); + invalidate_dcache_all(); + v7_inval_tlb(); +} + +void mmu_page_table_flush(unsigned long start, unsigned long stop) +{ + flush_dcache_range(start, stop); + v7_inval_tlb(); +} + +/* + * Flush range from all levels of d-cache/unified-cache used: + * Affects the range [start, start + size - 1] + */ +void flush_cache(unsigned long start, unsigned long size) +{ + flush_dcache_range(start, start + size); +} +#else /* #ifndef CONFIG_SYS_DCACHE_OFF */ +ulong dcache_get_line_size(void) +{ + return 0; +} + +void invalidate_dcache_all(void) +{ +} + +void flush_dcache_all(void) +{ +} + +void invalidate_dcache_range(unsigned long start, unsigned long stop) +{ +} + +void flush_dcache_range(unsigned long start, unsigned long stop) +{ +} + +void arm_init_before_mmu(void) +{ +} + +void flush_cache(unsigned long start, unsigned long size) +{ +} + +void mmu_page_table_flush(unsigned long start, unsigned long stop) +{ +} + +#endif /* #ifndef CONFIG_SYS_DCACHE_OFF */ + +#ifndef CONFIG_SYS_ICACHE_OFF +/* Invalidate entire I-cache and branch predictor array */ +void invalidate_icache_all(void) +{ + /* + * Invalidate all instruction caches to PoU. + * Also flushes branch target cache. + */ + asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); + + /* Invalidate entire branch predictor array */ + asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0)); + + /* Full system DSB - make sure that the invalidation is complete */ + CP15DSB; + + /* ISB - make sure the instruction stream sees it */ + CP15ISB; +} +#else +void invalidate_icache_all(void) +{ +} +#endif + +/* + * FIXME(dhendrix): had unexplainable compilation failure of weak symbols + * (in spite of having prototypes and whatnot)... ron's advice is "death + * to weak symbols!" + */ +#if 0 +/* + * Stub implementations for outer cache operations + */ +void __v7_outer_cache_enable(void) +{ +} +void v7_outer_cache_enable(void) + __attribute__((weak, alias("__v7_outer_cache_enable"))); + +void __v7_outer_cache_disable(void) +{ +} +void v7_outer_cache_disable(void) + __attribute__((weak, alias("__v7_outer_cache_disable"))); + +void __v7_outer_cache_flush_all(void) +{ +} +void v7_outer_cache_flush_all(void) + __attribute__((weak, alias("__v7_outer_cache_flush_all"))); + +void __v7_outer_cache_inval_all(void) +{ +} +void v7_outer_cache_inval_all(void) + __attribute__((weak, alias("__v7_outer_cache_inval_all"))); + +void __v7_outer_cache_flush_range(u32 start, u32 end) +{ +} +void v7_outer_cache_flush_range(u32 start, u32 end) + __attribute__((weak, alias("__v7_outer_cache_flush_range"))); + +void __v7_outer_cache_inval_range(u32 start, u32 end) +{ +} +void v7_outer_cache_inval_range(u32 start, u32 end) + __attribute__((weak, alias("__v7_outer_cache_inval_range"))); +#endif diff --git a/src/arch/armv7/lib/div.c b/src/arch/armv7/lib/div.c new file mode 100644 index 0000000..03352dc --- /dev/null +++ b/src/arch/armv7/lib/div.c @@ -0,0 +1,5 @@ +void __div0(void); // called from asm so no need for a prototype in a header +void __div0(void) +{ + // div by zero +} diff --git a/src/arch/armv7/lib/div0.c b/src/arch/armv7/lib/div0.c new file mode 100644 index 0000000..632c247 --- /dev/null +++ b/src/arch/armv7/lib/div0.c @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +void __div0(void); // called from asm so no need for a prototype in a header + +/* Replacement (=dummy) for GNU/Linux division-by zero handler */ +void __div0 (void) +{ + extern void hang (void); + + hang(); +} diff --git a/src/arch/armv7/lib/div64.S b/src/arch/armv7/lib/div64.S new file mode 100644 index 0000000..44edf48 --- /dev/null +++ b/src/arch/armv7/lib/div64.S @@ -0,0 +1,208 @@ +/* + * linux/arch/arm/lib/div64.S + * + * Optimized computation of 64-bit dividend / 32-bit divisor + * + * Author: Nicolas Pitre + * Created: Oct 5, 2003 + * Copyright: Monta Vista Software, Inc. + * + * 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. + */ + +// FIXME +//#include <linux/linkage.h> +#define __LINUX_ARM_ARCH__ 7 + +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +/* + * __do_div64: perform a division with 64-bit dividend and 32-bit divisor. + * + * Note: Calling convention is totally non standard for optimal code. + * This is meant to be used by do_div() from include/asm/div64.h only. + * + * Input parameters: + * xh-xl = dividend (clobbered) + * r4 = divisor (preserved) + * + * Output values: + * yh-yl = result + * xh = remainder + * + * Clobbered regs: xl, ip + */ + + +.globl __do_div64; +.align 4,0x90 +__do_div64: + + @ Test for easy paths first. + subs ip, r4, #1 + bls 9f @ divisor is 0 or 1 + tst ip, r4 + beq 8f @ divisor is power of 2 + + @ See if we need to handle upper 32-bit result. + cmp xh, r4 + mov yh, #0 + blo 3f + + @ Align divisor with upper part of dividend. + @ The aligned divisor is stored in yl preserving the original. + @ The bit position is stored in ip. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz yl, r4 + clz ip, xh + sub yl, yl, ip + mov ip, #1 + mov ip, ip, lsl yl + mov yl, r4, lsl yl + +#else + + mov yl, r4 + mov ip, #1 +1: cmp yl, #0x80000000 + cmpcc yl, xh + movcc yl, yl, lsl #1 + movcc ip, ip, lsl #1 + bcc 1b + +#endif + + @ The division loop for needed upper bit positions. + @ Break out early if dividend reaches 0. +2: cmp xh, yl + orrcs yh, yh, ip + subcss xh, xh, yl + movnes ip, ip, lsr #1 + mov yl, yl, lsr #1 + bne 2b + + @ See if we need to handle lower 32-bit result. +3: cmp xh, #0 + mov yl, #0 + cmpeq xl, r4 + movlo xh, xl + movlo pc, lr + + @ The division loop for lower bit positions. + @ Here we shift remainer bits leftwards rather than moving the + @ divisor for comparisons, considering the carry-out bit as well. + mov ip, #0x80000000 +4: movs xl, xl, lsl #1 + adcs xh, xh, xh + beq 6f + cmpcc xh, r4 +5: orrcs yl, yl, ip + subcs xh, xh, r4 + movs ip, ip, lsr #1 + bne 4b + mov pc, lr + + @ The top part of remainder became zero. If carry is set + @ (the 33th bit) this is a false positive so resume the loop. + @ Otherwise, if lower part is also null then we are done. +6: bcs 5b + cmp xl, #0 + moveq pc, lr + + @ We still have remainer bits in the low part. Bring them up. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz xh, xl @ we know xh is zero here so... + add xh, xh, #1 + mov xl, xl, lsl xh + mov ip, ip, lsr xh + +#else + +7: movs xl, xl, lsl #1 + mov ip, ip, lsr #1 + bcc 7b + +#endif + + @ Current remainder is now 1. It is worthless to compare with + @ divisor at this point since divisor can not be smaller than 3 here. + @ If possible, branch for another shift in the division loop. + @ If no bit position left then we are done. + movs ip, ip, lsr #1 + mov xh, #1 + bne 4b + mov pc, lr + +8: @ Division by a power of 2: determine what that divisor order is + @ then simply shift values around + +#if __LINUX_ARM_ARCH__ >= 5 + + clz ip, r4 + rsb ip, ip, #31 + +#else + + mov yl, r4 + cmp r4, #(1 << 16) + mov ip, #0 + movhs yl, yl, lsr #16 + movhs ip, #16 + + cmp yl, #(1 << 8) + movhs yl, yl, lsr #8 + addhs ip, ip, #8 + + cmp yl, #(1 << 4) + movhs yl, yl, lsr #4 + addhs ip, ip, #4 + + cmp yl, #(1 << 2) + addhi ip, ip, #3 + addls ip, ip, yl, lsr #1 + +#endif + + mov yh, xh, lsr ip + mov yl, xl, lsr ip + rsb ip, ip, #32 + orr yl, yl, xh, lsl ip + mov xh, xl, lsl ip + mov xh, xh, lsr ip + mov pc, lr + + @ eq -> division by 1: obvious enough... +9: moveq yl, xl + moveq yh, xh + moveq xh, #0 + moveq pc, lr + + @ Division by 0: + str lr, [sp, #-8]! + bl __div0 + + @ as wrong as it could be... + mov yl, #0 + mov yh, #0 + mov xh, #0 + ldr pc, [sp], #8 + +@.type __do_div64, @function; +@.size __do_div64, .-__do_div64, + diff --git a/src/arch/armv7/lib/eabi_compat.c b/src/arch/armv7/lib/eabi_compat.c new file mode 100644 index 0000000..5f59892 --- /dev/null +++ b/src/arch/armv7/lib/eabi_compat.c @@ -0,0 +1,33 @@ +/* + * Utility functions needed for (some) EABI conformant tool chains. + * + * (C) Copyright 2009 Wolfgang Denk wd@denx.de + * + * This program is Free Software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + */ + +#include <common.h> + +/* FIXME(dhendrix): prototypes added for assembler */ +int raise (int signum); +int raise (int signum) +{ +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("raise: Signal # %d caught\n", signum); +#endif + return 0; +} + +/* Dummy function to avoid linker complaints */ +void __aeabi_unwind_cpp_pr0(void); +void __aeabi_unwind_cpp_pr0(void) +{ +}; + +void __aeabi_unwind_cpp_pr1(void); +void __aeabi_unwind_cpp_pr1(void) +{ +}; diff --git a/src/arch/armv7/lib/hang_spl.c b/src/arch/armv7/lib/hang_spl.c new file mode 100644 index 0000000..630a57a --- /dev/null +++ b/src/arch/armv7/lib/hang_spl.c @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +#include <hang.h> + +void hang(void) +{ + for (;;); +} diff --git a/src/arch/armv7/lib/id.inc b/src/arch/armv7/lib/id.inc new file mode 100644 index 0000000..4da7024 --- /dev/null +++ b/src/arch/armv7/lib/id.inc @@ -0,0 +1,18 @@ + .section ".id", "a", %progbits + + .globl __id_start +__id_start: +ver: + .asciz COREBOOT_VERSION +vendor: + .asciz CONFIG_MAINBOARD_VENDOR +part: + .asciz CONFIG_MAINBOARD_PART_NUMBER +.long __id_end + CONFIG_ID_SECTION_OFFSET - ver /* Reverse offset to the vendor id */ +.long __id_end + CONFIG_ID_SECTION_OFFSET - vendor /* Reverse offset to the vendor id */ +.long __id_end + CONFIG_ID_SECTION_OFFSET - part /* Reverse offset to the part number */ +.long CONFIG_ROM_SIZE /* Size of this romimage */ + .globl __id_end + +__id_end: +.previous diff --git a/src/arch/armv7/lib/id.lds b/src/arch/armv7/lib/id.lds new file mode 100644 index 0000000..9e31ee6 --- /dev/null +++ b/src/arch/armv7/lib/id.lds @@ -0,0 +1,6 @@ +SECTIONS { + . = (0x100000000 - CONFIG_ID_SECTION_OFFSET) - (__id_end - __id_start); + .id (.): { + *(.id) + } +} diff --git a/src/arch/armv7/lib/interrupts.c b/src/arch/armv7/lib/interrupts.c new file mode 100644 index 0000000..6ee3f26 --- /dev/null +++ b/src/arch/armv7/lib/interrupts.c @@ -0,0 +1,198 @@ +/* + * (C) Copyright 2003 + * Texas Instruments <www.ti.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke azu@sysgo.de + * + * (C) Copyright 2002-2004 + * Gary Jennejohn, DENX Software Engineering, garyj@denx.de + * + * (C) Copyright 2004 + * Philippe Robin, ARM Ltd. philippe.robin@arm.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +//#include <asm/proc-armv/ptrace.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_USE_IRQ +int interrupt_init (void) +{ + /* + * setup up stacks if necessary + */ + IRQ_STACK_START = gd->irq_sp - 4; + IRQ_STACK_START_IN = gd->irq_sp + 8; + FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; + + return arch_interrupt_init(); +} + +/* enable IRQ interrupts */ +void enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0x80\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + + +/* + * disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them + */ +int disable_interrupts (void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} +#else +int interrupt_init (void) +{ + /* + * setup up stacks if necessary + */ + IRQ_STACK_START_IN = gd->irq_sp + 8; + + return 0; +} + +void enable_interrupts (void) +{ + return; +} +int disable_interrupts (void) +{ + return 0; +} +#endif + + +void bad_mode (void) +{ + panic ("Resetting CPU ...\n"); + reset_cpu (0); +} + +void show_regs (struct pt_regs *regs) +{ + unsigned long flags; + const char *processor_modes[] = { + "USER_26", "FIQ_26", "IRQ_26", "SVC_26", + "UK4_26", "UK5_26", "UK6_26", "UK7_26", + "UK8_26", "UK9_26", "UK10_26", "UK11_26", + "UK12_26", "UK13_26", "UK14_26", "UK15_26", + "USER_32", "FIQ_32", "IRQ_32", "SVC_32", + "UK4_32", "UK5_32", "UK6_32", "ABT_32", + "UK8_32", "UK9_32", "UK10_32", "UND_32", + "UK12_32", "UK13_32", "UK14_32", "SYS_32", + }; + + flags = condition_codes (regs); + + printf ("pc : [<%08lx>] lr : [<%08lx>]\n" + "sp : %08lx ip : %08lx fp : %08lx\n", + instruction_pointer (regs), + regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); + printf ("r10: %08lx r9 : %08lx r8 : %08lx\n", + regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); + printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", + regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4); + printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", + regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); + printf ("Flags: %c%c%c%c", + flags & CC_N_BIT ? 'N' : 'n', + flags & CC_Z_BIT ? 'Z' : 'z', + flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v'); + printf (" IRQs %s FIQs %s Mode %s%s\n", + interrupts_enabled (regs) ? "on" : "off", + fast_interrupts_enabled (regs) ? "on" : "off", + processor_modes[processor_mode (regs)], + thumb_mode (regs) ? " (T)" : ""); +} + +void do_undefined_instruction (struct pt_regs *pt_regs) +{ + printf ("undefined instruction\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_software_interrupt (struct pt_regs *pt_regs) +{ + printf ("software interrupt\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_prefetch_abort (struct pt_regs *pt_regs) +{ + printf ("prefetch abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_data_abort (struct pt_regs *pt_regs) +{ + printf ("data abort\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_not_used (struct pt_regs *pt_regs) +{ + printf ("not used\n"); + show_regs (pt_regs); + bad_mode (); +} + +void do_fiq (struct pt_regs *pt_regs) +{ + printf ("fast interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} + +#ifndef CONFIG_USE_IRQ +void do_irq (struct pt_regs *pt_regs) +{ + printf ("interrupt request\n"); + show_regs (pt_regs); + bad_mode (); +} +#endif diff --git a/src/arch/armv7/lib/memcpy.S b/src/arch/armv7/lib/memcpy.S new file mode 100644 index 0000000..f655256 --- /dev/null +++ b/src/arch/armv7/lib/memcpy.S @@ -0,0 +1,243 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * 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. + */ + +#include <asm/assembler.h> + +#define W(instr) instr + +#define LDR1W_SHIFT 0 +#define STR1W_SHIFT 0 + + .macro ldr1w ptr reg abort + W(ldr) \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + W(str) \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +.globl memcpy +memcpy: + + cmp r0, r1 + moveq pc, lr + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r0, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + subs r2, r2, #32 + str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 +#if LDR1W_SHIFT > 0 + lsl ip, ip, #LDR1W_SHIFT +#endif + addne pc, pc, ip @ C is always clear here + b 7f +6: + .rept (1 << LDR1W_SHIFT) + W(nop) + .endr + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + +#if LDR1W_SHIFT < STR1W_SHIFT + lsl ip, ip, #STR1W_SHIFT - LDR1W_SHIFT +#elif LDR1W_SHIFT > STR1W_SHIFT + lsr ip, ip, #LDR1W_SHIFT - STR1W_SHIFT +#endif + add pc, pc, ip + nop + .rept (1 << STR1W_SHIFT) + W(nop) + .endr + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r0, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 diff --git a/src/arch/armv7/lib/memset.S b/src/arch/armv7/lib/memset.S new file mode 100644 index 0000000..0cdf895 --- /dev/null +++ b/src/arch/armv7/lib/memset.S @@ -0,0 +1,126 @@ +/* + * linux/arch/arm/lib/memset.S + * + * Copyright (C) 1995-2000 Russell King + * + * 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. + * + * ASM optimised string functions + */ +#include <asm/assembler.h> + + .text + .align 5 + .word 0 + +1: subs r2, r2, #4 @ 1 do we have enough + blt 5f @ 1 bytes to align with? + cmp r3, #2 @ 1 + strltb r1, [r0], #1 @ 1 + strleb r1, [r0], #1 @ 1 + strb r1, [r0], #1 @ 1 + add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) +/* + * The pointer is now aligned and the length is adjusted. Try doing the + * memset again. + */ + +.globl memset +memset: + ands r3, r0, #3 @ 1 unaligned? + bne 1b @ 1 +/* + * we know that the pointer in r0 is aligned to a word boundary. + */ + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + mov r3, r1 + cmp r2, #16 + blt 4f + +#if ! CALGN(1)+0 + +/* + * We need an extra register for this loop - save the return address and + * use the LR + */ + str lr, [sp, #-4]! + mov ip, r1 + mov lr, r1 + +2: subs r2, r2, #64 + stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + bgt 2b + ldmeqfd sp!, {pc} @ Now <64 bytes to go. +/* + * No need to correct the count; we're only testing bits from now on + */ + tst r2, #32 + stmneia r0!, {r1, r3, ip, lr} + stmneia r0!, {r1, r3, ip, lr} + tst r2, #16 + stmneia r0!, {r1, r3, ip, lr} + ldr lr, [sp], #4 + +#else + +/* + * This version aligns the destination pointer in order to write + * whole cache lines at once. + */ + + stmfd sp!, {r4-r7, lr} + mov r4, r1 + mov r5, r1 + mov r6, r1 + mov r7, r1 + mov ip, r1 + mov lr, r1 + + cmp r2, #96 + tstgt r0, #31 + ble 3f + + and ip, r0, #31 + rsb ip, ip, #32 + sub r2, r2, ip + movs ip, ip, lsl #(32 - 4) + stmcsia r0!, {r4, r5, r6, r7} + stmmiia r0!, {r4, r5} + tst ip, #(1 << 30) + mov ip, r1 + strne r1, [r0], #4 + +3: subs r2, r2, #64 + stmgeia r0!, {r1, r3-r7, ip, lr} + stmgeia r0!, {r1, r3-r7, ip, lr} + bgt 3b + ldmeqfd sp!, {r4-r7, pc} + + tst r2, #32 + stmneia r0!, {r1, r3-r7, ip, lr} + tst r2, #16 + stmneia r0!, {r4-r7} + ldmfd sp!, {r4-r7, lr} + +#endif + +4: tst r2, #8 + stmneia r0!, {r1, r3} + tst r2, #4 + strne r1, [r0], #4 +/* + * When we get here, we've got less than 4 bytes to zero. We + * may have an unaligned pointer as well. + */ +5: tst r2, #2 + strneb r1, [r0], #1 + strneb r1, [r0], #1 + tst r2, #1 + strneb r1, [r0], #1 + mov pc, lr diff --git a/src/arch/armv7/lib/reset.c b/src/arch/armv7/lib/reset.c new file mode 100644 index 0000000..08e6acb --- /dev/null +++ b/src/arch/armv7/lib/reset.c @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke azu@sysgo.de + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, garyj@denx.de + * + * (C) Copyright 2004 + * DAVE Srl + * http://www.dave-tech.it + * http://www.wawnet.biz + * mailto:info@wawnet.biz + * + * (C) Copyright 2004 Texas Insturments + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + puts ("resetting ...\n"); + + udelay (50000); /* wait 50 ms */ + + disable_interrupts(); + reset_cpu(0); + + /*NOTREACHED*/ + return 0; +} diff --git a/src/arch/armv7/lib/romstage_console.c b/src/arch/armv7/lib/romstage_console.c new file mode 100644 index 0000000..ead26f3 --- /dev/null +++ b/src/arch/armv7/lib/romstage_console.c @@ -0,0 +1,75 @@ +/* + * This file is part of the coreboot project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/vtxprintf.h> +#if CONFIG_SERIAL_CONSOLE +#include <uart.h> +#endif +#if CONFIG_USBDEBUG +#include <usbdebug.h> +#endif + +/* FIXME: need to make console driver more generic */ +void console_tx_byte(unsigned char byte) +{ + if (byte == '\n') + console_tx_byte('\r'); + +#if CONFIG_SERIAL_CONSOLE + uart_tx_byte(byte); +#endif +#if CONFIG_USBDEBUG + usbdebug_tx_byte(0, byte); +#endif +#if CONFIG_CONSOLE_CBMEM + cbmemc_tx_byte(byte); +#endif +} + +/* FIXME(dhendrix): add this back in */ +#if 0 +static void console_tx_flush(void) +{ +#if CONFIG_CONSOLE_SERIAL + uart_tx_flush(CONFIG_CONSOLE_SERIAL_UART_ADDRESS); +#endif +#if CONFIG_USBDEBUG + usbdebug_tx_flush(0); +#endif +} +#endif + +int do_printk(int msg_level, const char *fmt, ...) +{ + va_list args; + int i; + + if (msg_level > console_loglevel) { + return 0; + } + + va_start(args, fmt); + i = vtxprintf(console_tx_byte, fmt, args); + va_end(args); + +// console_tx_flush(); + + return i; +} diff --git a/src/arch/armv7/lib/syslib.c b/src/arch/armv7/lib/syslib.c new file mode 100644 index 0000000..6f38bf9 --- /dev/null +++ b/src/arch/armv7/lib/syslib.c @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2008 + * Texas Instruments, <www.ti.com> + * + * Richard Woodruff r-woodruff2@ti.com + * Syed Mohammed Khasim khasim@ti.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +//#include <common.h> +#include <arch/io.h> +#include <system.h> /* FIXME: dumping ground for prototypes */ + +/************************************************************ + * sdelay() - simple spin loop. Will be constant time as + * its generally used in bypass conditions only. This + * is necessary until timers are accessible. + * + * not inline to increase chances its in cache when called + *************************************************************/ +void sdelay(unsigned long loops) +{ + __asm__ volatile ("1:\n" "subs %0, %1, #1\n" + "bne 1b":"=r" (loops):"0"(loops)); +} + +/***************************************************************** + * sr32 - clear & set a value in a bit range for a 32 bit address + *****************************************************************/ +void sr32(void *addr, u32 start_bit, u32 num_bits, u32 value) +{ + u32 tmp, msk = 0; + msk = 1 << num_bits; + --msk; + tmp = readl((u32)addr) & ~(msk << start_bit); + tmp |= value << start_bit; + writel(tmp, (u32)addr); +} + +/********************************************************************* + * wait_on_value() - common routine to allow waiting for changes in + * volatile regs. + *********************************************************************/ +u32 wait_on_value(u32 read_bit_mask, u32 match_value, void *read_addr, + u32 bound) +{ + u32 i = 0, val; + do { + ++i; + val = readl((u32)read_addr) & read_bit_mask; + if (val == match_value) + return 1; + if (i == bound) + return 0; + } while (1); +} diff --git a/src/arch/armv7/romstage.ld b/src/arch/armv7/romstage.ld new file mode 100644 index 0000000..0a2f7f2 --- /dev/null +++ b/src/arch/armv7/romstage.ld @@ -0,0 +1,119 @@ +/* + * Memory map: + * + * CONFIG_RAMBASE : text segment + * : rodata segment + * : data segment + * : bss segment + * : stack + * : heap + */ +/* + * Bootstrap code for the STPC Consumer + * Copyright (c) 1999 by Net Insight AB. All Rights Reserved. + */ + +/* + * Written by Johan Rydberg, based on work by Daniel Kahlin. + * Rewritten by Eric Biederman + * 2005.12 yhlu add coreboot_ram cross the vga font buffer handling + */ + +/* We use ELF as output format. So that we can debug the code in some form. */ +/* + INCLUDE ldoptions + */ + +/* + * FIXME: what exactly should these be? maybe defined on a per-CPU basis? + * FIXME 2: Somehow linker didn't like CONFIG_SPL_MAX_SIZE and CONFIG_SPL_TEXT_BASE... + */ +/* MEMORY { .sram : ORIGIN = 0x02023400, LENGTH = 0x3800 } */ +MEMORY { .sram : ORIGIN = 0x02023400, LENGTH = 0x10000 } + +/* We use ELF as output format. So that we can debug the code in some form. */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(_start) + +SECTIONS +{ + . = ROMSTAGE_BASE; + + /* + .rom . : { + _rom = .; + *(.rom.text); + *(.rom.data); + *(.rodata); + *(.rodata.*); + *(.rom.data.*); + . = ALIGN(16); + _erom = .; + } + */ + + /* First we place the code and read only data (typically const declared). + * This could theoretically be placed in rom. + */ + .text : { + _text = .; + *(.text); + *(.text.*); + . = ALIGN(4); + _etext = .; + } >.sram + + .rodata : { + _rodata = .; + . = ALIGN(4); + cpu_drivers = . ; + *(.rodata.cpu_driver) + ecpu_drivers = . ; + *(.rodata) + *(.rodata.*) + /* kevinh/Ispiri - Added an align, because the objcopy tool + * incorrectly converts sections that are not long word aligned. + */ + . = ALIGN(4); + + _erodata = .; + } >.sram + /* After the code we place initialized data (typically initialized + * global variables). This gets copied into ram by startup code. + * __data_start and __data_end shows where in ram this should be placed, + * whereas __data_loadstart and __data_loadend shows where in rom to + * copy from. + */ + .data : { + _data = .; + *(.data) + _edata = .; + } >.sram + + __image_copy_end = .; + + /* bss does not contain data, it is just a space that should be zero + * initialized on startup. (typically uninitialized global variables) + * crt0.S fills between _bss and _ebss with zeroes. + */ + .bss . : { + . = ALIGN(4); + _bss = .; + *(.bss) + *(.sbss) + *(COMMON) + } >.sram + _ebss = .; + _end = .; + + /* Discard the sections we don't need/want */ + /DISCARD/ : { + *(.comment) + *(.note) + *(.comment.*) + *(.note.*) + *(.eh_frame); + } +} diff --git a/src/arch/armv7/start.S b/src/arch/armv7/start.S new file mode 100644 index 0000000..63cac4b --- /dev/null +++ b/src/arch/armv7/start.S @@ -0,0 +1,543 @@ +/* + * armboot - Startup Code for OMAP3530/ARM Cortex CPU-core + * + * Copyright (c) 2004 Texas Instruments r-woodruff2@ti.com + * + * Copyright (c) 2001 Marius Gröger mag@sysgo.de + * Copyright (c) 2002 Alex Züpke azu@sysgo.de + * Copyright (c) 2002 Gary Jennejohn garyj@denx.de + * Copyright (c) 2003 Richard Woodruff r-woodruff2@ti.com + * Copyright (c) 2003 Kshitij kshitij@ti.com + * Copyright (c) 2006-2008 Syed Mohammed Khasim x0khasim@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#define __ASSEMBLY__ +#include <system.h> + +.globl _start +_start: b reset + ldr pc, _undefined_instruction + ldr pc, _software_interrupt + ldr pc, _prefetch_abort + ldr pc, _data_abort + ldr pc, _not_used + ldr pc, _irq + ldr pc, _fiq +#ifdef CONFIG_SPL_BUILD +_undefined_instruction: .word _undefined_instruction +_software_interrupt: .word _software_interrupt +_prefetch_abort: .word _prefetch_abort +_data_abort: .word _data_abort +_not_used: .word _not_used +_irq: .word _irq +_fiq: .word _fiq +_pad: .word 0x12345678 /* now 16*4=64 */ +#else +_undefined_instruction: .word undefined_instruction +_software_interrupt: .word software_interrupt +_prefetch_abort: .word prefetch_abort +_data_abort: .word data_abort +_not_used: .word not_used +_irq: .word irq +_fiq: .word fiq +_pad: .word 0x12345678 /* now 16*4=64 */ +#endif /* CONFIG_SPL_BUILD */ + +.global _end_vect +_end_vect: + + .balignl 16,0xdeadbeef +/************************************************************************* + * + * Startup Code (reset vector) + * + * do important init only if we don't start from memory! + * setup Memory and board specific bits prior to relocation. + * relocate armboot to ram + * setup stack + * + *************************************************************************/ + +.globl _TEXT_BASE +_TEXT_BASE: + .word CONFIG_SYS_TEXT_BASE + +/* + * These are defined in the board-specific linker script. + */ +.globl _bss_start_ofs +_bss_start_ofs: + .word _bss - _start + +.global _image_copy_end_ofs +_image_copy_end_ofs: + .word __image_copy_end - _start + +.globl _bss_end_ofs +_bss_end_ofs: + .word _ebss - _start +# .word __bss_end__ - _start + +.globl _end_ofs +_end_ofs: + .word _end - _start + +#ifdef CONFIG_USE_IRQ +/* IRQ stack memory (calculated at run-time) */ +.globl IRQ_STACK_START +IRQ_STACK_START: + .word 0x0badc0de + +/* IRQ stack memory (calculated at run-time) */ +.globl FIQ_STACK_START +FIQ_STACK_START: + .word 0x0badc0de +#endif + +/* IRQ stack memory (calculated at run-time) + 8 bytes */ +.globl IRQ_STACK_START_IN +IRQ_STACK_START_IN: + .word 0x0badc0de + +/* + * the actual reset code + */ + +reset: +/* bl save_boot_params */ + /* + * set the cpu to SVC32 mode + */ + mrs r0, cpsr + bic r0, r0, #0x1f + orr r0, r0, #0xd3 + msr cpsr,r0 + +#if !defined(CONFIG_TEGRA2) +/* + * Setup vector: + * (OMAP4 spl TEXT_BASE is not 32 byte aligned. + * Continue to use ROM code vector only in OMAP4 spl) + */ +#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD)) + /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */ + mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register + bic r0, #CR_V @ V = 0 + mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register + + /* Set vector address in CP15 VBAR register */ + ldr r0, =_start + mcr p15, 0, r0, c12, c0, 0 @Set VBAR +#endif +#endif /* !Tegra2 */ + + /* the mask ROM code should have PLL and others stable */ +#ifndef CONFIG_SKIP_LOWLEVEL_INIT + /* + * FIXME(dhendrix): Do we need to explicitly disable icache/dcache + * first? See example 15-3 in Cortex-A programmers guide... + */ + bl cpu_init_cp15 + bl cpu_init_crit +#endif + +/* Set stackpointer in internal RAM to call board_init_f */ +call_board_init_f: + ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) + bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ + ldr r0,=0x00000000 + bl 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: + mov r4, r0 /* save addr_sp */ + mov r5, r1 /* save addr of gd */ + mov r6, r2 /* save addr of destination */ + + /* Set up the stack */ +stack_setup: + mov sp, r4 + + adr r0, _start + subs r9, r6, r0 /* r9 <- relocation offset */ + beq clear_bss /* skip relocation */ + mov r1, r6 /* r1 <- scratch for copy_loop */ + ldr r3, _image_copy_end_ofs + add r2, r0, r3 /* r2 <- source end address */ + +copy_loop: + ldmia r0!, {r9-r10} /* copy from source address [r0] */ + stmia r1!, {r9-r10} /* copy to target address [r1] */ + cmp r0, r2 /* until source end address [r2] */ + blo copy_loop + +#ifndef CONFIG_SPL_BUILD + /* + * fix .rel.dyn relocations + */ + ldr r0, _TEXT_BASE /* r0 <- Text base */ + sub r9, r6, r0 /* r9 <- relocation offset */ + ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ + add r10, r10, r0 /* r10 <- sym table in FLASH */ + ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ + add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ + ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ + add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ +fixloop: + ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */ + add r0, r0, r9 /* r0 <- location to fix up in RAM */ + ldr r1, [r2, #4] + and r7, r1, #0xff + cmp r7, #23 /* relative fixup? */ + beq fixrel + cmp r7, #2 /* absolute fixup? */ + beq fixabs + /* ignore unknown type of fixup */ + b fixnext +fixabs: + /* absolute fix: set location to (offset) symbol value */ + mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */ + add r1, r10, r1 /* r1 <- address of symbol in table */ + ldr r1, [r1, #4] /* r1 <- symbol value */ + add r1, r1, r9 /* r1 <- relocated sym addr */ + b fixnext +fixrel: + /* relative fix: increase location by offset */ + ldr r1, [r0] + add r1, r1, r9 +fixnext: + str r1, [r0] + add r2, r2, #8 /* each rel.dyn entry is 8 bytes */ + cmp r2, r3 + blo fixloop + b clear_bss +_rel_dyn_start_ofs: + .word __rel_dyn_start - _start +_rel_dyn_end_ofs: + .word __rel_dyn_end - _start +_dynsym_start_ofs: + .word __dynsym_start - _start + +#endif /* #ifndef CONFIG_SPL_BUILD */ + +clear_bss: +#ifdef CONFIG_SPL_BUILD + /* No relocation for SPL */ + ldr r0, =_bss + ldr r1, =_ebss +#else + ldr r0, _bss_start_ofs + ldr r1, _bss_end_ofs + mov r4, r6 /* reloc addr */ + add r0, r0, r4 + add r1, r1, r4 +#endif + mov r2, #0x00000000 /* clear */ + +clbss_l:str r2, [r0] /* clear loop... */ + add r0, r0, #4 + cmp r0, r1 + bne clbss_l + +/* + * We are done. Do not return, instead branch to second part of board + * initialization, now running from RAM. + */ +jump_2_ram: +/* + * If I-cache is enabled invalidate it + */ +#ifndef CONFIG_SYS_ICACHE_OFF + mcr p15, 0, r0, c7, c5, 0 @ invalidate icache + mcr p15, 0, r0, c7, c10, 4 @ DSB + mcr p15, 0, r0, c7, c5, 4 @ ISB +#endif + ldr r0, _board_init_r_ofs + adr r1, _start + add lr, r0, r1 + add lr, lr, r9 + /* setup parameters for board_init_r */ + mov r0, r5 /* gd_t */ + mov r1, r6 /* dest_addr */ + /* jump to it ... */ + mov pc, lr + +_board_init_r_ofs: + .word board_init_r - _start + +/************************************************************************* + * + * cpu_init_cp15 + * + * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless + * CONFIG_SYS_ICACHE_OFF is defined. + * + *************************************************************************/ +.globl cpu_init_cp15 +cpu_init_cp15: + /* + * Invalidate L1 I/D + */ + mov r0, #0 @ set up for MCR + mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs + mcr p15, 0, r0, c7, c5, 0 @ invalidate icache + mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array + mcr p15, 0, r0, c7, c10, 4 @ DSB + mcr p15, 0, r0, c7, c5, 4 @ ISB + + /* + * disable MMU stuff and caches + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #0x00002000 @ clear bits 13 (--V-) + bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) + orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align + orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB +#ifdef CONFIG_SYS_ICACHE_OFF + bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache +#else + orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache +#endif + mcr p15, 0, r0, c1, c0, 0 + mov pc, lr @ back to my caller + + +#ifndef CONFIG_SKIP_LOWLEVEL_INIT +/************************************************************************* + * + * CPU_init_critical registers + * + * setup important registers + * setup memory timing + * + *************************************************************************/ +cpu_init_crit: + /* + * Jump to board specific initialization... + * The Mask ROM will have already initialized + * basic memory. Go here to bump up clock rate and handle + * wake up conditions. + */ + mov ip, lr @ persevere link reg across call + bl lowlevel_init @ go setup pll,mux,memory + mov lr, ip @ restore link + mov pc, lr @ back to my caller +#endif + +#ifndef CONFIG_SPL_BUILD +/* + ************************************************************************* + * + * Interrupt handling + * + ************************************************************************* + */ +@ +@ IRQ stack frame. +@ +#define S_FRAME_SIZE 72 + +#define S_OLD_R0 68 +#define S_PSR 64 +#define S_PC 60 +#define S_LR 56 +#define S_SP 52 + +#define S_IP 48 +#define S_FP 44 +#define S_R10 40 +#define S_R9 36 +#define S_R8 32 +#define S_R7 28 +#define S_R6 24 +#define S_R5 20 +#define S_R4 16 +#define S_R3 12 +#define S_R2 8 +#define S_R1 4 +#define S_R0 0 + +#define MODE_SVC 0x13 +#define I_BIT 0x80 + +/* + * use bad_save_user_regs for abort/prefetch/undef/swi ... + * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling + */ + + .macro bad_save_user_regs + sub sp, sp, #S_FRAME_SIZE @ carve out a frame on current + @ user stack + stmia sp, {r0 - r12} @ Save user registers (now in + @ svc mode) r0-r12 + ldr r2, IRQ_STACK_START_IN @ set base 2 words into abort + @ stack + ldmia r2, {r2 - r3} @ get values for "aborted" pc + @ and cpsr (into parm regs) + add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack + + add r5, sp, #S_SP + mov r1, lr + stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr + mov r0, sp @ save current stack into r0 + @ (param register) + .endm + + .macro irq_save_user_regs + sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - r12} @ Calling r0-r12 + add r8, sp, #S_PC @ !! R8 NEEDS to be saved !! + @ a reserved stack spot would + @ be good. + stmdb r8, {sp, lr}^ @ Calling SP, LR + str lr, [r8, #0] @ Save calling PC + mrs r6, spsr + str r6, [r8, #4] @ Save CPSR + str r0, [r8, #8] @ Save OLD_R0 + mov r0, sp + .endm + + .macro irq_restore_user_regs + ldmia sp, {r0 - lr}^ @ Calling r0 - lr + mov r0, r0 + ldr lr, [sp, #S_PC] @ Get PC + add sp, sp, #S_FRAME_SIZE + subs pc, lr, #4 @ return & move spsr_svc into + @ cpsr + .endm + + .macro get_bad_stack + ldr r13, IRQ_STACK_START_IN @ setup our mode stack (enter + @ in banked mode) + + str lr, [r13] @ save caller lr in position 0 + @ of saved stack + mrs lr, spsr @ get the spsr + str lr, [r13, #4] @ save spsr in position 1 of + @ saved stack + + mov r13, #MODE_SVC @ prepare SVC-Mode + @ msr spsr_c, r13 + msr spsr, r13 @ switch modes, make sure + @ moves will execute + mov lr, pc @ capture return pc + movs pc, lr @ jump to next instruction & + @ switch modes. + .endm + + .macro get_bad_stack_swi + sub r13, r13, #4 @ space on current stack for + @ scratch reg. + str r0, [r13] @ save R0's value. + ldr r0, IRQ_STACK_START_IN @ get data regions start + @ spots for abort stack + str lr, [r0] @ save caller lr in position 0 + @ of saved stack + mrs r0, spsr @ get the spsr + str lr, [r0, #4] @ save spsr in position 1 of + @ saved stack + ldr r0, [r13] @ restore r0 + add r13, r13, #4 @ pop stack entry + .endm + + .macro get_irq_stack @ setup IRQ stack + ldr sp, IRQ_STACK_START + .endm + + .macro get_fiq_stack @ setup FIQ stack + ldr sp, FIQ_STACK_START + .endm + +/* + * exception handlers + */ + .align 5 +undefined_instruction: + get_bad_stack + bad_save_user_regs + bl do_undefined_instruction + + .align 5 +software_interrupt: + get_bad_stack_swi + bad_save_user_regs + bl do_software_interrupt + + .align 5 +prefetch_abort: + get_bad_stack + bad_save_user_regs + bl do_prefetch_abort + + .align 5 +data_abort: + get_bad_stack + bad_save_user_regs + bl do_data_abort + + .align 5 +not_used: + get_bad_stack + bad_save_user_regs + bl do_not_used + +#ifdef CONFIG_USE_IRQ + + .align 5 +irq: + get_irq_stack + irq_save_user_regs + bl do_irq + irq_restore_user_regs + + .align 5 +fiq: + get_fiq_stack + /* someone ought to write a more effective fiq_save_user_regs */ + irq_save_user_regs + bl do_fiq + irq_restore_user_regs + +#else + + .align 5 +irq: + get_bad_stack + bad_save_user_regs + bl do_irq + + .align 5 +fiq: + get_bad_stack + bad_save_user_regs + bl do_fiq + +#endif /* CONFIG_USE_IRQ */ +#endif /* CONFIG_SPL_BUILD */ diff --git a/src/arch/x86/include/stddef.h b/src/arch/x86/include/stddef.h deleted file mode 100644 index fc89de5..0000000 --- a/src/arch/x86/include/stddef.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef I386_STDDEF_H -#define I386_STDDEF_H - -typedef long ptrdiff_t; -#ifndef __SIZE_TYPE__ -#define __SIZE_TYPE__ unsigned long -#endif -typedef __SIZE_TYPE__ size_t; -typedef long ssize_t; - -typedef int wchar_t; -typedef unsigned int wint_t; - -#define NULL ((void *)0) - -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) - -#ifdef __PRE_RAM__ -#define ROMSTAGE_CONST const -#else -#define ROMSTAGE_CONST -#endif - -#endif /* I386_STDDEF_H */ diff --git a/src/console/Kconfig b/src/console/Kconfig index f4e3528..117fdb8 100644 --- a/src/console/Kconfig +++ b/src/console/Kconfig @@ -1,18 +1,33 @@ menu "Console" -# TODO: Rename to SERIAL_CONSOLE once Kconfig transition is complete. -config CONSOLE_SERIAL8250 +config SERIAL_CONSOLE bool "Serial port console output" + default y + help + Send coreboot debug output to a serial port + +config CONSOLE_SERIAL8250 + bool "Serial port console output (I/O mapped, 8250-compatible)" + depends on SERIAL_CONSOLE depends on HAVE_UART_IO_MAPPED default y help Send coreboot debug output to an I/O mapped serial port console.
config CONSOLE_SERIAL8250MEM - bool "Serial port console output (memory mapped)" + bool "Serial port console output (memory mapped, 8250-compatible)" + depends on SERIAL_CONSOLE depends on HAVE_UART_MEMORY_MAPPED help Send coreboot debug output to a memory mapped serial port console.
+config CONSOLE_SERIAL_NONSTANDARD_MEM + bool "Serial port console output (memory-mapped, device-specific)" + depends on SERIAL_CONSOLE + depends on HAVE_UART_MEMORY_MAPPED + help + Send coreboot debug output to a memory mapped serial port console + on a device-specific UART. + choice prompt "Serial port" default CONSOLE_SERIAL_COM1 @@ -50,7 +65,7 @@ config TTYS0_BASE choice prompt "Baud rate" default CONSOLE_SERIAL_115200 - depends on CONSOLE_SERIAL8250 || CONSOLE_SERIAL8250MEM + depends on SERIAL_CONSOLE
config CONSOLE_SERIAL_115200 bool "115200" @@ -75,6 +90,7 @@ config CONSOLE_SERIAL_9600
endchoice
+#FIXME(dhendrix): Change name to SERIAL_BAUD? (Stefan sayz: yes!!) config TTYS0_BAUD int default 115200 if CONSOLE_SERIAL_115200 diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig index 986c68a..383ba79 100644 --- a/src/cpu/Kconfig +++ b/src/cpu/Kconfig @@ -1,3 +1,12 @@ +# Warning: This file is included whether or not the if is here. +# The if controls how the evaluation occurs. +# (See also src/Kconfig) +if ARCH_ARMV7 + +source src/cpu/samsung/Kconfig + +endif # ARCH_ARM + if ARCH_X86
source src/cpu/amd/Kconfig diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc index 938a8df..93b16ae 100644 --- a/src/cpu/Makefile.inc +++ b/src/cpu/Makefile.inc @@ -3,6 +3,7 @@ ################################################################################ subdirs-y += amd subdirs-y += intel +subdirs-y += samsung subdirs-y += via
################################################################################ diff --git a/src/cpu/samsung/Kconfig b/src/cpu/samsung/Kconfig new file mode 100644 index 0000000..3a14ab8 --- /dev/null +++ b/src/cpu/samsung/Kconfig @@ -0,0 +1,46 @@ +config CPU_SAMSUNG_EXYNOS + bool + default n + +config CPU_SAMSUNG_EXYNOS5 + depends on ARCH_ARMV7 + select CPU_SAMSUNG_EXYNOS + bool + default n + +config SKIP_LOWLEVEL_INIT + bool "Skip low-level init" + default n + help + Certain functions (ie PLL init) and processor features may already be + handled by masked ROM code. + +config IRAM_BOTTOM + hex + default 0x02020000 + +# FIXME(dhendrix): 0x02050000 was in the u-boot sources, but the docs say the +# iRAM range is 0x0202_0000 - 0x0207_7fff (352KB). +#config IRAM_TOP +# hex +# default 0x02050000 +config IRAM_TOP + hex + default 0x02077fff + +config SYS_INIT_SP_ADDR + hex + default 0x0204F800 + +config IRAM_STACK + hex + default SYS_INIT_SP_ADDR + +# FIXME(dhendrix): what should this really be? +config XIP_ROM_SIZE + hex + default 0x20000 + +if CPU_SAMSUNG_EXYNOS5 +source src/cpu/samsung/exynos5250/Kconfig +endif diff --git a/src/cpu/samsung/Makefile.inc b/src/cpu/samsung/Makefile.inc new file mode 100644 index 0000000..b6fcd4e --- /dev/null +++ b/src/cpu/samsung/Makefile.inc @@ -0,0 +1,5 @@ +subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5-common +subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5250 + +# S5P is a predecessor to Exynos +subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS) += s5p-common diff --git a/src/cpu/samsung/exynos5-common/Makefile.inc b/src/cpu/samsung/exynos5-common/Makefile.inc new file mode 100644 index 0000000..cad6cb3 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/Makefile.inc @@ -0,0 +1,2 @@ +#romstage-y += soc.c +romstage-y += spl_boot.c diff --git a/src/cpu/samsung/exynos5-common/soc.c b/src/cpu/samsung/exynos5-common/soc.c new file mode 100644 index 0000000..e73fd33 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/soc.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010 Samsung Electronics. + * Minkyu Kang mk7.kang@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <arch/io.h> + +void reset_cpu(unsigned long addr) +{ + writel(0x1, samsung_get_base_swreset()); +} diff --git a/src/cpu/samsung/exynos5-common/spl_boot.c b/src/cpu/samsung/exynos5-common/spl_boot.c new file mode 100644 index 0000000..8ae0511 --- /dev/null +++ b/src/cpu/samsung/exynos5-common/spl_boot.c @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* FIXME(dhendrix): pulled in a lot of extra crap such as partition and string + libs*/ +#include <assert.h> +#include <common.h> +#include <stdlib.h> +#include <string.h> + +#include <console/console.h> +#include <console/loglevel.h> +//#include <asm/arch/board.h> + +#include <config.h> +#include <spi.h> +#if 0 +#include <asm/arch/clock.h> +#include <asm/arch-exynos/spi.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/power.h> +#endif +#include <arch/hlt.h> +#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> +#include <cpu/samsung/exynos5250/pinmux.h> +#include <cpu/samsung/exynos5250/power.h> +#include <cpu/samsung/exynos5250/spi.h> + +#include <cpu/samsung/exynos5-common/cpu.h> +#include <cpu/samsung/exynos5-common/exynos5-common.h> + +//#include <asm/system.h> +#include <system.h> + +#include <arch/io.h> +#include <arch/types.h> + +/* FIXME(dhendrix): clean out u-boot global data stuff */ +//DECLARE_GLOBAL_DATA_PTR; + +#define OM_STAT (0x1f << 1) + +/** + * Copy data from SD or MMC device to RAM. + * + * @param offset Block offset of the data + * @param nblock Number of blocks + * @param dst Destination address + * @return 1 = True or 0 = False + */ +typedef u32 (*mmc_copy_func_t)(u32 offset, u32 nblock, u32 dst); + +/** + * Copy data from SPI flash to RAM. + * + * @param offset Block offset of the data + * @param nblock Number of blocks + * @param dst Destination address + * @return 1 = True or 0 = False + */ +typedef u32 (*spi_copy_func_t)(u32 offset, u32 nblock, u32 dst); + + +/** + * Copy data through USB. + * + * @return 1 = True or 0 = False + */ +typedef u32 (*usb_copy_func_t)(void); + +/* + * Set/clear program flow prediction and return the previous state. + */ +static int config_branch_prediction(int set_cr_z) +{ + unsigned int cr; + + /* System Control Register: 11th bit Z Branch prediction enable */ + cr = get_cr(); + set_cr(set_cr_z ? cr | CR_Z : cr & ~CR_Z); + + return cr & CR_Z; +} + +#if 0 +static void spi_rx_tx(struct exynos_spi *regs, int todo, + void *dinp, void const *doutp, int i) +{ + uint *rxp = (uint *)(dinp + (i * (32 * 1024))); + int rx_lvl, tx_lvl; + uint out_bytes, in_bytes; + + out_bytes = in_bytes = todo; + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, ®s->pkt_cnt); + + while (in_bytes) { + uint32_t spi_sts; + int temp; + + spi_sts = readl(®s->spi_sts); + rx_lvl = ((spi_sts >> 15) & 0x7f); + tx_lvl = ((spi_sts >> 6) & 0x7f); + while (tx_lvl < 32 && out_bytes) { + temp = 0xffffffff; + writel(temp, ®s->tx_data); + out_bytes -= 4; + tx_lvl += 4; + } + while (rx_lvl >= 4 && in_bytes) { + temp = readl(®s->rx_data); + if (rxp) + *rxp++ = temp; + in_bytes -= 4; + rx_lvl -= 4; + } + } +} +#endif + +/* FIXME(dhendrix): feels like exynos_spi_copy should go somewhere else... */ +#if 0 +/** + * Copy uboot from spi flash to RAM + * + * @parma uboot_size size of u-boot to copy + */ +static void exynos_spi_copy(unsigned int uboot_size) +{ + int upto, todo; + int i; +// struct exynos_spi *regs = (struct exynos_spi *)samsung_get_base_spi1(); + struct exynos_spi *regs = (struct exynos_spi *)0x12d30000; + + clock_set_rate(PERIPH_ID_SPI1, 50000000); /* set spi clock to 50Mhz */ + /* set the spi1 GPIO */ + exynos_pinmux_config(PERIPH_ID_SPI1, PINMUX_FLAG_NONE); + + /* set pktcnt and enable it */ + writel(4 | SPI_PACKET_CNT_EN, ®s->pkt_cnt); + /* set FB_CLK_SEL */ + writel(SPI_FB_DELAY_180, ®s->fb_clk); + /* set CH_WIDTH and BUS_WIDTH as word */ + setbits_le32(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD | + SPI_MODE_BUS_WIDTH_WORD); + clrbits_le32(®s->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */ + + /* clear rx and tx channel if set priveously */ + clrbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON); + + setbits_le32(®s->swap_cfg, SPI_RX_SWAP_EN | + SPI_RX_BYTE_SWAP | + SPI_RX_HWORD_SWAP); + + /* do a soft reset */ + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + + /* now set rx and tx channel ON */ + setbits_le32(®s->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN); + clrbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */ + + /* Send read instruction (0x3h) followed by a 24 bit addr */ + writel((SF_READ_DATA_CMD << 24) | SPI_FLASH_UBOOT_POS, ®s->tx_data); + + /* waiting for TX done */ + while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE)); + + for (upto = 0, i = 0; upto < uboot_size; upto += todo, i++) { + todo = MIN(uboot_size - upto, (1 << 15)); + spi_rx_tx(regs, todo, (void *)(CONFIG_SYS_TEXT_BASE), + (void *)(SPI_FLASH_UBOOT_POS), i); + } + + setbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */ + + /* + * Let put controller mode to BYTE as + * SPI driver does not support WORD mode yet + */ + clrbits_le32(®s->mode_cfg, SPI_MODE_CH_WIDTH_WORD | + SPI_MODE_BUS_WIDTH_WORD); + writel(0, ®s->swap_cfg); + + /* + * Flush spi tx, rx fifos and reset the SPI controller + * and clear rx/tx channel + */ + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} +#endif + +/* Copy U-Boot image to RAM */ +static void copy_uboot_to_ram(void) +{ + unsigned int sec_boot_check; + unsigned int uboot_size = CONFIG_COREBOOT_ROMSIZE_KB_4096; + int is_cr_z_set; + enum boot_mode boot_mode = BOOT_MODE_OM; + mmc_copy_func_t mmc_copy; + + usb_copy_func_t usb_copy; + +#if 0 + uboot_size = exynos_get_uboot_size(); + boot_mode = exynos_get_boot_device(); +#endif + + if (boot_mode == BOOT_MODE_OM) { + /* Read iRAM location to check for secondary USB boot mode */ + sec_boot_check = readl(EXYNOS_IRAM_SECONDARY_BASE); + if (sec_boot_check == EXYNOS_USB_SECONDARY_BOOT) + boot_mode = BOOT_MODE_USB; + } + debug("U-Boot size %u\n", uboot_size); + + if (boot_mode == BOOT_MODE_OM) + boot_mode = readl(EXYNOS_POWER_BASE) & OM_STAT; + + switch (boot_mode) { +#if defined(CONFIG_EXYNOS_SPI_BOOT) + case BOOT_MODE_SERIAL: + /* let us our own function to copy u-boot from SF */ + exynos_spi_copy(uboot_size); + break; +#endif + case BOOT_MODE_MMC: + mmc_copy = *(mmc_copy_func_t *)EXYNOS_COPY_MMC_FNPTR_ADDR; + assert(!(uboot_size & 511)); + mmc_copy(BL2_START_OFFSET, uboot_size / 512, + CONFIG_SYS_TEXT_BASE); + break; + case BOOT_MODE_USB: + /* + * iROM needs program flow prediction to be disabled + * before copy from USB device to RAM + */ + is_cr_z_set = config_branch_prediction(0); + usb_copy = *(usb_copy_func_t *) + EXYNOS_COPY_USB_FNPTR_ADDR; + usb_copy(); + config_branch_prediction(is_cr_z_set); + break; + default: + printk(BIOS_ERR, "Invalid boot mode selection\n"); + hlt(); + break; + } + debug("U-Boot copied\n"); +} + +#if 0 +/** + * Set up the U-Boot global_data pointer + * + * This sets the address of the global data, and sets up basic values. + * + * @param gdp Value to give to gd + */ +static void setup_global_data(gd_t *gdp) +{ + gd = gdp; + memzero((void *)gd, sizeof(gd_t)); + gd->flags |= GD_FLG_RELOC; + gd->baudrate = CONFIG_BAUDRATE; + gd->have_console = 1; +} +#endif + +#if 0 +/* Tell the loaded U-Boot that it was loaded from SPL */ +static void exynos5_set_spl_marker(void) +{ + uint32_t *marker = (uint32_t *)CONFIG_SPL_MARKER; + + *marker = EXYNOS5_SPL_MARKER; +} +#endif + +/* Board-specific call to see if wakeup is allowed. */ +static int __def_board_wakeup_permitted(void) +{ + return 1; +} +int board_wakeup_permitted(void) + __attribute__((weak, alias("__def_board_wakeup_permitted"))); + +void board_init_f(unsigned long bootflag) +{ + /* + * The gd struct is only needed for serial initialization. Since this + * function is called in SPL u-boot. We store the gd struct in the + * stack instead of the default memory region which may not be + * initialized. + */ +// __attribute__((aligned(8))) gd_t local_gd; +// __attribute__((noreturn)) void (*uboot)(void); + +// exynos5_set_spl_marker(); +// setup_global_data(&local_gd); + + /* + * Init subsystems, and resume if required. For a normal boot this + * will set up the UART and display a message. + */ + if (lowlevel_init_subsystems()) { + if (!board_wakeup_permitted()) + power_reset(); + power_exit_wakeup(); + } + +// printk(BIOS_INFO, "\n\nU-Boot SPL, board rev %u\n", board_get_revision()); + + copy_uboot_to_ram(); + /* Jump to U-Boot image */ +// uboot = (void *)CONFIG_SYS_TEXT_BASE; +// uboot(); + /* Never returns Here */ +// printk(BIOS_ERR, "%s: u-boot jump failed", __func__); + printk(BIOS_INFO, "%s: we should not be here...", __func__); + hlt(); +} + +/* Place Holders */ +void board_init_r(gd_t *id, ulong dest_addr) +{ + /* Function attribute is no-return */ + /* This Function never executes */ + while (1) + ; +} + +//void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) {} + +#if 0 +/* + * The following functions are required when linking console library to SPL. + * + * Enabling UART in SPL u-boot requires console library. But some + * functions we needed in the console library depends on a bunch + * of library in libgeneric, like lib/ctype.o, lib/div64.o, lib/string.o, + * and lib/vsprintf.o. Adding them makes the SPL u-boot too large and not + * fit into the expected size. + * + * So we mock these functions in SPL, i.e. vsprintf(), panic(), etc., + * in order to cut its dependency. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + char *str = buf, *s; + ulong u; + + /* + * We won't implement all full functions of vsprintf(). + * We only implement %s and %u, and ignore others and directly use + * the original format string as its result. + */ + + while (*fmt) { + if (*fmt != '%') { + *str++ = *fmt++; + continue; + } + fmt++; + switch (*fmt) { + case '%': + *str++ = *fmt++; + break; + case 's': + fmt++; + s = va_arg(args, char *); + while (*s) + *str++ = *s++; + break; + case 'u': + fmt++; + u = va_arg(args, ulong); + s = simple_itoa(u); + while (*s) + *str++ = *s++; + break; + default: + /* Print the original string for unsupported formats */ + *str++ = '%'; + *str++ = *fmt++; + } + } + *str = '\0'; + return str - buf; +} +#endif + +#if 0 +void panic(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + putc('\n'); + va_end(args); +#if defined(CONFIG_PANIC_HANG) + hang(); +#else + udelay(100000); /* allow messages to go out */ + do_reset(NULL, 0, 0, NULL); +#endif + while (1) + ; +} +#endif + +#if 0 +void __assert_fail(const char *assertion, const char *file, unsigned line, + const char *function) +{ + /* This will not return */ + panic("%s:%u: %s: Assertion `%s' failed.", file, line, function, + assertion); +} +#endif + +#if 0 +char *simple_itoa(ulong i) +{ + /* 21 digits plus null terminator, good for 64-bit or smaller ints */ + static char local[22] __attribute__((section(".data"))); + char *p = &local[21]; + + *p-- = '\0'; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; +} +#endif diff --git a/src/cpu/samsung/exynos5250/Kconfig b/src/cpu/samsung/exynos5250/Kconfig new file mode 100644 index 0000000..8d4ba2d --- /dev/null +++ b/src/cpu/samsung/exynos5250/Kconfig @@ -0,0 +1,28 @@ +config EXYNOS_ACE_SHA + bool + default n + +config SATA_AHCI + bool + default n + +config SPL_BUILD + bool + default n + +config SYS_TEXT_BASE + hex "Executable code section" + default 0x43e00000 + +config SYS_SDRAM_BASE + hex "SDRAM base address" + default 0x40000000 + +#FIXME(dhendrix, reinauer): re-visit this RAMBASE/RAMTOP stuff... +config RAMBASE + hex + default SYS_SDRAM_BASE +# according to stefan, this is RAMBASE + 1M. +config RAMTOP + hex + default 0x40100000 diff --git a/src/cpu/samsung/exynos5250/Makefile.inc b/src/cpu/samsung/exynos5250/Makefile.inc new file mode 100644 index 0000000..556631a --- /dev/null +++ b/src/cpu/samsung/exynos5250/Makefile.inc @@ -0,0 +1,32 @@ +romstage-y += clock.c +romstage-y += clock_init.c +romstage-y += exynos_cache.c +romstage-y += lowlevel_init.S +romstage-y += lowlevel_init_c.c +romstage-y += pinmux.c +romstage-y += power.c +romstage-y += soc.c +romstage-y += uart.c + +#ramstage-y += clock.c +#ramstage-y += clock_init.c +#ramstage-y += power.c +#ramstage-y += uart.c +##ramstage-y += spl.c +#ramstage-y += pinmux.c +##ramstage-y += tzpc_init.c +ramstage-y += clock.c +ramstage-y += clock_init.c +ramstage-y += exynos_cache.c +ramstage-y += lowlevel_init.S +ramstage-y += lowlevel_init_c.c +ramstage-y += pinmux.c +ramstage-y += power.c +ramstage-y += soc.c +ramstage-y += uart.c + +#ramstage-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.c +#ramstage-$(CONFIG_SATA_AHCI) += sata.c +ramstage-$(CONFIG_SPL_BUILD) += lowlevel_init_c.c +ramstage-$(CONFIG_SPL_BUILD) += dmc_common.c +ramstage-$(CONFIG_SPL_BUILD) += dmc_init_ddr3.c diff --git a/src/cpu/samsung/exynos5250/ace_sha.c b/src/cpu/samsung/exynos5250/ace_sha.c new file mode 100644 index 0000000..2715a03 --- /dev/null +++ b/src/cpu/samsung/exynos5250/ace_sha.c @@ -0,0 +1,118 @@ +/* + * Advanced Crypto Engine - SHA Firmware + * + * Copyright (c) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <common.h> +#include <asm/arch/ace_sha.h> +#include <asm/arch/ace_sfr.h> + +/* Maximum input data size is 8 MB. Timeout observed for data size above 8MB */ +#define TIMEOUT_MS 100 + +#define SHA1_DIGEST_LEN 20 +#define SHA256_DIGEST_LEN 32 + +/* SHA1 value for the message of zero length */ +static const unsigned char sha1_digest_emptymsg[SHA1_DIGEST_LEN] = { + 0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D, + 0x32, 0x55, 0xBF, 0xFF, 0x95, 0x60, 0x18, 0x90, + 0xAF, 0xD8, 0x07, 0x09}; + +/* SHA256 value for the message of zero length */ +static const unsigned char sha256_digest_emptymsg[SHA256_DIGEST_LEN] = { + 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, + 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, + 0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, + 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55}; + +int ace_sha_hash_digest( + unsigned char *pout, unsigned char *pbuf, + unsigned int buf_len, unsigned int hash_type) +{ + unsigned int i, reg, len; + unsigned int *pdigest; + ulong start; + struct exynos_ace_sfr *ace_sha_reg = + (struct exynos_ace_sfr *) samsung_get_base_ace_sfr(); + + if (buf_len == 0) { + /* ACE H/W cannot compute hash value for empty string */ + if (hash_type == ACE_SHA_TYPE_SHA1) + memcpy(pout, sha1_digest_emptymsg, SHA1_DIGEST_LEN); + else + memcpy(pout, sha256_digest_emptymsg, SHA256_DIGEST_LEN); + return 0; + } + + /* Flush HRDMA */ + writel(ACE_FC_HRDMACFLUSH_ON, &ace_sha_reg->fc_hrdmac); + writel(ACE_FC_HRDMACFLUSH_OFF, &ace_sha_reg->fc_hrdmac); + + /* Set byte swap of data in */ + writel(ACE_HASH_SWAPDI_ON | ACE_HASH_SWAPDO_ON | ACE_HASH_SWAPIV_ON, + &ace_sha_reg->hash_byteswap); + + /* Select Hash input mux as external source */ + reg = readl(&ace_sha_reg->fc_fifoctrl); + reg = (reg & ~ACE_FC_SELHASH_MASK) | ACE_FC_SELHASH_EXOUT; + writel(reg, &ace_sha_reg->fc_fifoctrl); + + /* Set Hash as SHA1 or SHA256 and start Hash engine */ + reg = (hash_type == ACE_SHA_TYPE_SHA1) ? + ACE_HASH_ENGSEL_SHA1HASH : ACE_HASH_ENGSEL_SHA256HASH; + reg |= ACE_HASH_STARTBIT_ON; + writel(reg, &ace_sha_reg->hash_control); + + /* Enable FIFO mode */ + writel(ACE_HASH_FIFO_ON, &ace_sha_reg->hash_fifo_mode); + + /* Set message length */ + writel(buf_len, &ace_sha_reg->hash_msgsize_low); + writel(0, &ace_sha_reg->hash_msgsize_high); + + /* Set HRDMA */ + writel((unsigned int)pbuf, &ace_sha_reg->fc_hrdmas); + writel(buf_len, &ace_sha_reg->fc_hrdmal); + + start = get_timer(0); + + while ((readl(&ace_sha_reg->hash_status) & ACE_HASH_MSGDONE_MASK) == + ACE_HASH_MSGDONE_OFF) { + + if (get_timer(start) > TIMEOUT_MS) { + debug("%s: Timeout waiting for ACE\n", __func__); + return -1; + } + } + + /* Clear MSG_DONE bit */ + writel(ACE_HASH_MSGDONE_ON, &ace_sha_reg->hash_status); + + /* Read hash result */ + pdigest = (unsigned int *)pout; + len = (hash_type == ACE_SHA_TYPE_SHA1) ? 5 : 8; + + for (i = 0; i < len ; i++) + pdigest[i] = readl(&ace_sha_reg->hash_result[i]); + + /* Clear HRDMA pending bit */ + writel(ACE_FC_HRDMA, &ace_sha_reg->fc_intpend); + + return 0; +} diff --git a/src/cpu/samsung/exynos5250/clock.c b/src/cpu/samsung/exynos5250/clock.c new file mode 100644 index 0000000..0250d77 --- /dev/null +++ b/src/cpu/samsung/exynos5250/clock.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2010 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <console/console.h> +#include <stdlib.h> +//#include <fdtdec.h> +#include <arch/io.h> +//#include <asm/arch/clock.h> +//#include <asm/arch/clk.h> +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> +#include <cpu/samsung/s5p-common/clk.h> + +/* input clock of PLL: SMDK5250 has 24MHz input clock */ +#define CONFIG_SYS_CLK_FREQ 24000000 + + +/* src_bit div_bit prediv_bit */ +static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = { + {0, 4, 0, -1}, + {4, 4, 4, -1}, + {8, 4, 8, -1}, + {12, 4, 12, -1}, + {0, 4, 0, 8}, + {4, 4, 16, 24}, + {8, 4, 0, 8}, + {12, 4, 16, 24}, + {-1, -1, -1, -1}, + {16, 4, 0, 8}, /* PERIPH_ID_SROMC */ + {20, 4, 16, 24}, + {24, 4, 0, 8}, + {0, 4, 0, 4}, + {4, 4, 12, 16}, + {-1, 4, -1, -1}, + {-1, 4, -1, -1}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {-1, 4, 24, 0}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {24, 4, 0, -1}, + {-1, -1, -1, -1}, + {-1, -1, -1, -1}, + {-1, -1, -1, -1}, /* PERIPH_ID_I2S1 */ + {24, 1, 20, -1}, /* PERIPH_ID_SATA */ +}; + +/* Epll Clock division values to achive different frequency output */ +static struct st_epll_con_val epll_div[] = { + { 192000000, 0, 48, 3, 1, 0 }, + { 180000000, 0, 45, 3, 1, 0 }, + { 73728000, 1, 73, 3, 3, 47710 }, + { 67737600, 1, 90, 4, 3, 20762 }, + { 49152000, 0, 49, 3, 3, 9961 }, + { 45158400, 0, 45, 3, 3, 10381 }, + { 180633600, 0, 45, 3, 1, 10381 } +}; + +/* exynos5: return pll clock frequency */ +unsigned long get_pll_clk(int pllreg) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned long r, m, p, s, k = 0, mask, fout; + unsigned int freq; + + switch (pllreg) { + case APLL: + r = readl(&clk->apll_con0); + break; + case BPLL: + r = readl(&clk->bpll_con0); + break; + case MPLL: + r = readl(&clk->mpll_con0); + break; + case EPLL: + r = readl(&clk->epll_con0); + k = readl(&clk->epll_con1); + break; + case VPLL: + r = readl(&clk->vpll_con0); + k = readl(&clk->vpll_con1); + break; + default: + printk(BIOS_DEBUG, "Unsupported PLL (%d)\n", pllreg); + return 0; + } + + /* + * APLL_CON: MIDV [25:16] + * MPLL_CON: MIDV [25:16] + * EPLL_CON: MIDV [24:16] + * VPLL_CON: MIDV [24:16] + */ + if (pllreg == APLL || pllreg == BPLL || pllreg == MPLL) + mask = 0x3ff; + else + mask = 0x1ff; + + m = (r >> 16) & mask; + + /* PDIV [13:8] */ + p = (r >> 8) & 0x3f; + /* SDIV [2:0] */ + s = r & 0x7; + + freq = CONFIG_SYS_CLK_FREQ; + + if (pllreg == EPLL) { + k = k & 0xffff; + /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */ + fout = (m + k / 65536) * (freq / (p * (1 << s))); + } else if (pllreg == VPLL) { + k = k & 0xfff; + /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */ + fout = (m + k / 1024) * (freq / (p * (1 << s))); + } else { + /* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */ + fout = m * (freq / (p * (1 << s))); + } + + return fout; +} + +unsigned long clock_get_periph_rate(enum periph_id peripheral) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + struct clk_bit_info *bit_info = &clk_bit_info[peripheral]; + unsigned long sclk, sub_clk; + unsigned int src, div, sub_div; + + switch (peripheral) { + case PERIPH_ID_UART0: + case PERIPH_ID_UART1: + case PERIPH_ID_UART2: + case PERIPH_ID_UART3: + src = readl(&clk->src_peric0); + div = readl(&clk->div_peric0); + break; + case PERIPH_ID_PWM0: + case PERIPH_ID_PWM1: + case PERIPH_ID_PWM2: + case PERIPH_ID_PWM3: + case PERIPH_ID_PWM4: + src = readl(&clk->src_peric0); + div = readl(&clk->div_peric3); + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + src = readl(&clk->src_peric1); + div = readl(&clk->div_peric1); + break; + case PERIPH_ID_SPI2: + src = readl(&clk->src_peric1); + div = readl(&clk->div_peric2); + break; + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + src = readl(&clk->sclk_src_isp); + div = readl(&clk->sclk_div_isp); + break; + case PERIPH_ID_SATA: + src = readl(&clk->src_fsys); + div = readl(&clk->div_fsys0); + break; + case PERIPH_ID_SDMMC0: + case PERIPH_ID_SDMMC1: + case PERIPH_ID_SDMMC2: + case PERIPH_ID_SDMMC3: + src = readl(&clk->src_fsys); + div = readl(&clk->div_fsys1); + break; + case PERIPH_ID_I2C0: + case PERIPH_ID_I2C1: + case PERIPH_ID_I2C2: + case PERIPH_ID_I2C3: + case PERIPH_ID_I2C4: + case PERIPH_ID_I2C5: + case PERIPH_ID_I2C6: + case PERIPH_ID_I2C7: + sclk = get_pll_clk(MPLL); + sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit) & 0x7) + 1; + div = ((readl(&clk->div_top0) >> bit_info->prediv_bit) & 0x7) + 1; + return (sclk / sub_div) / div; + default: + printk(BIOS_DEBUG, "%s: invalid peripheral %d", __func__, peripheral); + return -1; + }; + + src = (src >> bit_info->src_bit) & ((1 << bit_info->n_src_bits) - 1); + if (peripheral == PERIPH_ID_SATA) { + if (src) + sclk = get_pll_clk(BPLL); + else + sclk = get_pll_clk(MPLL); + } else { + if (src == SRC_MPLL) + sclk = get_pll_clk(MPLL); + else if (src == SRC_EPLL) + sclk = get_pll_clk(EPLL); + else if (src == SRC_VPLL) + sclk = get_pll_clk(VPLL); + else + return 0; + } + + sub_div = (div >> bit_info->div_bit) & 0xf; + sub_clk = sclk / (sub_div + 1); + + if (peripheral == PERIPH_ID_SDMMC0 || peripheral == PERIPH_ID_SDMMC2) { + div = (div >> bit_info->prediv_bit) & 0xff; + return sub_clk / (div + 1); + } + + return sub_clk; +} + +/* exynos5: return ARM clock frequency */ +unsigned long get_arm_clk(void) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned long div; + unsigned long armclk; + unsigned int arm_ratio; + unsigned int arm2_ratio; + + div = readl(&clk->div_cpu0); + + /* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */ + arm_ratio = (div >> 0) & 0x7; + arm2_ratio = (div >> 28) & 0x7; + + armclk = get_pll_clk(APLL) / (arm_ratio + 1); + armclk /= (arm2_ratio + 1); + + return armclk; +} + +/* exynos5: set the mmc clock */ +void set_mmc_clk(int dev_index, unsigned int div) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned int addr; + unsigned int val; + + /* + * CLK_DIV_FSYS1 + * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24] + * CLK_DIV_FSYS2 + * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24] + */ + if (dev_index < 2) { + addr = (unsigned int)&clk->div_fsys1; + } else { + addr = (unsigned int)&clk->div_fsys2; + dev_index -= 2; + } + + val = readl(addr); + val &= ~(0xff << ((dev_index << 4) + 8)); + val |= (div & 0xff) << ((dev_index << 4) + 8); + writel(val, addr); +} + +void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + /* + * For now we only handle a very small subset of peipherals here. + * Others will need to (and do) mangle the clock registers + * themselves, At some point it is hoped that this function can work + * from a table or calculated register offset / mask. For now this + * is at least better than spreading clock control code around + * U-Boot. + */ + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 8; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 24; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 8; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 4; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 16; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned shift; + unsigned mask = 0xff; + u32 *reg; + + switch (periph_id) { + case PERIPH_ID_SPI0: + reg = &clk->div_peric1; + shift = 0; + break; + case PERIPH_ID_SPI1: + reg = &clk->div_peric1; + shift = 16; + break; + case PERIPH_ID_SPI2: + reg = &clk->div_peric2; + shift = 0; + break; + case PERIPH_ID_SPI3: + reg = &clk->sclk_div_isp; + shift = 0; + break; + case PERIPH_ID_SPI4: + reg = &clk->sclk_div_isp; + shift = 12; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return; + } + clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift); +} + +/** + * Linearly searches for the most accurate main and fine stage clock scalars + * (divisors) for a specified target frequency and scalar bit sizes by checking + * all multiples of main_scalar_bits values. Will always return scalars up to or + * slower than target. + * + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32 + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32 + * @param input_freq Clock frequency to be scaled in Hz + * @param target_freq Desired clock frequency in Hz + * @param best_fine_scalar Pointer to store the fine stage divisor + * + * @return best_main_scalar Main scalar for desired frequency or -1 if none + * found + */ +static int clock_calc_best_scalar(unsigned int main_scaler_bits, + unsigned int fine_scalar_bits, unsigned int input_rate, + unsigned int target_rate, unsigned int *best_fine_scalar) +{ + int i; + int best_main_scalar = -1; + unsigned int best_error = target_rate; + const unsigned int cap = (1 << fine_scalar_bits) - 1; + const unsigned int loops = 1 << main_scaler_bits; + + debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate, + target_rate, cap); + + assert(best_fine_scalar != NULL); + assert(main_scaler_bits <= fine_scalar_bits); + + *best_fine_scalar = 1; + + if (input_rate == 0 || target_rate == 0) + return -1; + + if (target_rate >= input_rate) + return 1; + + for (i = 1; i <= loops; i++) { + const unsigned int effective_div = MAX(MIN(input_rate / i / + target_rate, cap), 1); + const unsigned int effective_rate = input_rate / i / + effective_div; + const int error = target_rate - effective_rate; + + debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div, + effective_rate, error); + + if (error >= 0 && error <= best_error) { + best_error = error; + best_main_scalar = i; + *best_fine_scalar = effective_div; + } + } + + return best_main_scalar; +} + +int clock_set_rate(enum periph_id periph_id, unsigned int rate) +{ + int main; + unsigned int fine; + + switch (periph_id) { + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + case PERIPH_ID_SPI3: + case PERIPH_ID_SPI4: + main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine); + if (main < 0) { + debug("%s: Cannot set clock rate for periph %d", + __func__, periph_id); + return -1; + } + clock_ll_set_ratio(periph_id, main - 1); + clock_ll_set_pre_ratio(periph_id, fine - 1); + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + + return 0; +} + +int clock_set_mshci(enum periph_id peripheral) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + u32 *addr; + unsigned int clock; + unsigned int tmp; + unsigned int i; + + /* get mpll clock */ + clock = get_pll_clk(MPLL) / 1000000; + + /* + * CLK_DIV_FSYS1 + * MMC0_PRE_RATIO [15:8], MMC0_RATIO [3:0] + * CLK_DIV_FSYS2 + * MMC2_PRE_RATIO [15:8], MMC2_RATIO [3:0] + */ + switch (peripheral) { + case PERIPH_ID_SDMMC0: + addr = &clk->div_fsys1; + break; + case PERIPH_ID_SDMMC2: + addr = &clk->div_fsys2; + break; + default: + debug("invalid peripheral\n"); + return -1; + } + tmp = readl(addr) & ~0xff0f; + for (i = 0; i <= 0xf; i++) { + if ((clock / (i + 1)) <= 400) { + writel(tmp | i << 0, addr); + break; + } + } + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int clock_decode_periph_id(const void *blob, int node) +{ + enum periph_id id; + + /* + * For now the peripheral ID is directly encoded. Once we have clock + * support in the fdt and properly in exynos U-Boot we may have + * another way of changing the clock. + */ + id = fdtdec_get_int(blob, node, "samsung,periph-id", -1); + assert(id != PERIPH_ID_NONE); + assert(id >= 0 && id < PERIPH_ID_COUNT); + + return id; +} +#endif + +int clock_epll_set_rate(unsigned long rate) +{ + unsigned int epll_con, epll_con_k; + unsigned int i; + unsigned int lockcnt; + unsigned int start; + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + + epll_con = readl(&clk->epll_con0); + epll_con &= ~((EPLL_CON0_LOCK_DET_EN_MASK << + EPLL_CON0_LOCK_DET_EN_SHIFT) | + EPLL_CON0_MDIV_MASK << EPLL_CON0_MDIV_SHIFT | + EPLL_CON0_PDIV_MASK << EPLL_CON0_PDIV_SHIFT | + EPLL_CON0_SDIV_MASK << EPLL_CON0_SDIV_SHIFT); + + for (i = 0; i < ARRAY_SIZE(epll_div); i++) { + if (epll_div[i].freq_out == rate) + break; + } + + if (i == ARRAY_SIZE(epll_div)) + return -1; + + epll_con_k = epll_div[i].k_dsm << 0; + epll_con |= epll_div[i].en_lock_det << EPLL_CON0_LOCK_DET_EN_SHIFT; + epll_con |= epll_div[i].m_div << EPLL_CON0_MDIV_SHIFT; + epll_con |= epll_div[i].p_div << EPLL_CON0_PDIV_SHIFT; + epll_con |= epll_div[i].s_div << EPLL_CON0_SDIV_SHIFT; + + /* + * Required period ( in cycles) to genarate a stable clock output. + * The maximum clock time can be up to 3000 * PDIV cycles of PLLs + * frequency input (as per spec) + */ + lockcnt = 3000 * epll_div[i].p_div; + + writel(lockcnt, &clk->epll_lock); + writel(epll_con, &clk->epll_con0); + writel(epll_con_k, &clk->epll_con1); + + start = get_timer(0); + + while (!(readl(&clk->epll_con0) & + (0x1 << EXYNOS5_EPLLCON0_LOCKED_SHIFT))) { + if (get_timer(start) > TIMEOUT_EPLL_LOCK) { + printk(BIOS_DEBUG, "%s: Timeout waiting for EPLL lock\n", __func__); + return -1; + } + } + + return 0; +} + +void clock_select_i2s_clk_source(void) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + + clrsetbits_le32(&clk->src_peric1, AUDIO1_SEL_MASK, + (CLK_SRC_SCLK_EPLL)); +} + +int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq) +{ + struct exynos5_clock *clk = + (struct exynos5_clock *)samsung_get_base_clock(); + unsigned int div ; + + if ((dst_frq == 0) || (src_frq == 0)) { + debug("%s: Invalid requency input for prescaler\n", __func__); + debug("src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + + div = (src_frq / dst_frq); + if (div > AUDIO_1_RATIO_MASK) { + debug("%s: Frequency ratio is out of range\n", __func__); + debug("src frq = %d des frq = %d ", src_frq, dst_frq); + return -1; + } + clrsetbits_le32(&clk->div_peric4, AUDIO_1_RATIO_MASK, + (div & AUDIO_1_RATIO_MASK)); + return 0; +} diff --git a/src/cpu/samsung/exynos5250/clock_init.c b/src/cpu/samsung/exynos5250/clock_init.c new file mode 100644 index 0000000..fd2a90f --- /dev/null +++ b/src/cpu/samsung/exynos5250/clock_init.c @@ -0,0 +1,1189 @@ +/* + * Clock setup for SMDK5250 board based on EXYNOS5 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#if 0 +#include <common.h> +#include <config.h> +//#include <fdtdec.h> +#include <arch/io.h> +#include <asm/arch/board.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch-exynos/spl.h> +#endif +#include <delay.h> +#include <stdlib.h> +#include <types.h> +#include <system.h> + +#include <console/console.h> + +#include <cpu/samsung/exynos5-common/spl.h> +#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/dmc.h> +#include <cpu/samsung/exynos5250/s5p-dp.h> +#include <cpu/samsung/s5p-common/clk.h> + +#include "clock_init.h" +#include "setup.h" + +//DECLARE_GLOBAL_DATA_PTR; + +struct arm_clk_ratios arm_clk_ratios[] = { + { + .arm_freq_mhz = 600, + + .apll_mdiv = 0xc8, + .apll_pdiv = 0x4, + .apll_sdiv = 0x1, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x2, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x1, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 800, + + .apll_mdiv = 0x64, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x3, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1000, + + .apll_mdiv = 0x7d, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x1, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x4, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x2, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1200, + + .apll_mdiv = 0x96, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x5, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1400, + + .apll_mdiv = 0xaf, + .apll_pdiv = 0x3, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + }, { + .arm_freq_mhz = 1700, + + .apll_mdiv = 0x1a9, + .apll_pdiv = 0x6, + .apll_sdiv = 0x0, + + .arm2_ratio = 0x0, + .apll_ratio = 0x3, + .pclk_dbg_ratio = 0x1, + .atb_ratio = 0x6, + .periph_ratio = 0x7, + .acp_ratio = 0x7, + .cpud_ratio = 0x3, + .arm_ratio = 0x0, + } +}; + +struct mem_timings mem_timings[] = { + { + .mem_manuf = MEM_MANUF_ELPIDA, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 800, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x64, + .bpll_pdiv = 0x3, + .bpll_sdiv = 0x0, + .use_bpll = 0, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010042, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x4, + .phy1_tFS = 0x4, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x7, + .zq_mode_term = 0x1, + .zq_mode_noterm = 0, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_30_OHM, + .gate_leveling_enable = 0, + }, { + .mem_manuf = MEM_MANUF_SAMSUNG, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 800, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x64, + .bpll_pdiv = 0x3, + .bpll_sdiv = 0x0, + .use_bpll = 0, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010000, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x8, + .phy1_tFS = 0x8, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x5, + .zq_mode_term = 0x1, + .zq_mode_noterm = 1, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_40_OHM, + .gate_leveling_enable = 1, + }, + { + .mem_manuf = MEM_MANUF_ELPIDA, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 780, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x82, + .bpll_pdiv = 0x4, + .bpll_sdiv = 0x0, + .use_bpll = 1, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010042, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x4, + .phy1_tFS = 0x4, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x7, + .zq_mode_term = 0x1, + .zq_mode_noterm = 0, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_30_OHM, + .gate_leveling_enable = 0, + }, { + .mem_manuf = MEM_MANUF_SAMSUNG, + .mem_type = DDR_MODE_DDR3, + .frequency_mhz = 780, + .mpll_mdiv = 0x64, + .mpll_pdiv = 0x3, + .mpll_sdiv = 0x0, + .cpll_mdiv = 0xde, + .cpll_pdiv = 0x4, + .cpll_sdiv = 0x2, + .gpll_mdiv = 0x215, + .gpll_pdiv = 0xc, + .gpll_sdiv = 0x1, + .epll_mdiv = 0x60, + .epll_pdiv = 0x3, + .epll_sdiv = 0x3, + .vpll_mdiv = 0x96, + .vpll_pdiv = 0x3, + .vpll_sdiv = 0x2, + + .bpll_mdiv = 0x82, + .bpll_pdiv = 0x4, + .bpll_sdiv = 0x0, + .use_bpll = 1, + .pclk_cdrex_ratio = 0x5, + .direct_cmd_msr = { + 0x00020018, 0x00030000, 0x00010000, 0x00000d70 + }, + .timing_ref = 0x000000bb, + .timing_row = 0x8c36660f, + .timing_data = 0x3630580b, + .timing_power = 0x41000a44, + .phy0_dqs = 0x08080808, + .phy1_dqs = 0x08080808, + .phy0_dq = 0x08080808, + .phy1_dq = 0x08080808, + .phy0_tFS = 0x8, + .phy1_tFS = 0x8, + .phy0_pulld_dqs = 0xf, + .phy1_pulld_dqs = 0xf, + + .lpddr3_ctrl_phy_reset = 0x1, + .ctrl_start_point = 0x10, + .ctrl_inc = 0x10, + .ctrl_start = 0x1, + .ctrl_dll_on = 0x1, + .ctrl_ref = 0x8, + + .ctrl_force = 0x1a, + .ctrl_rdlat = 0x0b, + .ctrl_bstlen = 0x08, + + .fp_resync = 0x8, + .iv_size = 0x7, + .dfi_init_start = 1, + .aref_en = 1, + + .rd_fetch = 0x3, + + .zq_mode_dds = 0x5, + .zq_mode_term = 0x1, + .zq_mode_noterm = 1, + + /* + * Dynamic Clock: Always Running + * Memory Burst length: 8 + * Number of chips: 1 + * Memory Bus width: 32 bit + * Memory Type: DDR3 + * Additional Latancy for PLL: 0 Cycle + */ + .memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE | + DMC_MEMCONTROL_DPWRDN_DISABLE | + DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE | + DMC_MEMCONTROL_TP_DISABLE | + DMC_MEMCONTROL_DSREF_ENABLE | + DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) | + DMC_MEMCONTROL_MEM_TYPE_DDR3 | + DMC_MEMCONTROL_MEM_WIDTH_32BIT | + DMC_MEMCONTROL_NUM_CHIP_1 | + DMC_MEMCONTROL_BL_8 | + DMC_MEMCONTROL_PZQ_DISABLE | + DMC_MEMCONTROL_MRR_BYTE_7_0, + .memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED | + DMC_MEMCONFIGx_CHIP_COL_10 | + DMC_MEMCONFIGx_CHIP_ROW_15 | + DMC_MEMCONFIGx_CHIP_BANK_8, + .membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40), + .membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80), + .prechconfig_tp_cnt = 0xff, + .dpwrdn_cyc = 0xff, + .dsref_cyc = 0xffff, + .concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE | + DMC_CONCONTROL_TIMEOUT_LEVEL0 | + DMC_CONCONTROL_RD_FETCH_DISABLE | + DMC_CONCONTROL_EMPTY_DISABLE | + DMC_CONCONTROL_AREF_EN_DISABLE | + DMC_CONCONTROL_IO_PD_CON_DISABLE, + .dmc_channels = 2, + .chips_per_channel = 2, + .chips_to_configure = 1, + .send_zq_init = 1, + .impedance = IMP_OUTPUT_DRV_40_OHM, + .gate_leveling_enable = 1, + } +}; + +/** + * Detect what memory is present based on board strappings + * + * Boards have various resistor stuff options that are supposed to match + * which SDRAM is present (and which revision of the board this is). This + * uses the resistor stuff options to figure out what memory manufacturer + * to use for matching in the memory tables. + * + * @return A MEM_MANUF_XXX constant, or -1 if an error occurred. + */ +/* + * FIXME(dhendrix): This unwinds into a mess of board-specific code. The + * board's romstage.c file should detect the memory type and pass in + * appropriate parameters to whatever calls this. + */ +#define BOARD_REV_ELPIDA_MEMORY 3 +#define BOARD_REV_SAMSUNG_MEMORY 4 +static int autodetect_memory(void) +{ +// int board_rev = board_get_revision(); + int board_rev = BOARD_REV_ELPIDA_MEMORY; + + if (board_rev == -1) + return -1; + + switch (board_rev) { + case BOARD_REV_SAMSUNG_MEMORY: + return MEM_MANUF_SAMSUNG; + case BOARD_REV_ELPIDA_MEMORY: + return MEM_MANUF_ELPIDA; + } + + return -1; +} + +#ifdef CONFIG_SPL_BUILD + +/** + * Get the required memory type and speed (SPL version). + * + * In SPL we have no device tree, so we use the machine parameters + */ +int clock_get_mem_selection(enum ddr_mode *mem_type, + unsigned *frequency_mhz, unsigned *arm_freq, + enum mem_manuf *mem_manuf) +{ + struct spl_machine_param *params; + + params = spl_get_machine_params(); + *mem_type = params->mem_type; + *frequency_mhz = params->frequency_mhz; + *arm_freq = params->arm_freq_mhz; + if (params->mem_manuf == MEM_MANUF_AUTODETECT) { + *mem_manuf = autodetect_memory(); + if (*mem_manuf == -1) + return -1; + } else { + *mem_manuf = params->mem_manuf; + } + + return 0; +} + +#else + +static const char *mem_types[DDR_MODE_COUNT] = { + "DDR2", "DDR3", "LPDDR2", "LPDDR3" +}; + +static const char *mem_manufs[MEM_MANUF_COUNT] = { + "autodetect", "Elpida", "Samsung" +}; + +int clock_get_mem_selection(enum ddr_mode *mem_type, + unsigned *frequency_mhz, unsigned *arm_freq, + enum mem_manuf *mem_manuf) +{ + const char *typestr; + int node, i; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SAMSUNG_EXYNOS_DMC); + if (node < 0) + die("No memory information available in device tree"); + typestr = fdt_getprop(gd->fdt_blob, node, "mem-type", NULL); + for (i = 0; i < DDR_MODE_COUNT; i++) { + if (!stricmp(typestr, mem_types[i])) + break; + } + if (i == DDR_MODE_COUNT) + die("Invalid memory type in device tree"); + *mem_type = i; + + typestr = fdt_getprop(gd->fdt_blob, node, "mem-manuf", NULL); + for (i = 0; i < MEM_MANUF_COUNT; i++) { + if (!stricmp(typestr, mem_manufs[i])) + break; + } + if (i == MEM_MANUF_COUNT) + die("Invalid memory manufacturer in device tree"); + + if (i == MEM_MANUF_AUTODETECT) { + *mem_manuf = autodetect_memory(); + if (*mem_manuf == -1) + return -1; + } else { + *mem_manuf = i; + } + + *frequency_mhz = fdtdec_get_int(gd->fdt_blob, node, "clock-frequency", + 0); + if (!*frequency_mhz) + die("Invalid memory frequency in device tree"); + + *arm_freq = fdtdec_get_int(gd->fdt_blob, node, "arm-frequency", 0); + /* TODO: Remove all these panics/dies, and just return an error code */ + if (!*arm_freq) + die("Invalid ARM frequency in device tree"); + + return 0; +} + +const char *clock_get_mem_type_name(enum ddr_mode mem_type) +{ + if (mem_type >= 0 && mem_type < DDR_MODE_COUNT) + return mem_types[mem_type]; + + return "<unknown>"; +} + +const char *clock_get_mem_manuf_name(enum mem_manuf mem_manuf) +{ + if (mem_manuf >= 0 && mem_manuf < MEM_MANUF_COUNT) + return mem_manufs[mem_manuf]; + + return "<unknown>"; +} +#endif + +/* Get the ratios for setting ARM clock */ +struct arm_clk_ratios *get_arm_ratios(void); /* FIXME: silence compiler... */ +struct arm_clk_ratios *get_arm_ratios(void) +{ + struct arm_clk_ratios *arm_ratio; + enum ddr_mode mem_type; + enum mem_manuf mem_manuf; + unsigned frequency_mhz, arm_freq; + int i; + + /* TODO(sjg@chromium.org): Return NULL and have caller deal with it */ + if (clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf)) + ; + for (i = 0, arm_ratio = arm_clk_ratios; i < ARRAY_SIZE(arm_clk_ratios); + i++, arm_ratio++) { + if (arm_ratio->arm_freq_mhz == arm_freq) + return arm_ratio; + } + + die("get_arm_ratios: Failed to find ratio\n"); + return NULL; +} + +struct mem_timings *clock_get_mem_timings(void) +{ + struct mem_timings *mem; + enum ddr_mode mem_type; + enum mem_manuf mem_manuf; + unsigned frequency_mhz, arm_freq; + int i; + + if (!clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf)) { + for (i = 0, mem = mem_timings; i < ARRAY_SIZE(mem_timings); + i++, mem++) { + if (mem->mem_type == mem_type && + mem->frequency_mhz == frequency_mhz && + mem->mem_manuf == mem_manuf) + return mem; + } + } + /* TODO: Call panic() here */ + while (1) + ; + return NULL; +} + +void system_clock_init() +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + struct exynos5_mct_regs *mct_regs = + (struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE; + struct mem_timings *mem; + struct arm_clk_ratios *arm_clk_ratio; + u32 val, tmp; + + /* Turn on the MCT as early as possible. */ + mct_regs->g_tcon |= (1 << 8); + + mem = clock_get_mem_timings(); + arm_clk_ratio = get_arm_ratios(); + + clrbits_le32(&clk->src_cpu, MUX_APLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_cpu); + } while ((val | MUX_APLL_SEL_MASK) != val); + + clrbits_le32(&clk->src_core1, MUX_MPLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_core1); + } while ((val | MUX_MPLL_SEL_MASK) != val); + + clrbits_le32(&clk->src_top2, MUX_CPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_EPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_VPLL_SEL_MASK); + clrbits_le32(&clk->src_top2, MUX_GPLL_SEL_MASK); + tmp = MUX_CPLL_SEL_MASK | MUX_EPLL_SEL_MASK | MUX_VPLL_SEL_MASK + | MUX_GPLL_SEL_MASK; + do { + val = readl(&clk->mux_stat_top2); + } while ((val | tmp) != val); + + clrbits_le32(&clk->src_cdrex, MUX_BPLL_SEL_MASK); + do { + val = readl(&clk->mux_stat_cdrex); + } while ((val | MUX_BPLL_SEL_MASK) != val); + + /* PLL locktime */ + writel(APLL_LOCK_VAL, &clk->apll_lock); + + writel(MPLL_LOCK_VAL, &clk->mpll_lock); + + writel(BPLL_LOCK_VAL, &clk->bpll_lock); + + writel(CPLL_LOCK_VAL, &clk->cpll_lock); + + writel(GPLL_LOCK_VAL, &clk->gpll_lock); + + writel(EPLL_LOCK_VAL, &clk->epll_lock); + + writel(VPLL_LOCK_VAL, &clk->vpll_lock); + + writel(CLK_REG_DISABLE, &clk->pll_div2_sel); + + writel(MUX_HPM_SEL_MASK, &clk->src_cpu); + do { + val = readl(&clk->mux_stat_cpu); + } while ((val | HPM_SEL_SCLK_MPLL) != val); + + val = arm_clk_ratio->arm2_ratio << 28 + | arm_clk_ratio->apll_ratio << 24 + | arm_clk_ratio->pclk_dbg_ratio << 20 + | arm_clk_ratio->atb_ratio << 16 + | arm_clk_ratio->periph_ratio << 12 + | arm_clk_ratio->acp_ratio << 8 + | arm_clk_ratio->cpud_ratio << 4 + | arm_clk_ratio->arm_ratio; + writel(val, &clk->div_cpu0); + do { + val = readl(&clk->div_stat_cpu0); + } while (0 != val); + + writel(CLK_DIV_CPU1_VAL, &clk->div_cpu1); + do { + val = readl(&clk->div_stat_cpu1); + } while (0 != val); + + /* Set APLL */ + writel(APLL_CON1_VAL, &clk->apll_con1); + val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv, + arm_clk_ratio->apll_sdiv); + writel(val, &clk->apll_con0); + while ((readl(&clk->apll_con0) & APLL_CON0_LOCKED) == 0) + ; + + /* Set MPLL */ + writel(MPLL_CON1_VAL, &clk->mpll_con1); + val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv); + writel(val, &clk->mpll_con0); + while ((readl(&clk->mpll_con0) & MPLL_CON0_LOCKED) == 0) + ; + + /* + * Configure MUX_MPLL_FOUT to choose the direct clock source + * path and avoid the fixed DIV/2 block to save power + */ + setbits_le32(&clk->pll_div2_sel, MUX_MPLL_FOUT_SEL); + + /* Set BPLL */ + if (mem->use_bpll) { + writel(BPLL_CON1_VAL, &clk->bpll_con1); + val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv); + writel(val, &clk->bpll_con0); + while ((readl(&clk->bpll_con0) & BPLL_CON0_LOCKED) == 0) + ; + + setbits_le32(&clk->pll_div2_sel, MUX_BPLL_FOUT_SEL); + } + + /* Set CPLL */ + writel(CPLL_CON1_VAL, &clk->cpll_con1); + val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv); + writel(val, &clk->cpll_con0); + while ((readl(&clk->cpll_con0) & CPLL_CON0_LOCKED) == 0) + ; + + /* Set GPLL */ + writel(GPLL_CON1_VAL, &clk->gpll_con1); + val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv); + writel(val, &clk->gpll_con0); + while ((readl(&clk->gpll_con0) & GPLL_CON0_LOCKED) == 0) + ; + + /* Set EPLL */ + writel(EPLL_CON2_VAL, &clk->epll_con2); + writel(EPLL_CON1_VAL, &clk->epll_con1); + val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv); + writel(val, &clk->epll_con0); + while ((readl(&clk->epll_con0) & EPLL_CON0_LOCKED) == 0) + ; + + /* Set VPLL */ + writel(VPLL_CON2_VAL, &clk->vpll_con2); + writel(VPLL_CON1_VAL, &clk->vpll_con1); + val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv); + writel(val, &clk->vpll_con0); + while ((readl(&clk->vpll_con0) & VPLL_CON0_LOCKED) == 0) + ; + + writel(CLK_SRC_CORE0_VAL, &clk->src_core0); + writel(CLK_DIV_CORE0_VAL, &clk->div_core0); + while (readl(&clk->div_stat_core0) != 0) + ; + + writel(CLK_DIV_CORE1_VAL, &clk->div_core1); + while (readl(&clk->div_stat_core1) != 0) + ; + + writel(CLK_DIV_SYSRGT_VAL, &clk->div_sysrgt); + while (readl(&clk->div_stat_sysrgt) != 0) + ; + + writel(CLK_DIV_ACP_VAL, &clk->div_acp); + while (readl(&clk->div_stat_acp) != 0) + ; + + writel(CLK_DIV_SYSLFT_VAL, &clk->div_syslft); + while (readl(&clk->div_stat_syslft) != 0) + ; + + writel(CLK_SRC_TOP0_VAL, &clk->src_top0); + writel(CLK_SRC_TOP1_VAL, &clk->src_top1); + writel(TOP2_VAL, &clk->src_top2); + writel(CLK_SRC_TOP3_VAL, &clk->src_top3); + + writel(CLK_DIV_TOP0_VAL, &clk->div_top0); + while (readl(&clk->div_stat_top0)) + ; + + writel(CLK_DIV_TOP1_VAL, &clk->div_top1); + while (readl(&clk->div_stat_top1)) + ; + + writel(CLK_SRC_LEX_VAL, &clk->src_lex); + while (1) { + val = readl(&clk->mux_stat_lex); + if (val == (val | 1)) + break; + } + + writel(CLK_DIV_LEX_VAL, &clk->div_lex); + while (readl(&clk->div_stat_lex)) + ; + + writel(CLK_DIV_R0X_VAL, &clk->div_r0x); + while (readl(&clk->div_stat_r0x)) + ; + + writel(CLK_DIV_R0X_VAL, &clk->div_r0x); + while (readl(&clk->div_stat_r0x)) + ; + + writel(CLK_DIV_R1X_VAL, &clk->div_r1x); + while (readl(&clk->div_stat_r1x)) + ; + + if (mem->use_bpll) { + writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL | + MUX_MCLK_DPHY_SEL, &clk->src_cdrex); + } else { + writel(CLK_REG_DISABLE, &clk->src_cdrex); + } + + writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex); + while (readl(&clk->div_stat_cdrex)) + ; + + val = readl(&clk->src_cpu); + val |= CLK_SRC_CPU_VAL; + writel(val, &clk->src_cpu); + + val = readl(&clk->src_top2); + val |= CLK_SRC_TOP2_VAL; + writel(val, &clk->src_top2); + + val = readl(&clk->src_core1); + val |= CLK_SRC_CORE1_VAL; + writel(val, &clk->src_core1); + + writel(CLK_SRC_FSYS0_VAL, &clk->src_fsys); + writel(CLK_DIV_FSYS0_VAL, &clk->div_fsys0); + while (readl(&clk->div_stat_fsys0)) + ; + + writel(CLK_REG_DISABLE, &clk->clkout_cmu_cpu); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_core); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_acp); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_top); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_lex); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_r0x); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_r1x); + writel(CLK_REG_DISABLE, &clk->clkout_cmu_cdrex); + + writel(CLK_SRC_PERIC0_VAL, &clk->src_peric0); + writel(CLK_DIV_PERIC0_VAL, &clk->div_peric0); + + writel(CLK_SRC_PERIC1_VAL, &clk->src_peric1); + writel(CLK_DIV_PERIC1_VAL, &clk->div_peric1); + writel(CLK_DIV_PERIC2_VAL, &clk->div_peric2); + writel(SCLK_SRC_ISP_VAL, &clk->sclk_src_isp); + writel(SCLK_DIV_ISP_VAL, &clk->sclk_div_isp); + writel(CLK_DIV_ISP0_VAL, &clk->div_isp0); + writel(CLK_DIV_ISP1_VAL, &clk->div_isp1); + writel(CLK_DIV_ISP2_VAL, &clk->div_isp2); + + /* FIMD1 SRC CLK SELECTION */ + writel(CLK_SRC_DISP1_0_VAL, &clk->src_disp1_0); + + val = MMC2_PRE_RATIO_VAL << MMC2_PRE_RATIO_OFFSET + | MMC2_RATIO_VAL << MMC2_RATIO_OFFSET + | MMC3_PRE_RATIO_VAL << MMC3_PRE_RATIO_OFFSET + | MMC3_RATIO_VAL << MMC3_RATIO_OFFSET; + writel(val, &clk->div_fsys2); +} + +void clock_gate(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + /* CLK_GATE_IP_SYSRGT */ + clrbits_le32(&clk->gate_ip_sysrgt, CLK_C2C_MASK); + + /* CLK_GATE_IP_ACP */ + clrbits_le32(&clk->gate_ip_acp, CLK_SMMUG2D_MASK | + CLK_SMMUSSS_MASK | + CLK_SMMUMDMA_MASK | + CLK_ID_REMAPPER_MASK | + CLK_G2D_MASK | + CLK_SSS_MASK | + CLK_MDMA_MASK | + CLK_SECJTAG_MASK); + + /* CLK_GATE_BUS_SYSLFT */ + clrbits_le32(&clk->gate_bus_syslft, CLK_EFCLK_MASK); + + /* CLK_GATE_IP_ISP0 */ + clrbits_le32(&clk->gate_ip_isp0, CLK_UART_ISP_MASK | + CLK_WDT_ISP_MASK | + CLK_PWM_ISP_MASK | + CLK_MTCADC_ISP_MASK | + CLK_I2C1_ISP_MASK | + CLK_I2C0_ISP_MASK | + CLK_MPWM_ISP_MASK | + CLK_MCUCTL_ISP_MASK | + CLK_INT_COMB_ISP_MASK | + CLK_SMMU_MCUISP_MASK | + CLK_SMMU_SCALERP_MASK | + CLK_SMMU_SCALERC_MASK | + CLK_SMMU_FD_MASK | + CLK_SMMU_DRC_MASK | + CLK_SMMU_ISP_MASK | + CLK_GICISP_MASK | + CLK_ARM9S_MASK | + CLK_MCUISP_MASK | + CLK_SCALERP_MASK | + CLK_SCALERC_MASK | + CLK_FD_MASK | + CLK_DRC_MASK | + CLK_ISP_MASK); + + /* CLK_GATE_IP_ISP1 */ + clrbits_le32(&clk->gate_ip_isp1, CLK_SPI1_ISP_MASK | + CLK_SPI0_ISP_MASK | + CLK_SMMU3DNR_MASK | + CLK_SMMUDIS1_MASK | + CLK_SMMUDIS0_MASK | + CLK_SMMUODC_MASK | + CLK_3DNR_MASK | + CLK_DIS_MASK | + CLK_ODC_MASK); + + /* CLK_GATE_SCLK_ISP */ + clrbits_le32(&clk->gate_sclk_isp, SCLK_MPWM_ISP_MASK); + + /* CLK_GATE_IP_GSCL */ + clrbits_le32(&clk->gate_ip_gscl, CLK_SMMUFIMC_LITE2_MASK | + CLK_SMMUFIMC_LITE1_MASK | + CLK_SMMUFIMC_LITE0_MASK | + CLK_SMMUGSCL3_MASK | + CLK_SMMUGSCL2_MASK | + CLK_SMMUGSCL1_MASK | + CLK_SMMUGSCL0_MASK | + CLK_GSCL_WRAP_B_MASK | + CLK_GSCL_WRAP_A_MASK | + CLK_CAMIF_TOP_MASK | + CLK_GSCL3_MASK | + CLK_GSCL2_MASK | + CLK_GSCL1_MASK | + CLK_GSCL0_MASK); + + /* CLK_GATE_IP_DISP1 */ + clrbits_le32(&clk->gate_ip_disp1, CLK_SMMUTVX_MASK | + CLK_ASYNCTVX_MASK | + CLK_HDMI_MASK | + CLK_MIXER_MASK | + CLK_DSIM1_MASK); + + /* CLK_GATE_IP_MFC */ + clrbits_le32(&clk->gate_ip_mfc, CLK_SMMUMFCR_MASK | + CLK_SMMUMFCL_MASK | + CLK_MFC_MASK); + + /* CLK_GATE_IP_GEN */ + clrbits_le32(&clk->gate_ip_gen, CLK_SMMUMDMA1_MASK | + CLK_SMMUJPEG_MASK | + CLK_SMMUROTATOR_MASK | + CLK_MDMA1_MASK | + CLK_JPEG_MASK | + CLK_ROTATOR_MASK); + + /* CLK_GATE_IP_FSYS */ + clrbits_le32(&clk->gate_ip_fsys, CLK_WDT_IOP_MASK | + CLK_SMMUMCU_IOP_MASK | + CLK_SATA_PHY_I2C_MASK | + CLK_SATA_PHY_CTRL_MASK | + CLK_MCUCTL_MASK | + CLK_NFCON_MASK | + CLK_SMMURTIC_MASK | + CLK_RTIC_MASK | + CLK_MIPI_HSI_MASK | + CLK_USBOTG_MASK | + CLK_SATA_MASK | + CLK_PDMA1_MASK | + CLK_PDMA0_MASK | + CLK_MCU_IOP_MASK); + + /* CLK_GATE_IP_PERIC */ + clrbits_le32(&clk->gate_ip_peric, CLK_HS_I2C3_MASK | + CLK_HS_I2C2_MASK | + CLK_HS_I2C1_MASK | + CLK_HS_I2C0_MASK | + CLK_AC97_MASK | + CLK_SPDIF_MASK | + CLK_PCM2_MASK | + CLK_PCM1_MASK | + CLK_I2S2_MASK | + CLK_SPI2_MASK | + CLK_SPI0_MASK); + + /* CLK_GATE_IP_PERIS */ + clrbits_le32(&clk->gate_ip_peris, CLK_RTC_MASK | + CLK_TZPC9_MASK | + CLK_TZPC8_MASK | + CLK_TZPC7_MASK | + CLK_TZPC6_MASK | + CLK_TZPC5_MASK | + CLK_TZPC4_MASK | + CLK_TZPC3_MASK | + CLK_TZPC2_MASK | + CLK_TZPC1_MASK | + CLK_TZPC0_MASK | + CLK_CHIPID_MASK); + + /* CLK_GATE_BLOCK */ + clrbits_le32(&clk->gate_block, CLK_ACP_MASK); + + /* CLK_GATE_IP_CDREX */ + clrbits_le32(&clk->gate_ip_cdrex, CLK_DPHY0_MASK | + CLK_DPHY1_MASK | + CLK_TZASC_DRBXR_MASK); + +} + +void clock_init_dp_clock(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + /* DP clock enable */ + setbits_le32(&clk->gate_ip_disp1, CLK_GATE_DP1_ALLOW); + + /* We run DP at 267 Mhz */ + setbits_le32(&clk->div_disp1_0, CLK_DIV_DISP1_0_FIMD1); +} + +#ifdef CONFIG_SPL_BUILD +/* + * This is a custom implementation for the udelay(), as we do not the timer + * initialise during the SPL boot. We are assuming the cpu takes 3 instruction + * pre cycle. This is based on the implementation of sdelay() function. + */ +void udelay(unsigned usec) +{ + unsigned long count; + + /* TODO(alim.akhtar@samsung.com): Comment on why divided by 30000000 */ + count = usec * (get_pll_clk(APLL) / (3 * 10000000)); + sdelay(count); +} +#endif diff --git a/src/cpu/samsung/exynos5250/dmc_common.c b/src/cpu/samsung/exynos5250/dmc_common.c new file mode 100644 index 0000000..35a13ac --- /dev/null +++ b/src/cpu/samsung/exynos5250/dmc_common.c @@ -0,0 +1,200 @@ +/* + * Mem setup common file for different types of DDR present on SMDK5250 boards. + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <assert.h> +#include <common.h> +#include <console/console.h> +#include <cpu/samsung/exynos5250/setup.h> +#include <cpu/samsung/exynos5-common/spl.h> +#include <system.h> + +#include "clock_init.h" +#include "setup.h" + +#define ZQ_INIT_TIMEOUT 10000 + +int dmc_config_zq(struct mem_timings *mem, + struct exynos5_phy_control *phy0_ctrl, + struct exynos5_phy_control *phy1_ctrl) +{ + unsigned long val = 0; + int i; + + /* + * ZQ Calibration: + * Select Driver Strength, + * long calibration for manual calibration + */ + val = PHY_CON16_RESET_VAL; + val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT; + val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT; + val |= ZQ_CLK_DIV_EN; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* Disable termination */ + if (mem->zq_mode_noterm) + val |= PHY_CON16_ZQ_MODE_NOTERM_MASK; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Enable */ + val |= ZQ_MANUAL_STR; + writel(val, &phy0_ctrl->phy_con16); + writel(val, &phy1_ctrl->phy_con16); + + /* ZQ_MANUAL_START: Disable */ + val &= ~ZQ_MANUAL_STR; + + /* + * Since we are manaully calibrating the ZQ values, + * we are looping for the ZQ_init to complete. + */ + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + sdelay(100); + i--; + } + if (!i) + return -1; + writel(val, &phy0_ctrl->phy_con16); + + i = ZQ_INIT_TIMEOUT; + while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) { + sdelay(100); + i--; + } + if (!i) + return -1; + writel(val, &phy1_ctrl->phy_con16); + + return 0; +} + +void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode) +{ + unsigned long val; + + if (mode == DDR_MODE_DDR3) { + val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE; + writel(val, &dmc->phycontrol0); + } + + /* Update DLL Information: Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val |= FP_RSYNC; + writel(val, &dmc->phycontrol0); + + /* Reset Force DLL Resyncronization */ + val = readl(&dmc->phycontrol0); + val &= ~FP_RSYNC; + writel(val, &dmc->phycontrol0); +} + +void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_to_configure; chip++) { + int i; + + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* Sending NOP command */ + writel(DIRECT_CMD_NOP | mask, &dmc->directcmd); + + /* + * TODO(alim.akhtar@samsung.com): Do we need these + * delays? This one and the next were not there for + * DDR3. + */ + sdelay(0x10000); + + /* Sending EMRS/MRS commands */ + for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) { + writel(mem->direct_cmd_msr[i] | mask, + &dmc->directcmd); + sdelay(0x10000); + } + + if (mem->send_zq_init) { + /* Sending ZQINIT command */ + writel(DIRECT_CMD_ZQINIT | mask, + &dmc->directcmd); + + sdelay(10000); + } + } + } +} + +void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + int channel, chip; + + for (channel = 0; channel < mem->dmc_channels; channel++) { + unsigned long mask; + + mask = channel << DIRECT_CMD_CHANNEL_SHIFT; + for (chip = 0; chip < mem->chips_per_channel; chip++) { + mask |= chip << DIRECT_CMD_CHIP_SHIFT; + + /* PALL (all banks precharge) CMD */ + writel(DIRECT_CMD_PALL | mask, &dmc->directcmd); + sdelay(0x10000); + } + } +} + +void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc) +{ + writel(mem->memconfig, &dmc->memconfig0); + writel(mem->memconfig, &dmc->memconfig1); + writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0); + writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1); +} + +void mem_ctrl_init() +{ + struct spl_machine_param *param = spl_get_machine_params(); + struct mem_timings *mem; + int ret; + + mem = clock_get_mem_timings(); + + /* If there are any other memory variant, add their init call below */ + if (param->mem_type == DDR_MODE_DDR3) { + ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size); + if (ret) { + printk(BIOS_ERR, "Memory controller init failed, err: %u", ret); + BUG(); + } + } else { + die("Unknown memory type"); + } +} diff --git a/src/cpu/samsung/exynos5250/dmc_init_ddr3.c b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c new file mode 100644 index 0000000..6ef10bf --- /dev/null +++ b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c @@ -0,0 +1,249 @@ +/* + * DDR3 mem setup file for SMDK5250 board based on EXYNOS5 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <arch/io.h> +//#include "clock.h" +/* FIXME(dhendrix): untangle clock/clk ... */ +#include <cpu/samsung/s5p-common/clock.h> +#include <system.h> +#include "clk.h" +#include "cpu.h" +#include "dmc.h" +#include "setup.h" +#include "clock_init.h" + +#define RDLVL_COMPLETE_TIMEOUT 10000 + +static void reset_phy_ctrl(void) +{ + struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE; + + writel(LPDDR3PHY_CTRL_PHY_RESET_OFF, &clk->lpddr3phy_ctrl); + writel(LPDDR3PHY_CTRL_PHY_RESET, &clk->lpddr3phy_ctrl); + + /* + * For proper memory initialization there should be a minimum delay of + * 500us after the LPDDR3PHY_CTRL_PHY_RESET signal. + * The below value is an approximate value whose calculation in done + * considering that sdelay takes 2 instruction for every 1 delay cycle. + * And assuming each instruction takes 1 clock cycle i.e 1/(1.7 Ghz)sec + * So for 500 usec, the number of delay cycle should be + * (500 * 10^-6) * (1.7 * 10^9) / 2 = 425000 + * + * TODO(hatim.rv@samsung.com): Implement the delay using timer/counter + */ + sdelay(425000); +} + +int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size) +{ + unsigned int val; + struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl; + struct exynos5_dmc *dmc; + int i; + + phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE; + phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE; + dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE; + + reset_phy_ctrl(); + + /* Set Impedance Output Driver */ + val = (mem->impedance << CA_CK_DRVR_DS_OFFSET) | + (mem->impedance << CA_CKE_DRVR_DS_OFFSET) | + (mem->impedance << CA_CS_DRVR_DS_OFFSET) | + (mem->impedance << CA_ADR_DRVR_DS_OFFSET); + writel(val, &phy0_ctrl->phy_con39); + writel(val, &phy1_ctrl->phy_con39); + + /* Set Read Latency and Burst Length for PHY0 and PHY1 */ + val = (mem->ctrl_bstlen << PHY_CON42_CTRL_BSTLEN_SHIFT) | + (mem->ctrl_rdlat << PHY_CON42_CTRL_RDLAT_SHIFT); + writel(val, &phy0_ctrl->phy_con42); + writel(val, &phy1_ctrl->phy_con42); + + /* ZQ Calibration */ + if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl)) + return SETUP_ERR_ZQ_CALIBRATION_FAILURE; + + /* DQ Signal */ + writel(mem->phy0_pulld_dqs, &phy0_ctrl->phy_con14); + writel(mem->phy1_pulld_dqs, &phy1_ctrl->phy_con14); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT) + | (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT), + &dmc->concontrol); + + update_reset_dll(dmc, DDR_MODE_DDR3); + + /* DQS Signal */ + writel(mem->phy0_dqs, &phy0_ctrl->phy_con4); + writel(mem->phy1_dqs, &phy1_ctrl->phy_con4); + + writel(mem->phy0_dq, &phy0_ctrl->phy_con6); + writel(mem->phy1_dq, &phy1_ctrl->phy_con6); + + writel(mem->phy0_tFS, &phy0_ctrl->phy_con10); + writel(mem->phy1_tFS, &phy1_ctrl->phy_con10); + + val = (mem->ctrl_start_point << PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + /* Start DLL locking */ + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &phy0_ctrl->phy_con12); + writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT), + &phy1_ctrl->phy_con12); + + update_reset_dll(dmc, DDR_MODE_DDR3); + + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT), + &dmc->concontrol); + + /* Memory Channel Inteleaving Size */ + writel(mem->iv_size, &dmc->ivcontrol); + + /* Set DMC MEMCONTROL register */ + val = mem->memcontrol & ~DMC_MEMCONTROL_DSREF_ENABLE; + writel(val, &dmc->memcontrol); + + writel(mem->memconfig, &dmc->memconfig0); + writel(mem->memconfig, &dmc->memconfig1); + writel(mem->membaseconfig0, &dmc->membaseconfig0); + writel(mem->membaseconfig1, &dmc->membaseconfig1); + + /* Precharge Configuration */ + writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT, + &dmc->prechconfig); + + /* Power Down mode Configuration */ + writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT | + mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT, + &dmc->pwrdnconfig); + + /* TimingRow, TimingData, TimingPower and Timingaref + * values as per Memory AC parameters + */ + writel(mem->timing_ref, &dmc->timingref); + writel(mem->timing_row, &dmc->timingrow); + writel(mem->timing_data, &dmc->timingdata); + writel(mem->timing_power, &dmc->timingpower); + + /* Send PALL command */ + dmc_config_prech(mem, dmc); + + /* Send NOP, MRS and ZQINIT commands */ + dmc_config_mrs(mem, dmc); + + if (mem->gate_leveling_enable) { + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + writel(val, &phy0_ctrl->phy_con2); + writel(val, &phy1_ctrl->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = (mem->ctrl_start_point << + PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) | + (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + val = PHY_CON2_RESET_VAL; + val |= INIT_DESKEW_EN; + val |= RDLVL_GATE_EN; + writel(val, &phy0_ctrl->phy_con2); + writel(val, &phy1_ctrl->phy_con2); + + val = PHY_CON0_RESET_VAL; + val |= P0_CMD_EN; + val |= BYTE_RDLVL_EN; + val |= CTRL_SHGATE; + writel(val, &phy0_ctrl->phy_con0); + writel(val, &phy1_ctrl->phy_con0); + + val = PHY_CON1_RESET_VAL; + val &= ~(CTRL_GATEDURADJ_MASK); + writel(val, &phy0_ctrl->phy_con1); + writel(val, &phy1_ctrl->phy_con1); + + writel(CTRL_RDLVL_GATE_ENABLE, &dmc->rdlvl_config); + i = RDLVL_COMPLETE_TIMEOUT; + while ((readl(&dmc->phystatus) & + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) != + (RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) { + /* + * TODO(waihong): Comment on how long this take to + * timeout + */ + sdelay(100); + i--; + } + if (!i) + return SETUP_ERR_RDLV_COMPLETE_TIMEOUT; + writel(CTRL_RDLVL_GATE_DISABLE, &dmc->rdlvl_config); + + writel(0, &phy0_ctrl->phy_con14); + writel(0, &phy1_ctrl->phy_con14); + + val = (mem->ctrl_start_point << + PHY_CON12_CTRL_START_POINT_SHIFT) | + (mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) | + (mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) | + (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) | + (mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) | + (mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT); + writel(val, &phy0_ctrl->phy_con12); + writel(val, &phy1_ctrl->phy_con12); + + update_reset_dll(dmc, DDR_MODE_DDR3); + } + + /* Send PALL command */ + dmc_config_prech(mem, dmc); + + writel(mem->memcontrol, &dmc->memcontrol); + + /* Set DMC Concontrol and enable auto-refresh counter */ + writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT) + | (mem->aref_en << CONCONTROL_AREF_EN_SHIFT), &dmc->concontrol); + return 0; +} diff --git a/src/cpu/samsung/exynos5250/exynos_cache.c b/src/cpu/samsung/exynos5250/exynos_cache.c new file mode 100644 index 0000000..ec858d1 --- /dev/null +++ b/src/cpu/samsung/exynos5250/exynos_cache.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Samsung Electronics. + * Arun Mankuzhi arun.m@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <system.h> + +#include <armv7.h> + +enum l2_cache_params { + CACHE_TAG_RAM_SETUP = (1<<9), + CACHE_DATA_RAM_SETUP = (1<<5), + CACHE_TAG_RAM_LATENCY = (2<<6), + CACHE_DATA_RAM_LATENCY = (2<<0) +}; + + +/* FIXME(dhendrix): maybe move this to a romstage-specific file? */ +#ifdef __PRE_RAM__ +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} +#endif + +/* + * Set L2 cache parameters + */ +static void exynos5_set_l2cache_params(void) +{ + unsigned int val = 0; + + asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r"(val)); + + val |= CACHE_TAG_RAM_SETUP | + CACHE_DATA_RAM_SETUP | + CACHE_TAG_RAM_LATENCY | + CACHE_DATA_RAM_LATENCY; + + asm volatile("mcr p15, 1, %0, c9, c0, 2\n" : : "r"(val)); +} + +/* + * Sets L2 cache related parameters before enabling data cache + */ +void v7_outer_cache_enable(void) +{ + exynos5_set_l2cache_params(); +} + +/* stubs so we don't need weak symbols in cache_v7.c */ +void v7_outer_cache_disable(void) +{ +} + +void v7_outer_cache_flush_all(void) +{ +} + +void v7_outer_cache_inval_all(void) +{ +} + +void v7_outer_cache_flush_range(u32 start, u32 end) +{ +} + +void v7_outer_cache_inval_range(u32 start, u32 end) +{ +} diff --git a/src/cpu/samsung/exynos5250/lowlevel_init.S b/src/cpu/samsung/exynos5250/lowlevel_init.S new file mode 100644 index 0000000..883db9d --- /dev/null +++ b/src/cpu/samsung/exynos5250/lowlevel_init.S @@ -0,0 +1,32 @@ +/* + * Lowlevel setup for SMDK5250 board based on S5PC520 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + .globl lowlevel_init +lowlevel_init: + /* + * Set the stack pointer, although it will be overwriten by the caller + * It seems we will not boot if this function is empty. + */ + ldr sp, =CONFIG_IRAM_STACK + mov pc, lr diff --git a/src/cpu/samsung/exynos5250/lowlevel_init_c.c b/src/cpu/samsung/exynos5250/lowlevel_init_c.c new file mode 100644 index 0000000..2215fb7 --- /dev/null +++ b/src/cpu/samsung/exynos5250/lowlevel_init_c.c @@ -0,0 +1,120 @@ +/* + * Lowlevel setup for SMDK5250 board based on S5PC520 + * + * Copyright (C) 2012 Samsung Electronics + * Copyright (c) 2012 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <config.h> +#include <cpu/samsung/exynos5-common/exynos5-common.h> +#include <cpu/samsung/exynos5-common/spl.h> +#include <cpu/samsung/exynos5250/clock_init.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/dmc.h> +#include <cpu/samsung/exynos5250/pinmux.h> +#include <cpu/samsung/exynos5250/power.h> +#include <cpu/samsung/exynos5250/setup.h> +#include <cpu/samsung/exynos5250/tzpc.h> +#include "setup.h" + + +void do_barriers(void); /* FIXME: make gcc shut up about "no previous prototype" */ + +void do_barriers(void) +{ + /* + * The reason we don't write out the instructions dsb/isb/sev: + * While ARM Cortex-A8 supports ARM v7 instruction set (-march=armv7a), + * we compile with -march=armv5 to allow more compilers to work. + * For U-Boot code this has no performance impact. + */ + __asm__ __volatile__( +#if defined(__thumb__) + ".hword 0xF3BF, 0x8F4F\n" /* dsb; darn -march=armv5 */ + ".hword 0xF3BF, 0x8F6F\n" /* isb; darn -march=armv5 */ + ".hword 0xBF40\n" /* sev; darn -march=armv5 */ +#else + ".word 0xF57FF04F\n" /* dsb; darn -march=armv5 */ + ".word 0xF57FF06F\n" /* isb; darn -march=armv5 */ + ".word 0xE320F004\n" /* sev; darn -march=armv5 */ +#endif + ); +} + +/* These are the things we can do during low-level init */ +enum { + DO_WAKEUP = 1 << 0, + DO_UART = 1 << 1, + DO_CLOCKS = 1 << 2, + DO_POWER = 1 << 3, +}; + +int lowlevel_init_subsystems(void) +{ + uint32_t reset_status; + int actions = 0; + + do_barriers(); + + /* Setup cpu info which is needed to select correct register offsets */ + cpu_info_init(); + + reset_status = power_read_reset_status(); + + switch (reset_status) { + case S5P_CHECK_SLEEP: + actions = DO_CLOCKS | DO_WAKEUP; + break; + case S5P_CHECK_DIDLE: + case S5P_CHECK_LPA: + actions = DO_WAKEUP; + default: + /* This is a normal boot (not a wake from sleep) */ + actions = DO_UART | DO_CLOCKS | DO_POWER; + } + + if (actions & DO_POWER) + power_init(); + if (actions & DO_CLOCKS) + system_clock_init(); + if (actions & DO_UART) { + /* Set up serial UART so we can printf() */ +// exynos_pinmux_config(EXYNOS_UART, PINMUX_FLAG_NONE); + /* FIXME(dhendrix): add a function for mapping + CONFIG_CONSOLE_SERIAL_UART_ADDRESS to PERIPH_ID_UARTn */ + exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE); + /* FIXME(dhendrix): serial_init() does not seem to + actually do anything !?!? */ +// serial_init(); + init_timer(); /* FIXME(dhendrix): was timer_init() */ + } + +/* FIXME(dhendrix): place this somewhere for ramstage... */ +#if 0 + if (actions & DO_CLOCKS) { + mem_ctrl_init(); + tzpc_init(); + } +#endif + + return actions & DO_WAKEUP; +} diff --git a/src/cpu/samsung/exynos5250/pinmux.c b/src/cpu/samsung/exynos5250/pinmux.c new file mode 100644 index 0000000..becced2 --- /dev/null +++ b/src/cpu/samsung/exynos5250/pinmux.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2012 Samsung Electronics. + * Abhilash Kesavan a.kesavan@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <arch/gpio.h> +#include <cpu/samsung/exynos5250/gpio.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/pinmux.h> +#include <cpu/samsung/exynos5-common/sromc.h> + +int exynos_pinmux_config(enum periph_id peripheral, int flags) +{ + int i, start, count, start_ext, pin_ext, pin, drv; + + switch (peripheral) { + case PERIPH_ID_UART0: + case PERIPH_ID_UART1: + case PERIPH_ID_UART2: + case PERIPH_ID_UART3: + switch (peripheral) { + default: + case PERIPH_ID_UART0: + start = GPIO_A00; count = 4; + break; + case PERIPH_ID_UART1: + start = GPIO_A04; count = 4; + break; + case PERIPH_ID_UART2: + start = GPIO_A10; count = 4; + break; + case PERIPH_ID_UART3: + start = GPIO_A14; count = 2; + break; + } + for (i = start; i < start + count; i++) { + gpio_set_pull(i, EXYNOS_GPIO_PULL_NONE); + gpio_cfg_pin(i, EXYNOS_GPIO_FUNC(0x2)); + } + break; + case PERIPH_ID_SDMMC0: + case PERIPH_ID_SDMMC1: + case PERIPH_ID_SDMMC2: + case PERIPH_ID_SDMMC3: + pin = EXYNOS_GPIO_FUNC(0x2); + pin_ext = EXYNOS_GPIO_FUNC(0x2); + drv = EXYNOS_GPIO_DRV_4X; + switch (peripheral) { + default: + case PERIPH_ID_SDMMC0: + start = GPIO_C00; + start_ext = GPIO_C10; + break; + case PERIPH_ID_SDMMC1: + start = GPIO_C20; + start_ext = 0; + break; + case PERIPH_ID_SDMMC2: + start = GPIO_C30; + /* + * TODO: (alim.akhtar@samsung.com) + * add support for 8 bit mode (needs to be a per-board + * option, so in the FDT). + */ + start_ext = 0; + break; + case PERIPH_ID_SDMMC3: + /* + * TODO: Need to add defintions for GPC4 before + * enabling this. + */ + debug("SDMMC3 not supported yet"); + return -1; + } + if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) { + debug("SDMMC device %d does not support 8bit mode", + peripheral); + return -1; + } + if (flags & PINMUX_FLAG_8BIT_MODE) { + assert(peripheral == PERIPH_ID_SDMMC0); + for (i = 0; i <= 3; i++) { + gpio_cfg_pin(start_ext + i, pin_ext); + gpio_set_pull(start_ext + i, + EXYNOS_GPIO_PULL_UP); + gpio_set_drv(start_ext + i, drv); + } + } + for (i = 0; i < 2; i++) { + gpio_cfg_pin(start + i, pin); + gpio_set_pull(start + i, EXYNOS_GPIO_PULL_NONE); + gpio_set_drv(start + i, drv); + } + for (i = 2; i <= 6; i++) { + gpio_cfg_pin(start + i, pin); + gpio_set_pull(start + i, EXYNOS_GPIO_PULL_UP); + gpio_set_drv(start + i, drv); + } + break; + case PERIPH_ID_SROMC: + /* + * SROM:CS1 and EBI + * + * GPY0[0] SROM_CSn[0] + * GPY0[1] SROM_CSn[1](2) + * GPY0[2] SROM_CSn[2] + * GPY0[3] SROM_CSn[3] + * GPY0[4] EBI_OEn(2) + * GPY0[5] EBI_EEn(2) + * + * GPY1[0] EBI_BEn[0](2) + * GPY1[1] EBI_BEn[1](2) + * GPY1[2] SROM_WAIT(2) + * GPY1[3] EBI_DATA_RDn(2) + */ + gpio_cfg_pin(GPIO_Y00 + (flags & PINMUX_FLAG_BANK), + EXYNOS_GPIO_FUNC(2)); + gpio_cfg_pin(GPIO_Y04, EXYNOS_GPIO_FUNC(2)); + gpio_cfg_pin(GPIO_Y05, EXYNOS_GPIO_FUNC(2)); + + for (i = 2; i < 4; i++) + gpio_cfg_pin(GPIO_Y10 + i, EXYNOS_GPIO_FUNC(2)); + + /* + * EBI: 8 Addrss Lines + * + * GPY3[0] EBI_ADDR[0](2) + * GPY3[1] EBI_ADDR[1](2) + * GPY3[2] EBI_ADDR[2](2) + * GPY3[3] EBI_ADDR[3](2) + * GPY3[4] EBI_ADDR[4](2) + * GPY3[5] EBI_ADDR[5](2) + * GPY3[6] EBI_ADDR[6](2) + * GPY3[7] EBI_ADDR[7](2) + * + * EBI: 16 Data Lines + * + * GPY5[0] EBI_DATA[0](2) + * GPY5[1] EBI_DATA[1](2) + * GPY5[2] EBI_DATA[2](2) + * GPY5[3] EBI_DATA[3](2) + * GPY5[4] EBI_DATA[4](2) + * GPY5[5] EBI_DATA[5](2) + * GPY5[6] EBI_DATA[6](2) + * GPY5[7] EBI_DATA[7](2) + * + * GPY6[0] EBI_DATA[8](2) + * GPY6[1] EBI_DATA[9](2) + * GPY6[2] EBI_DATA[10](2) + * GPY6[3] EBI_DATA[11](2) + * GPY6[4] EBI_DATA[12](2) + * GPY6[5] EBI_DATA[13](2) + * GPY6[6] EBI_DATA[14](2) + * GPY6[7] EBI_DATA[15](2) + */ + for (i = 0; i < 8; i++) { + gpio_cfg_pin(GPIO_Y30 + i, EXYNOS_GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y30 + i, EXYNOS_GPIO_PULL_UP); + + gpio_cfg_pin(GPIO_Y50 + i, EXYNOS_GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y50 + i, EXYNOS_GPIO_PULL_UP); + + if (flags & PINMUX_FLAG_16BIT) { + gpio_cfg_pin(GPIO_Y60 + i, EXYNOS_GPIO_FUNC(2)); + gpio_set_pull(GPIO_Y60 + i, + EXYNOS_GPIO_PULL_UP); + } + } + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + case PERIPH_ID_SPI3: { + int cfg; + + switch (peripheral) { + default: + case PERIPH_ID_SPI0: + start = GPIO_A20; + cfg = 0x2; + break; + case PERIPH_ID_SPI1: + start = GPIO_A24; + cfg = 0x2; + break; + case PERIPH_ID_SPI2: + start = GPIO_B11; + cfg = 0x5; + break; + case PERIPH_ID_SPI3: + start = GPIO_E00; + cfg = 0x2; + break; + } + + for (i = 0; i < 4; i++) + gpio_cfg_pin(start + i, EXYNOS_GPIO_FUNC(cfg)); + break; + } + case PERIPH_ID_SPI4: + for (i = 0; i < 2; i++) + gpio_cfg_pin(GPIO_F02 + i, EXYNOS_GPIO_FUNC(0x4)); + for (i = 2; i < 4; i++) + gpio_cfg_pin(GPIO_E02 + i, EXYNOS_GPIO_FUNC(0x4)); + break; + case PERIPH_ID_BACKLIGHT: + gpio_cfg_pin(GPIO_B20, EXYNOS_GPIO_OUTPUT); + gpio_set_value(GPIO_B20, 1); + break; + case PERIPH_ID_LCD: + gpio_cfg_pin(GPIO_Y25, EXYNOS_GPIO_OUTPUT); + gpio_set_value(GPIO_Y25, 1); + gpio_cfg_pin(GPIO_X15, EXYNOS_GPIO_OUTPUT); + gpio_set_value(GPIO_X15, 1); + gpio_cfg_pin(GPIO_X30, EXYNOS_GPIO_OUTPUT); + gpio_set_value(GPIO_X30, 1); + break; + case PERIPH_ID_I2C0: + gpio_cfg_pin(GPIO_B30, EXYNOS_GPIO_FUNC(0x2)); + gpio_cfg_pin(GPIO_B31, EXYNOS_GPIO_FUNC(0x2)); + gpio_set_pull(GPIO_B30, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_B31, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C1: + gpio_cfg_pin(GPIO_B32, EXYNOS_GPIO_FUNC(0x2)); + gpio_cfg_pin(GPIO_B33, EXYNOS_GPIO_FUNC(0x2)); + gpio_set_pull(GPIO_B32, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_B33, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C2: + gpio_cfg_pin(GPIO_A06, EXYNOS_GPIO_FUNC(0x3)); + gpio_cfg_pin(GPIO_A07, EXYNOS_GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_A06, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_A07, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C3: + gpio_cfg_pin(GPIO_A12, EXYNOS_GPIO_FUNC(0x3)); + gpio_cfg_pin(GPIO_A13, EXYNOS_GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_A12, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_A13, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C4: + gpio_cfg_pin(GPIO_A20, EXYNOS_GPIO_FUNC(0x3)); + gpio_cfg_pin(GPIO_A21, EXYNOS_GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_A20, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_A21, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C5: + gpio_cfg_pin(GPIO_A22, EXYNOS_GPIO_FUNC(0x3)); + gpio_cfg_pin(GPIO_A23, EXYNOS_GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_A22, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_A23, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2C6: + gpio_cfg_pin(GPIO_B13, EXYNOS_GPIO_FUNC(0x4)); + gpio_cfg_pin(GPIO_B14, EXYNOS_GPIO_FUNC(0x4)); + break; + case PERIPH_ID_I2C7: + gpio_cfg_pin(GPIO_B22, EXYNOS_GPIO_FUNC(0x3)); + gpio_cfg_pin(GPIO_B23, EXYNOS_GPIO_FUNC(0x3)); + gpio_set_pull(GPIO_B22, EXYNOS_GPIO_PULL_NONE); + gpio_set_pull(GPIO_B23, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_DPHPD: + /* Set Hotplug detect for DP */ + gpio_cfg_pin(GPIO_X07, EXYNOS_GPIO_FUNC(0x3)); + + /* + * Hotplug detect should have an external pullup; disable the + * internal pulldown so they don't fight. + */ + gpio_set_pull(GPIO_X07, EXYNOS_GPIO_PULL_NONE); + break; + case PERIPH_ID_I2S1: + for (i = 0; i < 5; i++) + gpio_cfg_pin(GPIO_B00 + i, EXYNOS_GPIO_FUNC(0x02)); + break; + default: + debug("%s: invalid peripheral %d", __func__, peripheral); + return -1; + } + + return 0; +} diff --git a/src/cpu/samsung/exynos5250/power.c b/src/cpu/samsung/exynos5250/power.c new file mode 100644 index 0000000..2000fee --- /dev/null +++ b/src/cpu/samsung/exynos5250/power.c @@ -0,0 +1,202 @@ +/* + * Power setup code for EXYNOS5 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <arch/io.h> +#include <console/console.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/power.h> +#include <cpu/samsung/exynos5250/sysreg.h> +#include <cpu/samsung/exynos5-common/spl.h> +#include <device/i2c.h> +#include <device/power/max77686.h> + +static void ps_hold_setup(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + /* Set PS-Hold high */ + setbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH); +} + +void power_reset(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + /* Clear inform1 so there's no change we think we've got a wake reset */ + power->inform1 = 0; + + setbits_le32(&power->sw_reset, 1); +} + +/* This function never returns */ +void power_shutdown(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + clrbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH); + + hang(); +} + +void power_enable_dp_phy(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE); +} + +void power_enable_usb_phy(void) +{ + struct exynos5_sysreg *sysreg = + (struct exynos5_sysreg *)samsung_get_base_sysreg(); + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + unsigned int phy_cfg; + + /* Setting USB20PHY_CONFIG register to USB 2.0 HOST link */ + phy_cfg = readl(&sysreg->usb20_phy_cfg); + if (phy_cfg & USB20_PHY_CFG_EN) { + debug("USB 2.0 HOST link already selected\n"); + } else { + phy_cfg |= USB20_PHY_CFG_EN; + writel(phy_cfg, &sysreg->usb20_phy_cfg); + } + + /* Enabling USBHOST_PHY */ + setbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN); +} + +void power_disable_usb_phy(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + /* Disabling USBHost_PHY */ + clrbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN); +} + +void power_enable_hw_thermal_trip(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + /* Enable HW thermal trip */ + setbits_le32(&power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP); +} + +uint32_t power_read_reset_status(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + return power->inform1; +} + +void power_exit_wakeup(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + typedef void (*resume_func)(void); + + ((resume_func)power->inform0)(); +} + +/** + * Initialize the pmic voltages to power up the system + * This also calls i2c_init so that we can program the pmic + * + * REG_ENABLE = 0, needed to set the buck/ldo enable bit ON + * + * @return Return 0 if ok, else -1 + */ +int power_init(void) +{ + int error = 0; + + /* FIXME(dhendrix): not necessary for initial bringup... */ +#if 0 +#ifdef CONFIG_SPL_BUILD + struct spl_machine_param *param = spl_get_machine_params(); + + /* Set the i2c register address base so i2c works before FDT */ + i2c_set_early_reg(param->i2c_base); +#endif +#endif + + ps_hold_setup(); + + /* FIXME(dhendrix): not necessary for initial bringup... */ +#if 0 + /* init the i2c so that we can program pmic chip */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /* + * We're using CR1616 coin cell battery that is non-rechargeable + * battery. But, BBCHOSTEN bit of the BBAT Charger Register in + * MAX77686 is enabled by default for charging coin cell. + * + * Also, we cannot meet the coin cell reverse current spec. in UL + * standard if BBCHOSTEN bit is enabled. + * + * Disable Coin BATT Charging + */ + error = max77686_disable_backup_batt(); + + error |= max77686_volsetting(PMIC_BUCK2, CONFIG_VDD_ARM_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_BUCK3, CONFIG_VDD_INT_UV, + REG_ENABLE, MAX77686_UV); + error |= max77686_volsetting(PMIC_BUCK1, CONFIG_VDD_MIF_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_BUCK4, CONFIG_VDD_G3D_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_LDO2, CONFIG_VDD_LDO2_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_LDO3, CONFIG_VDD_LDO3_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_LDO5, CONFIG_VDD_LDO5_MV, + REG_ENABLE, MAX77686_MV); + error |= max77686_volsetting(PMIC_LDO10, CONFIG_VDD_LDO10_MV, + REG_ENABLE, MAX77686_MV); +#endif + if (error != 0) + printk(BIOS_ERR, "power init failed\n"); + + return error; +} + +void power_enable_xclkout(void) +{ + struct exynos5_power *power = + (struct exynos5_power *)samsung_get_base_power(); + + /* use xxti for xclk out */ + clrsetbits_le32(&power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK, + PMU_DEBUG_XXTI); +} diff --git a/src/cpu/samsung/exynos5250/sata.c b/src/cpu/samsung/exynos5250/sata.c new file mode 100644 index 0000000..b3e464e --- /dev/null +++ b/src/cpu/samsung/exynos5250/sata.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <ahci.h> +#include <common.h> +#include <fdtdec.h> +#include <scsi.h> +#include <asm/arch-exynos5/sata.h> +#include <asm/arch/pinmux.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/types.h> + +#define SATA_AHCI_AXI 0x122f0000 +#define SATA_PHCTRL_APB 0x12170000 +#define SATA_PHY_I2C_ABP 0x121d0000 +#define EXYNOS5_SATA_PHY_CONTROL (0x10040000 + 0x724) +#define S5P_PMU_SATA_PHY_CONTROL_EN 0x1 + +void * const phy_ctrl = (void *)SATA_PHCTRL_APB; +void * const phy_i2c_base = (void *)SATA_PHY_I2C_ABP; + +typedef unsigned char bool; +#define true 1 +#define false 0 + + +#define SATA_TIME_LIMIT 10000 +#define SATA_PHY_I2C_SLAVE_ADDRS 0x70 + +#define SATA_RESET 0x4 +#define RESET_CMN_RST_N (1 << 1) +#define LINK_RESET 0xF0000 + +#define SATA_MODE0 0x10 + +#define SATA_CTRL0 0x14 +#define CTRL0_P0_PHY_CALIBRATED_SEL (1 << 9) +#define CTRL0_P0_PHY_CALIBRATED (1 << 8) + +#define SATA_PHSATA_CTRLM 0xE0 +#define PHCTRLM_REF_RATE (1 << 1) +#define PHCTRLM_HIGH_SPEED (1 << 0) + +#define SATA_PHSATA_STATM 0xF0 +#define PHSTATM_PLL_LOCKED (1 << 0) + + +/********************** I2C**************/ +#define SATA_I2C_CON 0x00 +#define SATA_I2C_STAT 0x04 +#define SATA_I2C_ADDR 0x08 +#define SATA_I2C_DS 0x0C +#define SATA_I2C_LC 0x10 + +/* I2CCON reg */ +#define CON_ACKEN (1 << 7) +#define CON_CLK512 (1 << 6) +#define CON_CLK16 (~CON_CLK512) +#define CON_INTEN (1 << 5) +#define CON_INTPND (1 << 4) +#define CON_TXCLK_PS (0xF) + +/* I2CSTAT reg */ +#define STAT_MSTT (0x3 << 6) +#define STAT_BSYST (1 << 5) +#define STAT_RTEN (1 << 4) +#define STAT_LAST (1 << 0) + +#define LC_FLTR_EN (1 << 2) + +#define SATA_PHY_CON_RESET 0xF003F + +#define SCLK_SATA_FREQ (66 * MHZ) + + + +enum { + SATA_GENERATION1, + SATA_GENERATION2, + SATA_GENERATION3, +}; + +static bool sata_is_reg(void __iomem *base, u32 reg, u32 checkbit, u32 Status) +{ + if ((__raw_readl(base + reg) & checkbit) == Status) + return true; + else + return false; +} + +static bool wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, + u32 Status) +{ + u32 time_limit_cnt = 0; + while (!sata_is_reg(base, reg, checkbit, Status)) { + if (time_limit_cnt == SATA_TIME_LIMIT) { + return false; + } + udelay(1000); + time_limit_cnt++; + } + return true; +} + + +static void sata_set_gen(u8 gen) +{ + __raw_writel(gen, phy_ctrl + SATA_MODE0); +} + +/* Address :I2C Address */ +static void sata_i2c_write_addrs(u8 data) +{ + __raw_writeb((data & 0xFE), phy_i2c_base + SATA_I2C_DS); +} + +static void sata_i2c_write_data(u8 data) +{ + __raw_writeb((data), phy_i2c_base + SATA_I2C_DS); +} + +static void sata_i2c_start(void) +{ + u32 val; + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val |= STAT_BSYST; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); +} + +static void sata_i2c_stop(void) +{ + u32 val; + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val &= ~STAT_BSYST; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); +} + +static bool sata_i2c_get_int_status(void) +{ + if ((__raw_readl(phy_i2c_base + SATA_I2C_CON)) & CON_INTPND) + return true; + else + return false; +} + +static bool sata_i2c_is_tx_ack(void) +{ + if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_LAST) + return false; + else + return true; +} + +static bool sata_i2c_is_bus_ready(void) +{ + if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_BSYST) + return false; + else + return true; +} + +static bool sata_i2c_wait_for_busready(u32 time_out) +{ + while (--time_out) { + if (sata_i2c_is_bus_ready()) + return true; + udelay(100); + } + return false; +} + +static bool sata_i2c_wait_for_tx_ack(u32 time_out) +{ + while (--time_out) { + if (sata_i2c_get_int_status()) { + if (sata_i2c_is_tx_ack()) + return true; + } + udelay(100); + } + return false; +} + +static void sata_i2c_clear_int_status(void) +{ + u32 val; + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val &= ~CON_INTPND; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); +} + + +static void sata_i2c_set_ack_gen(bool enable) +{ + u32 val; + if (enable) { + val = (__raw_readl(phy_i2c_base + SATA_I2C_CON)) | CON_ACKEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + } else { + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val &= ~CON_ACKEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + } + +} + +static void sata_i2c_set_master_tx(void) +{ + u32 val; + /* Disable I2C */ + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val &= ~STAT_RTEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); + /* Clear Mode */ + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val &= ~STAT_MSTT; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); + + sata_i2c_clear_int_status(); + /* interrupt disable */ + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val &= ~CON_INTEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + + /* Master, Send mode */ + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val |= STAT_MSTT; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); + + /* interrupt enable */ + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val |= CON_INTEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + + /* Enable I2C */ + val = __raw_readl(phy_i2c_base + SATA_I2C_STAT); + val |= STAT_RTEN; + __raw_writel(val, phy_i2c_base + SATA_I2C_STAT); +} + +static void sata_i2c_init(void) +{ + u32 val; + + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val &= CON_CLK16; + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val &= ~(CON_TXCLK_PS); + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + + val = __raw_readl(phy_i2c_base + SATA_I2C_CON); + val |= (2 & CON_TXCLK_PS); + __raw_writel(val, phy_i2c_base + SATA_I2C_CON); + + val = __raw_readl(phy_i2c_base + SATA_I2C_LC); + val &= ~(LC_FLTR_EN); + __raw_writel(val, phy_i2c_base + SATA_I2C_LC); + + sata_i2c_set_ack_gen(false); +} +static bool sata_i2c_send(u8 slave_addrs, u8 addrs, u8 ucData) +{ + s32 ret = 0; + if (!sata_i2c_wait_for_busready(SATA_TIME_LIMIT)) + return false; + + sata_i2c_init(); + sata_i2c_set_master_tx(); + + __raw_writel(SATA_PHY_CON_RESET, phy_ctrl + SATA_RESET); + sata_i2c_write_addrs(slave_addrs); + sata_i2c_start(); + if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) { + ret = false; + goto STOP; + } + sata_i2c_write_data(addrs); + sata_i2c_clear_int_status(); + if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) { + ret = false; + goto STOP; + } + sata_i2c_write_data(ucData); + sata_i2c_clear_int_status(); + if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) { + ret = false; + goto STOP; + } + ret = true; + +STOP: + sata_i2c_stop(); + sata_i2c_clear_int_status(); + sata_i2c_wait_for_busready(SATA_TIME_LIMIT); + + return ret; +} + +static bool ahci_phy_init(void __iomem *mmio) +{ + u8 uCount, i = 0; + /* 0x3A for 40bit I/F */ + u8 reg_addrs[] = {0x22, 0x21, 0x3A}; + /* 0x0B for 40bit I/F */ + u8 default_setting_value[] = {0x30, 0x4f, 0x0B}; + + uCount = sizeof(reg_addrs)/sizeof(u8); + while (i < uCount) { + if (!sata_i2c_send(SATA_PHY_I2C_SLAVE_ADDRS, reg_addrs[i], + default_setting_value[i])) + return false; + i++; + } + return true; +} + +static int exynos5_ahci_init(void __iomem *mmio) +{ + int val, ret; + + __raw_writel(S5P_PMU_SATA_PHY_CONTROL_EN, EXYNOS5_SATA_PHY_CONTROL); + + val = 0; + __raw_writel(val, phy_ctrl + SATA_RESET); + val = __raw_readl(phy_ctrl + SATA_RESET); + val |= 0x3D; + __raw_writel(val, phy_ctrl + SATA_RESET); + + val = __raw_readl(phy_ctrl + SATA_RESET); + val |= LINK_RESET; + __raw_writel(val, phy_ctrl + SATA_RESET); + + val = __raw_readl(phy_ctrl + SATA_RESET); + val |= RESET_CMN_RST_N; + __raw_writel(val, phy_ctrl + SATA_RESET); + + val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM); + val &= ~PHCTRLM_REF_RATE; + __raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM); + + /* High speed enable for Gen3 */ + val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM); + val |= PHCTRLM_HIGH_SPEED; + __raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM); + + /* Port0 is available */ + __raw_writel(0x1, mmio + HOST_PORTS_IMPL); + + ret = ahci_phy_init(mmio); + + val = __raw_readl(phy_ctrl + SATA_CTRL0); + val |= CTRL0_P0_PHY_CALIBRATED_SEL|CTRL0_P0_PHY_CALIBRATED; + __raw_writel(val, phy_ctrl + SATA_CTRL0); + sata_set_gen(SATA_GENERATION3); + + /* release cmu reset */ + val = __raw_readl(phy_ctrl + SATA_RESET); + val &= ~RESET_CMN_RST_N; + __raw_writel(val, phy_ctrl + SATA_RESET); + + val = __raw_readl(phy_ctrl + SATA_RESET); + val |= RESET_CMN_RST_N; + __raw_writel(val, phy_ctrl + SATA_RESET); + + if (wait_for_reg_status(phy_ctrl, SATA_PHSATA_STATM, + PHSTATM_PLL_LOCKED, 1)) { + return ret; + } + return 0; +} + +static int exynos5_sata_enable_power(const void *blob) +{ + int node; + struct fdt_gpio_state gpio; + + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_SATA); + if (node >= 0 && + fdtdec_decode_gpio(blob, node, "enable-gpios", &gpio) == 0) { + gpio_cfg_pin(gpio.gpio, EXYNOS_GPIO_OUTPUT); + gpio_set_value(gpio.gpio, 1); + return 0; + } + return -ENODEV; +} + +static void exynos5_enable_clock_gates(void) +{ + /* Turn on all SATA clock gates & DMA gates. */ + const unsigned cmu_toppart = 0x10020000; + const unsigned addr = cmu_toppart + 0x944; + const unsigned sata_clocks = (1 << 25) | (1 << 24) | (1 << 6); + const unsigned dma_clocks = (2 << 1) | (1 << 1); + const unsigned clk_gate_ip_fsys = readl(addr); + writel(clk_gate_ip_fsys | sata_clocks | dma_clocks, addr); +} + +int exynos5_sata_init(const void *blob) +{ + if (exynos5_sata_enable_power(blob) == 0) { + exynos5_enable_clock_gates(); + + if (exynos5_ahci_init((void *)SATA_AHCI_AXI)) { + ahci_init(SATA_AHCI_AXI); + scsi_scan(1); + return 0; + } + } + return -ENODEV; +} diff --git a/src/cpu/samsung/exynos5250/setup.h b/src/cpu/samsung/exynos5250/setup.h index 5e59232..bb438c5 100644 --- a/src/cpu/samsung/exynos5250/setup.h +++ b/src/cpu/samsung/exynos5250/setup.h @@ -25,6 +25,10 @@ #ifndef _SMDK5250_SETUP_H #define _SMDK5250_SETUP_H
+struct exynos5_dmc; +enum ddr_mode; +struct exynos5_phy_control; + /* TZPC : Register Offsets */ #define TZPC0_BASE 0x10100000 #define TZPC1_BASE 0x10110000 @@ -683,7 +687,8 @@ enum { };
/* Functions common between LPDDR2 and DDR3 */ -void sdelay(unsigned long); +/* FIXME(dhendrix): conflicts with arch system.h version of sdelay()... */ +//void sdelay(unsigned long);
/* CPU info initialization code */ void cpu_info_init(void); diff --git a/src/cpu/samsung/exynos5250/soc.c b/src/cpu/samsung/exynos5250/soc.c new file mode 100644 index 0000000..be28d8c --- /dev/null +++ b/src/cpu/samsung/exynos5250/soc.c @@ -0,0 +1,47 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> + +#include <cpu/samsung/exynos5250/uart.h> + +enum periph_id exynos5_get_periph_id(unsigned base_addr) +{ + enum periph_id id = PERIPH_ID_NONE; + + switch (base_addr) { + case EXYNOS5_UART0_BASE: + id = PERIPH_ID_UART0; + break; + case EXYNOS5_UART1_BASE: + id = PERIPH_ID_UART1; + break; + case EXYNOS5_UART2_BASE: + id = PERIPH_ID_UART2; + break; + case EXYNOS5_UART3_BASE: + id = PERIPH_ID_UART3; + break; + default: + break; + } + + return id; +} diff --git a/src/cpu/samsung/exynos5250/spl.c b/src/cpu/samsung/exynos5250/spl.c new file mode 100644 index 0000000..00b584a --- /dev/null +++ b/src/cpu/samsung/exynos5250/spl.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* FIXME(dhendrix): file unneeded? */ +#if 0 +#include <asm/types.h> +#include <asm/arch-exynos/cpu.h> +#include <asm/arch-exynos/spl.h> + +/* Get the u-boot size from the SPL parameter table */ +unsigned int exynos_get_uboot_size(void) +{ + struct spl_machine_param *param = spl_get_machine_params(); + + return param->uboot_size; +} + +/* Get the boot device from the SPL parameter table */ +enum boot_mode exynos_get_boot_device(void) +{ + struct spl_machine_param *param = spl_get_machine_params(); + + return param->boot_source; +} +#endif diff --git a/src/cpu/samsung/exynos5250/tzpc_init.c b/src/cpu/samsung/exynos5250/tzpc_init.c new file mode 100644 index 0000000..370c2ed --- /dev/null +++ b/src/cpu/samsung/exynos5250/tzpc_init.c @@ -0,0 +1,57 @@ +/* + * Lowlevel setup for SMDK5250 board based on S5PC520 + * + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <cpu/samsung/exynos5250/cpu.h> +#include <asm/arch/dmc.h> +#include <asm/arch/tzpc.h> +#include"setup.h" + +/* Setting TZPC[TrustZone Protection Controller] */ +void tzpc_init(void) +{ + struct exynos5_tzpc *tzpc; + unsigned int addr; + + for (addr = TZPC0_BASE; addr <= TZPC9_BASE; addr += TZPC_BASE_OFFSET) { + tzpc = (struct exynos5_tzpc *)addr; + + if (addr == TZPC0_BASE) + writel(R0SIZE, &tzpc->r0size); + + writel(DECPROTXSET, &tzpc->decprot0set); + writel(DECPROTXSET, &tzpc->decprot1set); + + if (addr == TZPC9_BASE) { + + /* TODO: Add comment here describing the numerical values + * used below. + */ + writel(0xf0, &tzpc->decprot2set); + writel(0x50, &tzpc->decprot3set); + } else { + writel(DECPROTXSET, &tzpc->decprot2set); + writel(DECPROTXSET, &tzpc->decprot3set); + } + } +} diff --git a/src/cpu/samsung/exynos5250/uart.c b/src/cpu/samsung/exynos5250/uart.c new file mode 100644 index 0000000..3d43976 --- /dev/null +++ b/src/cpu/samsung/exynos5250/uart.c @@ -0,0 +1,235 @@ +/* + * (C) Copyright 2009 SAMSUNG Electronics + * Minkyu Kang mk7.kang@samsung.com + * Heungjun Kim riverful.kim@samsung.com + * + * based on drivers/serial/s3c64xx.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +//#include <common.h> +//#include <linux/compiler.h> +#include <uart.h> +#include <arch/io.h> +//#include <asm/arch-exynos/spl.h> +//#include <asm/global_data.h> +//#include <fdtdec.h> +//#include <serial.h> + +#include <console/console.h> /* for __console definition */ + +#include <cpu/samsung/exynos5-common/exynos5-common.h> +#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/exynos5250/uart.h> + +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + +/* FIXME(dhendrix): exynos5 has 4 UARTs and its functions in u-boot take a + base_port argument. However console_driver functions do not. */ +static uint32_t base_port = CONFIG_CONSOLE_SERIAL_UART_ADDRESS; +#if 0 +/* Information about a serial port */ +struct fdt_serial { + u32 base_addr; /* address of registers in physical memory */ + u8 port_id; /* uart port number */ + u8 enabled; /* 1 if enabled, 0 if disabled */ +} config = { + -1U +}; +#endif + +#if 0 +static inline struct s5p_uart *s5p_get_base_uart(int dev_index) +{ + /* FIXME: there should be an assertion here if dev_index is >3 */ + return (struct s5p_uart *)(EXYNOS5_UART0_BASE + (0x10000 * dev_index)); +} +#endif + +/* + * The coefficient, used to calculate the baudrate on S5P UARTs is + * calculated as + * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT + * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, + * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: + */ +static const int udivslot[] = { + 0, + 0x0080, + 0x0808, + 0x0888, + 0x2222, + 0x4924, + 0x4a52, + 0x54aa, + 0x5555, + 0xd555, + 0xd5d5, + 0xddd5, + 0xdddd, + 0xdfdd, + 0xdfdf, + 0xffdf, +}; + +static void serial_setbrg_dev(void) +{ +// struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + struct s5p_uart *uart = (struct s5p_uart *)base_port; + u32 uclk; + u32 baudrate = CONFIG_TTYS0_BAUD; + u32 val; + enum periph_id periph; + + periph = exynos5_get_periph_id(base_port); + uclk = clock_get_periph_rate(periph); + val = uclk / baudrate; + + writel(val / 16 - 1, &uart->ubrdiv); + + /* + * FIXME(dhendrix): the original uart.h had a "br_rest" value which + * does not seem relevant to the exynos5250... not entirely sure + * where/if we need to worry about it here + */ +#if 0 + if (s5p_uart_divslot()) + writew(udivslot[val % 16], &uart->rest.slot); + else + writeb(val % 16, &uart->rest.value); +#endif +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static void exynos5_init_dev(void) +{ +// struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + struct s5p_uart *uart = (struct s5p_uart *)base_port; + + /* enable FIFOs */ + writel(0x1, &uart->ufcon); + writel(0, &uart->umcon); + /* 8N1 */ + writel(0x3, &uart->ulcon); + /* No interrupts, no DMA, pure polling */ + writel(0x245, &uart->ucon); + + serial_setbrg_dev(); +} + +static int exynos5_uart_err_check(int op) +{ + //struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + struct s5p_uart *uart = (struct s5p_uart *)base_port; + unsigned int mask; + + /* + * UERSTAT + * Break Detect [3] + * Frame Err [2] : receive operation + * Parity Err [1] : receive operation + * Overrun Err [0] : receive operation + */ + if (op) + mask = 0x8; + else + mask = 0xf; + + return readl(&uart->uerstat) & mask; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +static unsigned char exynos5_uart_rx_byte(void) +{ +// struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + struct s5p_uart *uart = (struct s5p_uart *)base_port; + + /* wait for character to arrive */ + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { + if (exynos5_uart_err_check(0)) + return 0; + } + + return readb(&uart->urxh) & 0xff; +} + +/* + * Output a single byte to the serial port. + */ +/* FIXME: ordering of arguments for coreboot v. u-boot for tx_byte */ +//static void exynos5_tx_byte(const char c, const int dev_index) +static void exynos5_uart_tx_byte(unsigned char data) +{ +// struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + struct s5p_uart *uart = (struct s5p_uart *)base_port; + + /* wait for room in the tx FIFO */ + while ((readl(uart->ufstat) & TX_FIFO_FULL_MASK)) { + if (exynos5_uart_err_check(1)) + return; + } + + writeb(data, &uart->utxh); +} + +#ifndef __PRE_RAM__ +static const struct console_driver exynos5_uart_console __console = { +//static const struct console_driver exynos5_uart_console __console = { +#if 0 + void (*init)(void); + void (*tx_byte)(unsigned char byte); + void (*tx_flush)(void); + unsigned char (*rx_byte)(void); + int (*tst_byte)(void); +#endif + .init = exynos5_init_dev, + .tx_byte = exynos5_uart_tx_byte, +// .tx_flush = exynos5_uart_tx_flush, + .rx_byte = exynos5_uart_rx_byte, +// .tst_byte = exynos5_uart_tst_byte, +}; +#else +/* for romstage_console... */ +//void (*uart_init)(void) = exynos5_init_dev; +//unsigned char (*uart_rx_byte)(unsigned base_port) = exynos5_uart_rx_byte; +//void (*uart_tx_byte)(unsigned base_port, unsigned char data) = exynos5_uart_tx_byte; +/* FIXME: trivial wrappers */ +void uart_init() +{ + exynos5_init_dev(); +} + +unsigned char uart_rx_byte() +{ + return exynos5_uart_rx_byte(); +} + +void uart_tx_byte(unsigned char data) +{ + exynos5_uart_tx_byte(data); +} +#endif diff --git a/src/cpu/samsung/s5p-common/Makefile.inc b/src/cpu/samsung/s5p-common/Makefile.inc new file mode 100644 index 0000000..9747f0d --- /dev/null +++ b/src/cpu/samsung/s5p-common/Makefile.inc @@ -0,0 +1,12 @@ +romstage-y += cpu_info.c +romstage-y += pwm.c # needed by timer.c +romstage-y += s5p_gpio.c +romstage-y += timer.c + +#romstage-y += sromc.c +#romstage-y += wdt.c + +ramstage-y += cpu_info.c +ramstage-y += pwm.c # needed by timer.c +ramstage-y += timer.c +ramstage-y += s5p_gpio.c diff --git a/src/cpu/samsung/s5p-common/Makefile.uboot b/src/cpu/samsung/s5p-common/Makefile.uboot new file mode 100644 index 0000000..f975f3f --- /dev/null +++ b/src/cpu/samsung/s5p-common/Makefile.uboot @@ -0,0 +1,49 @@ +# +# Copyright (C) 2009 Samsung Electronics +# Minkyu Kang mk7.kang@samsung.com +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libs5p-common.o + +COBJS-y += cpu_info.o +COBJS-y += timer.o +COBJS-y += sromc.o +COBJS-y += wdt.o +COBJS-$(CONFIG_PWM) += pwm.o + +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y) $(SOBJS)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/src/cpu/samsung/s5p-common/cpu_info.c b/src/cpu/samsung/s5p-common/cpu_info.c new file mode 100644 index 0000000..6b4742e --- /dev/null +++ b/src/cpu/samsung/s5p-common/cpu_info.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#if 0 +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/clock.h> +#include <asm/arch/dmc.h> +#endif +#include <arch/io.h> + +#include <cpu/samsung/s5p-common/clk.h> +#include <cpu/samsung/s5p-common/clock.h> +#include <cpu/samsung/s5p-common/cpu.h> + +#include <cpu/samsung/exynos5250/dmc.h> +#include <cpu/samsung/exynos5-common/cpu.h> /* for EXYNOS_PRO_ID */ + +/* FIXME(dhendrix): consolidate samsung ID code/#defines to a common location */ +#include <cpu/samsung/exynos5250/setup.h> /* cpu_info_init() prototype */ + +/* + * The following CPU infos are initialized in lowlevel_init(). They should be + * put in the .data section. Otherwise, a compile will put them in the .bss + * section since they don't have initial values. The relocation code which + * runs after lowlevel_init() will reset them to zero. + */ +unsigned int s5p_cpu_id __attribute__((section(".data"))); +unsigned int s5p_cpu_rev __attribute__((section(".data"))); + +void cpu_info_init(void) +{ + s5p_set_cpu_id(); +} + +int s5p_get_cpu_id(void) +{ + return s5p_cpu_id; +} + +int s5p_get_cpu_rev(void) +{ + return s5p_cpu_rev; +} + +void s5p_set_cpu_id(void) +{ + s5p_cpu_id = readl(EXYNOS_PRO_ID); + s5p_cpu_id = (0xC000 | ((s5p_cpu_id & 0x00FFF000) >> 12)); + + /* + * 0xC200: EXYNOS4210 EVT0 + * 0xC210: EXYNOS4210 EVT1 + */ + if (s5p_cpu_id == 0xC200) { + s5p_cpu_id |= 0x10; + s5p_cpu_rev = 0; + } else if (s5p_cpu_id == 0xC210) { + s5p_cpu_rev = 1; + } +} + +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) +{ + char buf[32]; + + printf("CPU: S5P%X @ %sMHz\n", + s5p_cpu_id, strmhz(buf, get_arm_clk())); + + return 0; +} +#endif + +#ifndef CONFIG_SPL_BUILD +void board_show_dram(ulong size) +{ + enum ddr_mode mem_type; + unsigned frequency_mhz; + unsigned arm_freq; + enum mem_manuf mem_manuf; + char buf[32]; + int ret; + + /* Get settings from the fdt */ + ret = clock_get_mem_selection(&mem_type, &frequency_mhz, + &arm_freq, &mem_manuf); + if (ret) + panic("Invalid DRAM information"); + + puts("DRAM: "); + print_size(size, " "); + printf("%s %s @ %sMHz", + clock_get_mem_manuf_name(mem_manuf), + clock_get_mem_type_name(mem_type), + strmhz(buf, frequency_mhz)); + putc('\n'); +} +#endif + +#ifdef CONFIG_ARCH_CPU_INIT +int arch_cpu_init(void) +{ + cpu_info_init(); + + return 0; +} +#endif diff --git a/src/cpu/samsung/s5p-common/pwm.c b/src/cpu/samsung/s5p-common/pwm.c new file mode 100644 index 0000000..5300420 --- /dev/null +++ b/src/cpu/samsung/s5p-common/pwm.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * + * Donghwa Lee dh09.lee@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +//#include <pwm.h> +#include <arch/io.h> +//#include <arch/pwm.h> +//#include <arch/clk.h> +/* FIXME(dhendrix): this is a godawful mess of similar-but-different includes... */ +#include <cpu/samsung/exynos5-common/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5250/periph.h> +#include <cpu/samsung/s5p-common/pwm.h> +#include <cpu/samsung/s5p-common/clk.h> +//#include <arch/periph.h> + +int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); + + return 0; +} + +int pwm_check_enabled(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + const unsigned long tcon = readl(&pwm->tcon); + + return tcon & TCON_START(pwm_id); +} + +void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); +} + +static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; + + tin_parent_rate = clock_get_periph_rate(PERIPH_ID_PWM0); + + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + } + + return tin_parent_rate / 16; +} + +#define NS_IN_SEC 1000000000UL + +int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp; + + /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -1; +// return -ERANGE; + + if (duty_ns > period_ns) + return -1; +// return -EINVAL; + + frequency = NS_IN_SEC / period_ns; + + /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency); + + tin_ns = NS_IN_SEC / tin_rate; + tcnt = period_ns / tin_ns; + + /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp; + + /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + } + + tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon); + + tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon); + + return 0; +} + +int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = + (struct s5p_timer *)samsung_get_base_timer(); + unsigned long ticks_per_period; + unsigned int offset, prescaler; + + /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */ + + val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1); + + + if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; + unsigned long timer_rate_hz = clock_get_periph_rate( + PERIPH_ID_PWM0) / ((prescaler + 1) * (1 << div)); + + ticks_per_period = timer_rate_hz / pwm_hz; + } + + /* set count value */ + offset = pwm_id * 3; + + writel(ticks_per_period, &pwm->tcntb0 + offset); + + val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && (pwm_id < 4)) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon); + + pwm_enable(pwm_id); + + return 0; +} diff --git a/src/cpu/samsung/s5p-common/s5p_gpio.c b/src/cpu/samsung/s5p-common/s5p_gpio.c new file mode 100644 index 0000000..50c4519 --- /dev/null +++ b/src/cpu/samsung/s5p-common/s5p_gpio.c @@ -0,0 +1,490 @@ +/* + * (C) Copyright 2009 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* FIXME(dhendrix): fix this up so it doesn't require a bunch of #ifdefs... */ +#include <common.h> +//#include <arch/io.h> +#include <arch/gpio.h> +#include <console/console.h> +#include <cpu/samsung/s5p-common/gpio.h> +#include <cpu/samsung/exynos5-common/gpio.h> +#include <cpu/samsung/exynos5250/gpio.h> /* FIXME: for gpio_decode_number prototype */ + +#define CON_MASK(x) (0xf << ((x) << 2)) +#define CON_SFR(x, v) ((v) << ((x) << 2)) + +#define DAT_MASK(x) (0x1 << (x)) +#define DAT_SET(x) (0x1 << (x)) + +#define PULL_MASK(x) (0x3 << ((x) << 1)) +#define PULL_MODE(x, v) ((v) << ((x) << 1)) + +#define DRV_MASK(x) (0x3 << ((x) << 1)) +#define DRV_SET(x, m) ((m) << ((x) << 1)) +#define RATE_MASK(x) (0x1 << (x + 16)) +#define RATE_SET(x) (0x1 << (x + 16)) + +struct gpio_info { + unsigned int reg_addr; /* Address of register for this part */ + unsigned int max_gpio; /* Maximum GPIO in this part */ +}; + +#ifdef CONFIG_CPU_SAMSUNG_EXYNOS5 + +#include <cpu/samsung/exynos5250/cpu.h> +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, + { EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 }, + { EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 }, + { EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT }, +}; + +#define HAVE_GENERIC_GPIO + +#elif defined(CONFIG_CPU_SAMSUNG_EXYNOS4) + +static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = { + { EXYNOS4_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 }, + { EXYNOS4_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 }, + { EXYNOS4_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 }, +}; + +#define HAVE_GENERIC_GPIO + +#endif + +/* This macro gets gpio pin offset from 0..7 */ +#define GPIO_BIT(x) ((x) & 0x7) + +//#ifdef HAVE_GENERIC_GPIO +static struct s5p_gpio_bank *gpio_get_bank(unsigned int gpio) +{ + const struct gpio_info *data; + unsigned int upto; + int i; + + for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS; + i++, upto = data->max_gpio, data++) { + if (gpio < data->max_gpio) { + struct s5p_gpio_bank *bank; + + bank = (struct s5p_gpio_bank *)data->reg_addr; + bank += (gpio - upto) / GPIO_PER_BANK; + return bank; + } + } + +#ifndef CONFIG_SPL_BUILD + assert(gpio < GPIO_MAX_PORT); /* ...which it will not be */ +#endif + return NULL; +} +//#endif + +/* TODO: Deprecation this interface in favour of asm-generic/gpio.h */ +void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg) +{ + unsigned int value; + + value = readl(&bank->con); + value &= ~CON_MASK(gpio); + value |= CON_SFR(gpio, cfg); + writel(value, &bank->con); +} + +void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + + s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_OUTPUT); + + value = readl(&bank->dat); + value &= ~DAT_MASK(gpio); + if (en) + value |= DAT_SET(gpio); + writel(value, &bank->dat); +} + +void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio) +{ + s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_INPUT); +} + +void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en) +{ + unsigned int value; + + value = readl(&bank->dat); + value &= ~DAT_MASK(gpio); + if (en) + value |= DAT_SET(gpio); + writel(value, &bank->dat); +} + +unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio) +{ + unsigned int value; + + value = readl(&bank->dat); + return !!(value & DAT_MASK(gpio)); +} + +void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->pull); + value &= ~PULL_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_PULL_DOWN: + case EXYNOS_GPIO_PULL_UP: + value |= PULL_MODE(gpio, mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~DRV_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_DRV_1X: + case EXYNOS_GPIO_DRV_2X: + case EXYNOS_GPIO_DRV_3X: + case EXYNOS_GPIO_DRV_4X: + value |= DRV_SET(gpio, mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) +{ + unsigned int value; + + value = readl(&bank->drv); + value &= ~RATE_MASK(gpio); + + switch (mode) { + case EXYNOS_GPIO_DRV_FAST: + case EXYNOS_GPIO_DRV_SLOW: + value |= RATE_SET(gpio); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +/* Common GPIO API - only available on Exynos5 */ +/* FIXME(dhendrix): If this stuff is really only applicable to exynos5, + move it to a more sensible location. */ +#ifdef HAVE_GENERIC_GPIO + +void gpio_cfg_pin(int gpio, int cfg) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->con); + value &= ~CON_MASK(GPIO_BIT(gpio)); + value |= CON_SFR(GPIO_BIT(gpio), cfg); + writel(value, &bank->con); +} + +static int gpio_get_cfg(int gpio) +{ + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + int shift = GPIO_BIT(gpio) << 2; + + return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift; +} + +void gpio_set_pull(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->pull); + value &= ~PULL_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_PULL_DOWN: + case EXYNOS_GPIO_PULL_UP: + value |= PULL_MODE(GPIO_BIT(gpio), mode); + break; + default: + break; + } + + writel(value, &bank->pull); +} + +void gpio_set_drv(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~DRV_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_DRV_1X: + case EXYNOS_GPIO_DRV_2X: + case EXYNOS_GPIO_DRV_3X: + case EXYNOS_GPIO_DRV_4X: + value |= DRV_SET(GPIO_BIT(gpio), mode); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +void gpio_set_rate(int gpio, int mode) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->drv); + value &= ~RATE_MASK(GPIO_BIT(gpio)); + + switch (mode) { + case EXYNOS_GPIO_DRV_FAST: + case EXYNOS_GPIO_DRV_SLOW: + value |= RATE_SET(GPIO_BIT(gpio)); + break; + default: + return; + } + + writel(value, &bank->drv); +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + gpio_cfg_pin(gpio, EXYNOS_GPIO_INPUT); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned int val; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + gpio_cfg_pin(gpio, EXYNOS_GPIO_OUTPUT); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + unsigned int value; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + value = readl(&bank->dat); + return !!(value & DAT_MASK(GPIO_BIT(gpio))); +} + +int gpio_set_value(unsigned gpio, int value) +{ + unsigned int val; + struct s5p_gpio_bank *bank = gpio_get_bank(gpio); + + val = readl(&bank->dat); + val &= ~DAT_MASK(GPIO_BIT(gpio)); + if (value) + val |= DAT_SET(GPIO_BIT(gpio)); + writel(val, &bank->dat); + + return 0; +} +#else + +static int s5p_gpio_get_pin(unsigned gpio) +{ + return gpio % GPIO_PER_BANK; +} + +/* + * If we have the old-style GPIO numbering setup, use these functions + * which don't necessary provide sequentially increasing GPIO numbers. + */ +static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) +{ + int bank = gpio / GPIO_PER_BANK; + bank *= sizeof(struct s5p_gpio_bank); + + return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank); +} + +int gpio_request(unsigned gpio, const char *label) +{ + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + s5p_gpio_direction_input(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + s5p_gpio_direction_output(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio)); +} + +int gpio_set_value(unsigned gpio, int value) +{ + s5p_gpio_set_value(s5p_gpio_get_bank(gpio), + s5p_gpio_get_pin(gpio), value); + + return 0; +} + +#endif /* HAVE_GENERIC_GPIO */ + +/* + * Add a delay here to give the lines time to settle + * TODO(sjg): 1us does not always work, 2 is stable, so use 5 to be safe + * Come back to this and sort out what the datasheet says + */ +#define GPIO_DELAY_US 5 + +/* FIXME(dhendrix): this should probably go to a more generic location */ +int gpio_decode_number(unsigned gpio_list[], int count) +{ + int result = 0; + int multiplier = 1; + int value, high, low; + int gpio, i; + + for (i = 0; i < count; i++) { + gpio = gpio_list[i]; + if (gpio >= GPIO_MAX_PORT) + return -1; + gpio_direction_input(gpio); + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP); + udelay(GPIO_DELAY_US); + high = gpio_get_value(gpio); + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_DOWN); + udelay(GPIO_DELAY_US); + low = gpio_get_value(gpio); + + if (high && low) /* external pullup */ + value = 2; + else if (!high && !low) /* external pulldown */ + value = 1; + else /* floating */ + value = 0; + + /* + * Check if line is externally pulled high and + * configure the internal pullup to match. For + * floating and pulldowns, the GPIO is already + * configured with an internal pulldown from the + * above test. + */ + if (value == 2) + gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP); + + result += value * multiplier; + multiplier *= 3; + } + + return result; +} + +static const char *get_cfg_name(int cfg) +{ + static char name[8]; + + if (cfg == EXYNOS_GPIO_INPUT) + return "input"; + else if (cfg == EXYNOS_GPIO_OUTPUT) + return "output"; + printk(BIOS_INFO, "func %d", cfg); +// sprintf(name, "func %d", cfg); + + return name; +} + +/* + * Display Exynos GPIO information + */ +void gpio_info(void) +{ + unsigned gpio; + + for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) { + int cfg = gpio_get_cfg(gpio); + + printk(BIOS_INFO, "GPIO_%-3d: %s", gpio, get_cfg_name(cfg)); + if (cfg == EXYNOS_GPIO_INPUT || cfg == EXYNOS_GPIO_OUTPUT) + printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio)); + printk(BIOS_INFO, "\n"); + } +} diff --git a/src/cpu/samsung/s5p-common/sromc.c b/src/cpu/samsung/s5p-common/sromc.c new file mode 100644 index 0000000..091e8d1 --- /dev/null +++ b/src/cpu/samsung/s5p-common/sromc.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Samsung Electronics + * Naveen Krishna Ch ch.naveen@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/sromc.h> + +/* + * s5p_config_sromc() - select the proper SROMC Bank and configure the + * band width control and bank control registers + * srom_bank - SROM + * srom_bw_conf - SMC Band witdh reg configuration value + * srom_bc_conf - SMC Bank Control reg configuration value + */ +void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf) +{ + u32 tmp; + struct s5p_sromc *srom = + (struct s5p_sromc *)samsung_get_base_sromc(); + + /* Configure SMC_BW register to handle proper SROMC bank */ + tmp = srom->bw; + tmp &= ~(0xF << (srom_bank * 4)); + tmp |= srom_bw_conf; + srom->bw = tmp; + + /* Configure SMC_BC register */ + srom->bc[srom_bank] = srom_bc_conf; +} diff --git a/src/cpu/samsung/s5p-common/timer.c b/src/cpu/samsung/s5p-common/timer.c new file mode 100644 index 0000000..373c374 --- /dev/null +++ b/src/cpu/samsung/s5p-common/timer.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009 Samsung Electronics + * Heungjun Kim riverful.kim@samsung.com + * Inki Dae inki.dae@samsung.com + * Minkyu Kang mk7.kang@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <arch/io.h> +//#include <cpu/samsung/exynos5250/pwm.h> +//#include <cpu/samsung/exynos5250/clk.h> +#include <cpu/samsung/s5p-common/pwm.h> +#include <cpu/samsung/s5p-common/clk.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <cpu/samsung/exynos5-common/exynos5-common.h> + +//#include <pwm.h> + +//DECLARE_GLOBAL_DATA_PTR; +static unsigned long long timer_reset_value; +static unsigned long lastinc; + +/* macro to read the 16 bit timer */ +static inline struct s5p_timer *s5p_get_base_timer(void) +{ + return (struct s5p_timer *)samsung_get_base_timer(); +} + +/** + * Read the countdown timer. + * + * This operates at 1MHz and counts downwards. It will wrap about every + * hour (2^32 microseconds). + * + * @return current value of timer + */ +static unsigned long timer_get_us_down(void) +{ + struct s5p_timer *const timer = s5p_get_base_timer(); + + return readl(&timer->tcnto4); +} + +int init_timer(void) +{ + /* Timer may have been enabled in SPL */ + if (!pwm_check_enabled(4)) { + /* PWM Timer 4 */ + pwm_init(4, MUX_DIV_4, 0); + pwm_config(4, 100000, 100000); + pwm_enable(4); +#ifndef CONFIG_SPL_BUILD + /* Use this as the current monotonic time in us */ + //gd->timer_reset_value = 0; + timer_reset_value = 0; + + /* Use this as the last timer value we saw */ + //gd->lastinc = timer_get_us_down(); + lastinc = timer_get_us_down(); +#endif + } + + return 0; +} + +/* + * timer without interrupts + */ +unsigned long get_timer(unsigned long base) +{ + ulong now = timer_get_us_down(); + + /* + * Increment the time by the amount elapsed since the last read. + * The timer may have wrapped around, but it makes no difference to + * our arithmetic here. + */ +#if 0 + gd->timer_reset_value += gd->lastinc - now; + gd->lastinc = now; + + /* Divide by 1000 to convert from us to ms */ + return gd->timer_reset_value / 1000 - base; +#endif + timer_reset_value += lastinc - now; + lastinc = now; + + /* Divide by 1000 to convert from us to ms */ + return timer_reset_value / 1000 - base; +} + +unsigned long timer_get_us(void) +{ + struct s5p_timer *const timer = s5p_get_base_timer(); + unsigned long now_downward_us = readl(&timer->tcnto4); + + /* + * Note that this timer counts downward. The pre-SPL process (BL1) + * takes about 100ms, so add this in here. + */ + return CONFIG_SPL_TIME_US - now_downward_us; +} + +/* delay x useconds */ +void __udelay(unsigned long usec) +{ + unsigned long count_value; + + count_value = timer_get_us_down(); + while ((int)(count_value - timer_get_us_down()) < (int)usec) + ; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +unsigned long get_tbclk(void) +{ + return CONFIG_SYS_HZ; +} + +unsigned long timer_get_boot_us(void) +{ + return timer_get_us(); +} diff --git a/src/cpu/samsung/s5p-common/wdt.c b/src/cpu/samsung/s5p-common/wdt.c new file mode 100644 index 0000000..94acc1e --- /dev/null +++ b/src/cpu/samsung/s5p-common/wdt.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Minkyu Kang mk7.kang@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/watchdog.h> + +#define PRESCALER_VAL 255 + +void wdt_stop(void) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wtcon = readl(&wdt->wtcon); + wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); + + writel(wtcon, &wdt->wtcon); +} + +void wdt_start(unsigned int timeout) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wdt_stop(); + + wtcon = readl(&wdt->wtcon); + wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128)); + wtcon &= ~WTCON_INT; + wtcon |= WTCON_RESET; + wtcon |= WTCON_PRESCALER(PRESCALER_VAL); + + writel(timeout, &wdt->wtdat); + writel(timeout, &wdt->wtcnt); + writel(wtcon, &wdt->wtcon); +} diff --git a/src/drivers/Kconfig b/src/drivers/Kconfig index ef38f34..ca94d2b 100644 --- a/src/drivers/Kconfig +++ b/src/drivers/Kconfig @@ -33,3 +33,4 @@ source src/drivers/realtek/Kconfig source src/drivers/sil/Kconfig source src/drivers/spi/Kconfig source src/drivers/trident/Kconfig +source src/drivers/maxim/Kconfig diff --git a/src/drivers/Makefile.inc b/src/drivers/Makefile.inc index a02b705..65800f0 100644 --- a/src/drivers/Makefile.inc +++ b/src/drivers/Makefile.inc @@ -30,4 +30,5 @@ subdirs-y += ics subdirs-y += spi subdirs-y += ipmi subdirs-y += elog +subdirs-y += maxim subdirs-$(CONFIG_ARCH_X86) += pc80 diff --git a/src/drivers/maxim/Kconfig b/src/drivers/maxim/Kconfig new file mode 100644 index 0000000..84ace13 --- /dev/null +++ b/src/drivers/maxim/Kconfig @@ -0,0 +1,5 @@ +config DRIVER_MAXIM_77686 + bool "Maxim 77686" + default n + help + Maxim 77686 power regulator diff --git a/src/drivers/maxim/Makefile.inc b/src/drivers/maxim/Makefile.inc new file mode 100644 index 0000000..3fc7104 --- /dev/null +++ b/src/drivers/maxim/Makefile.inc @@ -0,0 +1,20 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2012 The ChromiumOS Authors +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +driver-$(CONFIG_DRIVER_MAXIM_77686) += max77686.c diff --git a/src/drivers/maxim/max77686.c b/src/drivers/maxim/max77686.c new file mode 100644 index 0000000..6c24240 --- /dev/null +++ b/src/drivers/maxim/max77686.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Alim Akhtar alim.akhtar@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <i2c.h> +#include <max77686.h> + +/* Chip register numbers (not exported from this module) */ +enum { + REG_BBAT = 0x7e, + + /* Bits for BBAT */ + BBAT_BBCHOSTEN_MASK = 1 << 0, + BBAT_BBCVS_SHIFT = 3, + BBAT_BBCVS_MASK = 3 << BBAT_BBCVS_SHIFT, +}; + +/* + * Max77686 parameters values + * see max77686.h for parameters details + */ +struct max77686_para max77686_param[] = {/*{regnum, vol_addr, vol_bitpos, + vol_bitmask, reg_enaddr, reg_enbitpos, reg_enbitmask, reg_enbiton, + reg_enbitoff, vol_min, vol_div}*/ + {PMIC_BUCK1, 0x11, 0x0, 0x3F, 0x10, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_BUCK2, 0x14, 0x0, 0xFF, 0x12, 0x4, 0x3, 0x1, 0x0, 600, 12500}, + {PMIC_BUCK3, 0x1E, 0x0, 0xFF, 0x1C, 0x4, 0x3, 0x1, 0x0, 600, 12500}, + {PMIC_BUCK4, 0x28, 0x0, 0xFF, 0x26, 0x4, 0x3, 0x1, 0x0, 600, 12500}, + {PMIC_BUCK5, 0x31, 0x0, 0x3F, 0x30, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_BUCK6, 0x33, 0x0, 0x3F, 0x32, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_BUCK7, 0x35, 0x0, 0x3F, 0x34, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_BUCK8, 0x37, 0x0, 0x3F, 0x36, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_BUCK9, 0x39, 0x0, 0x3F, 0x38, 0x0, 0x3, 0x3, 0x0, 750, 50000}, + {PMIC_LDO1, 0x40, 0x0, 0x3F, 0x40, 0x6, 0x3, 0x3, 0x0, 800, 25000}, + {PMIC_LDO2, 0x41, 0x0, 0x3F, 0x41, 0x6, 0x3, 0x1, 0x0, 800, 25000}, + {PMIC_LDO3, 0x42, 0x0, 0x3F, 0x42, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO4, 0x43, 0x0, 0x3F, 0x43, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO5, 0x44, 0x0, 0x3F, 0x44, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO6, 0x45, 0x0, 0x3F, 0x45, 0x6, 0x3, 0x1, 0x0, 800, 25000}, + {PMIC_LDO7, 0x46, 0x0, 0x3F, 0x46, 0x6, 0x3, 0x1, 0x0, 800, 25000}, + {PMIC_LDO8, 0x47, 0x0, 0x3F, 0x47, 0x6, 0x3, 0x1, 0x0, 800, 25000}, + {PMIC_LDO9, 0x48, 0x0, 0x3F, 0x48, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO10, 0x49, 0x0, 0x3F, 0x49, 0x6, 0x3, 0x1, 0x0, 800, 50000}, + {PMIC_LDO11, 0x4A, 0x0, 0x3F, 0x4A, 0x6, 0x3, 0x1, 0x0, 800, 50000}, + {PMIC_LDO12, 0x4B, 0x0, 0x3F, 0x4B, 0x6, 0x3, 0x1, 0x0, 800, 50000}, + {PMIC_LDO13, 0x4C, 0x0, 0x3F, 0x4C, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO14, 0x4D, 0x0, 0x3F, 0x4D, 0x6, 0x3, 0x1, 0x0, 800, 50000}, + {PMIC_LDO15, 0x4E, 0x0, 0x3F, 0x4E, 0x6, 0x3, 0x1, 0x0, 800, 25000}, + {PMIC_LDO16, 0x4F, 0x0, 0x3F, 0x4F, 0x6, 0x3, 0x1, 0x0, 800, 50000}, + {PMIC_LDO17, 0x50, 0x0, 0x3F, 0x50, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO18, 0x51, 0x0, 0x3F, 0x51, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO19, 0x52, 0x0, 0x3F, 0x52, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO20, 0x53, 0x0, 0x3F, 0x53, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO21, 0x54, 0x0, 0x3F, 0x54, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO22, 0x55, 0x0, 0x3F, 0x55, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO23, 0x56, 0x0, 0x3F, 0x56, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO24, 0x57, 0x0, 0x3F, 0x57, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO25, 0x58, 0x0, 0x3F, 0x58, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_LDO26, 0x59, 0x0, 0x3F, 0x59, 0x6, 0x3, 0x3, 0x0, 800, 50000}, + {PMIC_EN32KHZ_CP, 0x0, 0x0, 0x0, 0x7F, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0}, +}; + +/* + * Write a value to a register + * + * @param chip_addr i2c addr for max77686 + * @param reg reg number to write + * @param val value to be written + * + */ +static inline int max77686_i2c_write(unsigned char chip_addr, + unsigned int reg, unsigned char val) +{ + return i2c_write(chip_addr, reg, 1, &val, 1); +} + +/* + * Read a value from a register + * + * @param chip_addr i2c addr for max77686 + * @param reg reg number to write + * @param val value to be written + * + */ +static inline int max77686_i2c_read(unsigned char chip_addr, + unsigned int reg, unsigned char *val) +{ + return i2c_read(chip_addr, reg, 1, val, 1); +} + +/* + * Enable the max77686 register + * + * @param reg register number of buck/ldo to be enabled + * @param enable enable or disable bit + * + * REG_DISABLE = 0, + needed to set the buck/ldo enable bit OFF + * @return Return 0 if ok, else -1 + */ +static int max77686_enablereg(enum max77686_regnum reg, int enable) +{ + struct max77686_para *pmic; + unsigned char read_data; + int ret; + + pmic = &max77686_param[reg]; + + ret = max77686_i2c_read(MAX77686_I2C_ADDR, pmic->reg_enaddr, + &read_data); + if (ret != 0) { + debug("max77686 i2c read failed.\n"); + return -1; + } + + if (enable == REG_DISABLE) { + clrbits_8(&read_data, + pmic->reg_enbitmask << pmic->reg_enbitpos); + } else { + clrsetbits_8(&read_data, + pmic->reg_enbitmask << pmic->reg_enbitpos, + pmic->reg_enbiton << pmic->reg_enbitpos); + } + + ret = max77686_i2c_write(MAX77686_I2C_ADDR, + pmic->reg_enaddr, read_data); + if (ret != 0) { + debug("max77686 i2c write failed.\n"); + return -1; + } + + return 0; +} + +static int max77686_do_volsetting(enum max77686_regnum reg, unsigned int volt, + int enable, int volt_units) +{ + struct max77686_para *pmic; + unsigned char read_data; + int vol_level = 0; + int ret; + + pmic = &max77686_param[reg]; + + if (pmic->vol_addr == 0) { + debug("not a voltage register.\n"); + return -1; + } + + ret = max77686_i2c_read(MAX77686_I2C_ADDR, pmic->vol_addr, &read_data); + if (ret != 0) { + debug("max77686 i2c read failed.\n"); + return -1; + } + + if (volt_units == MAX77686_UV) + vol_level = volt - pmic->vol_min * 1000; + else + vol_level = (volt - pmic->vol_min) * 1000; + + if (vol_level < 0) { + debug("Not a valid voltage level to set\n"); + return -1; + } + vol_level /= pmic->vol_div; + + clrsetbits_8(&read_data, pmic->vol_bitmask << pmic->vol_bitpos, + vol_level << pmic->vol_bitpos); + + ret = max77686_i2c_write(MAX77686_I2C_ADDR, pmic->vol_addr, read_data); + if (ret != 0) { + debug("max77686 i2c write failed.\n"); + return -1; + } + + ret = max77686_enablereg(reg, enable); + if (ret != 0) { + debug("Failed to enable buck/ldo.\n"); + return -1; + } + + return 0; +} + +int max77686_volsetting(enum max77686_regnum reg, unsigned int volt, + int enable, int volt_units) +{ + int old_bus = i2c_get_bus_num(); + int ret; + + i2c_set_bus_num(0); + ret = max77686_do_volsetting(reg, volt, enable, volt_units); + i2c_set_bus_num(old_bus); + return ret; +} + +int max77686_enable_32khz_cp(void) +{ + i2c_set_bus_num(0); + return max77686_enablereg(PMIC_EN32KHZ_CP, REG_ENABLE); +} + +int max77686_disable_backup_batt(void) +{ + unsigned char val; + int ret; + + i2c_set_bus_num(0); + ret = max77686_i2c_read(MAX77686_I2C_ADDR, REG_BBAT, &val); + if (ret) { + debug("max77686 i2c read failed\n"); + return ret; + } + + /* If we already have the correct values, exit */ + if ((val & (BBAT_BBCVS_MASK | BBAT_BBCHOSTEN_MASK)) == + BBAT_BBCVS_MASK) + return 0; + + /* First disable charging */ + val &= ~BBAT_BBCHOSTEN_MASK; + ret = max77686_i2c_write(MAX77686_I2C_ADDR, REG_BBAT, val); + if (ret) { + debug("max77686 i2c write failed\n"); + return -1; + } + + /* Finally select 3.5V to minimize power consumption */ + val |= BBAT_BBCVS_MASK; + ret = max77686_i2c_write(MAX77686_I2C_ADDR, REG_BBAT, val); + if (ret) { + debug("max77686 i2c write failed\n"); + return -1; + } + + return 0; +} diff --git a/src/include/cbfs_core.h b/src/include/cbfs_core.h index 32f2670..9a21a78 100644 --- a/src/include/cbfs_core.h +++ b/src/include/cbfs_core.h @@ -84,6 +84,9 @@ #define CBFS_HEADER_MAGIC 0x4F524243 #if CONFIG_ARCH_X86 #define CBFS_HEADPTR_ADDR 0xFFFFFFFC +#elif CONFIG_ARCH_ARMV7 +/* FIXME: This could also be 0xFFFF0000 with HIVECS enabled */ +#define CBFS_HEADPTR_ADDR 0x0000000C #endif #define VERSION1 0x31313131 #define VERSION2 0x31313132 diff --git a/src/include/compiler.h b/src/include/compiler.h new file mode 100644 index 0000000..39ed285 --- /dev/null +++ b/src/include/compiler.h @@ -0,0 +1,140 @@ +/* + * Keep all the ugly #ifdef for system stuff here + */ + +#ifndef __COMPILER_H__ +#define __COMPILER_H__ + +#include <stddef.h> + +#ifdef USE_HOSTCC + +#if defined(__BEOS__) || \ + defined(__NetBSD__) || \ + defined(__FreeBSD__) || \ + defined(__sun__) || \ + defined(__APPLE__) +# include <inttypes.h> +#elif defined(__linux__) || defined(__WIN32__) || defined(__MINGW32__) +# include <stdint.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#if !defined(__WIN32__) && !defined(__MINGW32__) +# include <sys/mman.h> +#endif + +/* Not all systems (like Windows) has this define, and yes + * we do replace/emulate mmap() on those systems ... + */ +#ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +#endif + +#include <fcntl.h> +#ifndef O_BINARY /* should be define'd on __WIN32__ */ +#define O_BINARY 0 +#endif + +#ifdef __linux__ +# include <endian.h> +# include <byteswap.h> +#elif defined(__MACH__) || defined(__FreeBSD__) +# include <machine/endian.h> +typedef unsigned long ulong; +#endif + +typedef uint8_t __u8; +typedef uint16_t __u16; +typedef uint32_t __u32; +typedef unsigned int uint; + +#define uswap_16(x) \ + ((((x) & 0xff00) >> 8) | \ + (((x) & 0x00ff) << 8)) +#define uswap_32(x) \ + ((((x) & 0xff000000) >> 24) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) +#define _uswap_64(x, sfx) \ + ((((x) & 0xff00000000000000##sfx) >> 56) | \ + (((x) & 0x00ff000000000000##sfx) >> 40) | \ + (((x) & 0x0000ff0000000000##sfx) >> 24) | \ + (((x) & 0x000000ff00000000##sfx) >> 8) | \ + (((x) & 0x00000000ff000000##sfx) << 8) | \ + (((x) & 0x0000000000ff0000##sfx) << 24) | \ + (((x) & 0x000000000000ff00##sfx) << 40) | \ + (((x) & 0x00000000000000ff##sfx) << 56)) +#if defined(__GNUC__) +# define uswap_64(x) _uswap_64(x, ull) +#else +# define uswap_64(x) _uswap_64(x, ) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define cpu_to_le16(x) (x) +# define cpu_to_le32(x) (x) +# define cpu_to_le64(x) (x) +# define le16_to_cpu(x) (x) +# define le32_to_cpu(x) (x) +# define le64_to_cpu(x) (x) +# define cpu_to_be16(x) uswap_16(x) +# define cpu_to_be32(x) uswap_32(x) +# define cpu_to_be64(x) uswap_64(x) +# define be16_to_cpu(x) uswap_16(x) +# define be32_to_cpu(x) uswap_32(x) +# define be64_to_cpu(x) uswap_64(x) +#else +# define cpu_to_le16(x) uswap_16(x) +# define cpu_to_le32(x) uswap_32(x) +# define cpu_to_le64(x) uswap_64(x) +# define le16_to_cpu(x) uswap_16(x) +# define le32_to_cpu(x) uswap_32(x) +# define le64_to_cpu(x) uswap_64(x) +# define cpu_to_be16(x) (x) +# define cpu_to_be32(x) (x) +# define cpu_to_be64(x) (x) +# define be16_to_cpu(x) (x) +# define be32_to_cpu(x) (x) +# define be64_to_cpu(x) (x) +#endif + +#else /* !USE_HOSTCC */ + +//#include <linux/string.h> +//#include <linux/types.h> +//#include <asm/byteorder.h> +//#include <arch/byteorder.h> + +#if __SIZEOF_LONG__ == 8 +# define __WORDSIZE 64 +#elif __SIZEOF_LONG__ == 4 +# define __WORDSIZE 32 +#else +/* + * Assume 32-bit for now - only newer toolchains support this feature and + * this is only required for sandbox support at present. + */ +#define __WORDSIZE 32 +#endif + +#if 0 +/* Type for `void *' pointers. */ +typedef unsigned long int uintptr_t; +#endif + +#endif /* USE_HOSTCC */ + +/* compiler options */ +#define uninitialized_var(x) x = x + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#endif diff --git a/src/include/device/i2c.h b/src/include/device/i2c.h new file mode 100644 index 0000000..f1275e3 --- /dev/null +++ b/src/include/device/i2c.h @@ -0,0 +1,279 @@ +/* + * (C) Copyright 2001 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The original I2C interface was + * (C) 2000 by Paolo Scaffardi (arsenio@tin.it) + * AIRVENT SAM s.p.a - RIMINI(ITALY) + * but has been changed substantially. + */ + +#ifndef _I2C_H_ +#define _I2C_H_ + +/* + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * The implementation MUST NOT use static or global variables if the + * I2C routines are used to read SDRAM configuration information + * because this is done before the memories are initialized. Limited + * use of stack-based variables are OK (the initial stack size is + * limited). + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +/* + * Configuration items. + */ +#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ + +#ifdef CONFIG_I2C_MULTI_BUS +#define MAX_I2C_BUS 2 +#define I2C_MULTI_BUS 1 +#else +#define MAX_I2C_BUS 1 +#define I2C_MULTI_BUS 0 +#endif + +#if !defined(CONFIG_SYS_MAX_I2C_BUS) +#define CONFIG_SYS_MAX_I2C_BUS MAX_I2C_BUS +#endif + +/* define the I2C bus number for RTC and DTT if not already done */ +#if !defined(CONFIG_SYS_RTC_BUS_NUM) +#define CONFIG_SYS_RTC_BUS_NUM 0 +#endif +#if !defined(CONFIG_SYS_DTT_BUS_NUM) +#define CONFIG_SYS_DTT_BUS_NUM 0 +#endif +#if !defined(CONFIG_SYS_SPD_BUS_NUM) +#define CONFIG_SYS_SPD_BUS_NUM 0 +#endif + +#ifndef I2C_SOFT_DECLARATIONS +# if defined(CONFIG_MPC8260) +# define I2C_SOFT_DECLARATIONS volatile ioport_t *iop = ioport_addr((immap_t *)CONFIG_SYS_IMMR, I2C_PORT); +# elif defined(CONFIG_8xx) +# define I2C_SOFT_DECLARATIONS volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; + +# elif (defined(CONFIG_AT91RM9200) || \ + defined(CONFIG_AT91SAM9260) || defined(CONFIG_AT91SAM9261) || \ + defined(CONFIG_AT91SAM9263)) && !defined(CONFIG_AT91_LEGACY) +# define I2C_SOFT_DECLARATIONS at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; +# else +# define I2C_SOFT_DECLARATIONS +# endif +#endif + +#ifdef CONFIG_8xx +/* Set default value for the I2C bus speed on 8xx. In the + * future, we'll define these in all 8xx board config files. + */ +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 50000 +#endif +#endif + +/* + * Many boards/controllers/drivers don't support an I2C slave interface so + * provide a default slave address for them for use in common code. A real + * value for CONFIG_SYS_I2C_SLAVE should be defined for any board which does + * support a slave interface. + */ +#ifndef CONFIG_SYS_I2C_SLAVE +#define CONFIG_SYS_I2C_SLAVE 0xfe +#endif + +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +void i2c_init(int speed, int slaveaddr); +void i2c_init_board(void); +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT +void i2c_board_late_init(void); +#endif + +#if defined(CONFIG_I2C_MUX) + +typedef struct _mux { + uchar chip; + uchar channel; + char *name; + struct _mux *next; +} I2C_MUX; + +typedef struct _mux_device { + int busid; + I2C_MUX *mux; /* List of muxes, to reach the device */ + struct _mux_device *next; +} I2C_MUX_DEVICE; + +I2C_MUX_DEVICE *i2c_mux_search_device(int id); +I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf); +int i2x_mux_select_mux(int bus); +int i2c_mux_ident_muxstring_f (uchar *buf); +#endif + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uchar chip); + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len); +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len); + +/* + * Utility routines to read/write registers. + */ +static inline u8 i2c_reg_read(u8 addr, u8 reg) +{ + u8 buf; + +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + +#ifdef DEBUG + printf("%s: addr=0x%02x, reg=0x%02x\n", __func__, addr, reg); +#endif + + i2c_read(addr, reg, 1, &buf, 1); + + return buf; +} + +static inline void i2c_reg_write(u8 addr, u8 reg, u8 val) +{ +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif + +#ifdef DEBUG + printf("%s: addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, addr, reg, val); +#endif + + i2c_write(addr, reg, 1, &val, 1); +} + +/* + * Functions for setting the current I2C bus and its speed + */ + +/* + * i2c_set_bus_num: + * + * Change the active I2C bus. Subsequent read/write calls will + * go to this one. + * + * bus - bus index, zero based + * + * Returns: 0 on success, not 0 on failure + * + */ +int i2c_set_bus_num(unsigned int bus); + +/* + * i2c_get_bus_num: + * + * Returns index of currently active I2C bus. Zero-based. + */ + +unsigned int i2c_get_bus_num(void); + +/* + * i2c_set_bus_speed: + * + * Change the speed of the active I2C bus + * + * speed - bus speed in Hz + * + * Returns: 0 on success, not 0 on failure + * + */ +int i2c_set_bus_speed(unsigned int); + +/* + * i2c_get_bus_speed: + * + * Returns speed of currently active I2C bus in Hz + */ + +unsigned int i2c_get_bus_speed(void); + +/* NOTE: These two functions MUST be always_inline to avoid code growth! */ +static inline unsigned int I2C_GET_BUS(void) __attribute__((always_inline)); +static inline unsigned int I2C_GET_BUS(void) +{ + return I2C_MULTI_BUS ? i2c_get_bus_num() : 0; +} + +static inline void I2C_SET_BUS(unsigned int bus) __attribute__((always_inline)); +static inline void I2C_SET_BUS(unsigned int bus) +{ + if (I2C_MULTI_BUS) + i2c_set_bus_num(bus); +} + +/* + * Set an early register base for a given i2c peripheral. + * This is used to access a single i2c bus early on in the boot sequence. + * + * @param base: The base address of the i2c peripheral's register map + */ +void i2c_set_early_reg(unsigned int base); + +/** + * Find the I2C bus number by given a FDT I2C node. + * + * @param blob Device tree blbo + * @param node FDT I2C node to find + * @return the number of I2C bus (zero based), or -1 on error + */ +int i2c_get_bus_num_fdt(const void *blob, int node); + +/** + * Reset the I2C bus represented by the given a FDT I2C node. + * + * @param blob Device tree blbo + * @param node FDT I2C node to find + * @return 0 if port was reset, -1 if not found + */ +int i2c_reset_port_fdt(const void *blob, int node); + +#endif /* _I2C_H_ */ diff --git a/src/include/device/power/max77686.h b/src/include/device/power/max77686.h new file mode 100644 index 0000000..345471e --- /dev/null +++ b/src/include/device/power/max77686.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Alim Akhtar alim.akhtar@samsung.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __MAX77686_H_ +#define __MAX77686_H_ + +enum max77686_regnum { + PMIC_BUCK1 = 0, + PMIC_BUCK2, + PMIC_BUCK3, + PMIC_BUCK4, + PMIC_BUCK5, + PMIC_BUCK6, + PMIC_BUCK7, + PMIC_BUCK8, + PMIC_BUCK9, + PMIC_LDO1, + PMIC_LDO2, + PMIC_LDO3, + PMIC_LDO4, + PMIC_LDO5, + PMIC_LDO6, + PMIC_LDO7, + PMIC_LDO8, + PMIC_LDO9, + PMIC_LDO10, + PMIC_LDO11, + PMIC_LDO12, + PMIC_LDO13, + PMIC_LDO14, + PMIC_LDO15, + PMIC_LDO16, + PMIC_LDO17, + PMIC_LDO18, + PMIC_LDO19, + PMIC_LDO20, + PMIC_LDO21, + PMIC_LDO22, + PMIC_LDO23, + PMIC_LDO24, + PMIC_LDO25, + PMIC_LDO26, + PMIC_EN32KHZ_CP, +}; + +/** + * struct max77686_para - max77686 register parameters + * @param vol_addr i2c address of the given buck/ldo register + * @param vol_bitpos bit position to be set or clear within register + * @param vol_bitmask bit mask value + * @param reg_enaddr control register address, which enable the given + * given buck/ldo. + * @param reg_enbitpos bit position to be enabled + * @param reg_enbiton value to be written to buck/ldo to make it ON + * @param reg_enbitoff value to be written to buck/ldo to make it OFF + * @param vol_min minimum voltage level supported by given buck/ldo + * @param vol_div voltage division value of given buck/ldo + */ +struct max77686_para { + enum max77686_regnum regnum; + u8 vol_addr; + u8 vol_bitpos; + u8 vol_bitmask; + u8 reg_enaddr; + u8 reg_enbitpos; + u8 reg_enbitmask; + u8 reg_enbiton; + u8 reg_enbitoff; + u32 vol_min; + u32 vol_div; +}; + +/* I2C device address for pmic max77686 */ +#define MAX77686_I2C_ADDR (0x12 >> 1) + +enum { + REG_DISABLE = 0, + REG_ENABLE +}; + +enum { + MAX77686_MV = 0, /* mili volt */ + MAX77686_UV /* micro volt */ +}; + +/** + * This function enables the 32KHz coprocessor clock. + * + * Return 0 if ok, else -1 + */ +int max77686_enable_32khz_cp(void); + +/** + * Set the required voltage level of pmic + * + * @param reg register number of buck/ldo to be set + * @param volt voltage level to be set + * @param enable enable or disable bit + * @param volt_units MAX77686_MV or MAX77686_UV, unit of the + * voltage parameters + * + * @return Return 0 if ok, else -1 + */ +int max77686_volsetting(enum max77686_regnum reg, unsigned int volt, + int enable, int volt_units); + +/** + * Disable charging of the RTC backup battery + * + * @return Return 0 if ok, else -1 + */ +int max77686_disable_backup_batt(void); + +#endif /* __MAX77686_PMIC_H_ */ diff --git a/src/include/stddef.h b/src/include/stddef.h new file mode 100644 index 0000000..888d497 --- /dev/null +++ b/src/include/stddef.h @@ -0,0 +1,24 @@ +#ifndef STDDEF_H +#define STDDEF_H + +typedef long ptrdiff_t; +#ifndef __SIZE_TYPE__ +#define __SIZE_TYPE__ unsigned long +#endif +typedef __SIZE_TYPE__ size_t; +typedef long ssize_t; + +typedef int wchar_t; +typedef unsigned int wint_t; + +#define NULL ((void *)0) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#ifdef __PRE_RAM__ +#define ROMSTAGE_CONST const +#else +#define ROMSTAGE_CONST +#endif + +#endif /* STDDEF_H */ diff --git a/src/include/uart.h b/src/include/uart.h new file mode 100644 index 0000000..931d6dc --- /dev/null +++ b/src/include/uart.h @@ -0,0 +1,33 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * FIXME(dhendrix): This file contains generic prototypes for UART functions. + * The existing headers are too specific to the 8250, so we need a better + * abstraction for use with non-8250 UARTs. + */ + +#ifndef UART_H +#define UART_H + +unsigned char uart_rx_byte(void); +void uart_tx_byte(unsigned char data); +void uart_tx_flush(void); + +void uart_init(void); + +#endif /* UART_H */ diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 3b16170..6b3f0d8 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -60,7 +60,7 @@ ramstage-y += cbfs.c ramstage-y += lzma.c #ramstage-y += lzmadecode.c ramstage-y += stack.c -ramstage-y += gcc.c +ramstage-$(CONFIG_ARCH_X86) += gcc.c ramstage-y += clog2.c ramstage-y += cbmem.c ramstage-$(CONFIG_CONSOLE_SERIAL8250) += uart8250.c diff --git a/src/mainboard/Kconfig b/src/mainboard/Kconfig index da76327..734aaf2 100644 --- a/src/mainboard/Kconfig +++ b/src/mainboard/Kconfig @@ -58,6 +58,8 @@ config VENDOR_GETAC bool "Getac" config VENDOR_GIGABYTE bool "GIGABYTE" +config VENDOR_GOOGLE + bool "Google" config VENDOR_HP bool "HP" config VENDOR_IBASE @@ -158,6 +160,7 @@ source "src/mainboard/ecs/Kconfig" source "src/mainboard/emulation/Kconfig" source "src/mainboard/getac/Kconfig" source "src/mainboard/gigabyte/Kconfig" +source "src/mainboard/google/Kconfig" source "src/mainboard/hp/Kconfig" source "src/mainboard/ibase/Kconfig" source "src/mainboard/ibm/Kconfig" @@ -304,6 +307,19 @@ config ROM_SIZE default 0x800000 if COREBOOT_ROMSIZE_KB_8192 default 0x1000000 if COREBOOT_ROMSIZE_KB_16384
+# FIXME: does this need to be here or in sandybridge/ ? +#config CBFS_SIZE +# hex +# default ROM_SIZE + +config CACHE_ROM_SIZE + hex + default ROM_SIZE + +config CACHE_ROM_SIZE + hex + default CBFS_SIZE + config ENABLE_POWER_BUTTON bool "Enable the power button" if POWER_BUTTON_IS_OPTIONAL default y if POWER_BUTTON_DEFAULT_ENABLE diff --git a/src/mainboard/google/Kconfig b/src/mainboard/google/Kconfig new file mode 100644 index 0000000..126a37d --- /dev/null +++ b/src/mainboard/google/Kconfig @@ -0,0 +1,17 @@ +if VENDOR_GOOGLE + +choice + prompt "Mainboard model" + +config BOARD_GOOGLE_SNOW + bool "Snow" + +endchoice + +source "src/mainboard/google/snow/Kconfig" + +config MAINBOARD_VENDOR + string + default "Google" + +endif # VENDOR_GOOGLE diff --git a/src/mainboard/google/snow/Kconfig b/src/mainboard/google/snow/Kconfig new file mode 100644 index 0000000..eedccba --- /dev/null +++ b/src/mainboard/google/snow/Kconfig @@ -0,0 +1,162 @@ +if BOARD_GOOGLE_SNOW + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + select ARCH_ARMV7 + select CPU_SAMSUNG_EXYNOS5 + select HAVE_UART_MEMORY_MAPPED + select CONSOLE_SERIAL_NONSTANDARD_MEM # enable serial debugging +# select NORTHBRIDGE_INTEL_IVYBRIDGE +# select EC_GOOGLE_CHROMEEC + select BOARD_ROMSIZE_KB_4096 + select DRIVER_MAXIM_77686 +# select HAVE_ACPI_TABLES +# select MMCONF_SUPPORT +# select CHROMEOS + +config MAINBOARD_DIR + string + default google/snow + +config MAINBOARD_PART_NUMBER + string + default "SNOW" + +#config MMCONF_BASE_ADDRESS +# hex +# default 0xf0000000 + +#config IRQ_SLOT_COUNT +# int +# default 18 + +config MAX_CPUS + int + default 2 + +config MAINBOARD_VENDOR + string + default "Samsung" + +# SPL (second-phase loader) stuff +config SPL_TEXT_BASE + hex "SPL executable base" + default 0x02023400 + help + Location of SPL. Default location is within iRAM region. + +#FIXME: increased "SPL" size to get around build issues +#config SPL_MAX_SIZE +# hex "SPL executable max size" +# default 0x3800 +# help +# Max size of SPL. Default is 14KB +config SPL_MAX_SIZE + hex "SPL executable max size" + default 0x8000 + help + Max size of SPL. Let's say 32KB for now... + +config DRAM_SIZE_MB + int "DRAM size (MB)" + default 2048 + +config NR_DRAM_BANKS + int "Number of DRAM banks" + default 1 + +choice + prompt "Serial Console UART" + default CONSOLE_SERIAL_UART3 + depends on CONSOLE_SERIAL_NONSTANDARD_MEM + +config CONSOLE_SERIAL_UART0 + bool "UART0" + help + Serial console on UART0 + +config CONSOLE_SERIAL_UART1 + bool "UART1" + help + Serial console on UART1 + +config CONSOLE_SERIAL_UART2 + bool "UART2" + help + Serial console on UART2 + +config CONSOLE_SERIAL_UART3 + bool "UART3" + help + Serial console on UART3 + +endchoice + +config CONSOLE_SERIAL_UART_ADDRESS + hex + depends on CONSOLE_SERIAL_NONSTANDARD_MEM + default 0x12c00000 if CONSOLE_SERIAL_UART0 + default 0x12c10000 if CONSOLE_SERIAL_UART1 + default 0x12c20000 if CONSOLE_SERIAL_UART2 + default 0x12c30000 if CONSOLE_SERIAL_UART3 + help + Map the UART names to the respective MMIO address. + +################################################################# +# stuff from smdk5250.h # +# FIXME: can we move some of these to exynos5250's Kconfig? # +################################################################# +config SYS_I2C_SPEED + int + default 100000 + +config SYS_I2C_SLAVE + hex + default 0x0 + +config I2C_MULTI_BUS + bool + default y + +#config HARD_I2C +# bool +# default y +#CMD_I2C +#I2C_EDID +#DRIVER_S3C24X0_I2C + +config VDD_ARM_MV + int + default 1300 #1.3V + +config VDD_INT_UV + int + default 1012500 # 1.0125v + +config VDD_MIF_MV + int + default 1000 # 1.0v + +config VDD_G3D_MV + int + default 1200 # 1.2v + +config VDD_LDO2_MV + int + default 1500 # 1.5v + +config VDD_LDO3_MV + int + default 1800 # 1.8v + +config VDD_LDO5_MV + int + default 1800 # 1.8v + +config VDD_LDO10_MV + int + default 1800 # 1.8v + +######### smdk5250.h ######## + +endif # BOARD_GOOGLE_SNOW diff --git a/src/mainboard/google/snow/Makefile.inc b/src/mainboard/google/snow/Makefile.inc new file mode 100644 index 0000000..01488d0 --- /dev/null +++ b/src/mainboard/google/snow/Makefile.inc @@ -0,0 +1,36 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2012 The ChromiumOS Authors. All rights reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +#romstage-y += romstage.c + +# ramstage-y += ec.c + +# FIXME: smdk5250_spl and mainboard.c are a mess. In the long +# run we'll want to replace low-level code that calls mainboard code +# with mainboard code that calls low-level code with appropriate +# parameters. Grep around for spl_get_machine_params for examples. +romstage-y += smdk5250_spl.c +ramstage-y += smdk5250_spl.c +#ramstage-y += mainboard.c + +# romstage-$(CONFIG_CHROMEOS) += chromeos.c + +# FIXME: we should do something similar to x86 platforms for Snow SPDs + +SRC_ROOT = $(src)/mainboard/google/snow diff --git a/src/mainboard/google/snow/board.h b/src/mainboard/google/snow/board.h new file mode 100644 index 0000000..bedd740 --- /dev/null +++ b/src/mainboard/google/snow/board.h @@ -0,0 +1,33 @@ +/* + * Board header file for Exynos boards + * + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __SAMSUNG_BOARD_H +#define __SAMSUNG_BOARD_H + +/* Init the SPI driver */ +void spi_init(void); + +/* Init the I2C driver */ +void board_i2c_init(const void *blob); + +#endif diff --git a/src/mainboard/google/snow/devicetree.cb b/src/mainboard/google/snow/devicetree.cb new file mode 100644 index 0000000..1d9d553 --- /dev/null +++ b/src/mainboard/google/snow/devicetree.cb @@ -0,0 +1,7 @@ +# FIXME: this is just a stub for now +chip cpu/samsung/exynos5250 + chip drivers/generic/generic # I2C0 controller + device i2c 6 on end # ? + device i2c 9 on end # ? + end +end diff --git a/src/mainboard/google/snow/mainboard.c b/src/mainboard/google/snow/mainboard.c new file mode 100644 index 0000000..8afac3c --- /dev/null +++ b/src/mainboard/google/snow/mainboard.c @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * FIXME: Note: Most of this is taken from U-Boot. Coreboot stuff is + * appended at the end. Also, much of this should be broken up into + * smaller files anyway. + */ + +#include <device/device.h> +#if 0 /* big #if */ +#include <common.h> +#include <fdtdec.h> +#include <i2c.h> +#include <max77686.h> +#include <mkbp.h> +#include <mmc.h> +#include <netdev.h> +#include <tps65090.h> +#include <errno.h> +#include <asm/gpio.h> +#include <asm/arch/clock.h> +#include <cpu/samsung/exynos5250/cpu.h> +#include <asm/arch/ehci-s5p.h> +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> +#include <asm/arch/mmc.h> +#include <asm/arch/mshc.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/sromc.h> +#include <asm/arch-exynos5/power.h> +#include <asm/arch/sata.h> +#include <asm/arch/exynos-tmu.h> +#include <asm/arch/exynos-cpufreq.h> +#include <asm/arch/s5p-dp.h> + +#include "board.h" + +#ifndef CONFIG_EXYNOS_DISPLAYPORT +#error "CONFIG_EXYNOS_DISPLAYPORT must be defined for smdk5250" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +struct local_info { + struct mkbp_dev *mkbp_dev; /* Pointer to mkbp device */ + int arbitrate_node; + struct fdt_gpio_state ap_claim; + struct fdt_gpio_state ec_claim; + + /* DisplayPort gpios */ + struct fdt_gpio_state dp_pd; + struct fdt_gpio_state dp_rst; + struct fdt_gpio_state dp_hpd; + + /* Time between requesting bus and deciding that we have it */ + unsigned slew_delay_us; + + /* Time between retrying to see if the AP has released the bus */ + unsigned wait_retry_ms; + + /* Time to wait until the bus becomes free */ + unsigned wait_free_ms; +}; + +static struct local_info local; +static uint32_t cpufreq_loop_count; + +/* + * Called to do the needful when tstc has a character ready + * Meant to work in contrast to board_poll_devices + */ +void board_tstc_ready(void) +{ +#ifdef CONFIG_EXYNOS_CPUFREQ + if (cpufreq_loop_count >= 10000000) { + /* Character received, increase ARM frequency */ + exynos5250_set_frequency(CPU_FREQ_L1700); + } + cpufreq_loop_count = 0; +#endif /* CONFIG_EXYNOS_CPUFREQ */ +} + +/* + * Polling various devices on board for details and status monitoring purposes + */ +void board_poll_devices(void) +{ +#if defined CONFIG_EXYNOS_TMU + int temp; + + switch (tmu_monitor(&temp)) { + case TMU_STATUS_TRIPPED: + puts("EXYNOS_TMU: TRIPPING! Device power going down ...\n"); + power_shutdown(); + break; + case TMU_STATUS_WARNING: + puts("EXYNOS_TMU: WARNING! Temperature very high\n"); + break; + case TMU_STATUS_INIT: + case TMU_STATUS_NORMAL: + break; + default: + debug("Unknown TMU state\n"); + } +#endif /* CONFIG_EXYNOS_TMU */ +#ifdef CONFIG_EXYNOS_CPUFREQ + cpufreq_loop_count++; + if (cpufreq_loop_count == 10000000) { + /* User is idle, decrease ARM frequency*/ + exynos5250_set_frequency(CPU_FREQ_L200); + } +#endif /* CONFIG_EXYNOS_CPUFREQ */ +} + +#ifdef CONFIG_OF_CONTROL +static int decode_sromc(const void *blob, struct fdt_sromc *config) +{ + int err; + int node; + + node = fdtdec_next_compatible(blob, 0, COMPAT_SAMSUNG_EXYNOS5_SROMC); + if (node < 0) { + debug("Could not find SROMC node\n"); + return node; + } + + config->bank = fdtdec_get_int(blob, node, "bank", 0); + config->width = fdtdec_get_int(blob, node, "width", 2); + + err = fdtdec_get_int_array(blob, node, "srom-timing", config->timing, + FDT_SROM_TIMING_COUNT); + if (err < 0) { + debug("Could not decode SROMC configuration\n"); + return -FDT_ERR_NOTFOUND; + } + + return 0; +} +#endif + +#if 0 +/** + * Read and clear the marker value; then return the read value. + * + * This marker is set to EXYNOS5_SPL_MARKER when SPL runs. Then in U-Boot + * we can check (and clear) this marker to see if we were run from SPL. + * If we were called from another U-Boot, the marker will be clear. + * + * @return marker value (EXYNOS5_SPL_MARKER if we were run from SPL, else 0) + */ +static uint32_t exynos5_read_and_clear_spl_marker(void) +{ + uint32_t value, *marker = (uint32_t *)CONFIG_SPL_MARKER; + + value = *marker; + *marker = 0; + + return value; +} +#endif + +int board_is_processor_reset(void) +{ + static uint8_t inited, is_reset; + uint32_t marker_value; + + if (!inited) { + marker_value = exynos5_read_and_clear_spl_marker(); + is_reset = marker_value == EXYNOS5_SPL_MARKER; + inited = 1; + } + + return is_reset; +} + +int board_eth_init(bd_t *bis) +{ +#ifdef CONFIG_SMC911X + u32 smc_bw_conf, smc_bc_conf; + struct fdt_sromc config; + fdt_addr_t base_addr; + int node; + +#ifdef CONFIG_OF_CONTROL + node = decode_sromc(gd->fdt_blob, &config); + if (node < 0) { + debug("%s: Could not find sromc configuration\n", __func__); + return 0; + } + node = fdtdec_next_compatible(gd->fdt_blob, node, COMPAT_SMSC_LAN9215); + if (node < 0) { + debug("%s: Could not find lan9215 configuration\n", __func__); + return 0; + } + + /* We now have a node, so any problems from now on are errors */ + base_addr = fdtdec_get_addr(gd->fdt_blob, node, "reg"); + if (base_addr == FDT_ADDR_T_NONE) { + debug("%s: Could not find lan9215 address\n", __func__); + return -1; + } +#else + /* Non-FDT configuration - bank number and timing parameters*/ + config.bank = CONFIG_ENV_SROM_BANK; + config.width = 2; + + config.timing[FDT_SROM_TACS] = 0x01; + config.timing[FDT_SROM_TCOS] = 0x01; + config.timing[FDT_SROM_TACC] = 0x06; + config.timing[FDT_SROM_TCOH] = 0x01; + config.timing[FDT_SROM_TAH] = 0x0C; + config.timing[FDT_SROM_TACP] = 0x09; + config.timing[FDT_SROM_PMC] = 0x01; + base_addr = CONFIG_SMC911X_BASE; +#endif + + /* Ethernet needs data bus width of 16 bits */ + if (config.width != 2) { + debug("%s: Unsupported bus width %d\n", __func__, + config.width); + return -1; + } + smc_bw_conf = SROMC_DATA16_WIDTH(config.bank) + | SROMC_BYTE_ENABLE(config.bank); + + smc_bc_conf = SROMC_BC_TACS(config.timing[FDT_SROM_TACS]) |\ + SROMC_BC_TCOS(config.timing[FDT_SROM_TCOS]) |\ + SROMC_BC_TACC(config.timing[FDT_SROM_TACC]) |\ + SROMC_BC_TCOH(config.timing[FDT_SROM_TCOH]) |\ + SROMC_BC_TAH(config.timing[FDT_SROM_TAH]) |\ + SROMC_BC_TACP(config.timing[FDT_SROM_TACP]) |\ + SROMC_BC_PMC(config.timing[FDT_SROM_PMC]); + + /* Select and configure the SROMC bank */ + exynos_pinmux_config(PERIPH_ID_SROMC, config.bank | PINMUX_FLAG_16BIT); + s5p_config_sromc(config.bank, smc_bw_conf, smc_bc_conf); + return smc911x_initialize(0, base_addr); +#endif + return 0; +} + +int fdtdec_decode_memory(const void *blob, struct fdt_memory *config) +{ + int node, len; + const fdt_addr_t *cell; + + node = fdt_path_offset(blob, "/memory"); + if (node < 0) { + debug("Could not find the path /memory\n"); + return node; + } + cell = fdt_getprop(blob, node, "reg", &len); + if (cell && len == sizeof(fdt_addr_t) * 2) { + config->start = fdt_addr_to_cpu(cell[0]); + config->end = fdt_addr_to_cpu(cell[1]); + } else { + return -FDT_ERR_BADLAYOUT; + } + + return 0; +} + +int board_usb_vbus_init(void) +{ + /* Enable VBUS power switch */ + gpio_direction_output(GPIO_X11, 1); + /* VBUS turn ON time */ + mdelay(3); + + return 0; +} + +struct mkbp_dev *board_get_mkbp_dev(void) +{ + return local.mkbp_dev; +} + +/* + * This functions disable the USB3.0 PLL to save power + */ +static void disable_usb30_pll(void) +{ + int node, ret; + struct fdt_gpio_state en_gpio; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SAMSUNG_EXYNOS_USB); + if (node < 0) + return; + + ret = fdtdec_decode_gpio(gd->fdt_blob, node, "usb3-pll-gpio", &en_gpio); + if (ret) + return; + + fdtdec_setup_gpio(&en_gpio); + gpio_direction_output(en_gpio.gpio, en_gpio.flags); +} + +static int board_init_mkbp_devices(const void *blob) +{ + local.mkbp_dev = mkbp_init(blob); + if (!local.mkbp_dev) { + debug("%s: cannot init mkbp device\n", __func__); + return -1; + } + + return 0; +} + +static int board_i2c_arb_init(const void *blob) +{ + int node; + + local.arbitrate_node = -1; + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_ARBITRATOR); + if (node < 0) { + debug("Cannot find bus arbitrator node\n"); + return 0; + } + + if (fdtdec_decode_gpio(blob, node, "google,ap-claim-gpios", + &local.ap_claim) || + fdtdec_decode_gpio(blob, node, "google,ec-claim-gpios", + &local.ec_claim)) { + debug("Cannot find bus arbitrator GPIOs\n"); + return 0; + } + + if (fdtdec_setup_gpio(&local.ap_claim) || + fdtdec_setup_gpio(&local.ec_claim)) { + debug("Cannot claim arbitration GPIOs\n"); + return -1; + } + + /* We are currently not claiming the bus */ + gpio_direction_output(local.ap_claim.gpio, 1); + gpio_direction_input(local.ec_claim.gpio); + gpio_set_pull(local.ec_claim.gpio, EXYNOS_GPIO_PULL_UP); + + local.arbitrate_node = fdtdec_lookup_phandle(blob, node, + "google,arbitrate-bus"); + if (local.arbitrate_node < 0) { + debug("Cannot find bus to arbitrate\n"); + return -1; + } + + local.slew_delay_us = fdtdec_get_int(blob, node, + "google,slew-delay-us", 10); + local.wait_retry_ms = fdtdec_get_int(blob, node, + "google,wait-retry-us", 2000); + local.wait_retry_ms = DIV_ROUND_UP(local.wait_retry_ms, 1000); + local.wait_free_ms = fdtdec_get_int(blob, node, + "google,wait-free-us", 50000); + local.wait_free_ms = DIV_ROUND_UP(local.wait_free_ms, 1000); + debug("Bus arbitration ready on fdt node %d\n", local.arbitrate_node); + + return 0; +} + +/** + * Fix-up the kernel device tree so the bridge pd_n and rst_n gpios accurately + * reflect the current board rev. + */ +static void ft_board_setup_gpios(void *blob, bd_t *bd) +{ + int ret, rev, np, len; + const struct fdt_property *prop; + + /* Do nothing for newer boards */ + rev = board_get_revision(); + if (rev < 4 || rev == 6) + return; + + /* + * If this is an older board, replace powerdown-gpio contents with that + * of reset-gpio and delete reset-gpio from the dt. + */ + np = fdtdec_next_compatible(blob, 0, COMPAT_NXP_PTN3460); + if (np < 0) { + debug("%s: Could not find COMPAT_NXP_PTN3460\n", __func__); + return; + } + + prop = fdt_get_property(blob, np, "reset-gpio", &len); + if (!prop) { + debug("%s: Could not get property err=%d\n", __func__, len); + return; + } + + ret = fdt_setprop_inplace(blob, np, "powerdown-gpio", prop->data, + len); + if (ret) { + debug("%s: Could not setprop inplace err=%d\n", __func__, ret); + return; + } + + ret = fdt_delprop(blob, np, "reset-gpio"); + if (ret) { + debug("%s: Could not delprop err=%d\n", __func__, ret); + return; + } +} + +/** + * Fix-up the kernel device tree so the powered-while-resumed is added to MP + * device tree. + */ +static void ft_board_setup_tpm_resume(void *blob, bd_t *bd) +{ + const char kernel_tpm_compat[] = "infineon,slb9635tt"; + const char prop_name[] = "powered-while-suspended"; + int err, node, rev; + + /* Only apply fixup to MP machine */ + rev = board_get_revision(); + if (!(rev == 0 || rev == 3)) + return; + + node = fdt_node_offset_by_compatible(blob, 0, kernel_tpm_compat); + if (node < 0) { + debug("%s: fail to find %s: %d\n", __func__, + kernel_tpm_compat, node); + return; + } + + err = fdt_setprop(blob, node, prop_name, NULL, 0); + if (err) { + debug("%s: fail to setprop: %d\n", __func__, err); + return; + } +} + +void ft_board_setup(void *blob, bd_t *bd) +{ + ft_board_setup_gpios(blob, bd); + ft_board_setup_tpm_resume(blob, bd); +} + +#ifdef CONFIG_TPS65090_POWER +int board_dp_lcd_vdd(const void *blob, unsigned *wait_ms) +{ + *wait_ms = 0; + return tps65090_fet_enable(6); /* Enable FET6, lcd panel */ +} +#endif + +static int board_dp_fill_gpios(const void *blob) +{ + int np, ret, rev; + + np = fdtdec_next_compatible(blob, 0, COMPAT_NXP_PTN3460); + if (np < 0) { + debug("%s: Could not find COMPAT_NXP_PTN3460 (%d)\n", __func__, + ret); + return np; + } + ret = fdtdec_decode_gpio(blob, np, "powerdown-gpio", &local.dp_pd); + if (ret) { + debug("%s: Could not decode powerdown-gpio (%d)\n", __func__, + ret); + return ret; + } + ret = fdtdec_decode_gpio(blob, np, "reset-gpio", &local.dp_rst); + if (ret) { + debug("%s: Could not decode reset-gpio (%d)\n", __func__, ret); + return ret; + } + ret = fdtdec_decode_gpio(blob, np, "hotplug-gpio", &local.dp_hpd); + if (ret) { + debug("%s: Could not decode hotplug (%d)\n", __func__, ret); + return ret; + } + + /* If board is older, replace pd gpio with rst gpio */ + rev = board_get_revision(); + if (rev >= 4 && rev != 6) { + local.dp_pd = local.dp_rst; + local.dp_rst.gpio = FDT_GPIO_NONE; + } + return 0; +} + +int board_dp_bridge_setup(const void *blob, unsigned *wait_ms) +{ + int ret; + + ret = board_dp_fill_gpios(blob); + if (ret) + return ret; + + /* Mux HPHPD to the special hotplug detect mode */ + exynos_pinmux_config(PERIPH_ID_DPHPD, 0); + + /* Setup the GPIOs */ + ret = fdtdec_setup_gpio(&local.dp_pd); + if (ret) { + debug("%s: Could not setup pd gpio (%d)\n", __func__, ret); + return ret; + } + ret = fdtdec_setup_gpio(&local.dp_rst); + if (ret) { + debug("%s: Could not setup rst gpio (%d)\n", __func__, ret); + return ret; + } + ret = fdtdec_setup_gpio(&local.dp_hpd); + if (ret) { + debug("%s: Could not setup hpd gpio (%d)\n", __func__, ret); + return ret; + } + + fdtdec_set_gpio(&local.dp_pd, 0); + gpio_cfg_pin(local.dp_pd.gpio, EXYNOS_GPIO_OUTPUT); + gpio_set_pull(local.dp_pd.gpio, EXYNOS_GPIO_PULL_NONE); + if (fdt_gpio_isvalid(&local.dp_rst)) { + fdtdec_set_gpio(&local.dp_rst, 1); + gpio_cfg_pin(local.dp_rst.gpio, EXYNOS_GPIO_OUTPUT); + gpio_set_pull(local.dp_rst.gpio, EXYNOS_GPIO_PULL_NONE); + udelay(10); + fdtdec_set_gpio(&local.dp_rst, 0); + } + + *wait_ms = 0; + return 0; +} + +int board_dp_bridge_init(const void *blob, unsigned *wait_ms) +{ + /* De-assert PD (and possibly RST) to power up the bridge */ + fdtdec_set_gpio(&local.dp_pd, 0); + + /* Ignore the return value here, on some boards this is NC */ + fdtdec_set_gpio(&local.dp_rst, 0); + + /* + * We need to wait for 90ms after bringing up the bridge since there + * is a phantom "high" on the HPD chip during its bootup. The phantom + * high comes within 7ms of de-asserting PD and persists for at least + * 15ms. The real high comes roughly 50ms after PD is de-asserted. The + * phantom high makes it hard for us to know when the NXP chip is up. + */ + *wait_ms = 90; + return 0; +} + +int board_dp_bridge_reset(const void *blob, unsigned *wait_ms) +{ + debug("%s: eDP bridge failed to come up\n", __func__); + + /* + * If we're here, the bridge chip failed to initialize. + * Drive DP_N low in an attempt to reset. + */ + fdtdec_set_gpio(&local.dp_pd, 1); + + /* Ignore the return value here, on some boards this is NC */ + fdtdec_set_gpio(&local.dp_rst, 1); + + /* + * Arbitrarily wait 300ms here with DP_N low. Don't know for + * sure how long we should wait, but we're being paranoid. + */ + *wait_ms = 300; + return 0; +} + +int board_dp_hotplug(const void *blob, unsigned *wait_ms) +{ + const int MAX_TRIES = 10; + static int num_tries; + + /* Check HPD. If it's high, we're all good. */ + if (fdtdec_get_gpio(&local.dp_hpd)) { + *wait_ms = 0; + return 0; + } + + debug("%s: eDP bridge failed to come up; try %d of %d\n", __func__, + num_tries, MAX_TRIES); + /* Immediately go into bridge reset if the hp line is not high */ + *wait_ms = 0; + ++num_tries; + return num_tries <= MAX_TRIES ? -EAGAIN : -ENODEV; +} + +#ifdef CONFIG_TPS65090_POWER +int board_dp_backlight_vdd(const void *blob, unsigned *wait_ms) +{ + /* This delay is T5 in the LCD timing spec (defined as > 10ms) */ + *wait_ms = 10; + return tps65090_fet_enable(1); /* Enable FET1, backlight */ +} +#endif + +int board_dp_backlight_pwm(const void *blob, unsigned *wait_ms) +{ + /* + * Configure backlight PWM as a simple output high (100% brightness) + * TODO(hatim.rv@samsung.com): Move to FDT + */ + gpio_direction_output(GPIO_B20, 1); + /* This delay is T6 in the LCD timing spec (defined as > 10ms) */ + *wait_ms = 10; + return 0; +} + +int board_dp_backlight_en(const void *blob, unsigned *wait_ms) +{ + /* + * Configure GPIO for LCD_BL_EN + * TODO(hatim.rv@samsung.com): Move to FDT + */ + gpio_direction_output(GPIO_X30, 1); + /* We're done, no more delays! */ + *wait_ms = 0; + return 0; +} + +static void board_enable_audio_codec(void) +{ + int node, ret, value; + struct fdt_gpio_state en_gpio; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SAMSUNG_EXYNOS_SOUND); + if (node <= 0) + return; + + ret = fdtdec_decode_gpio(gd->fdt_blob, node, "codec-enable-gpio", + &en_gpio); + if (ret == -FDT_ERR_NOTFOUND) + return; + + /* Turn on the GPIO which connects to the codec's "enable" line. */ + value = (en_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? 0 : 1; + gpio_direction_output(en_gpio.gpio, value); + gpio_set_pull(en_gpio.gpio, EXYNOS_GPIO_PULL_NONE); +} + +int board_init(void) +{ + struct fdt_memory mem_config; + + /* Record the time we spent before SPL */ + bootstage_add_record(BOOTSTAGE_ID_START_SPL, "spl_start", 0, + CONFIG_SPL_TIME_US); + bootstage_mark_name(BOOTSTAGE_ID_BOARD_INIT, "board_init"); + + if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) { + debug("%s: Failed to decode memory\n", __func__); + return -1; + } + + gd->bd->bi_boot_params = mem_config.start + 0x100UL; + +#ifdef CONFIG_OF_CONTROL + gd->bd->bi_arch_number = fdtdec_get_config_int(gd->fdt_blob, + "machine-arch-id", -1); + if (gd->bd->bi_arch_number == -1U) + debug("Warning: No /config/machine-arch-id defined in fdt\n"); +#endif +#ifdef CONFIG_EXYNOS_SPI + spi_init(); +#endif + + if (board_i2c_arb_init(gd->fdt_blob)) + return -1; + + board_i2c_init(gd->fdt_blob); + +#ifdef CONFIG_TPS65090_POWER + tps65090_init(); + + /* + * If we just reset, disable the backlight and lcd fets before + * [re-]initializing the lcd. This ensures we are always in the same + * state during lcd init. We've seen some oddities with these fets, so + * this removes a bit of uncertainty. + */ + if (board_is_processor_reset()) { + tps65090_fet_disable(1); + tps65090_fet_disable(6); + } +#endif + exynos_lcd_check_next_stage(gd->fdt_blob, 0); + + if (max77686_enable_32khz_cp()) { + debug("%s: Failed to enable max77686 32khz coprocessor clock\n", + __func__); + return -1; + } + +#if defined CONFIG_EXYNOS_CPUFREQ + if (exynos5250_cpufreq_init(gd->fdt_blob)) { + debug("%s: Failed to init CPU frequency scaling\n", __func__); + return -1; + } +#endif + +#if defined CONFIG_EXYNOS_TMU + if (tmu_init(gd->fdt_blob)) { + debug("%s: Failed to init TMU\n", __func__); + return -1; + } +#endif + + /* Clock Gating all the unused IP's to save power */ + clock_gate(); + + /* Disable USB3.0 PLL to save 250mW of power */ + disable_usb30_pll(); + + if (board_init_mkbp_devices(gd->fdt_blob)) + return -1; + + board_enable_audio_codec(); + + exynos_lcd_check_next_stage(gd->fdt_blob, 0); + + bootstage_mark_name(BOOTSTAGE_ID_BOARD_INIT_DONE, "board_init_done"); + + return 0; +} + +int dram_init(void) +{ + struct fdt_memory mem_config; + + if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) { + debug("%s: Failed to decode memory\n", __func__); + return -1; + } + + gd->ram_size = get_ram_size((long *)mem_config.start, + mem_config.end); + return 0; +} + +void dram_init_banksize(void) +{ + struct fdt_memory mem_config; + + if (fdtdec_decode_memory(gd->fdt_blob, &mem_config)) { + debug("%s: Failed to decode memory\n", __func__); + return; + } + + gd->bd->bi_dram[0].start = mem_config.start; + gd->bd->bi_dram[0].size = get_ram_size((long *)mem_config.start, + mem_config.end); +} + +int board_get_revision(void) +{ + struct fdt_gpio_state gpios[CONFIG_BOARD_REV_GPIO_COUNT]; + unsigned gpio_list[CONFIG_BOARD_REV_GPIO_COUNT]; + int board_rev = -1; + int count = 0; + int node; + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_GOOGLE_BOARD_REV); + if (node >= 0) { + count = fdtdec_decode_gpios(gd->fdt_blob, node, + "google,board-rev-gpios", gpios, + CONFIG_BOARD_REV_GPIO_COUNT); + } + if (count > 0) { + int i; + + for (i = 0; i < count; i++) + gpio_list[i] = gpios[i].gpio; + board_rev = gpio_decode_number(gpio_list, count); + } else { + debug("%s: No board revision information in fdt\n", __func__); + } + + return board_rev; +} + +#ifdef CONFIG_DISPLAY_BOARDINFO +int checkboard(void) +{ +#ifdef CONFIG_OF_CONTROL + const char *board_name; + + board_name = fdt_getprop(gd->fdt_blob, 0, "model", NULL); + printf("\nBoard: %s, rev %d\n", board_name ? board_name : "<unknown>", + board_get_revision()); +#else + printf("\nBoard: SMDK5250\n"); +#endif + + return 0; +} +#endif + +#ifdef CONFIG_GENERIC_MMC +int board_mmc_getcd(struct mmc *mmc) +{ + struct mshci_host *host = mmc->priv; + int present = 1; /* for ch0 (eMMC) card is always present */ + + if (host->peripheral == PERIPH_ID_SDMMC2) + present = !readl(&host->reg->cdetect); + + return present; +} + +int board_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_S5P_MSHCI + s5p_mshci_init(gd->fdt_blob); +#endif + return 0; +} +#endif + +#ifdef CONFIG_BOARD_EARLY_INIT_F +int board_early_init_f(void) +{ + exynos_pinmux_config(EXYNOS_UART, PINMUX_FLAG_NONE); + return 0; +} +#endif + +void board_i2c_release_bus(int node) +{ + /* If this is us, release the bus */ + if (node == local.arbitrate_node) { + gpio_set_value(local.ap_claim.gpio, 1); + udelay(local.slew_delay_us); + } +} + +int board_i2c_claim_bus(int node) +{ + unsigned start; + + if (node != local.arbitrate_node) + return 0; + +// putc('c'); + + /* Start a round of trying to claim the bus */ + start = get_timer(0); + do { + unsigned start_retry; + int waiting = 0; + + /* Indicate that we want to claim the bus */ + gpio_set_value(local.ap_claim.gpio, 0); + udelay(local.slew_delay_us); + + /* Wait for the EC to release it */ + start_retry = get_timer(0); + while (get_timer(start_retry) < local.wait_retry_ms) { + if (gpio_get_value(local.ec_claim.gpio)) { + /* We got it, so return */ + return 0; + } + + if (!waiting) { + waiting = 1; + } + } + + /* It didn't release, so give up, wait, and try again */ + gpio_set_value(local.ap_claim.gpio, 1); + + mdelay(local.wait_retry_ms); + } while (get_timer(start) < local.wait_free_ms); + + /* Give up, release our claim */ + printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start)); + + return -1; +} + +#ifdef CONFIG_SATA_AHCI +int sata_initialize(void) +{ + return exynos5_sata_init(gd->fdt_blob); +} +#endif + +#ifdef CONFIG_BOARD_LATE_INIT +int board_late_init(void) +{ + stdio_print_current_devices(); + + return 0; +} +#endif + +#endif /* big #if */ +#if 0 +/* ACPI */ +void mainboard_suspend_resume(void) +{ + /* FIXME: stub */ + return; +} +#endif + +static void mainboard_init(device_t dev) +{ + /* FIXME: stub */ + return; +} + +// mainboard_enable is executed as first thing after +// enumerate_buses(). + +static void mainboard_enable(device_t dev) +{ + dev->ops->init = mainboard_init; +} + +struct chip_operations mainboard_ops = { + .name = "Samsung/Google ChromeBook", + .enable_dev = mainboard_enable, +}; + diff --git a/src/mainboard/google/snow/romstage.c b/src/mainboard/google/snow/romstage.c new file mode 100644 index 0000000..c3a9b53 --- /dev/null +++ b/src/mainboard/google/snow/romstage.c @@ -0,0 +1,291 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2010 coresystems GmbH + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if 0 +#include <stdint.h> +#include <string.h> +#include <lib.h> +#include <timestamp.h> +#include <arch/io.h> +//#include <arch/romcc_io.h> +#if 0 +#include <device/pci_def.h> +#include <device/pnp_def.h> +#include <cpu/x86/lapic.h> +#include <pc80/mc146818rtc.h> +#endif +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#if 0 +#include "northbridge/intel/sandybridge/sandybridge.h" +#include "northbridge/intel/sandybridge/raminit.h" +#include "southbridge/intel/bd82x6x/pch.h" +#include "southbridge/intel/bd82x6x/gpio.h" +#endif +#include <arch/cpu.h> +#if 0 +#include <cpu/x86/bist.h> +#include <cpu/x86/msr.h> +#endif +#include "option_table.h" +#include "gpio.h" +#if CONFIG_CONSOLE_SERIAL8250 +#if 0 +#include "superio/smsc/lpc47n207/lpc47n207.h" +#include "superio/smsc/lpc47n207/early_serial.c" +#endif +#endif +#if CONFIG_CHROMEOS +#include <vendorcode/google/chromeos/chromeos.h> +#endif +#endif +#include <types.h> +#include <system.h> +#include <cache.h> + +static void mmu_setup(void) +{ + dram_bank_mmu_setup(CONFIG_SYS_SDRAM_BASE, CONFIG_DRAM_SIZE_MB * 1024); +} + +void main(unsigned long bist) +{ + mmu_setup(); +} + +#if 0 +/* FIXME: This is based off lumpy's romstage.c main() and is gutted to + resemble snow's eventually... */ +void main(unsigned long bist) +{ + int boot_mode = 0; + int cbmem_was_initted; +// u32 pm1_cnt; +// u16 pm1_sts; + +#if CONFIG_COLLECT_TIMESTAMPS +#if 0 + tsc_t start_romstage_time; + tsc_t before_dram_time; + tsc_t after_dram_time; + tsc_t base_time = { + .lo = pci_read_config32(PCI_DEV(0, 0x00, 0), 0xdc), + .hi = pci_read_config32(PCI_DEV(0, 0x1f, 2), 0xd0) + }; +#endif +#endif + +#if 0 + typedef const uint8_t spd_blob[256]; + struct cbfs_file *spd_file; + spd_blob *spd_data; +#endif + + +#if CONFIG_COLLECT_TIMESTAMPS +// start_romstage_time = rdtsc(); +#endif + +#if 0 + if (bist == 0) + enable_lapic(); + + pch_enable_lpc(); + + /* Enable GPIOs */ + pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE|1); + pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10); + setup_pch_gpios(&lumpy_gpio_map); + + console_init(); +#endif + +#if CONFIG_CHROMEOS + save_chromeos_gpios(); +#endif + +#if 0 + /* Halt if there was a built in self test failure */ + report_bist_failure(bist); +#endif + +#if 0 + /* Perform some early chipset initialization required + * before RAM initialization can work + */ + sandybridge_early_initialization(SANDYBRIDGE_MOBILE); + printk(BIOS_DEBUG, "Back from sandybridge_early_initialization()\n"); + + /* Check PM1_STS[15] to see if we are waking from Sx */ + pm1_sts = inw(DEFAULT_PMBASE + PM1_STS); + + /* Read PM1_CNT[12:10] to determine which Sx state */ + pm1_cnt = inl(DEFAULT_PMBASE + PM1_CNT); +#endif +#if 0 + + if ((pm1_sts & WAK_STS) && ((pm1_cnt >> 10) & 7) == 5) { +#if CONFIG_HAVE_ACPI_RESUME + printk(BIOS_DEBUG, "Resume from S3 detected.\n"); + boot_mode = 2; + /* Clear SLP_TYPE. This will break stage2 but + * we care for that when we get there. + */ + outl(pm1_cnt & ~(7 << 10), DEFAULT_PMBASE + PM1_CNT); +#else + printk(BIOS_DEBUG, "Resume from S3 detected, but disabled.\n"); +#endif + } +#endif + +// post_code(0x38); + /* Enable SPD ROMs and DDR-III DRAM */ +// enable_smbus(); + +#if 0 + /* Prepare USB controller early in S3 resume */ + if (boot_mode == 2) + enable_usb_bar(); + + u32 gp_lvl2 = inl(DEFAULT_GPIOBASE + 0x38); + u8 gpio33, gpio41, gpio49; + gpio33 = (gp_lvl2 >> (33-32)) & 1; + gpio41 = (gp_lvl2 >> (41-32)) & 1; + gpio49 = (gp_lvl2 >> (49-32)) & 1; + printk(BIOS_DEBUG, "Memory Straps:\n"); + printk(BIOS_DEBUG, " - memory capacity %dGB\n", + gpio33 ? 2 : 1); + printk(BIOS_DEBUG, " - die revision %d\n", + gpio41 ? 2 : 1); + printk(BIOS_DEBUG, " - vendor %s\n", + gpio49 ? "Samsung" : "Other"); + + int spd_index = 0; + + switch ((gpio49 << 2) | (gpio41 << 1) | gpio33) { + case 0: // Other 1G Rev 1 + spd_index = 0; + break; + case 2: // Other 1G Rev 2 + spd_index = 1; + break; + case 1: // Other 2G Rev 1 + case 3: // Other 2G Rev 2 + spd_index = 2; + break; + case 4: // Samsung 1G Rev 1 + spd_index = 3; + break; + case 6: // Samsung 1G Rev 2 + spd_index = 4; + break; + case 5: // Samsung 2G Rev 1 + case 7: // Samsung 2G Rev 2 + spd_index = 5; + break; + } + + spd_file = cbfs_find("spd.bin"); + if (!spd_file) + die("SPD data not found."); + if (spd_file->len < (spd_index + 1) * 256) + die("Missing SPD data."); + spd_data = (spd_blob *)CBFS_SUBHEADER(spd_file); + // leave onboard dimm address at f0, and copy spd data there. + memcpy(pei_data.spd_data[0], spd_data[spd_index], 256); + + post_code(0x39); + pei_data.boot_mode = boot_mode; +#endif +#if CONFIG_COLLECT_TIMESTAMPS + before_dram_time = rdtsc(); +#endif + sdram_initialize(&pei_data); + +#if CONFIG_COLLECT_TIMESTAMPS + after_dram_time = rdtsc(); +#endif +#if 0 + post_code(0x3a); + /* Perform some initialization that must run before stage2 */ + early_pch_init(); + post_code(0x3b); + + rcba_config(); + post_code(0x3c); + + quick_ram_check(); + post_code(0x3e); + + MCHBAR16(SSKPD) = 0xCAFE; +#endif +#if CONFIG_EARLY_CBMEM_INIT + cbmem_was_initted = !cbmem_initialize(); +#else + cbmem_was_initted = cbmem_reinit((uint64_t) (get_top_of_ram() + - HIGH_MEMORY_SIZE)); +#endif + +#if 0 +#if CONFIG_HAVE_ACPI_RESUME + /* If there is no high memory area, we didn't boot before, so + * this is not a resume. In that case we just create the cbmem toc. + */ + + *(u32 *)CBMEM_BOOT_MODE = 0; + *(u32 *)CBMEM_RESUME_BACKUP = 0; + + if ((boot_mode == 2) && cbmem_was_initted) { + void *resume_backup_memory = cbmem_find(CBMEM_ID_RESUME); + if (resume_backup_memory) { + *(u32 *)CBMEM_BOOT_MODE = boot_mode; + *(u32 *)CBMEM_RESUME_BACKUP = (u32)resume_backup_memory; + } + /* Magic for S3 resume */ + pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, 0xcafed00d); + } else if (boot_mode == 2) { + /* Failed S3 resume, reset to come up cleanly */ + outb(0x6, 0xcf9); + hlt(); + } else { + pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, 0xcafebabe); + } +#endif +#endif +// post_code(0x3f); +#if CONFIG_CHROMEOS + init_chromeos(boot_mode); +#endif +#if 0 +#if CONFIG_COLLECT_TIMESTAMPS + timestamp_init(base_time); + timestamp_add(TS_START_ROMSTAGE, start_romstage_time ); + timestamp_add(TS_BEFORE_INITRAM, before_dram_time ); + timestamp_add(TS_AFTER_INITRAM, after_dram_time ); + timestamp_add_now(TS_END_ROMSTAGE); +#endif +#endif +#if CONFIG_CONSOLE_CBMEM + /* Keep this the last thing this function does. */ + cbmemc_reinit(); +#endif +} +#endif diff --git a/src/mainboard/google/snow/smdk5250_spl.c b/src/mainboard/google/snow/smdk5250_spl.c new file mode 100644 index 0000000..a802338 --- /dev/null +++ b/src/mainboard/google/snow/smdk5250_spl.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#if 0 +#include <common.h> +#include <asm/gpio.h> +#include <asm/arch-exynos/cpu.h> +#include <asm/arch/board.h> +#include <asm/arch/dmc.h> +#endif + +#include <arch/gpio.h> +#include <cpu/samsung/exynos5-common/spl.h> +#include <cpu/samsung/exynos5250/gpio.h> + +#define SIGNATURE 0xdeadbeef + +/* Parameters of early board initialization in SPL */ +static struct spl_machine_param machine_param + __attribute__((section(".machine_param"))) = { + .signature = SIGNATURE, + .version = 1, + .params = "vmubfasirMw", + .size = sizeof(machine_param), + + .mem_iv_size = 0x1f, + .mem_type = DDR_MODE_DDR3, + + /* + * Set uboot_size to 0x100000 bytes. + * + * This is an overly conservative value chosen to accommodate all + * possible U-Boot image. You are advised to set this value to a + * smaller realistic size via scripts that modifies the .machine_param + * section of output U-Boot image. + */ + .uboot_size = 0x100000, + + .boot_source = BOOT_MODE_OM, + .frequency_mhz = 800, + .arm_freq_mhz = 1700, + .serial_base = 0x12c30000, + .i2c_base = 0x12c60000, + .board_rev_gpios = GPIO_D00 | (GPIO_D01 << 16), + .mem_manuf = MEM_MANUF_SAMSUNG, + .bad_wake_gpio = GPIO_Y10, +}; + +struct spl_machine_param *spl_get_machine_params(void) +{ + if (machine_param.signature != SIGNATURE) { + /* TODO: Call panic() here */ + while (1) + ; + } + + return &machine_param; +} + +/* FIXME(dhendrix): Move all this board_* stuff into the actual mainboard + * directory. */ +#if 0 +/* FIXME(dhendrix): originally this #define was in exynos5-common.h which + seems wrong. */ +#define CONFIG_BOARD_REV_GPIO_COUNT 2 +int board_get_revision(void) +{ + struct spl_machine_param *params = spl_get_machine_params(); + unsigned gpio[CONFIG_BOARD_REV_GPIO_COUNT]; + + gpio[0] = params->board_rev_gpios & 0xffff; + gpio[1] = params->board_rev_gpios >> 16; + return gpio_decode_number(gpio, CONFIG_BOARD_REV_GPIO_COUNT); +} +#endif + +int board_wakeup_permitted(void) +{ + struct spl_machine_param *param = spl_get_machine_params(); + const int gpio = param->bad_wake_gpio; + int is_bad_wake; + + /* We're a bad wakeup if the gpio was defined and was high */ + is_bad_wake = ((gpio != -1) && gpio_get_value(gpio)); + + return !is_bad_wake; +} + +#if 0 +/* + * TODO(sjg@chromium.org): + * Declared there here for SPL, since there is no core i2c subsystem and + * cmd_i2c.c is not included. + */ +void board_i2c_release_bus(int node) +{ +} + +int board_i2c_claim_bus(int node) +{ + /* EC is not allowed to touch the bus until we enter U-Boot */ + return 0; +} +#endif