diff options
author | Patrick Georgi <pgeorgi@chromium.org> | 2015-06-22 19:41:29 +0200 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-06-30 21:43:01 +0200 |
commit | 40a3e321d4e8f2877de1700db67b8c7f7ea89820 (patch) | |
tree | b8270b2ceb9e290d2e4e9a99868acb9cd335de6f | |
parent | 7f641e68f25c0b79960a97a6b265851c46298aae (diff) |
nvidia/tegra210: add new SoC
This includes Chrome OS downstream up to Change-Id: Ic89ed54c.
Change-Id: I81853434600390d643160fe57554495b2bfe60ab
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Reviewed-on: http://review.coreboot.org/10633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
88 files changed, 19430 insertions, 0 deletions
diff --git a/src/soc/nvidia/tegra210/Kconfig b/src/soc/nvidia/tegra210/Kconfig new file mode 100644 index 0000000000..257345e303 --- /dev/null +++ b/src/soc/nvidia/tegra210/Kconfig @@ -0,0 +1,143 @@ +config SOC_NVIDIA_TEGRA210 + bool + default n + select ARCH_BOOTBLOCK_ARMV4 + select ARCH_VERSTAGE_ARMV4 + select ARCH_ROMSTAGE_ARMV4 + select ARCH_RAMSTAGE_ARMV8_64 + select ARCH_ARM64_CPU_CORTEX_A57 + select ARCH_ARM64_CORTEX_A57_POWER_DOWN_SUPPORT + select BOOTBLOCK_CONSOLE + select GIC + select HAVE_MONOTONIC_TIMER + select GENERIC_UDELAY + select HAVE_HARD_RESET + select HAVE_UART_SPECIAL + select HAVE_UART_MEMORY_MAPPED + select EARLY_CONSOLE + select ARM_BOOTBLOCK_CUSTOM + select DYNAMIC_CBMEM + select ARM64_USE_ARM_TRUSTED_FIRMWARE + select HAS_PRECBMEM_TIMESTAMP_REGION + select CHROMEOS_RAMOOPS_NON_ACPI + select GENERIC_GPIO_LIB + +if SOC_NVIDIA_TEGRA210 + +config MAINBOARD_DO_DSI_INIT + bool "Use dsi graphics interface" + depends on MAINBOARD_DO_NATIVE_VGA_INIT + default n + help + Initialize dsi display + +config MAINBOARD_DO_SOR_INIT + bool "Use dp graphics interface" + depends on MAINBOARD_DO_NATIVE_VGA_INIT + default n + help + Initialize dp display + +config BOOTBLOCK_CPU_INIT + string + default "soc/nvidia/tegra210/bootblock.c" + help + CPU/SoC-specific bootblock code. This is useful if the + bootblock must load microcode or copy data from ROM before + searching for the bootblock. + +config MAX_CPUS + int + default 4 + +choice CONSOLE_SERIAL_TEGRA210_UART_CHOICES + prompt "Serial Console UART" + default CONSOLE_SERIAL_TEGRA210_UARTA + depends on CONSOLE_SERIAL_UART + +config CONSOLE_SERIAL_TEGRA210_UARTA + bool "UARTA" + help + Serial console on UART A. + +config CONSOLE_SERIAL_TEGRA210_UARTB + bool "UARTB" + help + Serial console on UART B. + +config CONSOLE_SERIAL_TEGRA210_UARTC + bool "UARTC" + help + Serial console on UART C. + +config CONSOLE_SERIAL_TEGRA210_UARTD + bool "UARTD" + help + Serial console on UART D. + +config CONSOLE_SERIAL_TEGRA210_UARTE + bool "UARTE" + help + Serial console on UART E. + +endchoice + +config CONSOLE_SERIAL_TEGRA210_UART_ADDRESS + hex + depends on CONSOLE_SERIAL_UART + default 0x70006000 if CONSOLE_SERIAL_TEGRA210_UARTA + default 0x70006040 if CONSOLE_SERIAL_TEGRA210_UARTB + default 0x70006200 if CONSOLE_SERIAL_TEGRA210_UARTC + default 0x70006300 if CONSOLE_SERIAL_TEGRA210_UARTD + default 0x70006400 if CONSOLE_SERIAL_TEGRA210_UARTE + help + Map the UART names to the respective MMIO addres. + +config BOOTROM_SDRAM_INIT + bool "SoC BootROM does SDRAM init with full BCT" + default n + help + Use during Foster LPDDR4 bringup. + +config TRUSTZONE_CARVEOUT_SIZE_MB + hex "Size of Trust Zone region" + default 0x14 + help + Size of Trust Zone area in MiB to reserve in memory map. + +# Default to 700MHz. This value is based on nv bootloader setting. +config PLLX_KHZ + int + default 700000 +endif + +config HAVE_MTC + bool "Add external Memory controller Training Code binary" + default n + depends on USE_BLOBS + help + Select this option to add emc training firmware + +if HAVE_MTC + +config MTC_FILE + string "tegra mtc firmware filename" + default "tegra_mtc.bin" + help + The filename of the mtc firmware + +config MTC_DIRECTORY + string "Directory where MTC firmware file is located" + default "." + help + Path to directory where MTC firmware file is located. + +config MTC_ADDRESS + hex + default 0x81000000 + help + The DRAM location where MTC firmware to be loaded in. This location + needs to be consistent with the location defined in tegra_mtc.ld + +endif # HAVE_MTC + diff --git a/src/soc/nvidia/tegra210/Makefile.inc b/src/soc/nvidia/tegra210/Makefile.inc new file mode 100644 index 0000000000..98e752c1a3 --- /dev/null +++ b/src/soc/nvidia/tegra210/Makefile.inc @@ -0,0 +1,159 @@ +ifeq ($(CONFIG_SOC_NVIDIA_TEGRA210),y) + +CBOOTIMAGE_OPTS = --soc tegra210 + +bootblock-y += bootblock.c +bootblock-y += bootblock_asm.S +bootblock-y += clock.c +bootblock-y += spi.c +bootblock-y += i2c.c +bootblock-y += dma.c +bootblock-y += monotonic_timer.c +bootblock-y += padconfig.c +bootblock-y += power.c +bootblock-y += funitcfg.c +bootblock-y += reset.c +bootblock-y += ../tegra/gpio.c +bootblock-y += ../tegra/i2c.c +bootblock-y += ../tegra/pingroup.c +bootblock-y += ../tegra/pinmux.c +bootblock-y += ../tegra/apbmisc.c +bootblock-y += ../tegra/usb.c +ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) +bootblock-$(CONFIG_DRIVERS_UART) += uart.c +endif + +verstage-y += verstage.c +verstage-y += dma.c +verstage-y += monotonic_timer.c +verstage-y += spi.c +verstage-y += padconfig.c +verstage-y += funitcfg.c +verstage-$(CONFIG_DRIVERS_UART) += uart.c +verstage-y += ../tegra/gpio.c +verstage-y += ../tegra/i2c.c +verstage-y += ../tegra/pinmux.c +verstage-y += clock.c +verstage-y += i2c.c + +romstage-y += romstage_asm.S +romstage-y += addressmap.c +romstage-y += cbmem.c +romstage-y += ccplex.c +romstage-y += clock.c +romstage-y += cpu.c +romstage-y += reset.c +romstage-y += spi.c +romstage-y += i2c.c +romstage-y += dma.c +romstage-y += monotonic_timer.c +romstage-y += padconfig.c +romstage-y += funitcfg.c +romstage-y += romstage.c +romstage-y += power.c +romstage-y += ram_code.c +ifneq ($(CONFIG_BOOTROM_SDRAM_INIT),y) +romstage-y += sdram.c + ifeq ($(CONFIG_LP0_SUPPORT),y) +romstage-y += sdram_lp0.c + endif +endif +romstage-y += ../tegra/gpio.c +romstage-y += ../tegra/i2c.c +romstage-y += ../tegra/pinmux.c +romstage-y += ../tegra/usb.c +romstage-$(CONFIG_DRIVERS_UART) += uart.c + +ramstage-y += addressmap.c +ramstage-y += cbmem.c +ramstage-y += cpu.c +ramstage-y += cpu_lib.S +ramstage-y += clock.c +ramstage-$(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) += dc.c +ramstage-$(CONFIG_MAINBOARD_DO_DSI_INIT) += dsi.c +ramstage-$(CONFIG_MAINBOARD_DO_DSI_INIT) += mipi_dsi.c +ramstage-$(CONFIG_MAINBOARD_DO_DSI_INIT) += mipi.c +ramstage-$(CONFIG_MAINBOARD_DO_DSI_INIT) += mipi-phy.c +ramstage-$(CONFIG_MAINBOARD_DO_DSI_INIT) += ./jdi_25x18_display/panel-jdi-lpm102a188a.c +ramstage-$(CONFIG_MAINBOARD_DO_SOR_INIT) += dp.c +ramstage-$(CONFIG_MAINBOARD_DO_SOR_INIT) += sor.c + +ramstage-y += soc.c +ramstage-y += spi.c +ramstage-y += i2c.c +ramstage-y += i2c6.c +ramstage-y += ape.c +ramstage-y += power.c +ramstage-y += dma.c +ramstage-y += gic.c +ramstage-y += monotonic_timer.c +ramstage-y += padconfig.c +ramstage-y += funitcfg.c +ramstage-y += reset.c +ramstage-y += ram_code.c +ramstage-y += ../tegra/apbmisc.c +ramstage-y += ../tegra/gpio.c +ramstage-y += ../tegra/i2c.c +ramstage-y += ../tegra/pinmux.c +ramstage-y += ramstage.c +ramstage-y += mmu_operations.c +ramstage-$(CONFIG_DRIVERS_UART) += uart.c +ramstage-y += ../tegra/usb.c +ramstage-$(CONFIG_ARM64_USE_SECURE_MONITOR) += secmon.c +ramstage-$(CONFIG_HAVE_MTC) += mtc.c + +secmon-y += cpu.c +secmon-y += cpu_lib.S +secmon-y += flow_ctrl.c +secmon-y += power.c +secmon-y += psci.c +secmon-y += reset_handler.S +secmon-y += uart.c +secmon-y += gic.c + +rmodules_arm-y += monotonic_timer.c +VBOOT_STUB_DEPS += $(obj)/soc/nvidia/tegra210/monotonic_timer.rmodules_arm.o + +CPPFLAGS_common += -Isrc/soc/nvidia/tegra210/include/ + +# We want to grab the bootblock right before it goes into the image and wrap +# it inside a BCT, but ideally we would do that without making special, one +# use modifications to the main ARM Makefile. We do this in two ways. First, +# we copy bootblock.elf to bootblock.raw.elf and allow the %.bin: %.elf +# template rule to turn it into bootblock.raw.bin. This makes sure whatever +# processing is supposed to happen to turn an .elf into a .bin happens. +# +# Second, we add our own rule for creating bootblock.bin from +# bootblock.raw.bin which displaces the template rule. When other rules that +# package up the image pull in bootblock.bin, it will be this wrapped version +# instead of the raw bootblock. + +$(objcbfs)/bootblock.raw.elf: $(objcbfs)/bootblock.elf + cp $< $@ + +$(obj)/generated/bct.bin: $(obj)/generated/bct.cfg $(CBOOTIMAGE) + @printf " CBOOTIMAGE $(subst $(obj)/,,$(@))\n" + $(CBOOTIMAGE) -gbct $(CBOOTIMAGE_OPTS) $< $@ + +BCT_BIN = $(obj)/generated/bct.bin +BCT_WRAPPER = $(obj)/generated/bct.wrapper +$(objcbfs)/bootblock.bin: $(objcbfs)/bootblock.raw.bin $(BCT_BIN) + echo "Version = 1;" > $(BCT_WRAPPER) + echo "Redundancy = 1;" >> $(BCT_WRAPPER) + echo "Bctcopy = 1;" >> $(BCT_WRAPPER) + echo "Bctfile = $(BCT_BIN);" >> $(BCT_WRAPPER) + echo "BootLoader = $<,$(call loadaddr,bootblock),$(call loadaddr,bootblock),Complete;" >> $(BCT_WRAPPER) + @printf " CBOOTIMAGE $(subst $(obj)/,,$(@))\n" + $(CBOOTIMAGE) $(CBOOTIMAGE_OPTS) $(BCT_WRAPPER) $@ + +BL31_MAKEARGS += PLAT=tegra TARGET_SOC=t210 + +# MTC fw +MTC_DIR = $(CONFIG_MTC_DIRECTORY) +MTC_FILE = $(MTC_DIR)/$(CONFIG_MTC_FILE) +MTC_FILE_CBFS = $(CONFIG_MTC_FILE) +cbfs-files-$(CONFIG_HAVE_MTC) += $(MTC_FILE_CBFS) +$(MTC_FILE_CBFS)-file := $(MTC_FILE) +$(MTC_FILE_CBFS)-type := 0x50 + +endif diff --git a/src/soc/nvidia/tegra210/addressmap.c b/src/soc/nvidia/tegra210/addressmap.c new file mode 100644 index 0000000000..6e14d4a121 --- /dev/null +++ b/src/soc/nvidia/tegra210/addressmap.c @@ -0,0 +1,283 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <assert.h> +#include <arch/io.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/id.h> +#include <soc/mc.h> +#include <soc/sdram.h> +#include <stdlib.h> +#include <symbols.h> +#include <soc/nvidia/tegra/types.h> + +static uintptr_t tz_base_mib; +static const size_t tz_size_mib = CONFIG_TRUSTZONE_CARVEOUT_SIZE_MB; + +/* returns total amount of DRAM (in MB) from memory controller registers */ +int sdram_size_mb(void) +{ + struct tegra_mc_regs *mc = (struct tegra_mc_regs *)TEGRA_MC_BASE; + static int total_size = 0; + + if (total_size) + return total_size; + + /* + * This obtains memory size from the External Memory Aperture + * Configuration register. Nvidia confirmed that it is safe to assume + * this value represents the total physical DRAM size. + */ + total_size = (read32(&mc->emem_cfg) >> + MC_EMEM_CFG_SIZE_MB_SHIFT) & MC_EMEM_CFG_SIZE_MB_MASK; + + return total_size; +} + +static void carveout_from_regs(uintptr_t *base_mib, size_t *size_mib, + uint32_t bom, uint32_t bom_hi, uint32_t size) +{ + + /* All size regs of carveouts are in MiB. */ + if (size == 0) + return; + + *size_mib = size; + bom >>= 20; + bom |= bom_hi << (32 - 20); + + *base_mib = bom; +} + +void carveout_range(int id, uintptr_t *base_mib, size_t *size_mib) +{ + *base_mib = 0; + *size_mib = 0; + struct tegra_mc_regs * const mc = (struct tegra_mc_regs *)TEGRA_MC_BASE; + size_t region_size_mb; + + switch (id) { + case CARVEOUT_TZ: + *base_mib = tz_base_mib; + *size_mib = tz_size_mib; + break; + case CARVEOUT_SEC: + carveout_from_regs(base_mib, size_mib, + read32(&mc->sec_carveout_bom), + read32(&mc->sec_carveout_adr_hi), + read32(&mc->sec_carveout_size_mb)); + break; + case CARVEOUT_MTS: + carveout_from_regs(base_mib, size_mib, + read32(&mc->mts_carveout_bom), + read32(&mc->mts_carveout_adr_hi), + read32(&mc->mts_carveout_size_mb)); + break; + case CARVEOUT_VPR: + carveout_from_regs(base_mib, size_mib, + read32(&mc->video_protect_bom), + read32(&mc->video_protect_bom_adr_hi), + read32(&mc->video_protect_size_mb)); + break; + case CARVEOUT_GPU: + /* These carveout regs use 128KB granularity - convert to MB */ + region_size_mb = DIV_ROUND_UP(read32(&mc->security_carveout2_size_128kb), 8); + + /* BOM address set in gpu_region_init, below */ + carveout_from_regs(base_mib, size_mib, + read32(&mc->security_carveout2_bom), + read32(&mc->security_carveout2_bom_hi), + region_size_mb); + break; + default: + break; + } +} + +void print_carveouts(void) +{ + int i; + printk(BIOS_INFO, "Carveout ranges:\n"); + for (i = 0; i < CARVEOUT_NUM; i++) { + uintptr_t base, end; + size_t size; + carveout_range(i, &base, &size); + end = base + size; + if (end && base) + printk(BIOS_INFO, "ID:%d [%lx - %lx)\n", i, + (unsigned long)base * MiB, + (unsigned long)end * MiB); + } +} + +/* + * Memory Map is as follows + * + * ------------------------------ <-- Start of DRAM + * | | + * | Available DRAM | + * |____________________________| + * | | + * | CBMEM | + * |____________________________| + * | | + * | Other carveouts | + * | (with dynamic allocation) | + * |____________________________| + * | | + * | TZ carveout of size | + * | TRUSTZONE_CARVEOUT_SIZE_MB | + * |____________________________| <-- 0x100000000 + * | | + * | Available DRAM | + * | | + * ------------------------------ <-- End of DRAM + * + */ +static void memory_in_range(uintptr_t *base_mib, uintptr_t *end_mib, + int ignore_carveout_id) +{ + uintptr_t base; + uintptr_t end; + int i; + + base = (uintptr_t)_dram / MiB; + end = base + sdram_size_mb(); + + /* Requested limits out of range. */ + if (*end_mib <= base || *base_mib >= end) { + *end_mib = *base_mib = 0; + return; + } + + /* Clip region to passed in limits. */ + if (*end_mib < end) + end = *end_mib; + if (*base_mib > base) + base = *base_mib; + + for (i = 0; i < CARVEOUT_NUM; i++) { + uintptr_t carveout_base; + uintptr_t carveout_end; + size_t carveout_size; + + if (i == ignore_carveout_id) + continue; + + carveout_range(i, &carveout_base, &carveout_size); + + if (carveout_size == 0) + continue; + + carveout_end = carveout_base + carveout_size; + + /* Bypass carveouts out of requested range. */ + if (carveout_base >= end || carveout_end <= base) + continue; + + /* + * This is crude, but the assumption is that carveouts live + * at the upper range of physical memory. Therefore, update + * the end address to be equal to the base of the carveout. + */ + end = carveout_base; + } + + *base_mib = base; + *end_mib = end; +} + +void memory_in_range_below_4gb(uintptr_t *base_mib, uintptr_t *end_mib) +{ + *base_mib = 0; + *end_mib = 4096; + memory_in_range(base_mib, end_mib, CARVEOUT_NUM); +} + +void memory_in_range_above_4gb(uintptr_t *base_mib, uintptr_t *end_mib) +{ + *base_mib = 4096; + *end_mib = ~0UL; + memory_in_range(base_mib, end_mib, CARVEOUT_NUM); +} + +void trustzone_region_init(void) +{ + struct tegra_mc_regs * const mc = (void *)(uintptr_t)TEGRA_MC_BASE; + uintptr_t end = 4096; + + /* Already has been initialized. */ + if (tz_size_mib != 0 && tz_base_mib != 0) + return; + + /* + * Get memory layout below 4GiB ignoring the TZ carveout because + * that's the one to initialize. + */ + tz_base_mib = end - tz_size_mib; + memory_in_range(&tz_base_mib, &end, CARVEOUT_TZ); + + /* + * IMPORTANT!!!!! + * We need to ensure that trustzone region is located at the end of + * 32-bit address space. If any carveout is allocated space before + * trustzone_region_init is called, then this assert will ensure that + * the boot flow fails. If you are here because of this assert, please + * move your call to initialize carveout after trustzone_region_init in + * romstage and ramstage. + */ + assert(end == 4096); + + /* AVP cannot set the TZ registers proper as it is always non-secure. */ + if (context_avp()) + return; + + /* Set the carveout region. */ + write32(&mc->security_cfg0, tz_base_mib << 20); + write32(&mc->security_cfg1, tz_size_mib); + + /* Enable SMMU translations */ + write32(&mc->smmu_config, MC_SMMU_CONFIG_ENABLE); +} + +void gpu_region_init(void) +{ + struct tegra_mc_regs * const mc = (void *)(uintptr_t)TEGRA_MC_BASE; + uintptr_t gpu_base_mib = 0, end = 4096; + size_t gpu_size_mib = GPU_CARVEOUT_SIZE_MB; + + /* Get memory layout below 4GiB */ + memory_in_range(&gpu_base_mib, &end, CARVEOUT_GPU); + gpu_base_mib = end - gpu_size_mib; + + /* Set the carveout2 base address. Everything else has been set in the BCT cfg/inc */ + write32(&mc->security_carveout2_bom, gpu_base_mib << 20); + write32(&mc->security_carveout2_bom_hi, 0); + + /* Set the locked bit. This will lock out any other writes! */ + setbits_le32(&mc->security_carveout2_cfg0, MC_SECURITY_CARVEOUT_LOCKED); + + /* Set the carveout3 base to 0, unused */ + write32(&mc->security_carveout3_bom, 0); + write32(&mc->security_carveout3_bom_hi, 0); + + /* Set the locked bit. This will lock out any other writes! */ + setbits_le32(&mc->security_carveout3_cfg0, MC_SECURITY_CARVEOUT_LOCKED); +} diff --git a/src/soc/nvidia/tegra210/ape.c b/src/soc/nvidia/tegra210/ape.c new file mode 100644 index 0000000000..b46c11ad6e --- /dev/null +++ b/src/soc/nvidia/tegra210/ape.c @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2015, NVIDIA CORPORATION. + * + * 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. + */ + +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/clock.h> +#include <soc/padconfig.h> +#include <soc/power.h> + +static void enable_ape_periph_clocks(void) +{ + clock_enable(0, 0, 0, CLK_V_APB2APE, 0, 0, CLK_Y_APE); + + /* Give clocks time to stabilize. */ + udelay(IO_STABILIZATION_DELAY); +} + +static void unreset_ape_periphs(void) +{ + clock_clr_reset(0, 0, 0, CLK_V_APB2APE, 0, 0, CLK_Y_APE); +} + +/* + * Audio on Tegra210 requires some special init. + * The APE block must be unpowergated, and a couple of + * audio-based peripherals must be clocked and taken + * out of reset so that I2S/AXBAR/APB2APE registers can + * be configured to enable audio flow. + */ + +void soc_configure_ape(void) +{ + power_ungate_partition(POWER_PARTID_APE); + + enable_ape_periph_clocks(); + remove_clamps(POWER_PARTID_APE); + unreset_ape_periphs(); +} diff --git a/src/soc/nvidia/tegra210/bootblock.c b/src/soc/nvidia/tegra210/bootblock.c new file mode 100644 index 0000000000..f141927b75 --- /dev/null +++ b/src/soc/nvidia/tegra210/bootblock.c @@ -0,0 +1,97 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/exception.h> +#include <arch/hlt.h> +#include <bootblock_common.h> +#include <console/console.h> +#include <program_loading.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/nvidia/tegra/apbmisc.h> +#include <soc/pmc.h> +#include <soc/power.h> +#include <timestamp.h> + +#define BCT_OFFSET_IN_BIT 0x50 +#define ODMDATA_OFFSET_IN_BCT 0x6A8 +#define TEGRA_SRAM_MAX (TEGRA_SRAM_BASE + TEGRA_SRAM_SIZE) + +static void save_odmdata(void) +{ + struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs*)TEGRA_PMC_BASE; + uintptr_t bct_offset; + u32 odmdata; + + // pmc.odmdata: [18:19]: console type, [15:17]: UART id. + // TODO(twarren) ODMDATA is stored in the BCT, from bct/odmdata.cfg. + // I use the BCT offset in the BIT in SRAM to locate the BCT, and + // pick up the ODMDATA word at BCT offset 0x6A8. I could use a BCT + // struct header from cbootimage, but it seems like overkill for this. + + bct_offset = read32((void *)(TEGRA_SRAM_BASE + BCT_OFFSET_IN_BIT)); + if (bct_offset > TEGRA_SRAM_BASE && bct_offset < TEGRA_SRAM_MAX) { + odmdata = read32((void *)(bct_offset + ODMDATA_OFFSET_IN_BCT)); + write32(&pmc->odmdata, odmdata); + } +} + +void __attribute__((weak)) bootblock_mainboard_early_init(void) +{ + /* Empty default implementation. */ +} + +void main(void) +{ + // enable JTAG at the earliest stage + enable_jtag(); + + clock_early_uart(); + + /* Configure mselect clock. */ + clock_configure_source(mselect, PLLP, 102000); + + /* Enable AVP cache, timer, APB dma, and mselect blocks. */ + clock_enable_clear_reset(CLK_L_CACHE2 | CLK_L_TMR, + CLK_H_APBDMA, + 0, CLK_V_MSELECT, 0, 0, 0); + + /* Find ODMDATA in IRAM and save it to scratch reg */ + save_odmdata(); + + bootblock_mainboard_early_init(); + + if (CONFIG_BOOTBLOCK_CONSOLE) { + console_init(); + exception_init(); + printk(BIOS_INFO, "T210: Bootblock here\n"); + } + + clock_init(); + + printk(BIOS_INFO, "T210 bootblock: Clock init done\n"); + + pmc_print_rst_status(); + + bootblock_mainboard_init(); + + printk(BIOS_INFO, "T210 bootblock: Mainboard bootblock init done\n"); + + run_romstage(); +} diff --git a/src/soc/nvidia/tegra210/bootblock_asm.S b/src/soc/nvidia/tegra210/bootblock_asm.S new file mode 100644 index 0000000000..766a1b8fbb --- /dev/null +++ b/src/soc/nvidia/tegra210/bootblock_asm.S @@ -0,0 +1,45 @@ +/* + * Early initialization code for ARM architecture. + * + * This file is based off of the OMAP3530/ARM Cortex start.S file from Das + * U-Boot, which itself got the file from armboot. + * + * 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> + * Copyright (c) 2013 The Chromium OS 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. + */ + +#include <arch/asm.h> + +#include "stack.S" + +ENTRY(_start) + /* + * Set the cpu to System mode with IRQ and FIQ disabled. Prefetch/Data + * aborts may happen early and crash before the abort handlers are + * installed, but at least the problem will show up near the code that + * causes it. + */ + msr cpsr_cxf, #0xdf + + stack_init stack_top=_estack stack_bottom=_stack seed=1 func=main +ENDPROC(_start) diff --git a/src/soc/nvidia/tegra210/cbmem.c b/src/soc/nvidia/tegra210/cbmem.c new file mode 100644 index 0000000000..c161a36d3b --- /dev/null +++ b/src/soc/nvidia/tegra210/cbmem.c @@ -0,0 +1,40 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <cbmem.h> +#include <soc/addressmap.h> + +void *cbmem_top(void) +{ + static uintptr_t addr; + + if (addr == 0) { + uintptr_t begin_mib; + uintptr_t end_mib; + + memory_in_range_below_4gb(&begin_mib, &end_mib); + /* Make sure we consume everything up to 4GIB. */ + if (end_mib == 4096) + addr = ~(uint32_t)0; + else + addr = end_mib << 20; + } + + return (void *)addr; +} diff --git a/src/soc/nvidia/tegra210/ccplex.c b/src/soc/nvidia/tegra210/ccplex.c new file mode 100644 index 0000000000..f3c61cbbe6 --- /dev/null +++ b/src/soc/nvidia/tegra210/ccplex.c @@ -0,0 +1,122 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <cbfs.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/clk_rst.h> +#include <soc/ccplex.h> +#include <soc/cpu.h> +#include <soc/flow.h> +#include <soc/mc.h> +#include <soc/pmc.h> +#include <soc/power.h> +#include <soc/romstage.h> +#include <string.h> +#include <timer.h> + +#define PMC_REGS (void *)(uintptr_t)(TEGRA_PMC_BASE) + +static void enable_cpu_clocks(void) +{ + clock_enable(CLK_ENB_CPU, 0, 0, SET_CLK_ENB_CPUG_ENABLE | + SET_CLK_ENB_CPULP_ENABLE, 0, 0, 0); +} + +static void enable_cpu_power_partitions(void) +{ + /* Bring up fast cluster, non-CPU, CPU0, CPU1, CPU2 and CPU3 parts. */ + power_ungate_partition(POWER_PARTID_CRAIL); + power_ungate_partition(POWER_PARTID_C0NC); + power_ungate_partition(POWER_PARTID_CE0); + if (IS_ENABLED(CONFIG_ARM64_USE_SECURE_MONITOR)) { + power_ungate_partition(POWER_PARTID_CE1); + power_ungate_partition(POWER_PARTID_CE2); + power_ungate_partition(POWER_PARTID_CE3); + } + + if (IS_ENABLED(CONFIG_ARM64_USE_ARM_TRUSTED_FIRMWARE)) { + /* + * Deassert reset signal of all the secondary CPUs. + * PMC and flow controller will take over the power sequence + * controller in the ATF. + */ + uint32_t reg = CRC_RST_CPUG_CLR_CPU1 | CRC_RST_CPUG_CLR_DBG1 | + CRC_RST_CPUG_CLR_CORE1 | CRC_RST_CPUG_CLR_CX1 | + CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_DBG2 | + CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CX2 | + CRC_RST_CPUG_CLR_CPU3 | CRC_RST_CPUG_CLR_DBG3 | + CRC_RST_CPUG_CLR_CORE3 | CRC_RST_CPUG_CLR_CX3; + write32(CLK_RST_REG(rst_cpug_cmplx_clr), reg); + } +} + +static void request_ram_repair(void) +{ + struct flow_ctlr * const flow = (void *)(uintptr_t)TEGRA_FLOW_BASE; + const uint32_t req = 1 << 0; + const uint32_t sts = 1 << 1; + uint32_t reg; + struct stopwatch sw; + + printk(BIOS_DEBUG, "Requesting RAM repair.\n"); + + stopwatch_init(&sw); + + /* Perform ram repair */ + reg = read32(&flow->ram_repair); + reg |= req; + write32(&flow->ram_repair, reg); + while ((read32(&flow->ram_repair) & sts) != sts) + ; + + printk(BIOS_DEBUG, "RAM repair complete in %ld usecs.\n", + stopwatch_duration_usecs(&sw)); +} + +void ccplex_cpu_prepare(void) +{ + enable_cpu_clocks(); + enable_cpu_power_partitions(); + + mainboard_configure_pmc(); + mainboard_enable_vdd_cpu(); + + request_ram_repair(); +} + +static void start_common_clocks(void) +{ + /* Clear fast CPU partition reset. */ + write32(CLK_RST_REG(rst_cpug_cmplx_clr), CRC_RST_CPUG_CLR_NONCPU); + + /* Clear reset of L2 and CoreSight components. */ + write32(CLK_RST_REG(rst_cpug_cmplx_clr), + CRC_RST_CPUG_CLR_L2 | CRC_RST_CPUG_CLR_PDBG); +} + +void ccplex_cpu_start(void *entry_addr) +{ + /* Enable common clocks for the shared resources between the cores. */ + start_common_clocks(); + + start_cpu(0, entry_addr); +} diff --git a/src/soc/nvidia/tegra210/chip.h b/src/soc/nvidia/tegra210/chip.h new file mode 100644 index 0000000000..efce901cae --- /dev/null +++ b/src/soc/nvidia/tegra210/chip.h @@ -0,0 +1,99 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_CHIP_H__ +#define __SOC_NVIDIA_TEGRA210_CHIP_H__ +#include <soc/addressmap.h> +#include <stdint.h> +#include <soc/nvidia/tegra/dc.h> + +struct soc_nvidia_tegra210_config { + /* Address to monitor if spintable employed. */ + uintptr_t spintable_addr; + + /* + * panel resolution + * The two parameters below provides dc about panel spec. + */ + u32 xres; /* the width of H display active area */ + u32 yres; /* the height of V display active area */ + u32 framebuffer_bits_per_pixel; + u32 color_depth; /* color format */ + + u64 display_controller; /* dc block base address */ + u32 framebuffer_base; + + /* + * Technically, we can compute this. At the same time, some platforms + * might want to specify a specific size for their own reasons. If it + * is zero the soc code will compute it as + * xres*yres*framebuffer_bits_per_pixel/8 + */ + u32 framebuffer_size; + + /* + * Framebuffer resolution + * The two parameters below provides dc about framebuffer's sdram size. + * When they are not the same as panel resolution, we need to program + * dc's DDA_INCREMENT and some other registers to resize dc output. + */ + u32 display_xres; + u32 display_yres; + + int href_to_sync; /* HSYNC position with respect to line start */ + int hsync_width; /* the width of HSYNC pulses */ + int hback_porch; /* the distance between HSYNC trailing edge to + beginning of H display active area */ + int hfront_porch; /* the distance between end of H display active + area to the leading edge of HSYNC */ + int vref_to_sync; + int vsync_width; + int vback_porch; + int vfront_porch; + int refresh; /* display refresh rate */ + + int pixel_clock; /* dc pixel clock source rate */ + + u32 panel_bits_per_pixel; + + /* dp specific fields */ + struct { + /* pwm to use to set display contrast */ + int pwm; + + /* HPD related timing */ + int vdd_to_hpd_delay_ms; + int hpd_unplug_min_us; + int hpd_plug_min_us; + int hpd_irq_min_us; + + /* The minimum link configuraton settings */ + u32 lane_count; + u32 enhanced_framing; + u32 link_bw; + u32 drive_current; + u32 preemphasis; + u32 postcursor; + } dp; + + int win_opt; + void *dc_data; +}; + +#endif /* __SOC_NVIDIA_TEGRA210_CHIP_H__ */ diff --git a/src/soc/nvidia/tegra210/clock.c b/src/soc/nvidia/tegra210/clock.c new file mode 100644 index 0000000000..aa723403de --- /dev/null +++ b/src/soc/nvidia/tegra210/clock.c @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <delay.h> +#include <stdlib.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/clock.h> +#include <soc/clst_clk.h> +#include <soc/flow.h> +#include <soc/maincpu.h> +#include <soc/pmc.h> +#include <soc/sysctr.h> + +static struct flow_ctlr *flow = (void *)TEGRA_FLOW_BASE; +static struct tegra_pmc_regs *pmc = (void *)TEGRA_PMC_BASE; +static struct sysctr_regs *sysctr = (void *)TEGRA_SYSCTR0_BASE; + +enum { + PLLX_INDEX, + PLLC_INDEX, + PLLU_INDEX, + PLLDP_INDEX, + PLLD_INDEX, + PLL_MAX_INDEX, +}; + +struct pll_reg_info { + u32 *base_reg; + u32 *lock_enb_reg; + u32 lock_enb_val; + u32 *pll_lock_reg; + u32 pll_lock_val; + u32 *kcp_kvco_reg; + u32 n_shift:5; /* n bits location */ + u32 m_shift:5; /* m bits location */ + u32 p_shift:5; /* p bits location */ + u32 kcp_shift:5; /* kcp bits location */ + u32 kvco_shift:5; /* kvco bit location */ + u32 rsvd:7; +} static const pll_reg_table[] = { + [PLLX_INDEX] = { .base_reg = CLK_RST_REG(pllx_base), + .lock_enb_reg = CLK_RST_REG(pllx_misc), + .lock_enb_val = PLLPAXS_MISC_LOCK_ENABLE, + .pll_lock_reg = CLK_RST_REG(pllx_base), + .pll_lock_val = PLL_BASE_LOCK, + .kcp_kvco_reg = CLK_RST_REG(pllx_misc3), + .n_shift = 8, .m_shift = 0, .p_shift = 20, + .kcp_shift = 1, .kvco_shift = 0, }, + [PLLC_INDEX] = { .base_reg = CLK_RST_REG(pllc_base), + .lock_enb_reg = CLK_RST_REG(pllc_misc), + .pll_lock_reg = CLK_RST_REG(pllc_base), + .pll_lock_val = PLL_BASE_LOCK, + .n_shift = 10, .m_shift = 0, .p_shift = 20, }, + [PLLU_INDEX] = { .base_reg = CLK_RST_REG(pllu_base), + .lock_enb_reg = CLK_RST_REG(pllu_misc), + .lock_enb_val = PLLU_MISC_LOCK_ENABLE, + .pll_lock_reg = CLK_RST_REG(pllu_base), + .pll_lock_val = PLL_BASE_LOCK, + .kcp_kvco_reg = CLK_RST_REG(pllu_misc), + .n_shift = 8, .m_shift = 0, .p_shift = 16, + .kcp_shift = 25, .kvco_shift = 24, }, + [PLLDP_INDEX] = { .base_reg = CLK_RST_REG(plldp_base), + .lock_enb_reg = CLK_RST_REG(plldp_misc), + .lock_enb_val = PLLDPD2_MISC_LOCK_ENABLE, + .pll_lock_reg = CLK_RST_REG(plldp_base), + .pll_lock_val = PLL_BASE_LOCK, + .kcp_kvco_reg = CLK_RST_REG(plldp_misc), + .n_shift = 8, .m_shift = 0, .p_shift = 19, + .kcp_shift = 25, .kvco_shift = 24, }, + [PLLD_INDEX] = { .base_reg = CLK_RST_REG(plld_base), + .lock_enb_reg = CLK_RST_REG(plld_misc), + .lock_enb_val = PLLD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE, + .pll_lock_reg = CLK_RST_REG(plld_base), + .pll_lock_val = PLL_BASE_LOCK, + .kcp_kvco_reg = CLK_RST_REG(plld_misc), + .n_shift = 11, .m_shift = 0, .p_shift = 20, + .kcp_shift = 23, .kvco_shift = 22, }, +}; + +struct pll_fields { + u32 n:8; /* the feedback divider bits width */ + u32 m:8; /* the input divider bits width */ + u32 p:5; /* the post divider bits witch */ + u32 kcp:2; /* charge pump gain control */ + u32 kvco:1; /* vco gain */ + u32 rsvd:8; +}; + +#define PLL_HAS_KCP_KVCO(_n, _m, _p, _kcp, _kvco) \ + {.n = _n, .m = _m, .p = _p, .kcp = _kcp, .kvco = _kvco,} +#define PLL_NO_KCP_KVCO(_n, _m, _p) \ + {.n = _n, .m = _m, .p = _p,} + +#define PLLX(_n, _m, _p, _kcp, _kvco) \ + [PLLX_INDEX] = PLL_HAS_KCP_KVCO(_n, _m, _p, _kcp, _kvco) +#define PLLC(_n, _m, _p) \ + [PLLC_INDEX] = PLL_NO_KCP_KVCO(_n, _m, _p) +#define PLLU(_n, _m, _p, _kcp, _kvco) \ + [PLLU_INDEX] = PLL_HAS_KCP_KVCO(_n, _m, _p, _kcp, _kvco) +#define PLLDP(_n, _m, _p, _kcp, _kvco) \ + [PLLDP_INDEX] = PLL_HAS_KCP_KVCO(_n, _m, _p, _kcp, _kvco) +#define PLLD(_n, _m, _p, _kcp, _kvco) \ + [PLLD_INDEX] = PLL_HAS_KCP_KVCO(_n, _m, _p, _kcp, _kvco) + +/* This table defines the frequency dividers for every PLL to turn the external + * OSC clock into the frequencies defined by TEGRA_PLL*_KHZ in soc/clock.h. + * All PLLs have three dividers (n, m and p), with the governing formula for + * the output frequency being CF = (IN / m), VCO = CF * n and OUT = VCO / (2^p). + * All divisor configurations must meet the PLL's constraints for VCO and CF: + * PLLX: 12 MHz < CF < 50 MHz, 700 MHz < VCO < 3000 MHz + * PLLC: 12 MHz < CF < 50 MHz, 600 MHz < VCO < 1400 MHz + * PLLM: 12 MHz < CF < 50 MHz, 400 MHz < VCO < 1066 MHz + * PLLP: 1 MHz < CF < 6 MHz, 200 MHz < VCO < 700 MHz + * PLLD: 1 MHz < CF < 6 MHz, 500 MHz < VCO < 1000 MHz + * PLLU: 1 MHz < CF < 6 MHz, 480 MHz < VCO < 960 MHz + * PLLDP: 12 MHz < CF < 38 MHz, 600 MHz < VCO < 1200 MHz + * (values taken from Linux' drivers/clk/tegra/clk-tegra124.c). + * Target Frequencies: + * PLLX = CONFIG_PLLX_KHZ + * PLLC = 600 MHz + * PLLU = 240 MHz (As per TRM, m and n should be programmed to generate 480MHz + * VCO, and p should be programmed to do div-by-2.) + * PLLDP = 270 MHz (PLLDP treats p differently (OUT = VCO / (p + 1) for p < 6)). + * PLLM is set up dynamically by clock_sdram(). + * PLLP is hardwired to 408 MHz in HW (unless we set BASE_OVRD). + */ +struct { + int khz; + struct pll_fields plls[PLL_MAX_INDEX]; +} static osc_table[16] = { + [OSC_FREQ_12]{ + .khz = 12000, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 12000, 1, 0, 0, 0), + PLLC(50, 1, 0), /* 600 MHz */ + PLLU(40, 1, 1, 0, 0), /* 240 MHz */ + PLLDP(90, 1, 2, 0, 0), /* 270 MHz */ + }, + }, + [OSC_FREQ_13]{ + .khz = 13000, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 13000, 1, 0, 0, 0), + PLLC(46, 1, 0), /* 598.0 MHz */ + PLLU(74, 2, 1, 0, 0), /* 240.5 MHz */ + PLLDP(83, 1, 3, 0, 0), /* 269.8 MHz */ + }, + }, + [OSC_FREQ_16P8]{ + .khz = 16800, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 16800, 1, 0, 0, 0), + PLLC(71, 1, 1), /* 596.4 MHz */ + PLLU(115, 4, 1, 0, 0), /* 241.5 MHz */ + PLLDP(64, 1, 2, 0, 0), /* 268.8 MHz */ + }, + }, + [OSC_FREQ_19P2]{ + .khz = 19200, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 19200, 1, 0, 0, 0), + PLLC(62, 1, 1), /* 595.2 MHz */ + PLLU(25, 1, 1, 0, 0), /* 240.0 MHz */ + PLLDP(56, 1, 2, 0, 0), /* 268.8 MHz */ + }, + }, + [OSC_FREQ_26]{ + .khz = 26000, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 26000, 1, 0, 0, 0), + PLLC(23, 1, 0), /* 598.0 MHz */ + PLLU(37, 2, 1, 0, 0), /* 240.5 MHz */ + PLLDP(83, 2, 2, 0, 0), /* 269.8 MHz */ + }, + }, + [OSC_FREQ_38P4]{ + .khz = 38400, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 38400, 1, 0, 0, 0), + PLLC(62, 2, 1), /* 595.2 MHz */ + PLLU(25, 2, 1, 0, 0), /* 240 MHz */ + PLLDP(56, 2, 2, 0, 0), /* 268.8 MHz */ + }, + }, + [OSC_FREQ_48]{ + .khz = 48000, + .plls = { + PLLX(TEGRA_PLLX_KHZ / 48000, 1, 0, 0, 0), + PLLC(50, 2, 1), /* 600 MHz */ + PLLU(40, 4, 1, 0, 0), /* 240 MHz */ + PLLDP(90, 2, 3, 0, 0), /* 270 MHz */ + }, + }, +}; + +/* Get the oscillator frequency, from the corresponding hardware + * configuration field. This is actually a per-soc thing. Avoid the + * temptation to make it common. + */ +static u32 clock_get_osc_bits(void) +{ + return (read32(CLK_RST_REG(osc_ctrl)) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + +int clock_get_osc_khz(void) +{ + return osc_table[clock_get_osc_bits()].khz; +} + +int clock_get_pll_input_khz(void) +{ + u32 osc_ctrl = read32(CLK_RST_REG(osc_ctrl)); + u32 osc_bits = (osc_ctrl & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; + u32 pll_ref_div = (osc_ctrl & OSC_PREDIV_MASK) >> OSC_PREDIV_SHIFT; + return osc_table[osc_bits].khz >> pll_ref_div; +} + +void clock_init_arm_generic_timer(void) +{ + uint32_t freq = TEGRA_CLK_M_KHZ * 1000; + + // Record the system timer frequency. + write32(&sysctr->cntfid0, freq); + // Enable the system counter. + uint32_t cntcr = read32(&sysctr->cntcr); + cntcr |= SYSCTR_CNTCR_EN | SYSCTR_CNTCR_HDBG; + write32(&sysctr->cntcr, cntcr); +} + +#define SOR0_CLK_SEL0 (1 << 14) +#define SOR0_CLK_SEL1 (1 << 15) + +void sor_clock_stop(void) +{ + /* The Serial Output Resource clock has to be off + * before we start the plldp. Learned the hard way. + * FIXME: this has to be cleaned up a bit more. + * Waiting on some new info from Nvidia. + */ + clrbits_le32(CLK_RST_REG(clk_src_sor), SOR0_CLK_SEL0 | SOR0_CLK_SEL1); +} + +void sor_clock_start(void) +{ + /* uses PLLP, has a non-standard bit layout. */ + setbits_le32(CLK_RST_REG(clk_src_sor), SOR0_CLK_SEL0); +} + +static void init_pll(u32 index, u32 osc) +{ + assert(index <= PLL_MAX_INDEX); + + struct pll_fields *pll = &osc_table[osc].plls[index]; + const struct pll_reg_info *pll_reg = &pll_reg_table[index]; + + u32 dividers = pll->n << pll_reg->n_shift | + pll->m << pll_reg->m_shift | + pll->p << pll_reg->p_shift; + + /* Write dividers but BYPASS the PLL while we're messing with it. */ + write32(pll_reg->base_reg, dividers | PLL_BASE_BYPASS); + + /* Set Lock bit if needed. */ + if (pll_reg->lock_enb_val) + setbits_le32(pll_reg->lock_enb_reg, pll_reg->lock_enb_val); + + /* Set KCP/KVCO if needed. */ + if (pll_reg->kcp_kvco_reg) + setbits_le32(pll_reg->kcp_kvco_reg, + pll->kcp << pll_reg->kcp_shift | + pll->kvco << pll_reg->kvco_shift); + + /* Enable PLL and take it back out of BYPASS */ + write32(pll_reg->base_reg, dividers | PLL_BASE_ENABLE); + + /* Wait for lock ready */ + if (pll_reg->lock_enb_val) + while (!(read32(pll_reg->pll_lock_reg) & pll_reg->pll_lock_val)) + ; +} + +static void init_pllc(u32 osc) +{ + /* Clear PLLC reset */ + clrbits_le32(CLK_RST_REG(pllc_misc), PLLC_MISC_RESET); + + /* Clear PLLC IDDQ */ + clrbits_le32(CLK_RST_REG(pllc_misc_1), PLLC_MISC_1_IDDQ); + + /* Max out the AVP clock before everything else (need PLLC for that). */ + init_pll(PLLC_INDEX, osc); + + /* wait for pllc_lock (not the normal bit 27) */ + while (!(read32(CLK_RST_REG(pllc_base)) & PLLC_BASE_LOCK)) + ; +} + +static void init_pllu(u32 osc) +{ + /* Clear PLLU IDDQ */ + clrbits_le32(CLK_RST_REG(pllu_misc), PLLU_MISC_IDDQ); + + /* Wait 5 us */ + udelay(5); + + init_pll(PLLU_INDEX, osc); +} + +static void init_utmip_pll(void) +{ + int khz = clock_get_pll_input_khz(); + + /* Shut off PLL crystal clock while we mess with it */ + clrbits_le32(CLK_RST_REG(utmip_pll_cfg2), UTMIP_CFG2_PHY_XTAL_CLOCKEN); + udelay(1); + + /* CFG0 */ + u32 div_n = div_round_up(960000, khz); + write32(CLK_RST_REG(utmip_pll_cfg0), /* 960 MHz VCO */ + 1 << UTMIP_CFG0_PLL_MDIV_SHIFT | + div_n << UTMIP_CFG0_PLL_NDIV_SHIFT); + + /* CFG1 */ + u32 pllu_enb_ct = div_round_up(khz, 8000); /* pllu_enb_ct / 8 (1us) */ + u32 phy_stb_ct = div_round_up(khz, 102); /* phy_stb_ct / 256(2.5ms) */ + write32(CLK_RST_REG(utmip_pll_cfg1), + pllu_enb_ct << UTMIP_CFG1_PLLU_ENABLE_DLY_COUNT_SHIFT | + UTMIP_CFG1_FORCE_PLLU_POWERDOWN_ENABLE | + UTMIP_CFG1_FORCE_PLL_ENABLE_POWERDOWN_DISABLE | + UTMIP_CFG1_FORCE_PLL_ACTIVE_POWERDOWN_DISABLE | + UTMIP_CFG1_FORCE_PLL_ENABLE_POWERUP_ENABLE | + phy_stb_ct << UTMIP_CFG1_XTAL_FREQ_COUNT_SHIFT); + + /* CFG2 */ + u32 pllu_stb_ct = div_round_up(khz, 256); /* pllu_stb_ct / 256 (1ms) */ + u32 phy_act_ct = div_round_up(khz, 3200); /* phy_act_ct / 16 (5us) */ + write32(CLK_RST_REG(utmip_pll_cfg2), + phy_act_ct << UTMIP_CFG2_PLL_ACTIVE_DLY_COUNT_SHIFT | + pllu_stb_ct << UTMIP_CFG2_PLLU_STABLE_COUNT_SHIFT | + UTMIP_CFG2_FORCE_PD_SAMP_C_POWERDOWN_DISABLE | + UTMIP_CFG2_FORCE_PD_SAMP_B_POWERDOWN_DISABLE | + UTMIP_CFG2_FORCE_PD_SAMP_A_POWERDOWN_DISABLE); + + setbits_le32(CLK_RST_REG(utmip_pll_cfg2), UTMIP_CFG2_PHY_XTAL_CLOCKEN); +} + +/* Graphics just has to be different. There's a few more bits we + * need to set in here, but it makes sense just to restrict all the + * special bits to this one function. + */ +static void graphics_pll(void) +{ + int osc = clock_get_osc_bits(); + u32 *cfg = CLK_RST_REG(plldp_ss_cfg); + /* the vendor code sets the dither bit (28) + * an undocumented bit (24) + * and clamp while we mess with it (22) + * Dither is pretty important to display port + * so we really do need to handle these bits. + * I'm not willing to not clamp it, even if + * it might "mostly work" with it not set, + * I don't want to find out in a few months + * that it is needed. + */ + u32 scfg = (1<<28) | (1<<24) | (1<<22); + write32(cfg, scfg); + init_pll(PLLDP_INDEX, osc); + /* leave dither and undoc bits set, release clamp */ + scfg = (1<<28) | (1<<24); + write32(cfg, scfg); +} + +/* + * Init PLLD clock source. + * + * @frequency: the requested plld frequency + * + * Return the plld frequency if success, otherwise return 0. + */ +u32 clock_configure_plld(u32 frequency) +{ + /** + * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz + * = (cf * n) >> p, where 1MHz < cf < 6MHz + * = ((ref / m) * n) >> p + * + * Iterate the possible values of p (3 bits, 2^7) to find out a minimum + * safe vco, then find best (m, n). since m has only 5 bits, we can + * iterate all possible values. Note Tegra1xx supports 11 bits for n, + * but our pll_fields has only 10 bits for n. + * + * Note values undershoot or overshoot target output frequency may not + * work if the values are not in "safe" range by panel specification. + */ + struct pll_fields *plld; + u32 ref = clock_get_pll_input_khz() * 1000, m, n, p = 0; + u32 cf, vco, rounded_rate = frequency; + u32 diff, best_diff; + const u32 max_m = 1 << 8, max_n = 1 << 8, max_p = 1 << 3, + mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz, + min_cf = 1 * mhz, max_cf = 6 * mhz; + u32 osc = clock_get_osc_bits(); + + plld = &osc_table[osc].plls[PLLD_INDEX]; + + for (vco = frequency; vco < min_vco && p < max_p; p++) + vco <<= 1; + + if (vco < min_vco || vco > max_vco) { + printk(BIOS_ERR, "%s: Cannot find out a supported VCO" + " for Frequency (%u).\n", __func__, frequency); + return 0; + } + + plld->p = p; + best_diff = vco; + + for (m = 1; m < max_m && best_diff; m++) { + cf = ref / m; + if (cf < min_cf) + break; + if (cf > max_cf) + continue; + + n = vco / cf; + if (n >= max_n) + continue; + + diff = vco - n * cf; + if (n + 1 < max_n && diff > cf / 2) { + n++; + diff = cf - diff; + } + + if (diff >= best_diff) + continue; + + best_diff = diff; + plld->m = m; + plld->n = n; + } + + if (best_diff) { + printk(BIOS_WARNING, "%s: Failed to match output frequency %u, " + "best difference is %u.\n", __func__, frequency, + best_diff); + rounded_rate = (ref / plld->m * plld->n) >> plld->p; + } + + printk(BIOS_DEBUG, "%s: PLLD=%u ref=%u, m/n/p=%u/%u/%u\n", + __func__, rounded_rate, ref, plld->m, plld->n, plld->p); + + /* Write misc1 and misc */ + write32(CLK_RST_REG(plld_misc1), PLLD_MISC1_SETUP); + write32(CLK_RST_REG(plld_misc), (PLLD_MISC_EN_SDM | PLLD_MISC_SDM_DIN)); + + /* configure PLLD */ + init_pll(PLLD_INDEX, osc); + + if (rounded_rate != frequency) + printk(BIOS_DEBUG, "PLLD rate: %u vs %u\n", rounded_rate, + frequency); + + return rounded_rate; +} + +/* + * Initialize the UART and use PLLP as clock source. PLLP is hardwired to 408 + * MHz in HW (unless we set BASE_OVRD). We override the 16.0 UART divider with + * the 15.1 CLK_SOURCE divider to get more precision. The 1843(KHZ) is + * calculated thru BAUD_RATE*16/1000, ie, 115200*16/1000. + */ +void clock_early_uart(void) +{ + write32(CLK_RST_REG(clk_src_uarta), + CLK_SRC_DEV_ID(UARTA, PLLP) << CLK_SOURCE_SHIFT | + CLK_UART_DIV_OVERRIDE | + CLK_DIVIDER(TEGRA_PLLP_KHZ, 1843)); + + clock_enable_clear_reset_l(CLK_L_UARTA); +} + +/* Enable output clock (CLK1~3) for external peripherals. */ +void clock_external_output(int clk_id) +{ + switch (clk_id) { + case 1: + setbits_le32(&pmc->clk_out_cntrl, 1 << 2); + break; + case 2: + setbits_le32(&pmc->clk_out_cntrl, 1 << 10); + break; + case 3: + setbits_le32(&pmc->clk_out_cntrl, 1 << 18); + break; + default: + printk(BIOS_CRIT, "ERROR: Unknown output clock id %d\n", + clk_id); + break; + } +} + +/* Start PLLM for SDRAM. */ +void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 kvco, u32 kcp, + u32 stable_time, u32 emc_source, u32 same_freq) +{ + u32 misc1 = ((setup << PLLM_MISC1_SETUP_SHIFT)), + misc2 = ((kvco << PLLM_MISC2_KVCO_SHIFT) | + (kcp << PLLM_MISC2_KCP_SHIFT) | + PLLM_EN_LCKDET), + base; + + if (same_freq) + emc_source |= CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; + else + emc_source &= ~CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; + + /* + * Note PLLM_BASE.PLLM_OUT1_RSTN must be in RESET_ENABLE mode, and + * PLLM_BASE.ENABLE must be in DISABLE state (both are the default + * values after coldboot reset). + */ + + write32(CLK_RST_REG(pllm_misc1), misc1); + write32(CLK_RST_REG(pllm_misc2), misc2); + + /* PLLM.BASE needs BYPASS=0, different from general init_pll */ + base = read32(CLK_RST_REG(pllm_base)); + base &= ~(PLLCMX_BASE_DIVN_MASK | PLLCMX_BASE_DIVM_MASK | + PLLM_BASE_DIVP_MASK | PLL_BASE_BYPASS); + base |= ((m << PLL_BASE_DIVM_SHIFT) | (n << PLL_BASE_DIVN_SHIFT) | + (p << PLL_BASE_DIVP_SHIFT)); + write32(CLK_RST_REG(pllm_base), base); + + setbits_le32(CLK_RST_REG(pllm_base), PLL_BASE_ENABLE); + /* stable_time is required, before we can start to check lock. */ + udelay(stable_time); + + while (!(read32(CLK_RST_REG(pllm_base)) & PLL_BASE_LOCK)) + udelay(1); + + /* + * After PLLM reports being locked, we have to delay 10us before + * enabling PLLM_OUT. + */ + udelay(10); + + /* Enable and start MEM(MC) and EMC. */ + clock_enable_clear_reset(0, CLK_H_MEM | CLK_H_EMC, 0, 0, 0, 0, 0); + write32(CLK_RST_REG(clk_src_emc), emc_source); + udelay(IO_STABILIZATION_DELAY); +} + +void clock_halt_avp(void) +{ + for (;;) { + write32(&flow->halt_cop_events, + FLOW_EVENT_JTAG | FLOW_EVENT_LIC_IRQ | + FLOW_EVENT_GIC_IRQ | FLOW_MODE_WAITEVENT); + } +} + +void clock_init(void) +{ + u32 osc = clock_get_osc_bits(); + /* clk_m = osc/2 */ + clrsetbits_le32(CLK_RST_REG(spare_reg0), CLK_M_DIVISOR_MASK, + CLK_M_DIVISOR_BY_2); + + /* TIMERUS needs to be adjusted for new 19.2MHz CLK_M rate */ + write32((void *)TEGRA_TMRUS_BASE + TIMERUS_USEC_CFG, + TIMERUS_USEC_CFG_19P2_CLK_M); + + init_pllc(osc); + + /* Typical ratios are 1:2:2 or 1:2:3 sclk:hclk:pclk (See: APB DMA + * features section in the TRM). */ + write32(CLK_RST_REG(clk_sys_rate), /* pclk = hclk = sclk/2 */ + 1 << HCLK_DIVISOR_SHIFT | 0 << PCLK_DIVISOR_SHIFT); + write32(CLK_RST_REG(pllc_out), + CLK_DIVIDER(TEGRA_PLLC_KHZ, 300000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_CLKEN | PLL_OUT_RSTN); + write32(CLK_RST_REG(sclk_brst_pol), /* sclk = 300 MHz */ + SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT | + SCLK_SOURCE_PLLC_OUT1 << SCLK_RUN_SHIFT); + + /* Change the oscillator drive strength (from U-Boot -- why?) */ + clrsetbits_le32(CLK_RST_REG(osc_ctrl), OSC_XOFS_MASK, + OSC_DRIVE_STRENGTH << OSC_XOFS_SHIFT); + + /* + * Ambiguous quote from u-boot. TODO: what's this mean? + * "should update same value in PMC_OSC_EDPD_OVER XOFS + * field for warmboot " + */ + clrsetbits_le32(&pmc->osc_edpd_over, PMC_OSC_EDPD_OVER_XOFS_MASK, + OSC_DRIVE_STRENGTH << PMC_OSC_EDPD_OVER_XOFS_SHIFT); + + /* Disable IDDQ for PLLX before we set it up (from U-Boot -- why?) */ + clrbits_le32(CLK_RST_REG(pllx_misc3), PLLX_IDDQ_MASK); + + /* Set up PLLP_OUT(1|2|3|4) divisor to generate (9.6|48|102|204)MHz */ + write32(CLK_RST_REG(pllp_outa), + (CLK_DIVIDER(TEGRA_PLLP_KHZ, 9600) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT1_SHIFT | + (CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT2_SHIFT); + write32(CLK_RST_REG(pllp_outb), + (CLK_DIVIDER(TEGRA_PLLP_KHZ, TEGRA_PLLP_OUT3_KHZ) << + PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT3_SHIFT | + (CLK_DIVIDER(TEGRA_PLLP_KHZ, 204000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT4_SHIFT); + + /* init pllx */ + init_pll(PLLX_INDEX, osc); + write32(CLK_RST_REG(cclk_brst_pol), CCLK_BURST_POLICY_VAL); + + /* init pllu */ + init_pllu(osc); + + init_utmip_pll(); + graphics_pll(); +} + +void clock_grp_enable_clear_reset(u32 val, u32* clk_enb_set_reg, + u32 *rst_dev_clr_reg) +{ + write32(clk_enb_set_reg, val); + udelay(IO_STABILIZATION_DELAY); + write32(rst_dev_clr_reg, val); +} + +static u32 * const clk_enb_set_arr[DEV_CONFIG_BLOCKS] = { + CLK_RST_REG(clk_enb_l_set), + CLK_RST_REG(clk_enb_h_set), + CLK_RST_REG(clk_enb_u_set), + CLK_RST_REG(clk_enb_v_set), + CLK_RST_REG(clk_enb_w_set), + CLK_RST_REG(clk_enb_x_set), + CLK_RST_REG(clk_enb_y_set), +}; + +static u32 * const clk_enb_clr_arr[DEV_CONFIG_BLOCKS] = { + CLK_RST_REG(clk_enb_l_clr), + CLK_RST_REG(clk_enb_h_clr), + CLK_RST_REG(clk_enb_u_clr), + CLK_RST_REG(clk_enb_v_clr), + CLK_RST_REG(clk_enb_w_clr), + CLK_RST_REG(clk_enb_x_clr), + CLK_RST_REG(clk_enb_y_clr), +}; + +static u32 * const rst_dev_set_arr[DEV_CONFIG_BLOCKS] = { + CLK_RST_REG(rst_dev_l_set), + CLK_RST_REG(rst_dev_h_set), + CLK_RST_REG(rst_dev_u_set), + CLK_RST_REG(rst_dev_v_set), + CLK_RST_REG(rst_dev_w_set), + CLK_RST_REG(rst_dev_x_set), + CLK_RST_REG(rst_dev_y_set), +}; + +static u32 * const rst_dev_clr_arr[DEV_CONFIG_BLOCKS] = { + CLK_RST_REG(rst_dev_l_clr), + CLK_RST_REG(rst_dev_h_clr), + CLK_RST_REG(rst_dev_u_clr), + CLK_RST_REG(rst_dev_v_clr), + CLK_RST_REG(rst_dev_w_clr), + CLK_RST_REG(rst_dev_x_clr), + CLK_RST_REG(rst_dev_y_clr), +}; + +static void clock_write_regs(u32 * const regs[DEV_CONFIG_BLOCKS], + u32 bits[DEV_CONFIG_BLOCKS]) +{ + int i = 0; + + for (; i < DEV_CONFIG_BLOCKS; i++) + if (bits[i]) + write32(regs[i], bits[i]); +} + +void clock_enable_regs(u32 bits[DEV_CONFIG_BLOCKS]) +{ + clock_write_regs(clk_enb_set_arr, bits); +} + +void clock_disable_regs(u32 bits[DEV_CONFIG_BLOCKS]) +{ + clock_write_regs(clk_enb_clr_arr, bits); +} + +void clock_set_reset_regs(u32 bits[DEV_CONFIG_BLOCKS]) +{ + clock_write_regs(rst_dev_set_arr, bits); +} + +void clock_clr_reset_regs(u32 bits[DEV_CONFIG_BLOCKS]) +{ + clock_write_regs(rst_dev_clr_arr, bits); +} + +void clock_enable_clear_reset(u32 l, u32 h, u32 u, u32 v, u32 w, u32 x, u32 y) +{ + clock_enable(l, h, u, v, w, x, y); + + /* Give clocks time to stabilize. */ + udelay(IO_STABILIZATION_DELAY); + + clock_clr_reset(l, h, u, v, w, x, y); +} + +static void clock_reset_dev(u32 *setaddr, u32 *clraddr, u32 bit) +{ + write32(setaddr, bit); + udelay(LOGIC_STABILIZATION_DELAY); + write32(clraddr, bit); +} + +void clock_reset_l(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_l_set), CLK_RST_REG(rst_dev_l_clr), + bit); +} + +void clock_reset_h(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_h_set), CLK_RST_REG(rst_dev_h_clr), + bit); +} + +void clock_reset_u(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_u_set), CLK_RST_REG(rst_dev_u_clr), + bit); +} + +void clock_reset_v(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_v_set), CLK_RST_REG(rst_dev_v_clr), + bit); +} + +void clock_reset_w(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_w_set), CLK_RST_REG(rst_dev_w_clr), + bit); +} + +void clock_reset_x(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_x_set), CLK_RST_REG(rst_dev_x_clr), + bit); +} + +void clock_reset_y(u32 bit) +{ + clock_reset_dev(CLK_RST_REG(rst_dev_y_set), CLK_RST_REG(rst_dev_y_clr), + bit); +} + +/* Enable/unreset all audio toys under AHUB */ +void clock_enable_audio(void) +{ + /* + * As per NVIDIA hardware team, we need to take ALL audio devices + * connected to AHUB (AHUB, APB2APE, I2S, SPDIF, etc.) out of reset + * and clock-enabled, otherwise reading AHUB devices (in our case, + * I2S/APBIF/AUDIO<XBAR>) will hang. + */ + clock_enable_clear_reset(CLK_L_I2S1 | CLK_L_I2S2 | CLK_L_I2S3 | CLK_L_SPDIF, + 0, 0, + CLK_V_I2S4 | CLK_V_I2S5 | CLK_V_AHUB | CLK_V_APB2APE, + 0, 0, 0); +} diff --git a/src/soc/nvidia/tegra210/cpu.c b/src/soc/nvidia/tegra210/cpu.c new file mode 100644 index 0000000000..6e51a8cdae --- /dev/null +++ b/src/soc/nvidia/tegra210/cpu.c @@ -0,0 +1,79 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/cpu.h> +#include <soc/secure_boot.h> + +static void enable_core_clocks(int cpu) +{ + const uint32_t cpu_clocks[CONFIG_MAX_CPUS] = { + [0] = CRC_RST_CPUG_CLR_CPU0 | CRC_RST_CPUG_CLR_DBG0 | + CRC_RST_CPUG_CLR_CORE0 | CRC_RST_CPUG_CLR_CX0, + [1] = CRC_RST_CPUG_CLR_CPU1 | CRC_RST_CPUG_CLR_DBG1 | + CRC_RST_CPUG_CLR_CORE1 | CRC_RST_CPUG_CLR_CX1, + [2] = CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_DBG2 | + CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CX2, + [3] = CRC_RST_CPUG_CLR_CPU3 | CRC_RST_CPUG_CLR_DBG3 | + CRC_RST_CPUG_CLR_CORE3 | CRC_RST_CPUG_CLR_CX3, + }; + + assert (cpu < CONFIG_MAX_CPUS); + + /* Clear reset of CPU components. */ + write32(CLK_RST_REG(rst_cpug_cmplx_clr), cpu_clocks[cpu]); +} + +void cpu_prepare_startup(void *entry_64) +{ + struct tegra_secure_boot *sb = + (struct tegra_secure_boot *)TEGRA_SB_BASE; + + /* + * T210 TRM, section 12.4.4.2: "SB_AA64_RESET_LOW_0[0:0] is used to + * decide between CPU boot up in AARCH32 (=0) or AARCH64 (=1) mode. + * This bit .. is sampled only during 'cold reset of CPU'. Before the + * CPU is powered up, the CPU reset vector is loaded in + * EVP_CPU_REST_VECTOR_0 for 32-bit boot mode .... However, the CPU + * decides to boot in 32-/64-bit mode based on + * SB_AA64_RESET_LOW_0[0:0]. If this bit is set (=1), the CPU boots in + * 64-bit mode using SB_AA64_RESET_* as the reset address. If this bit + * is clear (=0), CPU boots in 32-bit mode using EVP_CPU_RESET_VECTOR." + */ + + write32(&sb->sb_aa64_reset_low, (uintptr_t)entry_64); + setbits_le32(&sb->sb_aa64_reset_low, 1); + write32(&sb->sb_aa64_reset_high, 0); +} + +void start_cpu_silent(int cpu, void *entry_64) +{ + cpu_prepare_startup(entry_64); + enable_core_clocks(cpu); +} + +void start_cpu(int cpu, void *entry_64) +{ + printk(BIOS_DEBUG, "Starting CPU%d @ %p.\n", cpu, entry_64); + start_cpu_silent(cpu, entry_64); +} diff --git a/src/soc/nvidia/tegra210/cpu_lib.S b/src/soc/nvidia/tegra210/cpu_lib.S new file mode 100644 index 0000000000..abfffb172b --- /dev/null +++ b/src/soc/nvidia/tegra210/cpu_lib.S @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + + +.text +.global smp_processor_id +smp_processor_id: + /* Core 0 and 1 are encoded in the Aff0 (7:0) field of MPIDR_EL1. */ + mrs x0, mpidr_el1 + uxtb w0, w0 + ret diff --git a/src/soc/nvidia/tegra210/dc.c b/src/soc/nvidia/tegra210/dc.c new file mode 100644 index 0000000000..f3beece7d8 --- /dev/null +++ b/src/soc/nvidia/tegra210/dc.c @@ -0,0 +1,249 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <stdlib.h> +#include <edid.h> +#include <device/device.h> +#include <soc/nvidia/tegra/dc.h> +#include "chip.h" +#include <soc/display.h> + +int dump = 0; +unsigned long READL(void * p) +{ + unsigned long value; + + /* + * In case of hard hung on readl(p), we can set dump > 1 to print out + * the address accessed. + */ + if (dump > 1) + printk(BIOS_SPEW, "readl %p\n", p); + + value = read32(p); + if (dump) + printk(BIOS_SPEW, "readl %p %08lx\n", p, value); + return value; +} + +void WRITEL(unsigned long value, void * p) +{ + if (dump) + printk(BIOS_SPEW, "writel %p %08lx\n", p, value); + write32(p, value); +} + +/* return in 1000ths of a Hertz */ +static int tegra_calc_refresh(const struct soc_nvidia_tegra210_config *config) +{ + int refresh; + int h_total = htotal(config); + int v_total = vtotal(config); + int pclk = config->pixel_clock; + + if (!pclk || !h_total || !v_total) + return 0; + refresh = pclk / h_total; + refresh *= 1000; + refresh /= v_total; + return refresh; +} + +static void print_mode(const struct soc_nvidia_tegra210_config *config) +{ + if (config) { + int refresh = tegra_calc_refresh(config); + printk(BIOS_ERR, + "Panel Mode: %dx%d@%d.%03uHz pclk=%d\n", + config->xres, config->yres, + refresh / 1000, refresh % 1000, + config->pixel_clock); + } +} + +int update_display_mode(struct display_controller *disp_ctrl, + struct soc_nvidia_tegra210_config *config) +{ + print_mode(config); + + printk(BIOS_ERR, "config: xres:yres: %d x %d\n ", + config->xres, config->yres); + printk(BIOS_ERR, " href_sync:vref_sync: %d x %d\n ", + config->href_to_sync, config->vref_to_sync); + printk(BIOS_ERR, " hsyn_width:vsyn_width: %d x %d\n ", + config->hsync_width, config->vsync_width); + printk(BIOS_ERR, " hfnt_porch:vfnt_porch: %d x %d\n ", + config->hfront_porch, config->vfront_porch); + printk(BIOS_ERR, " hbk_porch:vbk_porch: %d x %d\n ", + config->hback_porch, config->vback_porch); + + WRITEL(0x0, &disp_ctrl->disp.disp_timing_opt); + WRITEL(0x0, &disp_ctrl->disp.disp_color_ctrl); + + /* select win opt */ + WRITEL(config->win_opt, &disp_ctrl->disp.disp_win_opt); + + WRITEL(config->vref_to_sync << 16 | config->href_to_sync, + &disp_ctrl->disp.ref_to_sync); + + WRITEL(config->vsync_width << 16 | config->hsync_width, + &disp_ctrl->disp.sync_width); + + + WRITEL((config->vback_porch << 16) | config->hback_porch, + &disp_ctrl->disp.back_porch); + + WRITEL((config->vfront_porch << 16) | config->hfront_porch, + &disp_ctrl->disp.front_porch); + + WRITEL(config->xres | (config->yres << 16), + &disp_ctrl->disp.disp_active); + + /* + * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv. + * + * default: Set both shift_clk_div and pixel_clock_div to 1 + */ + update_display_shift_clock_divider(disp_ctrl, SHIFT_CLK_DIVIDER(1)); + + return 0; +} + +void update_display_shift_clock_divider(struct display_controller *disp_ctrl, + u32 shift_clock_div) +{ + WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) | + (shift_clock_div & 0xff) << SHIFT_CLK_DIVIDER_SHIFT, + &disp_ctrl->disp.disp_clk_ctrl); + printk(BIOS_DEBUG, "%s: ShiftClockDiv=%u\n", + __func__, shift_clock_div); +} + +/* + * update_window: + * set up window registers and activate window except two: + * frame buffer base address register (WINBUF_START_ADDR) and + * display enable register (_DISP_DISP_WIN_OPTIONS). This is + * becasue framebuffer is not available until payload stage. + */ +void update_window(const struct soc_nvidia_tegra210_config *config) +{ + struct display_controller *disp_ctrl = + (void *)config->display_controller; + u32 val; + + WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); + + WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size); + + WRITEL(((config->display_yres << 16) | + (config->display_xres * + config->framebuffer_bits_per_pixel / 8)), + &disp_ctrl->win.prescaled_size); + + val = ALIGN_UP((config->display_xres * + config->framebuffer_bits_per_pixel / 8), 64); + WRITEL(val, &disp_ctrl->win.line_stride); + + WRITEL(config->color_depth, &disp_ctrl->win.color_depth); + WRITEL(COLOR_BLACK, &disp_ctrl->disp.blend_background_color); + + WRITEL(((DDA_INC(config->display_yres, config->yres) << 16) | + DDA_INC(config->display_xres, config->xres)), + &disp_ctrl->win.dda_increment); + + WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd); + + WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); + + WRITEL(0, &disp_ctrl->win.buffer_addr_mode); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + WRITEL(val, &disp_ctrl->cmd.disp_pow_ctrl); + + val = GENERAL_UPDATE | WIN_A_UPDATE; + WRITEL(val, &disp_ctrl->cmd.state_ctrl); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + WRITEL(val, &disp_ctrl->cmd.state_ctrl); +} + +int tegra_dc_init(struct display_controller *disp_ctrl) +{ + /* do not accept interrupts during initialization */ + WRITEL(0x00000000, &disp_ctrl->cmd.int_mask); + WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, + &disp_ctrl->cmd.state_access); + WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header); + WRITEL(0x00000000, &disp_ctrl->win.win_opt); + WRITEL(0x00000000, &disp_ctrl->win.byte_swap); + WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl); + + WRITEL(0x00000000, &disp_ctrl->win.pos); + WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda); + WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda); + WRITEL(0x00000000, &disp_ctrl->win.dda_increment); + WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl); + + WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl); + WRITEL(0x00000000, &disp_ctrl->win.blend_match_select); + WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select); + WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit); + + WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi); + WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset); + WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset); + + WRITEL(0x00000000, &disp_ctrl->com.crc_checksum); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]); + WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]); + WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0); + + return 0; +} + +/* + * Save mode to cb tables + */ +void pass_mode_info_to_payload( + struct soc_nvidia_tegra210_config *config) +{ + struct edid edid; + /* Align bytes_per_line to 64 bytes as required by dc */ + edid.bytes_per_line = ALIGN_UP((config->display_xres * + config->framebuffer_bits_per_pixel / 8), 64); + edid.x_resolution = edid.bytes_per_line / + (config->framebuffer_bits_per_pixel / 8); + edid.y_resolution = config->display_yres; + edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel; + + printk(BIOS_INFO, "%s: bytes_per_line: %d, bits_per_pixel: %d\n " + " x_res x y_res: %d x %d, size: %d\n", + __func__, edid.bytes_per_line, + edid.framebuffer_bits_per_pixel, + edid.x_resolution, edid.y_resolution, + (edid.bytes_per_line * edid.y_resolution)); + + set_vbe_mode_info_valid(&edid, 0); +} diff --git a/src/soc/nvidia/tegra210/dma.c b/src/soc/nvidia/tegra210/dma.c new file mode 100644 index 0000000000..9f04e9777f --- /dev/null +++ b/src/soc/nvidia/tegra210/dma.c @@ -0,0 +1,147 @@ +/* + * (C) Copyright 2010,2011 + * NVIDIA Corporation <www.nvidia.com> + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 <arch/io.h> +#include <console/console.h> +#include <inttypes.h> +#include <soc/addressmap.h> +#include <soc/dma.h> +#include <stddef.h> +#include <stdlib.h> + +struct apb_dma * const apb_dma = (struct apb_dma *)TEGRA_APB_DMA_BASE; + +#define APB_DMA_OFFSET(n) \ + (struct apb_dma_channel_regs *)(TEGRA_APB_DMA_BASE + n) +struct apb_dma_channel apb_dma_channels[] = { + { .num = 0, .regs = APB_DMA_OFFSET(0x1000) }, + { .num = 1, .regs = APB_DMA_OFFSET(0x1040) }, + { .num = 2, .regs = APB_DMA_OFFSET(0x1080) }, + { .num = 3, .regs = APB_DMA_OFFSET(0x10c0) }, + { .num = 4, .regs = APB_DMA_OFFSET(0x1100) }, + { .num = 5, .regs = APB_DMA_OFFSET(0x1140) }, + { .num = 6, .regs = APB_DMA_OFFSET(0x1180) }, + { .num = 7, .regs = APB_DMA_OFFSET(0x11c0) }, + { .num = 8, .regs = APB_DMA_OFFSET(0x1200) }, + { .num = 9, .regs = APB_DMA_OFFSET(0x1240) }, + { .num = 10, .regs = APB_DMA_OFFSET(0x1280) }, + { .num = 11, .regs = APB_DMA_OFFSET(0x12c0) }, + { .num = 12, .regs = APB_DMA_OFFSET(0x1300) }, + { .num = 13, .regs = APB_DMA_OFFSET(0x1340) }, + { .num = 14, .regs = APB_DMA_OFFSET(0x1380) }, + { .num = 15, .regs = APB_DMA_OFFSET(0x13c0) }, + { .num = 16, .regs = APB_DMA_OFFSET(0x1400) }, + { .num = 17, .regs = APB_DMA_OFFSET(0x1440) }, + { .num = 18, .regs = APB_DMA_OFFSET(0x1480) }, + { .num = 19, .regs = APB_DMA_OFFSET(0x14c0) }, + { .num = 20, .regs = APB_DMA_OFFSET(0x1500) }, + { .num = 21, .regs = APB_DMA_OFFSET(0x1540) }, + { .num = 22, .regs = APB_DMA_OFFSET(0x1580) }, + { .num = 23, .regs = APB_DMA_OFFSET(0x15c0) }, + { .num = 24, .regs = APB_DMA_OFFSET(0x1600) }, + { .num = 25, .regs = APB_DMA_OFFSET(0x1640) }, + { .num = 26, .regs = APB_DMA_OFFSET(0x1680) }, + { .num = 27, .regs = APB_DMA_OFFSET(0x16c0) }, + { .num = 28, .regs = APB_DMA_OFFSET(0x1700) }, + { .num = 29, .regs = APB_DMA_OFFSET(0x1740) }, + { .num = 30, .regs = APB_DMA_OFFSET(0x1780) }, + { .num = 31, .regs = APB_DMA_OFFSET(0x17c0) }, +}; + +int dma_busy(struct apb_dma_channel * const channel) +{ + /* + * In continuous mode, the BSY_n bit in APB_DMA_STATUS and + * BSY in APBDMACHAN_CHANNEL_n_STA_0 will remain set as '1' so long + * as the channel is enabled. So for this function we'll use the + * DMA_ACTIVITY bit. + */ + return read32(&channel->regs->sta) & APB_STA_DMA_ACTIVITY ? 1 : 0; +} +/* claim a DMA channel */ +struct apb_dma_channel * const dma_claim(void) +{ + int i; + struct apb_dma_channel_regs *regs = NULL; + + /* + * Set global enable bit, otherwise register access to channel + * DMA registers will not be possible. + */ + setbits_le32(&apb_dma->command, APB_COMMAND_GEN); + + for (i = 0; i < ARRAY_SIZE(apb_dma_channels); i++) { + regs = apb_dma_channels[i].regs; + + if (!apb_dma_channels[i].in_use) { + u32 status = read32(®s->sta); + if (status & (1 << i)) { + /* FIXME: should this be fatal? */ + printk(BIOS_DEBUG, "%s: DMA channel %d busy?\n", + __func__, i); + } + break; + } + } + + if (i == ARRAY_SIZE(apb_dma_channels)) + return NULL; + + apb_dma_channels[i].in_use = 1; + return &apb_dma_channels[i]; +} + +/* release a DMA channel */ +void dma_release(struct apb_dma_channel * const channel) +{ + int i; + + /* FIXME: make this "thread" friendly */ + while (dma_busy(channel)) + ; + + channel->in_use = 0; + + /* clear the global enable bit if no channels are in use */ + for (i = 0; i < ARRAY_SIZE(apb_dma_channels); i++) { + if (apb_dma_channels[i].in_use) + return; + } + + clrbits_le32(&apb_dma->command, APB_COMMAND_GEN); +} + +int dma_start(struct apb_dma_channel * const channel) +{ + struct apb_dma_channel_regs *regs = channel->regs; + + /* Set ENB bit for this channel */ + setbits_le32(®s->csr, APB_CSR_ENB); + + return 0; +} + +int dma_stop(struct apb_dma_channel * const channel) +{ + struct apb_dma_channel_regs *regs = channel->regs; + + /* Clear ENB bit for this channel */ + clrbits_le32(®s->csr, APB_CSR_ENB); + + return 0; +} diff --git a/src/soc/nvidia/tegra210/dp.c b/src/soc/nvidia/tegra210/dp.c new file mode 100644 index 0000000000..ea6dbca74c --- /dev/null +++ b/src/soc/nvidia/tegra210/dp.c @@ -0,0 +1,1663 @@ +/* + * drivers/video/tegra/dc/dp.c + * + * Copyright (c) 2011-2015, NVIDIA Corporation. + * Copyright 2014 Google Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include <arch/io.h> +#include <console/console.h> +#include <device/device.h> +#include <device/i2c.h> +#include <edid.h> +#include <stdlib.h> +#include <string.h> +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/display.h> +#include <soc/nvidia/tegra/i2c.h> +#include <soc/nvidia/tegra/dc.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/nvidia/tegra/pwm.h> +#include <soc/nvidia/tegra/displayport.h> +#include <soc/sor.h> +#include "chip.h" + +#define DO_FAST_LINK_TRAINING 0 + +struct tegra_dc dc_data; + +enum { + DP_LT_SUCCESS = 0, + DP_LT_FAILED = -1, +}; + +struct tegra_dc_dp_data dp_data; + +static inline u32 tegra_dpaux_readl(struct tegra_dc_dp_data *dp, u32 reg) +{ + void *addr = dp->aux_base + (u32) (reg << 2); + u32 reg_val = READL(addr); + return reg_val; +} + +static inline void tegra_dpaux_writel(struct tegra_dc_dp_data *dp, + u32 reg, u32 val) +{ + void *addr = dp->aux_base + (u32) (reg << 2); + WRITEL(val, addr); +} + +static inline u32 tegra_dc_dpaux_poll_register(struct tegra_dc_dp_data *dp, + u32 reg, u32 mask, u32 exp_val, + u32 poll_interval_us, + u32 timeout_us) +{ + u32 reg_val = 0; + u32 temp = timeout_us; + + do { + udelay(poll_interval_us); + reg_val = tegra_dpaux_readl(dp, reg); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + } while ((reg_val & mask) != exp_val); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + printk(BIOS_ERR, + "dpaux_poll_register 0x%x: timeout: " + "(reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", + reg, reg_val, mask, exp_val); + return temp; +} + +static inline int tegra_dpaux_wait_transaction(struct tegra_dc_dp_data *dp) +{ + /* According to DP spec, each aux transaction needs to finish + within 40ms. */ + if (tegra_dc_dpaux_poll_register(dp, DPAUX_DP_AUXCTL, + DPAUX_DP_AUXCTL_TRANSACTREQ_MASK, + DPAUX_DP_AUXCTL_TRANSACTREQ_DONE, + 100, DP_AUX_TIMEOUT_MS * 1000) != 0) { + printk(BIOS_INFO, "dp: DPAUX transaction timeout\n"); + return -1; + } + return 0; +} + +static int tegra_dc_dpaux_write_chunk(struct tegra_dc_dp_data *dp, u32 cmd, + u32 addr, u8 *data, u32 *size, + u32 *aux_stat) +{ + int i; + u32 reg_val; + u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; + u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + u32 temp_data; + + if (*size > DP_AUX_MAX_BYTES) + return -1; /* only write one chunk of data */ + + /* Make sure the command is write command */ + switch (cmd) { + case DPAUX_DP_AUXCTL_CMD_I2CWR: + case DPAUX_DP_AUXCTL_CMD_MOTWR: + case DPAUX_DP_AUXCTL_CMD_AUXWR: + break; + default: + printk(BIOS_ERR, "dp: aux write cmd 0x%x is invalid\n", cmd); + return -1; + } + + tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); + for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) { + memcpy(&temp_data, data, 4); + tegra_dpaux_writel(dp, DPAUX_DP_AUXDATA_WRITE_W(i), temp_data); + data += 4; + } + + reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); + reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; + reg_val |= cmd; + reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; + reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); + + while ((timeout_retries > 0) && (defer_retries > 0)) { + if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || + (defer_retries != DP_AUX_DEFER_MAX_TRIES)) + udelay(1); + + reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; + tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); + + if (tegra_dpaux_wait_transaction(dp)) + printk(BIOS_ERR, "dp: aux write transaction timeout\n"); + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + + if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { + if (timeout_retries-- > 0) { + printk(BIOS_INFO, "dp: aux write retry (0x%x)" + " -- %d\n", + *aux_stat, timeout_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + printk(BIOS_ERR, "dp: aux write got error" + " (0x%x)\n", *aux_stat); + return -1; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || + (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { + if (defer_retries-- > 0) { + printk(BIOS_INFO, "dp: aux write defer (0x%x)" + " -- %d\n", *aux_stat, defer_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + printk(BIOS_ERR, "dp: aux write defer exceeds" + " max retries (0x%x)\n", *aux_stat); + return -1; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == + DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { + *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); + return 0; + } else { + printk(BIOS_ERR, "dp: aux write failed (0x%x)\n", + *aux_stat); + return -1; + } + } + /* Should never come to here */ + return -1; +} + +static int tegra_dc_dpaux_read_chunk(struct tegra_dc_dp_data *dp, u32 cmd, + u32 addr, u8 *data, u32 *size, + u32 *aux_stat) +{ + u32 reg_val; + u32 timeout_retries = DP_AUX_TIMEOUT_MAX_TRIES; + u32 defer_retries = DP_AUX_DEFER_MAX_TRIES; + + if (*size > DP_AUX_MAX_BYTES) + return -1; /* only read one chunk */ + + /* Check to make sure the command is read command */ + switch (cmd) { + case DPAUX_DP_AUXCTL_CMD_I2CRD: + case DPAUX_DP_AUXCTL_CMD_I2CREQWSTAT: + case DPAUX_DP_AUXCTL_CMD_MOTRD: + case DPAUX_DP_AUXCTL_CMD_AUXRD: + break; + default: + printk(BIOS_ERR, "dp: aux read cmd 0x%x is invalid\n", cmd); + return -1; + } + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + if (!(*aux_stat & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { + printk(BIOS_SPEW, "dp: HPD is not detected\n"); + return -1; + } + + tegra_dpaux_writel(dp, DPAUX_DP_AUXADDR, addr); + + reg_val = tegra_dpaux_readl(dp, DPAUX_DP_AUXCTL); + reg_val &= ~DPAUX_DP_AUXCTL_CMD_MASK; + reg_val |= cmd; + reg_val &= ~DPAUX_DP_AUXCTL_CMDLEN_FIELD; + reg_val |= ((*size - 1) << DPAUX_DP_AUXCTL_CMDLEN_SHIFT); + while ((timeout_retries > 0) && (defer_retries > 0)) { + if ((timeout_retries != DP_AUX_TIMEOUT_MAX_TRIES) || + (defer_retries != DP_AUX_DEFER_MAX_TRIES)) + udelay(DP_DPCP_RETRY_SLEEP_NS * 2); + + reg_val |= DPAUX_DP_AUXCTL_TRANSACTREQ_PENDING; + tegra_dpaux_writel(dp, DPAUX_DP_AUXCTL, reg_val); + + if (tegra_dpaux_wait_transaction(dp)) + printk(BIOS_INFO, "dp: aux read transaction timeout\n"); + + *aux_stat = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + + if ((*aux_stat & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_RX_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR_PENDING) || + (*aux_stat & DPAUX_DP_AUXSTAT_NO_STOP_ERROR_PENDING)) { + if (timeout_retries-- > 0) { + printk(BIOS_INFO, "dp: aux read retry (0x%x)" + " -- %d\n", *aux_stat, + timeout_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; /* retry */ + } else { + printk(BIOS_ERR, "dp: aux read got error" + " (0x%x)\n", *aux_stat); + return -1; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_I2CDEFER) || + (*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_DEFER)) { + if (defer_retries-- > 0) { + printk(BIOS_INFO, "dp: aux read defer (0x%x)" + " -- %d\n", *aux_stat, defer_retries); + /* clear the error bits */ + tegra_dpaux_writel(dp, DPAUX_DP_AUXSTAT, + *aux_stat); + continue; + } else { + printk(BIOS_INFO, "dp: aux read defer exceeds" + " max retries (0x%x)\n", *aux_stat); + return -1; + } + } + + if ((*aux_stat & DPAUX_DP_AUXSTAT_REPLYTYPE_MASK) == + DPAUX_DP_AUXSTAT_REPLYTYPE_ACK) { + int i; + u32 temp_data[4]; + + for (i = 0; i < DP_AUX_MAX_BYTES / 4; ++i) + temp_data[i] = tegra_dpaux_readl(dp, + DPAUX_DP_AUXDATA_READ_W(i)); + + *size = ((*aux_stat) & DPAUX_DP_AUXSTAT_REPLY_M_MASK); + memcpy(data, temp_data, *size); + + return 0; + } else { + printk(BIOS_ERR, "dp: aux read failed (0x%x\n", + *aux_stat); + return -1; + } + } + /* Should never come to here */ + printk(BIOS_ERR, "%s: can't\n", __func__); + return -1; +} + +#if DO_FAST_LINK_TRAINING +static int tegra_dc_dpaux_read(struct tegra_dc_dp_data *dp, u32 cmd, u32 addr, + u8 *data, u32 *size, u32 *aux_stat) +{ + u32 finished = 0; + u32 cur_size; + int ret = 0; + + do { + cur_size = *size - finished; + if (cur_size > DP_AUX_MAX_BYTES) + cur_size = DP_AUX_MAX_BYTES; + + ret = tegra_dc_dpaux_read_chunk(dp, cmd, addr, + data, &cur_size, aux_stat); + if (ret) + break; + + /* cur_size should be the real size returned */ + addr += cur_size; + data += cur_size; + finished += cur_size; + + } while (*size > finished); + + *size = finished; + return ret; +} +#endif /* DO_FAST_LINK_TRAINING */ + +static int tegra_dc_dp_dpcd_read(struct tegra_dc_dp_data *dp, u32 cmd, + u8 *data_ptr) +{ + u32 size = 1; + u32 status = 0; + int ret; + + ret = tegra_dc_dpaux_read_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + cmd, data_ptr, &size, &status); + if (ret) + printk(BIOS_ERR, + "dp: Failed to read DPCD data. CMD 0x%x, Status 0x%x\n", + cmd, status); + + return ret; +} + +static int tegra_dc_dp_dpcd_write(struct tegra_dc_dp_data *dp, u32 cmd, + u8 data) +{ + u32 size = 1; + u32 status = 0; + int ret; + + ret = tegra_dc_dpaux_write_chunk(dp, DPAUX_DP_AUXCTL_CMD_AUXWR, + cmd, &data, &size, &status); + if (ret) + printk(BIOS_ERR, + "dp: Failed to write DPCD data. CMD 0x%x, Status 0x%x\n", + cmd, status); + return ret; +} + +static int tegra_dc_i2c_aux_read(struct tegra_dc_dp_data *dp, u32 i2c_addr, + u8 addr, u8 *data, u32 *size, u32 *aux_stat) +{ + u32 finished = 0; + int ret = 0; + + do { + u32 cur_size = MIN(DP_AUX_MAX_BYTES, *size - finished); + + u32 len = 1; + ret = tegra_dc_dpaux_write_chunk( + dp, DPAUX_DP_AUXCTL_CMD_MOTWR, i2c_addr, + &addr, &len, aux_stat); + if (ret) { + printk(BIOS_ERR, "%s: error sending address to read.\n", + __func__); + break; + } + + ret = tegra_dc_dpaux_read_chunk( + dp, DPAUX_DP_AUXCTL_CMD_I2CRD, i2c_addr, + data, &cur_size, aux_stat); + if (ret) { + printk(BIOS_ERR, "%s: error reading data.\n", __func__); + break; + } + + /* cur_size should be the real size returned */ + addr += cur_size; + data += cur_size; + finished += cur_size; + } while (*size > finished); + + *size = finished; + return ret; +} + +static void tegra_dc_dpaux_enable(struct tegra_dc_dp_data *dp) +{ + /* clear interrupt */ + tegra_dpaux_writel(dp, DPAUX_INTR_AUX, 0xffffffff); + /* do not enable interrupt for now. Enable them when Isr in place */ + tegra_dpaux_writel(dp, DPAUX_INTR_EN_AUX, 0x0); + + tegra_dpaux_writel(dp, DPAUX_HYBRID_PADCTL, + DPAUX_HYBRID_PADCTL_AUX_DRVZ_OHM_50 | + DPAUX_HYBRID_PADCTL_AUX_CMH_V0_70 | + 0x18 << DPAUX_HYBRID_PADCTL_AUX_DRVI_SHIFT | + DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV_ENABLE); + + tegra_dpaux_writel(dp, DPAUX_HYBRID_SPARE, + DPAUX_HYBRID_SPARE_PAD_PWR_POWERUP); +} + +static void tegra_dc_dp_dump_link_cfg(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + printk(BIOS_INFO, "DP config: cfg_name " + "cfg_value\n"); + printk(BIOS_INFO, " Lane Count %d\n", + link_cfg->max_lane_count); + printk(BIOS_INFO, " SupportEnhancedFraming %s\n", + link_cfg->support_enhanced_framing ? "Y" : "N"); + printk(BIOS_INFO, " Bandwidth %d\n", + link_cfg->max_link_bw); + printk(BIOS_INFO, " bpp %d\n", + link_cfg->bits_per_pixel); + printk(BIOS_INFO, " EnhancedFraming %s\n", + link_cfg->enhanced_framing ? "Y" : "N"); + printk(BIOS_INFO, " Scramble_enabled %s\n", + link_cfg->scramble_ena ? "Y" : "N"); + printk(BIOS_INFO, " LinkBW %d\n", + link_cfg->link_bw); + printk(BIOS_INFO, " lane_count %d\n", + link_cfg->lane_count); + printk(BIOS_INFO, " activespolarity %d\n", + link_cfg->activepolarity); + printk(BIOS_INFO, " active_count %d\n", + link_cfg->active_count); + printk(BIOS_INFO, " tu_size %d\n", + link_cfg->tu_size); + printk(BIOS_INFO, " active_frac %d\n", + link_cfg->active_frac); + printk(BIOS_INFO, " watermark %d\n", + link_cfg->watermark); + printk(BIOS_INFO, " hblank_sym %d\n", + link_cfg->hblank_sym); + printk(BIOS_INFO, " vblank_sym %d\n", + link_cfg->vblank_sym); +} + +static int _tegra_dp_lower_link_config(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *link_cfg) +{ + + switch (link_cfg->link_bw) { + case SOR_LINK_SPEED_G1_62: + if (link_cfg->max_link_bw > SOR_LINK_SPEED_G1_62) + link_cfg->link_bw = SOR_LINK_SPEED_G2_7; + link_cfg->lane_count /= 2; + break; + case SOR_LINK_SPEED_G2_7: + link_cfg->link_bw = SOR_LINK_SPEED_G1_62; + break; + case SOR_LINK_SPEED_G5_4: + if (link_cfg->lane_count == 1) { + link_cfg->link_bw = SOR_LINK_SPEED_G2_7; + link_cfg->lane_count = link_cfg->max_lane_count; + } else + link_cfg->lane_count /= 2; + break; + default: + printk(BIOS_ERR, "dp: Error link rate %d\n", link_cfg->link_bw); + return DP_LT_FAILED; + } + + return (link_cfg->lane_count > 0) ? DP_LT_SUCCESS : DP_LT_FAILED; +} + +/* Calcuate if given cfg can meet the mode request. */ +/* Return true if mode is possible, false otherwise. */ +static int tegra_dc_dp_calc_config(struct tegra_dc_dp_data *dp, + const struct soc_nvidia_tegra210_config *config, + struct tegra_dc_dp_link_config *link_cfg) +{ + const u32 link_rate = 27 * link_cfg->link_bw * 1000 * 1000; + const u64 f = 100000; /* precision factor */ + + u32 num_linkclk_line; /* Number of link clocks per line */ + u64 ratio_f; /* Ratio of incoming to outgoing data rate */ + + u64 frac_f; + u64 activesym_f; /* Activesym per TU */ + u64 activecount_f; + u32 activecount; + u32 activepolarity; + u64 approx_value_f; + u32 activefrac = 0; + u64 accumulated_error_f = 0; + u32 lowest_neg_activecount = 0; + u32 lowest_neg_activepolarity = 0; + u32 lowest_neg_tusize = 64; + u32 num_symbols_per_line; + u64 lowest_neg_activefrac = 0; + u64 lowest_neg_error_f = 64 * f; + u64 watermark_f; + + int i; + int neg; + + printk(BIOS_INFO, "dp: %s\n", __func__); + + if (!link_rate || !link_cfg->lane_count || !config->pixel_clock || + !link_cfg->bits_per_pixel) + return -1; + + if ((u64)config->pixel_clock * link_cfg->bits_per_pixel >= + (u64)link_rate * 8 * link_cfg->lane_count) + return -1; + + num_linkclk_line = (u32)((u64)link_rate * (u64)config->xres / + config->pixel_clock); + + ratio_f = (u64)config->pixel_clock * link_cfg->bits_per_pixel * f; + ratio_f /= 8; + ratio_f = (u64)(ratio_f / (link_rate * link_cfg->lane_count)); + + for (i = 64; i >= 32; --i) { + activesym_f = ratio_f * i; + activecount_f = (u64)(activesym_f / (u32)f) * f; + frac_f = activesym_f - activecount_f; + activecount = (u32)((u64)(activecount_f / (u32)f)); + + if (frac_f < (f / 2)) /* fraction < 0.5 */ + activepolarity = 0; + else { + activepolarity = 1; + frac_f = f - frac_f; + } + + if (frac_f != 0) { + frac_f = (u64)((f * f) / frac_f); /* 1/fraction */ + if (frac_f > (15 * f)) + activefrac = activepolarity ? 1 : 15; + else + activefrac = activepolarity ? + (u32)((u64)(frac_f / (u32)f)) + 1 : + (u32)((u64)(frac_f / (u32)f)); + } + + if (activefrac == 1) + activepolarity = 0; + + if (activepolarity == 1) + approx_value_f = activefrac ? (u64)( + (activecount_f + (activefrac * f - f) * f) / + (activefrac * f)) : + activecount_f + f; + else + approx_value_f = activefrac ? + activecount_f + (u64)(f / activefrac) : + activecount_f; + + if (activesym_f < approx_value_f) { + accumulated_error_f = num_linkclk_line * + (u64)((approx_value_f - activesym_f) / i); + neg = 1; + } else { + accumulated_error_f = num_linkclk_line * + (u64)((activesym_f - approx_value_f) / i); + neg = 0; + } + + if ((neg && (lowest_neg_error_f > accumulated_error_f)) || + (accumulated_error_f == 0)) { + lowest_neg_error_f = accumulated_error_f; + lowest_neg_tusize = i; + lowest_neg_activecount = activecount; + lowest_neg_activepolarity = activepolarity; + lowest_neg_activefrac = activefrac; + + if (accumulated_error_f == 0) + break; + } + } + + if (lowest_neg_activefrac == 0) { + link_cfg->activepolarity = 0; + link_cfg->active_count = lowest_neg_activepolarity ? + lowest_neg_activecount : lowest_neg_activecount - 1; + link_cfg->tu_size = lowest_neg_tusize; + link_cfg->active_frac = 1; + } else { + link_cfg->activepolarity = lowest_neg_activepolarity; + link_cfg->active_count = (u32)lowest_neg_activecount; + link_cfg->tu_size = lowest_neg_tusize; + link_cfg->active_frac = (u32)lowest_neg_activefrac; + } + + watermark_f = (u64)((ratio_f * link_cfg->tu_size * (f - ratio_f)) / f); + link_cfg->watermark = (u32)((u64)((watermark_f + lowest_neg_error_f) / + f)) + link_cfg->bits_per_pixel / 4 - 1; + num_symbols_per_line = (config->xres * link_cfg->bits_per_pixel) / + (8 * link_cfg->lane_count); + + if (link_cfg->watermark > 30) { + printk(BIOS_INFO, + "dp: sor setting: unable to get a good tusize, " + "force watermark to 30.\n"); + link_cfg->watermark = 30; + return -1; + } else if (link_cfg->watermark > num_symbols_per_line) { + printk(BIOS_INFO, + "dp: sor setting: force watermark to the number " + "of symbols in the line.\n"); + link_cfg->watermark = num_symbols_per_line; + return -1; + } + + /* Refer to dev_disp.ref for more information. */ + /* # symbols/hblank = ((SetRasterBlankEnd.X + SetRasterSize.Width - */ + /* SetRasterBlankStart.X - 7) * link_clk / pclk) */ + /* - 3 * enhanced_framing - Y */ + /* where Y = (# lanes == 4) 3 : (# lanes == 2) ? 6 : 12 */ + link_cfg->hblank_sym = (int)((u64)(((u64)(config->hback_porch + + config->hfront_porch + config->hsync_width - 7) * + link_rate) / config->pixel_clock)) - + 3 * link_cfg->enhanced_framing - + (12 / link_cfg->lane_count); + + if (link_cfg->hblank_sym < 0) + link_cfg->hblank_sym = 0; + + + /* Refer to dev_disp.ref for more information. */ + /* # symbols/vblank = ((SetRasterBlankStart.X - */ + /* SetRasterBlankEen.X - 25) * link_clk / pclk) */ + /* - Y - 1; */ + /* where Y = (# lanes == 4) 12 : (# lanes == 2) ? 21 : 39 */ + link_cfg->vblank_sym = (int)((u64)((u64)(config->xres - 25) + * link_rate / config->pixel_clock)) - (36 / + link_cfg->lane_count) - 4; + + if (link_cfg->vblank_sym < 0) + link_cfg->vblank_sym = 0; + + link_cfg->is_valid = 1; + tegra_dc_dp_dump_link_cfg(dp, link_cfg); + + return 0; +} + +static int tegra_dc_dp_init_max_link_cfg( + struct soc_nvidia_tegra210_config *config, + struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *link_cfg) +{ + u8 dpcd_data; + int ret; + + printk(BIOS_INFO, "dp: %s\n", __func__); + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_LANE_COUNT, + &dpcd_data)); + link_cfg->max_lane_count = dpcd_data & NV_DPCD_MAX_LANE_COUNT_MASK; + link_cfg->tps3_supported = (dpcd_data & + NV_DPCD_MAX_LANE_COUNT_TPS3_SUPPORTED_YES) ? 1 : 0; + + link_cfg->support_enhanced_framing = + (dpcd_data & NV_DPCD_MAX_LANE_COUNT_ENHANCED_FRAMING_YES) ? + 1 : 0; + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_DOWNSPREAD, + &dpcd_data)); + link_cfg->downspread = (dpcd_data & NV_DPCD_MAX_DOWNSPREAD_VAL_0_5_PCT) + ? 1 : 0; + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_TRAINING_AUX_RD_INTERVAL, + &link_cfg->aux_rd_interval)); + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_MAX_LINK_BANDWIDTH, + &link_cfg->max_link_bw)); + + link_cfg->bits_per_pixel = config->panel_bits_per_pixel; + + /* + * Set to a high value for link training and attach. + * Will be re-programmed when dp is enabled. + */ + link_cfg->drive_current = config->dp.drive_current; + link_cfg->preemphasis = config->dp.preemphasis; + link_cfg->postcursor = config->dp.postcursor; + + CHECK_RET(tegra_dc_dp_dpcd_read(dp, NV_DPCD_EDP_CONFIG_CAP, + &dpcd_data)); + link_cfg->alt_scramber_reset_cap = + (dpcd_data & NV_DPCD_EDP_CONFIG_CAP_ASC_RESET_YES) ? + 1 : 0; + link_cfg->only_enhanced_framing = + (dpcd_data & NV_DPCD_EDP_CONFIG_CAP_FRAMING_CHANGE_YES) ? + 1 : 0; + + link_cfg->lane_count = link_cfg->max_lane_count; + link_cfg->link_bw = link_cfg->max_link_bw; + link_cfg->enhanced_framing = link_cfg->support_enhanced_framing; + + tegra_dc_dp_calc_config(dp, config, link_cfg); + return 0; +} + +static int tegra_dc_dp_set_assr(struct tegra_dc_dp_data *dp, int ena) +{ + int ret; + + u8 dpcd_data = ena ? + NV_DPCD_EDP_CONFIG_SET_ASC_RESET_ENABLE : + NV_DPCD_EDP_CONFIG_SET_ASC_RESET_DISABLE; + + CHECK_RET(tegra_dc_dp_dpcd_write(dp, NV_DPCD_EDP_CONFIG_SET, + dpcd_data)); + + /* Also reset the scrambler to 0xfffe */ + tegra_dc_sor_set_internal_panel(&dp->sor, ena); + return 0; +} + +static int tegra_dp_set_link_bandwidth(struct tegra_dc_dp_data *dp, u8 link_bw) +{ + tegra_dc_sor_set_link_bandwidth(&dp->sor, link_bw); + + /* Sink side */ + return tegra_dc_dp_dpcd_write(dp, NV_DPCD_LINK_BANDWIDTH_SET, link_bw); +} + +static int tegra_dp_set_lane_count(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + u8 dpcd_data; + int ret; + + /* check if panel support enhanched_framing */ + dpcd_data = link_cfg->lane_count; + if (link_cfg->enhanced_framing) + dpcd_data |= NV_DPCD_LANE_COUNT_SET_ENHANCEDFRAMING_T; + CHECK_RET(tegra_dc_dp_dpcd_write(dp, NV_DPCD_LANE_COUNT_SET, + dpcd_data)); + + tegra_dc_sor_set_lane_count(&dp->sor, link_cfg->lane_count); + + /* Also power down lanes that will not be used */ + return 0; +} + +#if DO_FAST_LINK_TRAINING +static int tegra_dc_dp_link_trained(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + u32 lane; + u8 mask; + u8 data; + int ret; + + for (lane = 0; lane < link_cfg->lane_count; ++lane) { + CHECK_RET(tegra_dc_dp_dpcd_read(dp, (lane/2) ? + NV_DPCD_LANE2_3_STATUS : NV_DPCD_LANE0_1_STATUS, + &data)); + mask = (lane & 1) ? + NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES | + NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_YES | + NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_YES : + NV_DPCD_STATUS_LANEX_CR_DONE_YES | + NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_YES | + NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_YES; + if ((data & mask) != mask) + return -1; + } + return 0; +} +#endif /* DO_FAST_LINK_TRAINING */ + +static int tegra_dp_channel_eq_status(struct tegra_dc_dp_data *dp) +{ + u32 cnt; + u32 n_lanes = dp->link_cfg.lane_count; + u8 data; + u8 ce_done = 1; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + tegra_dc_dp_dpcd_read(dp, (NV_DPCD_LANE0_1_STATUS + cnt), + &data); + + if (n_lanes == 1) { + ce_done = + (data & (0x1 << NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) && + (data & (0x1 << NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)); + break; + } else + if (!(data & (0x1 << NV_DPCD_STATUS_LANEX_CHN_EQ_DONE_SHIFT)) || + !(data & (0x1 << NV_DPCD_STATUS_LANEX_SYMBOL_LOCKED_SHFIT)) || + !(data & (0x1 << NV_DPCD_STATUS_LANEXPLUS1_CHN_EQ_DONE_SHIFT)) || + !(data & (0x1 << NV_DPCD_STATUS_LANEXPLUS1_SYMBOL_LOCKED_SHIFT))) + return 0; + } + + if (ce_done) { + tegra_dc_dp_dpcd_read(dp, NV_DPCD_LANE_ALIGN_STATUS_UPDATED, + &data); + if (!(data & NV_DPCD_LANE_ALIGN_STATUS_UPDATED_DONE_YES)) + ce_done = 0; + } + + return ce_done; +} + +static u8 tegra_dp_clock_recovery_status(struct tegra_dc_dp_data *dp) +{ + u32 cnt; + u32 n_lanes = dp->link_cfg.lane_count; + u8 data_ptr; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + tegra_dc_dp_dpcd_read(dp, + (NV_DPCD_LANE0_1_STATUS + cnt), &data_ptr); + + if (n_lanes == 1) + return (data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) ? 1 : 0; + else if (!(data_ptr & NV_DPCD_STATUS_LANEX_CR_DONE_YES) || + !(data_ptr & (NV_DPCD_STATUS_LANEXPLUS1_CR_DONE_YES))) + return 0; + } + + return 1; +} + +static void tegra_dp_lt_adjust(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4], + u8 pc_supported) +{ + size_t cnt; + u8 data_ptr; + u32 n_lanes = dp->link_cfg.lane_count; + + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + tegra_dc_dp_dpcd_read(dp, + (NV_DPCD_LANE0_1_ADJUST_REQ + cnt), &data_ptr); + pe[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_PE_MASK) >> + NV_DPCD_ADJUST_REQ_LANEX_PE_SHIFT; + vs[2 * cnt] = (data_ptr & NV_DPCD_ADJUST_REQ_LANEX_DC_MASK) >> + NV_DPCD_ADJUST_REQ_LANEX_DC_SHIFT; + pe[1 + 2 * cnt] = + (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_MASK) >> + NV_DPCD_ADJUST_REQ_LANEXPLUS1_PE_SHIFT; + vs[1 + 2 * cnt] = + (data_ptr & NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_MASK) >> + NV_DPCD_ADJUST_REQ_LANEXPLUS1_DC_SHIFT; + } + if (pc_supported) { + tegra_dc_dp_dpcd_read(dp, + NV_DPCD_ADJUST_REQ_POST_CURSOR2, &data_ptr); + for (cnt = 0; cnt < n_lanes; cnt++) { + pc[cnt] = (data_ptr >> + NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_SHIFT(cnt)) & + NV_DPCD_ADJUST_REQ_POST_CURSOR2_LANE_MASK; + } + } +} + +static inline u32 tegra_dp_wait_aux_training(struct tegra_dc_dp_data *dp, + u8 is_clk_recovery) +{ + if (!dp->link_cfg.aux_rd_interval) + is_clk_recovery ? udelay(200) : + udelay(500); + else + mdelay(dp->link_cfg.aux_rd_interval * 4); + + return dp->link_cfg.aux_rd_interval; +} + +static void tegra_dp_tpg(struct tegra_dc_dp_data *dp, u32 tp, u32 n_lanes) +{ + u8 data = (tp == training_pattern_disabled) + ? (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_F) + : (tp | NV_DPCD_TRAINING_PATTERN_SET_SC_DISABLED_T); + + tegra_dc_sor_set_dp_linkctl(&dp->sor, 1, tp, &dp->link_cfg); + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, data); +} + +static int tegra_dp_link_config(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + u8 dpcd_data; + u32 retry; + + if (link_cfg->lane_count == 0) { + printk(BIOS_ERR, "dp: error: lane count is 0. " + "Can not set link config.\n"); + return DP_LT_FAILED; + } + + /* Set power state if it is not in normal level */ + if (tegra_dc_dp_dpcd_read(dp, NV_DPCD_SET_POWER, &dpcd_data)) + return DP_LT_FAILED; + + if (dpcd_data == NV_DPCD_SET_POWER_VAL_D3_PWRDWN) { + dpcd_data = NV_DPCD_SET_POWER_VAL_D0_NORMAL; + + /* DP spec requires 3 retries */ + for (retry = 3; retry > 0; --retry) { + if (tegra_dc_dp_dpcd_write(dp, NV_DPCD_SET_POWER, + dpcd_data)) + break; + if (retry == 1) { + printk(BIOS_ERR, "dp: Failed to set DP panel" + " power\n"); + return DP_LT_FAILED; + } + } + } + + /* Enable ASSR if possible */ + if (link_cfg->alt_scramber_reset_cap) + if (tegra_dc_dp_set_assr(dp, 1)) + return DP_LT_FAILED; + + if (tegra_dp_set_link_bandwidth(dp, link_cfg->link_bw)) { + printk(BIOS_ERR, "dp: Failed to set link bandwidth\n"); + return DP_LT_FAILED; + } + if (tegra_dp_set_lane_count(dp, link_cfg)) { + printk(BIOS_ERR, "dp: Failed to set lane count\n"); + return DP_LT_FAILED; + } + tegra_dc_sor_set_dp_linkctl(&dp->sor, 1, training_pattern_none, + link_cfg); + return DP_LT_SUCCESS; +} + +static int tegra_dp_lower_link_config(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *cfg) +{ + struct tegra_dc_dp_link_config tmp_cfg; + + tmp_cfg = dp->link_cfg; + cfg->is_valid = 0; + + if (_tegra_dp_lower_link_config(dp, cfg)) + goto fail; + + if (tegra_dc_dp_calc_config(dp, dp->dc->config, cfg)) + goto fail; + tegra_dp_link_config(dp, cfg); + + return DP_LT_SUCCESS; +fail: + dp->link_cfg = tmp_cfg; + tegra_dp_link_config(dp, &tmp_cfg); + return DP_LT_FAILED; +} + +static void tegra_dp_lt_config(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]) +{ + struct tegra_dc_sor_data *sor = &dp->sor; + u32 n_lanes = dp->link_cfg.lane_count; + u8 pc_supported = dp->link_cfg.tps3_supported; + u32 cnt; + u32 val; + + for (cnt = 0; cnt < n_lanes; cnt++) { + u32 mask = 0; + u32 pe_reg, vs_reg, pc_reg; + u32 shift = 0; + + switch (cnt) { + case 0: + mask = NV_SOR_PR_LANE2_DP_LANE0_MASK; + shift = NV_SOR_PR_LANE2_DP_LANE0_SHIFT; + break; + case 1: + mask = NV_SOR_PR_LANE1_DP_LANE1_MASK; + shift = NV_SOR_PR_LANE1_DP_LANE1_SHIFT; + break; + case 2: + mask = NV_SOR_PR_LANE0_DP_LANE2_MASK; + shift = NV_SOR_PR_LANE0_DP_LANE2_SHIFT; + break; + case 3: + mask = NV_SOR_PR_LANE3_DP_LANE3_MASK; + shift = NV_SOR_PR_LANE3_DP_LANE3_SHIFT; + break; + default: + printk(BIOS_ERR, + "dp: incorrect lane cnt\n"); + } + + pe_reg = tegra_dp_pe_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + vs_reg = tegra_dp_vs_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + pc_reg = tegra_dp_pc_regs[pc[cnt]][vs[cnt]][pe[cnt]]; + + tegra_dp_set_pe_vs_pc(sor, mask, pe_reg << shift, + vs_reg << shift, pc_reg << shift, pc_supported); + } + + tegra_dp_disable_tx_pu(&dp->sor); + udelay(20); + + for (cnt = 0; cnt < n_lanes; cnt++) { + u32 max_vs_flag = tegra_dp_is_max_vs(pe[cnt], vs[cnt]); + u32 max_pe_flag = tegra_dp_is_max_pe(pe[cnt], vs[cnt]); + + val = (vs[cnt] << NV_DPCD_TRAINING_LANEX_SET_DC_SHIFT) | + (max_vs_flag ? + NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_T : + NV_DPCD_TRAINING_LANEX_SET_DC_MAX_REACHED_F) | + (pe[cnt] << NV_DPCD_TRAINING_LANEX_SET_PE_SHIFT) | + (max_pe_flag ? + NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_T : + NV_DPCD_TRAINING_LANEX_SET_PE_MAX_REACHED_F); + tegra_dc_dp_dpcd_write(dp, + (NV_DPCD_TRAINING_LANE0_SET + cnt), val); + } + + if (pc_supported) { + for (cnt = 0; cnt < n_lanes / 2; cnt++) { + u32 max_pc_flag0 = tegra_dp_is_max_pc(pc[cnt]); + u32 max_pc_flag1 = tegra_dp_is_max_pc(pc[cnt + 1]); + val = (pc[cnt] << NV_DPCD_LANEX_SET2_PC2_SHIFT) | + (max_pc_flag0 ? + NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_T : + NV_DPCD_LANEX_SET2_PC2_MAX_REACHED_F) | + (pc[cnt + 1] << + NV_DPCD_LANEXPLUS1_SET2_PC2_SHIFT) | + (max_pc_flag1 ? + NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_T : + NV_DPCD_LANEXPLUS1_SET2_PC2_MAX_REACHED_F); + tegra_dc_dp_dpcd_write(dp, + (NV_DPCD_TRAINING_LANE0_1_SET2 + cnt), val); + } + } +} + +static int _tegra_dp_channel_eq(struct tegra_dc_dp_data *dp, u32 pe[4], + u32 vs[4], u32 pc[4], u8 pc_supported, + u32 n_lanes) +{ + u32 retry_cnt; + + for (retry_cnt = 0; retry_cnt < 4; retry_cnt++) { + if (retry_cnt) { + tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported); + tegra_dp_lt_config(dp, pe, vs, pc); + } + + tegra_dp_wait_aux_training(dp, 0); + + if (!tegra_dp_clock_recovery_status(dp)) { + printk(BIOS_ERR, "dp: CR failed in channel EQ" + " sequence!\n"); + break; + } + + if (tegra_dp_channel_eq_status(dp)) + return DP_LT_SUCCESS; + } + + return DP_LT_FAILED; +} + +static int tegra_dp_channel_eq(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]) +{ + u32 n_lanes = dp->link_cfg.lane_count; + u8 pc_supported = dp->link_cfg.tps3_supported; + int err; + u32 tp_src = training_pattern_2; + + if (pc_supported) + tp_src = training_pattern_3; + + tegra_dp_tpg(dp, tp_src, n_lanes); + + err = _tegra_dp_channel_eq(dp, pe, vs, pc, pc_supported, n_lanes); + + tegra_dp_tpg(dp, training_pattern_disabled, n_lanes); + + return err; +} + +static int _tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, u32 pe[4], + u32 vs[4], u32 pc[4], u8 pc_supported, + u32 n_lanes) +{ + u32 vs_temp[4]; + u32 retry_cnt = 0; + + do { + tegra_dp_lt_config(dp, pe, vs, pc); + tegra_dp_wait_aux_training(dp, 1); + + if (tegra_dp_clock_recovery_status(dp)) + return DP_LT_SUCCESS; + + memcpy(vs_temp, vs, sizeof(vs_temp)); + tegra_dp_lt_adjust(dp, pe, vs, pc, pc_supported); + + if (memcmp(vs_temp, vs, sizeof(vs_temp))) + retry_cnt = 0; + else + ++retry_cnt; + } while (retry_cnt < 5); + + return DP_LT_FAILED; +} + +static int tegra_dp_clk_recovery(struct tegra_dc_dp_data *dp, + u32 pe[4], u32 vs[4], u32 pc[4]) +{ + u32 n_lanes = dp->link_cfg.lane_count; + u8 pc_supported = dp->link_cfg.tps3_supported; + int err; + + tegra_dp_tpg(dp, training_pattern_1, n_lanes); + + err = _tegra_dp_clk_recovery(dp, pe, vs, pc, pc_supported, n_lanes); + if (err < 0) + tegra_dp_tpg(dp, training_pattern_disabled, n_lanes); + + return err; +} + +static int tegra_dc_dp_full_link_training(struct tegra_dc_dp_data *dp) +{ + struct tegra_dc_sor_data *sor = &dp->sor; + int err; + u32 pe[4], vs[4], pc[4]; + + printk(BIOS_INFO, "dp: %s\n", __func__); + tegra_sor_precharge_lanes(sor); + +retry_cr: + memset(pe, preEmphasis_Disabled, sizeof(pe)); + memset(vs, driveCurrent_Level0, sizeof(vs)); + memset(pc, postCursor2_Level0, sizeof(pc)); + + err = tegra_dp_clk_recovery(dp, pe, vs, pc); + if (err != DP_LT_SUCCESS) { + if (!tegra_dp_lower_link_config(dp, &dp->link_cfg)) + goto retry_cr; + + printk(BIOS_ERR, "dp: clk recovery failed\n"); + goto fail; + } + + err = tegra_dp_channel_eq(dp, pe, vs, pc); + if (err != DP_LT_SUCCESS) { + if (!tegra_dp_lower_link_config(dp, &dp->link_cfg)) + goto retry_cr; + + printk(BIOS_ERR, + "dp: channel equalization failed\n"); + goto fail; + } + + tegra_dc_dp_dump_link_cfg(dp, &dp->link_cfg); + + return 0; + +fail: + return err; +} + +/* + * All link training functions are ported from kernel dc driver. + * See more details at drivers/video/tegra/dc/dp.c + */ +#if DO_FAST_LINK_TRAINING +static int tegra_dc_dp_fast_link_training(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + struct tegra_dc_sor_data *sor = &dp->sor; + u8 link_bw; + u8 lane_count; + u16 data16; + u32 data32; + u32 size; + u32 status; + int j; + u32 mask = 0xffff >> ((4 - link_cfg->lane_count) * 4); + + + printk(BIOS_INFO, "dp: %s\n", __func__); + + tegra_dc_sor_set_lane_parm(sor, link_cfg); + tegra_dc_dp_dpcd_write(dp, NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET, + NV_DPCD_MAIN_LINK_CHANNEL_CODING_SET_ANSI_8B10B); + + /* Send TP1 */ + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_1, link_cfg); + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, + NV_DPCD_TRAINING_PATTERN_SET_TPS_TP1); + + for (j = 0; j < link_cfg->lane_count; ++j) + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_LANE0_SET + j, + 0x24); + udelay(520); + + size = sizeof(data16); + tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + NV_DPCD_LANE0_1_STATUS, (u8 *)&data16, &size, &status); + status = mask & 0x1111; + if ((data16 & status) != status) { + printk(BIOS_ERR, + "dp: Link training error for TP1 (%#x)\n", data16); + return -EFAULT; + } + + /* enable ASSR */ + tegra_dc_dp_set_assr(dp, link_cfg->scramble_ena); + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_3, link_cfg); + + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, + link_cfg->link_bw == 20 ? 0x23 : 0x22); + for (j = 0; j < link_cfg->lane_count; ++j) + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_LANE0_SET + j, + 0x24); + udelay(520); + + size = sizeof(data32); + tegra_dc_dpaux_read(dp, DPAUX_DP_AUXCTL_CMD_AUXRD, + NV_DPCD_LANE0_1_STATUS, (u8 *)&data32, &size, &status); + if ((data32 & mask) != (0x7777 & mask)) { + printk(BIOS_ERR, + "dp: Link training error for TP2/3 (0x%x)\n", data32); + return -EFAULT; + } + + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_disabled, + link_cfg); + tegra_dc_dp_dpcd_write(dp, NV_DPCD_TRAINING_PATTERN_SET, 0); + + if (tegra_dc_dp_link_trained(dp, link_cfg)) { + tegra_dc_sor_read_link_config(&dp->sor, &link_bw, + &lane_count); + printk(BIOS_ERR, + "Fast link trainging failed, link bw %d, lane # %d\n", + link_bw, lane_count); + return -EFAULT; + } + + printk(BIOS_INFO, + "Fast link trainging succeeded, link bw %d, lane %d\n", + link_cfg->link_bw, link_cfg->lane_count); + + return 0; +} +#endif /* DO_FAST_LINK_TRAINING */ + +static int tegra_dp_do_link_training(struct tegra_dc_dp_data *dp, + const struct tegra_dc_dp_link_config *link_cfg) +{ + u8 link_bw; + u8 lane_count; +#if DO_FAST_LINK_TRAINING + int ret; + + /* Now do the fast link training for eDP */ + ret = tegra_dc_dp_fast_link_training(dp, link_cfg); + if (ret) { + printk(BIOS_ERR, "dp: fast link training failed\n"); + + /* Try full link training then */ + if (tegra_dc_dp_full_link_training(dp)) { + printk(BIOS_ERR, "dp: full link training failed\n"); + return ret; + } + } else { + /* set to a known-good drive setting if fast link succeeded */ + tegra_dc_sor_set_voltage_swing(&dp->sor); + } +#else + if (tegra_dc_dp_full_link_training(dp)) { + printk(BIOS_ERR, "dp: full link training failed\n"); + return -EFAULT; + } +#endif + + /* Everything goes well, double check the link config */ + /* TODO: record edc/c2 data for debugging */ + tegra_dc_sor_read_link_config(&dp->sor, &link_bw, &lane_count); + + if ((link_cfg->link_bw == link_bw) && + (link_cfg->lane_count == lane_count)) + return 0; + else + return -EFAULT; +} + +static int tegra_dc_dp_explore_link_cfg(struct tegra_dc_dp_data *dp, + struct tegra_dc_dp_link_config *link_cfg, + const struct soc_nvidia_tegra210_config *config) +{ + struct tegra_dc_dp_link_config temp_cfg; + + if (!config->pixel_clock || !config->xres || !config->yres) { + printk(BIOS_ERR, + "dp: error mode configuration"); + return -EINVAL; + } + if (!link_cfg->max_link_bw || !link_cfg->max_lane_count) { + printk(BIOS_ERR, + "dp: error link configuration"); + return -EINVAL; + } + + link_cfg->is_valid = 0; + + memcpy(&temp_cfg, link_cfg, sizeof(temp_cfg)); + + temp_cfg.link_bw = temp_cfg.max_link_bw; + temp_cfg.lane_count = temp_cfg.max_lane_count; + + /* + * set to max link config + */ + if ((!tegra_dc_dp_calc_config(dp, config, &temp_cfg)) && + (!tegra_dp_link_config(dp, &temp_cfg)) && + (!tegra_dp_do_link_training(dp, &temp_cfg))) + /* the max link cfg is doable */ + memcpy(link_cfg, &temp_cfg, sizeof(temp_cfg)); + + return link_cfg->is_valid ? 0 : -EFAULT; +} + +static void tegra_dp_update_config(struct tegra_dc_dp_data *dp, + struct soc_nvidia_tegra210_config *config) +{ + struct edid edid; + u8 buf[128] = {0}; + u32 size = sizeof(buf), aux_stat = 0; + + printk(BIOS_ERR, "%s: enable r/w dump.\n", + __func__); + + tegra_dc_dpaux_enable(dp); + if (tegra_dc_i2c_aux_read(dp, TEGRA_EDID_I2C_ADDRESS, 0, buf, &size, + &aux_stat)) { + printk(BIOS_ERR, "%s: Failed to read EDID. Use defaults.\n", + __func__); + return; + } + + if (decode_edid(buf, sizeof(buf), &edid)) { + printk(BIOS_ERR, "%s: Failed to decode EDID. Use defaults.\n", + __func__); + return; + } + + config->xres = config->display_xres = edid.ha; + config->yres = config->display_yres = edid.va; + + config->pixel_clock = edid.pixel_clock * 1000; + + config->hfront_porch = edid.hso; + config->hsync_width = edid.hspw; + config->hback_porch = edid.hbl - edid.hso - edid.hspw; + + config->vfront_porch = edid.vso; + config->vsync_width = edid.vspw; + config->vback_porch = edid.vbl - edid.vso - edid.vspw; + + /** + * Note edid->framebuffer_bits_per_pixel is currently hard-coded as 32, + * so we should keep the default value in device config. + * + * EDID v1.3 panels may not have color depth info, so we need to check + * if these values are zero before updating config. + */ + if (edid.panel_bits_per_pixel) + config->panel_bits_per_pixel = edid.panel_bits_per_pixel; + if (edid.panel_bits_per_color) + config->color_depth = edid.panel_bits_per_color; + printk(BIOS_SPEW, "%s: configuration updated by EDID.\n", __func__); +} + +void dp_init(void *_config) +{ + struct soc_nvidia_tegra210_config *config = (void *)_config; + struct tegra_dc *dc = config->dc_data; + struct tegra_dc_dp_data *dp = &dp_data; + + /* set up links among config, dc, dp and sor */ + dp->dc = dc; + dc->out = dp; + dp->sor.dc = dc; + + dp->sor.power_is_up = 0; + dp->sor.base = (void *)TEGRA_ARM_SOR; + dp->sor.pmc_base = (void *)TEGRA_PMC_BASE; + dp->sor.portnum = 0; + dp->sor.link_cfg = &dp->link_cfg; + dp->aux_base = (void *)TEGRA_ARM_DPAUX; + dp->link_cfg.is_valid = 0; + dp->enabled = 0; + + tegra_dp_update_config(dp, config); +} + +static void tegra_dp_hpd_config(struct tegra_dc_dp_data *dp, + struct soc_nvidia_tegra210_config *config) +{ + u32 val; + + val = config->dp.hpd_plug_min_us | + (config->dp.hpd_unplug_min_us << + DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME_SHIFT); + tegra_dpaux_writel(dp, DPAUX_HPD_CONFIG, val); + + tegra_dpaux_writel(dp, DPAUX_HPD_IRQ_CONFIG, config->dp.hpd_irq_min_us); +} + +static int tegra_dp_hpd_plug(struct tegra_dc_dp_data *dp, int timeout_ms) +{ + u32 val; + u32 timeout = timeout_ms * 1000; + do { + val = tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT); + if (val & DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED) + return 0; + udelay(100); + timeout -= 100; + } while (timeout > 0); + return -1; +} + +static int tegra_dc_dp_sink_out_of_sync(struct tegra_dc_dp_data *dp, + u32 delay_ms) +{ + u8 dpcd_data; + int out_of_sync; + + mdelay(delay_ms); + tegra_dc_dp_dpcd_read(dp, NV_DPCD_SINK_STATUS, &dpcd_data); + + out_of_sync = ((dpcd_data & NV_DPCD_SINK_STATUS_PORT0_IN_SYNC) != + NV_DPCD_SINK_STATUS_PORT0_IN_SYNC); + + if (out_of_sync) + printk(BIOS_ERR, + "SINK receive port 0 is out of synchronization\n"); + else + printk(BIOS_INFO, + "SINK is in synchronization\n"); + + return out_of_sync; +} + +static void tegra_dc_dp_check_sink(struct tegra_dc_dp_data *dp, + struct soc_nvidia_tegra210_config *config) +{ + + u8 max_retry = 3; + int delay_frame; + + /* DP TCON may skip some main stream frames, thus we need to wait + some delay before reading the DPCD SINK STATUS register, starting + from 5 */ + delay_frame = 5; + + while (tegra_dc_dp_sink_out_of_sync(dp, FRAME_IN_MS * delay_frame) && + max_retry--) { + tegra_dc_detach(&dp->sor); + if (tegra_dc_dp_explore_link_cfg(dp, &dp->link_cfg, config)) { + printk(BIOS_ERR, "dp: %s: error to configure link\n", + __func__); + continue; + } + + tegra_dc_sor_set_power_state(&dp->sor, 1); + tegra_dc_sor_attach(&dp->sor); + + /* Increase delay_frame for next try in case the sink is + skipping more frames */ + delay_frame += 10; + } +} + +void dp_enable(void *_dp) +{ + struct tegra_dc_dp_data *dp = _dp; + struct tegra_dc *dc = dp->dc; + struct soc_nvidia_tegra210_config *config = dc->config; + + u8 data; + u32 retry; + int ret; + + tegra_dc_dpaux_enable(dp); + + tegra_dp_hpd_config(dp, config); + if (tegra_dp_hpd_plug(dp, config->dp.vdd_to_hpd_delay_ms) < 0) { + printk(BIOS_ERR, "dp: hpd plug failed\n"); + goto error_enable; + } + + if (tegra_dc_dp_init_max_link_cfg(config, dp, &dp->link_cfg)) { + printk(BIOS_ERR, "dp: failed to init link configuration\n"); + goto error_enable; + } + + tegra_dc_sor_enable_dp(&dp->sor); + + tegra_dc_sor_set_panel_power(&dp->sor, 1); + + /* Write power on to DPCD */ + data = NV_DPCD_SET_POWER_VAL_D0_NORMAL; + retry = 0; + do { + ret = tegra_dc_dp_dpcd_write(dp, + NV_DPCD_SET_POWER, data); + } while ((retry++ < DP_POWER_ON_MAX_TRIES) && ret); + + if (ret || retry >= DP_POWER_ON_MAX_TRIES) { + printk(BIOS_ERR, + "dp: failed to power on panel (0x%x)\n", ret); + goto error_enable; + } + + /* Confirm DP is plugging status */ + if (!(tegra_dpaux_readl(dp, DPAUX_DP_AUXSTAT) & + DPAUX_DP_AUXSTAT_HPD_STATUS_PLUGGED)) { + printk(BIOS_ERR, "dp: could not detect HPD\n"); + goto error_enable; + } + + /* Check DP version */ + if (tegra_dc_dp_dpcd_read(dp, NV_DPCD_REV, &dp->revision)) + printk(BIOS_ERR, + "dp: failed to read the revision number from sink\n"); + + if (tegra_dc_dp_explore_link_cfg(dp, &dp->link_cfg, config)) { + printk(BIOS_ERR, "dp: error to configure link\n"); + goto error_enable; + } + + tegra_dc_sor_set_power_state(&dp->sor, 1); + tegra_dc_sor_attach(&dp->sor); + + tegra_dc_dp_check_sink(dp, config); + + /* + * Power down the unused lanes to save power + * (about hundreds milli-watts, varies from boards). + */ + tegra_dc_sor_power_down_unused_lanes(&dp->sor); + + dp->enabled = 1; +error_enable: + return; +} + +void dp_display_startup(device_t dev) +{ + struct soc_nvidia_tegra210_config *config = dev->chip_info; + struct display_controller *disp_ctrl = + (void *)config->display_controller; + + u32 framebuffer_size_mb = config->framebuffer_size / MiB; + u32 framebuffer_base_mb = config->framebuffer_base / MiB; + + struct pwm_controller *pwm = (void *)TEGRA_PWM_BASE; + struct tegra_dc *dc = &dc_data; + u32 plld_rate; + + printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n", + __func__, disp_ctrl); + + if (disp_ctrl == NULL) { + printk(BIOS_ERR, "Error: No dc is assigned by dt.\n"); + return; + } + + dc->base = (void *)disp_ctrl; + dc->config = config; + config->dc_data = dc; + + /* Note dp_init may read EDID and change some config values. */ + dp_init(config); + + if (framebuffer_size_mb == 0) { + framebuffer_size_mb = ALIGN_UP(config->display_xres * + config->display_yres * + (config->framebuffer_bits_per_pixel / 8), MiB)/MiB; + } + + config->framebuffer_size = framebuffer_size_mb * MiB; + config->framebuffer_base = framebuffer_base_mb * MiB; + + /* The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER + * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the + * update_display_mode() for detail. + */ + plld_rate = clock_configure_plld(config->pixel_clock * 2); + if (plld_rate == 0) { + printk(BIOS_ERR, "dc: clock init failed\n"); + return; + } else if (plld_rate != config->pixel_clock * 2) { + printk(BIOS_WARNING, "dc: plld rounded to %u\n", plld_rate); + config->pixel_clock = plld_rate / 2; + } + + /* set disp1's clock source to PLLD_OUT0 */ + clock_configure_source(disp1, PLLD, (plld_rate/KHz)/2); + + /* Init dc */ + if (tegra_dc_init(disp_ctrl)) { + printk(BIOS_ERR, "dc: init failed\n"); + return; + } + + /* Configure dc mode */ + if (update_display_mode(disp_ctrl, config)) { + printk(BIOS_ERR, "dc: failed to configure display mode.\n"); + return; + } + + /* Enable dp */ + dp_enable(dc->out); + + /* Set up Tegra PWM n (where n is specified in config->dp.pwm) to drive the + * panel backlight. + */ + printk(BIOS_SPEW, "%s: enable panel backlight pwm\n", __func__); + WRITEL(((1 << NV_PWM_CSR_ENABLE_SHIFT) | + (220 << NV_PWM_CSR_PULSE_WIDTH_SHIFT) | /* 220/256 */ + 0x02e), /* frequency divider */ + &pwm->pwm[config->dp.pwm].csr); + + /* Set up window */ + update_window(config); + printk(BIOS_INFO, "%s: display init done.\n", __func__); + + /* Save panel mode to cb tables */ + pass_mode_info_to_payload(config); + + /* + * After this point, it is payload's responsibility to allocate + * framebuffer and sets the base address to dc's + * WINBUF_START_ADDR register and enables window by setting dc's + * DISP_DISP_WIN_OPTIONS register. + */ +} diff --git a/src/soc/nvidia/tegra210/dsi.c b/src/soc/nvidia/tegra210/dsi.c new file mode 100644 index 0000000000..3d0b8502b1 --- /dev/null +++ b/src/soc/nvidia/tegra210/dsi.c @@ -0,0 +1,1045 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> +#include <delay.h> +#include <timer.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <device/device.h> +#include <edid.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/nvidia/tegra/dc.h> +#include "chip.h" +#include <soc/display.h> +#include <soc/mipi_dsi.h> +#include <soc/mipi_display.h> +#include <soc/tegra_dsi.h> +#include <soc/mipi-phy.h> +#include "jdi_25x18_display/panel-jdi-lpm102a188a.h" + +struct tegra_mipi_device mipi_device_data[NUM_DSI]; + +struct tegra_dsi dsi_data[NUM_DSI] = { + { + .regs = (void *)TEGRA_DSIA_BASE, + .channel = 0, + .slave = &dsi_data[DSI_B], + .master = NULL, + .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH, + .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH, + }, + { + .regs = (void *)TEGRA_DSIB_BASE, + .channel = 0, + .slave = NULL, + .master = &dsi_data[DSI_A], + .video_fifo_depth = MAX_DSI_VIDEO_FIFO_DEPTH, + .host_fifo_depth = MAX_DSI_HOST_FIFO_DEPTH, + }, +}; + +static const u32 init_reg[] = { + DSI_INT_ENABLE, + DSI_INT_STATUS, + DSI_INT_MASK, + DSI_INIT_SEQ_DATA_0, + DSI_INIT_SEQ_DATA_1, + DSI_INIT_SEQ_DATA_2, + DSI_INIT_SEQ_DATA_3, + DSI_INIT_SEQ_DATA_4, + DSI_INIT_SEQ_DATA_5, + DSI_INIT_SEQ_DATA_6, + DSI_INIT_SEQ_DATA_7, + DSI_INIT_SEQ_DATA_15, + DSI_DCS_CMDS, + DSI_PKT_SEQ_0_LO, + DSI_PKT_SEQ_1_LO, + DSI_PKT_SEQ_2_LO, + DSI_PKT_SEQ_3_LO, + DSI_PKT_SEQ_4_LO, + DSI_PKT_SEQ_5_LO, + DSI_PKT_SEQ_0_HI, + DSI_PKT_SEQ_1_HI, + DSI_PKT_SEQ_2_HI, + DSI_PKT_SEQ_3_HI, + DSI_PKT_SEQ_4_HI, + DSI_PKT_SEQ_5_HI, + DSI_CONTROL, + DSI_HOST_CONTROL, + DSI_PAD_CONTROL_0, + DSI_PAD_CONTROL_CD, + DSI_SOL_DELAY, + DSI_MAX_THRESHOLD, + DSI_TRIGGER, + DSI_TX_CRC, + DSI_INIT_SEQ_CONTROL, + DSI_PKT_LEN_0_1, + DSI_PKT_LEN_2_3, + DSI_PKT_LEN_4_5, + DSI_PKT_LEN_6_7, +}; + +static inline struct tegra_dsi *host_to_tegra(struct mipi_dsi_host *host) +{ + return container_of(host, struct tegra_dsi, host); +} + +/* + * non-burst mode with sync pulses + */ +static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = { + [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 1] = 0, + [ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 3] = 0, + [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), + [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | + PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | + PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), + [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) | + PKT_LP, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) | + PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0), + [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) | + PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) | + PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4), +}; + +/* + * non-burst mode with sync events + */ +static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = { + [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 1] = 0, + [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 3] = 0, + [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 5] = 0, + + [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + + [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), + [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) | + PKT_LP, + [ 9] = 0, + + [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) | + PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) | + PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3), + + [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4), +}; + +static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { + [ 0] = 0, + [ 1] = 0, + [ 2] = 0, + [ 3] = 0, + [ 4] = 0, + [ 5] = 0, + [ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP, + [ 7] = 0, + [ 8] = 0, + [ 9] = 0, + [10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP, + [11] = 0, +}; + +static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) +{ + int err; + + err = mipi_dphy_set_timing(dsi); + if (err < 0) { + printk(BIOS_ERR, "failed to set D-PHY timing: %d\n", err); + return err; + } + + if (dsi->slave) + tegra_dsi_set_phy_timing(dsi->slave); + return 0; +} + +static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, + unsigned int *mulp, unsigned int *divp) +{ + switch (format) { + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB888: + *mulp = 3; + *divp = 1; + break; + + case MIPI_DSI_FMT_RGB565: + *mulp = 2; + *divp = 1; + break; + + case MIPI_DSI_FMT_RGB666: + *mulp = 9; + *divp = 4; + break; + + default: + return -EINVAL; + } + return 0; +} + +static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format, + enum tegra_dsi_format *fmt) +{ + switch (format) { + case MIPI_DSI_FMT_RGB888: + *fmt = TEGRA_DSI_FORMAT_24P; + break; + + case MIPI_DSI_FMT_RGB666: + *fmt = TEGRA_DSI_FORMAT_18NP; + break; + + case MIPI_DSI_FMT_RGB666_PACKED: + *fmt = TEGRA_DSI_FORMAT_18P; + break; + + case MIPI_DSI_FMT_RGB565: + *fmt = TEGRA_DSI_FORMAT_16P; + break; + + default: + return -EINVAL; + } + return 0; +} + +static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start, + unsigned int size) +{ + u32 value; + + tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START); + tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE); + + value = DSI_GANGED_MODE_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL); +} + +static void tegra_dsi_enable(struct tegra_dsi *dsi) +{ + u32 value; + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value |= DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + if (dsi->slave) + tegra_dsi_enable(dsi->slave); +} + +static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, + const struct soc_nvidia_tegra210_config *mode) +{ + unsigned int hact, hsw, hbp, hfp, i, mul, div; + enum tegra_dsi_format format; + const u32 *pkt_seq; + u32 value; + int err; + + if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + printk(BIOS_SPEW, "Non-burst video mode with sync pulses\n"); + pkt_seq = pkt_seq_video_non_burst_sync_pulses; + } else if (dsi->flags & MIPI_DSI_MODE_VIDEO) { + printk(BIOS_SPEW, "Non-burst video mode with sync events\n"); + pkt_seq = pkt_seq_video_non_burst_sync_events; + } else { + printk(BIOS_SPEW, "Command mode\n"); + pkt_seq = pkt_seq_command_mode; + } + + err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); + if (err < 0) + return err; + + err = tegra_dsi_get_format(dsi->format, &format); + if (err < 0) + return err; + + value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | + DSI_CONTROL_LANES(dsi->lanes - 1) | + DSI_CONTROL_SOURCE(pipe); + tegra_dsi_writel(dsi, value, DSI_CONTROL); + + tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD); + + value = DSI_HOST_CONTROL_HS; + tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); + + value = tegra_dsi_readl(dsi, DSI_CONTROL); + + if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) + value |= DSI_CONTROL_HS_CLK_CTRL; + + value &= ~DSI_CONTROL_TX_TRIG(3); + + /* enable DCS commands for command mode */ + if (dsi->flags & MIPI_DSI_MODE_VIDEO) + value &= ~DSI_CONTROL_DCS_ENABLE; + else + value |= DSI_CONTROL_DCS_ENABLE; + + value |= DSI_CONTROL_VIDEO_ENABLE; + value &= ~DSI_CONTROL_HOST_ENABLE; + tegra_dsi_writel(dsi, value, DSI_CONTROL); + + for (i = 0; i < NUM_PKT_SEQ; i++) + tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); + + if (dsi->flags & MIPI_DSI_MODE_VIDEO) { + /* horizontal active pixels */ + hact = mode->xres * mul / div; + + /* horizontal sync width */ + hsw = (hsync_end(mode) - hsync_start(mode)) * mul / div; + + /* horizontal back porch */ + hbp = (htotal(mode) - hsync_end(mode)) * mul / div; + if ((dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) + hbp += hsw; + + /* horizontal front porch */ + hfp = (hsync_start(mode) - mode->xres) * mul / div; + + /* subtract packet overhead */ + hsw -= 10; + hbp -= 14; + hfp -= 8; + + tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); + tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); + tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); + tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7); + + /* set SOL delay (for non-burst mode only) */ + tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); + + /* TODO: implement ganged mode */ + } else { + u16 bytes; + if (dsi->ganged_mode) { + /* + * For ganged mode, assume symmetric left-right mode. + */ + bytes = 1 + (mode->xres / 2) * mul / div; + } else { + /* 1 byte (DCS command) + pixel data */ + bytes = 1 + mode->xres * mul / div; + } + + tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1); + tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3); + tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5); + tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7); + + value = MIPI_DCS_WRITE_MEMORY_START << 8 | + MIPI_DCS_WRITE_MEMORY_CONTINUE; + tegra_dsi_writel(dsi, value, DSI_DCS_CMDS); + + /* set SOL delay */ + if (dsi->ganged_mode) { + unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = dsi->ganged_lanes; + + /* SOL to valid, valid to FIFO and FIFO write delay */ + delay = 4 + 4 + 2; + delay = DIV_ROUND_UP(delay * mul, div * lanes); + /* FIFO read delay */ + delay = delay + 6; + + bclk = DIV_ROUND_UP(htotal(mode) * mul, div * lanes); + bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); + value = bclk - bclk_ganged + delay + 20; + } else { + /* TODO: revisit for non-ganged mode */ + value = 8 * mul / div; + } + + tegra_dsi_writel(dsi, value, DSI_SOL_DELAY); + } + + if (dsi->slave) { + err = tegra_dsi_configure(dsi->slave, pipe, mode); + if (err < 0) + return err; + + /* + * enable ganged mode + */ + if (dsi->ganged_mode) { + tegra_dsi_ganged_enable(dsi, mode->xres / 2, + mode->xres / 2); + tegra_dsi_ganged_enable(dsi->slave, 0, mode->xres / 2); + } + } + return 0; +} + +static int tegra_output_dsi_enable(struct tegra_dsi *dsi, + const struct soc_nvidia_tegra210_config *config) +{ + int err; + + if (dsi->enabled) + return 0; + + err = tegra_dsi_configure(dsi, 0, config); + if (err < 0) { + printk(BIOS_ERR, "DSI configuration failed\n"); + return err; + } + + /* enable DSI controller */ + tegra_dsi_enable(dsi); + + dsi->enabled = true; + return 0; +} + + +static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, + unsigned int vrefresh) +{ + unsigned int timeout; + u32 value; + + /* one frame high-speed transmission timeout */ + timeout = (bclk / vrefresh) / 512; + value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); + + /* 2 ms peripheral timeout for panel */ + timeout = 2 * bclk / 512 * 1000; + value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); + + value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); + tegra_dsi_writel(dsi, value, DSI_TO_TALLY); + + if (dsi->slave) + tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); +} + +static int tegra_output_dsi_setup_clock(struct tegra_dsi *dsi, + const struct soc_nvidia_tegra210_config *config) +{ + unsigned int mul, div, num_lanes; + unsigned long bclk; + unsigned long pclk = config->pixel_clock; + int plld; + int err; + struct display_controller *disp_ctrl = + (void *)config->display_controller; + unsigned int shift_clk_div; + + err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); + if (err < 0) + return err; + + /* + * In ganged mode, account for the total number of lanes across both + * DSI channels so that the bit clock is properly computed. + */ + if (dsi->ganged_mode) + num_lanes = dsi->ganged_lanes; + else + num_lanes = dsi->lanes; + + /* compute byte clock */ + bclk = (pclk * mul) / (div * num_lanes); + + /* + * Compute bit clock and round up to the next MHz. + */ + plld = DIV_ROUND_UP(bclk * 8, USECS_PER_SEC) * USECS_PER_SEC; + + /* + * the actual rate on PLLD_OUT0 is 1/2 plld + */ + dsi->clk_rate = plld / 2; + if (dsi->slave) + dsi->slave->clk_rate = dsi->clk_rate; + + /* set up plld */ + plld = clock_configure_plld(plld); + if (plld == 0) { + printk(BIOS_ERR, "%s: clock init failed\n", __func__); + return -1; + } else + printk(BIOS_INFO, "%s: plld is configured to: %u\n", + __func__, plld); + + /* + * Derive pixel clock from bit clock using the shift clock divider. + * Note that this is only half of what we would expect, but we need + * that to make up for the fact that we divided the bit clock by a + * factor of two above. + */ + shift_clk_div = ((8 * mul) / (div * num_lanes)) - 2; + update_display_shift_clock_divider(disp_ctrl, shift_clk_div); + + tegra_dsi_set_timeout(dsi, bclk, config->refresh); + return plld/1000000; +} + + + +static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) +{ + unsigned long value; + + value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); + return 0; +} + +static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) +{ + u32 value; + + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); + + /* start calibration */ + tegra_dsi_pad_enable(dsi); + + value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | + DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | + DSI_PAD_OUT_CLK(0x0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); + + value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); + + return tegra_mipi_calibrate(dsi->mipi); +} + +static const char * const error_report[16] = { + "SoT Error", + "SoT Sync Error", + "EoT Sync Error", + "Escape Mode Entry Command Error", + "Low-Power Transmit Sync Error", + "Peripheral Timeout Error", + "False Control Error", + "Contention Detected", + "ECC Error, single-bit", + "ECC Error, multi-bit", + "Checksum Error", + "DSI Data Type Not Recognized", + "DSI VC ID Invalid", + "Invalid Transmission Length", + "Reserved", + "DSI Protocol Violation", +}; + +static int tegra_dsi_read_response(struct tegra_dsi *dsi, + const struct mipi_dsi_msg *msg, + unsigned int count) +{ + u8 *rx = msg->rx_buf; + unsigned int i, j, k; + size_t size = 0; + u16 errors; + u32 value; + + /* read and parse packet header */ + value = tegra_dsi_readl(dsi, DSI_RD_DATA); + + switch (value & 0x3f) { + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + errors = (value >> 8) & 0xffff; + printk(BIOS_ERR, "Acknowledge and error report: %04x\n", + errors); + for (i = 0; i < ARRAY_SIZE(error_report); i++) + if (errors & BIT(i)) + printk(BIOS_INFO, " %2u: %s\n", i, + error_report[i]); + break; + + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + rx[0] = (value >> 8) & 0xff; + break; + + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + rx[0] = (value >> 8) & 0xff; + rx[1] = (value >> 16) & 0xff; + break; + + case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: + size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); + break; + + case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: + size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); + break; + + default: + printk(BIOS_ERR, "unhandled response type: %02x\n", + value & 0x3f); + break; + } + + size = MIN(size, msg->rx_len); + + if (msg->rx_buf && size > 0) { + for (i = 0, j = 0; i < count - 1; i++, j += 4) { + value = tegra_dsi_readl(dsi, DSI_RD_DATA); + + for (k = 0; k < 4 && (j + k) < msg->rx_len; k++) + rx[j + k] = (value >> (k << 3)) & 0xff; + } + } + return 0; +} + +static int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout_ms) +{ + u32 poll_interval_us = 2000; + u32 timeout_us = timeout_ms * 1000; + + tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER); + udelay(poll_interval_us); + + do { + u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER); + if ((value & DSI_TRIGGER_HOST) == 0) + return 0; + + //usleep_range(1000, 2000); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + + udelay(poll_interval_us); + } while (1); + + printk(BIOS_ERR, "%s: ERROR: timeout waiting for transmission" + " to complete\n", __func__); + return -ETIMEDOUT; +} + +static int tegra_dsi_wait_for_response(struct tegra_dsi *dsi, + unsigned long timeout_ms) +{ + u32 poll_interval_us = 2000; + u32 timeout_us = timeout_ms * 1000; + + do { + u32 value = tegra_dsi_readl(dsi, DSI_STATUS); + u8 count = value & 0x1f; + + if (count > 0) + return count; + + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + + udelay(poll_interval_us); + } while (1); + + printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__); + + return -ETIMEDOUT; +} + +static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg) +{ + struct tegra_dsi *dsi = host_to_tegra(host); + const u8 *tx = msg->tx_buf; + unsigned int count, i, j; + u32 value; + int err; + + if (msg->tx_len > dsi->video_fifo_depth * 4) + return -ENOSPC; + + /* reset underflow/overflow flags */ + value = tegra_dsi_readl(dsi, DSI_STATUS); + if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) { + value = DSI_HOST_CONTROL_FIFO_RESET; + tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); + udelay(20); // usleep_range(10, 20); + } + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value |= DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + udelay(7000); //usleep_range(5000, 10000); + + value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | + DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + + if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) + value |= DSI_HOST_CONTROL_HS; + + /* + * The host FIFO has a maximum of 64 words, so larger transmissions + * need to use the video FIFO. + */ + if (msg->tx_len > dsi->host_fifo_depth * 4) + value |= DSI_HOST_CONTROL_FIFO_SEL; + + tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); + + /* + * For reads and messages with explicitly requested ACK, generate a + * BTA sequence after the transmission of the packet. + */ + if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || + (msg->rx_buf && msg->rx_len > 0)) { + value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL); + value |= DSI_HOST_CONTROL_PKT_BTA; + tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); + } + + value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE; + tegra_dsi_writel(dsi, value, DSI_CONTROL); + + /* write packet header */ + value = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f); + + if (tx && msg->tx_len > 0) + value |= tx[0] << 8; + + if (tx && msg->tx_len > 1) + value |= tx[1] << 16; + + tegra_dsi_writel(dsi, value, DSI_WR_DATA); + + /* write payload (if any) */ + if (msg->tx_len > 2) { + for (j = 2; j < msg->tx_len; j += 4) { + value = 0; + + for (i = 0; i < 4 && j + i < msg->tx_len; i++) + value |= tx[j + i] << (i << 3); + + tegra_dsi_writel(dsi, value, DSI_WR_DATA); + } + } + + err = tegra_dsi_transmit(dsi, 250); + if (err < 0) { + printk(BIOS_INFO, "Failed to transmit. %d\n", err); + return err; + } + + if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || + (msg->rx_buf && msg->rx_len > 0)) { + err = tegra_dsi_wait_for_response(dsi, 250); + if (err < 0) { + printk(BIOS_INFO, "Failed to read response. %d\n", err); + return err; + } + count = err; + + value = tegra_dsi_readl(dsi, DSI_RD_DATA); + switch (value) { + case 0x84: + printk(BIOS_INFO, "ACK\n"); + break; + + case 0x87: + printk(BIOS_INFO, "ESCAPE\n"); + break; + + default: + printk(BIOS_INFO, "unknown status: %08x\n", value); + break; + } + + if (count > 1) { + err = tegra_dsi_read_response(dsi, msg, count); + if (err < 0) + printk(BIOS_INFO, + "failed to parse response: %d\n", + err); + else { + /* + * For read commands, return the number of + * bytes returned by the peripheral. + */ + count = err; + } + } + } else { + /* + * For write commands, we have transmitted the 4-byte header + * plus the variable-length payload. + */ + count = 4 + msg->tx_len; + } + + return count; +} + +static int tegra_dsi_ganged_setup(struct tegra_dsi *dsi, + struct tegra_dsi *slave) +{ + /* + * The number of ganged lanes is the sum of lanes of all peripherals + * in the gang. + */ + dsi->slave->ganged_lanes = dsi->lanes + dsi->slave->lanes; + dsi->slave->ganged_mode = 1; + + dsi->ganged_lanes = dsi->lanes + dsi->slave->lanes; + dsi->ganged_mode = 1; + return 0; +} + +static int tegra_dsi_host_attach(struct mipi_dsi_host *host, + struct mipi_dsi_device *device) +{ + struct tegra_dsi *dsi = host_to_tegra(host); + int err; + + dsi->flags = device->mode_flags; + dsi->format = device->format; + dsi->lanes = device->lanes; + + if (dsi->master) { + err = tegra_dsi_ganged_setup(dsi->master, dsi); + if (err < 0) { + printk(BIOS_ERR, "failed to set up ganged mode: %d\n", + err); + return err; + } + } + return 0; +} + +static const struct mipi_dsi_host_ops tegra_dsi_host_ops = { + .attach = tegra_dsi_host_attach, + .transfer = tegra_dsi_host_transfer, +}; + +static int dsi_probe_if(int dsi_index, + struct soc_nvidia_tegra210_config *config) +{ + struct tegra_dsi *dsi = &dsi_data[dsi_index]; + int err; + + tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL); + + /* + * Set default value. Will be taken from attached device once detected + */ + dsi->flags = 0; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->lanes = 4; + + /* get tegra_mipi_device */ + dsi->mipi = tegra_mipi_request(&mipi_device_data[dsi_index], dsi_index); + + /* calibrate */ + err = tegra_dsi_pad_calibrate(dsi); + if (err < 0) { + printk(BIOS_ERR, "MIPI calibration failed: %d\n", err); + return err; + } + + dsi->host.ops = &tegra_dsi_host_ops; + err = mipi_dsi_host_register(&dsi->host); + if (err < 0) { + printk(BIOS_ERR, "failed to register DSI host: %d\n", err); + return err; + } + + /* get panel */ + dsi->panel = panel_jdi_dsi_probe((struct mipi_dsi_device *)dsi->host.dev); + if (IS_ERR_PTR(dsi->panel)) { + printk(BIOS_ERR, "failed to get dsi panel\n"); + return -EPTR; + } + dsi->panel->mode = config; + return 0; +} + +static int dsi_probe(struct soc_nvidia_tegra210_config *config) +{ + dsi_probe_if(DSI_A, config); + dsi_probe_if(DSI_B, config); + return 0; +} + +static void tegra_dsi_init_regs(struct tegra_dsi *dsi) +{ + int i; + for (i = 0; i < ARRAY_SIZE(init_reg); i++) + tegra_dsi_writel(dsi, 0, init_reg[i]); + + if (dsi->slave) + tegra_dsi_init_regs(dsi->slave); +} + +static int dsi_enable(struct soc_nvidia_tegra210_config *config) +{ + struct tegra_dsi *dsi_a = &dsi_data[DSI_A]; + + dsi_probe(config); + + /* set up clock and TimeOutRegisters */ + tegra_output_dsi_setup_clock(dsi_a, config); + + /* configure APB_MISC_GP_MIPI_PAD_CTRL_0 */ + write32((unsigned int *)APB_MISC_GP_MIPI_PAD_CTRL_0, DSIB_MODE_DSI); + + /* configure phy interface timing registers */ + tegra_dsi_set_phy_timing(dsi_a); + + /* Initialize DSI registers */ + tegra_dsi_init_regs(dsi_a); + + /* prepare panel */ + panel_jdi_prepare(dsi_a->panel); + + /* enable dsi */ + if (tegra_output_dsi_enable(dsi_a, config)) { + printk(BIOS_ERR,"%s: Error: failed to enable dsi output.\n", + __func__); + return -1; + } + + return 0; +} + +void dsi_display_startup(device_t dev) +{ + struct soc_nvidia_tegra210_config *config = dev->chip_info; + struct display_controller *disp_ctrl = + (void *)config->display_controller; + u32 plld_rate; + + u32 framebuffer_size_mb = config->framebuffer_size / MiB; + u32 framebuffer_base_mb= config->framebuffer_base / MiB; + + printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n", + __func__, disp_ctrl); + + if (disp_ctrl == NULL) { + printk(BIOS_ERR, "Error: No dc is assigned by dt.\n"); + return; + } + + if (framebuffer_size_mb == 0){ + framebuffer_size_mb = ALIGN_UP(config->display_xres * + config->display_yres * + (config->framebuffer_bits_per_pixel / 8), MiB)/MiB; + } + + config->framebuffer_size = framebuffer_size_mb * MiB; + config->framebuffer_base = framebuffer_base_mb * MiB; + + /* + * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER + * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the + * update_display_mode() for detail. + */ + /* set default plld */ + plld_rate = clock_configure_plld(config->pixel_clock * 2); + if (plld_rate == 0) { + printk(BIOS_ERR, "dc: clock init failed\n"); + return; + } + + /* set disp1's clock source to PLLD_OUT0 */ + clock_configure_source(disp1, PLLD_OUT0, (plld_rate/KHz)); + + /* Init dc */ + if (tegra_dc_init(disp_ctrl)) { + printk(BIOS_ERR, "dc: init failed\n"); + return; + } + + /* Configure dc mode */ + if (update_display_mode(disp_ctrl, config)) { + printk(BIOS_ERR, "dc: failed to configure display mode.\n"); + return; + } + + /* Configure and enable dsi controller and panel */ + if (dsi_enable(config)) { + printk(BIOS_ERR, "%s: failed to enable dsi controllers.\n", + __func__); + return; + } + + /* Set up window */ + update_window(config); + printk(BIOS_INFO, "%s: display init done.\n", __func__); + + /* Save panel information to cb tables */ + pass_mode_info_to_payload(config); + + /* + * After this point, it is payload's responsibility to allocate + * framebuffer and sets the base address to dc's + * WINBUF_START_ADDR register and enables window by setting dc's + * DISP_DISP_WIN_OPTIONS register. + */ +} diff --git a/src/soc/nvidia/tegra210/flow_ctrl.c b/src/soc/nvidia/tegra210/flow_ctrl.c new file mode 100644 index 0000000000..825274d30d --- /dev/null +++ b/src/soc/nvidia/tegra210/flow_ctrl.c @@ -0,0 +1,113 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2014, NVIDIA CORPORATION. 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. + */ + +#include <arch/io.h> +#include <soc/addressmap.h> +#include <soc/flow_ctrl.h> + +#define FLOW_CTRL_HALT_CPU0_EVENTS 0x0 +#define FLOW_CTRL_WAITEVENT (2 << 29) +#define FLOW_CTRL_WAIT_FOR_INTERRUPT (4 << 29) +#define FLOW_CTRL_HALT_SCLK (1 << 27) +#define FLOW_CTRL_HALT_LIC_IRQ (1 << 11) +#define FLOW_CTRL_HALT_LIC_FIQ (1 << 10) +#define FLOW_CTRL_HALT_GIC_IRQ (1 << 9) +#define FLOW_CTRL_HALT_GIC_FIQ (1 << 8) +#define FLOW_CTRL_CPU0_CSR 0x8 +#define FLOW_CTRL_CSR_INTR_FLAG (1 << 15) +#define FLOW_CTRL_CSR_EVENT_FLAG (1 << 14) +#define FLOW_CTRL_CSR_WFI_CPU0 (1 << 8) +#define FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8) +#define FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4) +#define FLOW_CTRL_CSR_ENABLE (1 << 0) +#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14 +#define FLOW_CTRL_CPU1_CSR 0x18 +#define FLOW_CTRL_CC4_CORE0_CTRL 0x6c + +static void *tegra_flowctrl_base = (void*)TEGRA_FLOW_BASE; + +static const uint8_t flowctrl_offset_halt_cpu[] = { + FLOW_CTRL_HALT_CPU0_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS + 8, + FLOW_CTRL_HALT_CPU1_EVENTS + 16 +}; + +static const uint8_t flowctrl_offset_cpu_csr[] = { + FLOW_CTRL_CPU0_CSR, + FLOW_CTRL_CPU1_CSR, + FLOW_CTRL_CPU1_CSR + 8, + FLOW_CTRL_CPU1_CSR + 16 +}; + +static const uint8_t flowctrl_offset_cc4_ctrl[] = { + FLOW_CTRL_CC4_CORE0_CTRL, + FLOW_CTRL_CC4_CORE0_CTRL + 4, + FLOW_CTRL_CC4_CORE0_CTRL + 8, + FLOW_CTRL_CC4_CORE0_CTRL + 12 +}; + +void flowctrl_write_cpu_csr(int cpu, uint32_t val) +{ + write32(tegra_flowctrl_base + flowctrl_offset_cpu_csr[cpu], val); + val = read32(tegra_flowctrl_base + flowctrl_offset_cpu_csr[cpu]); +} + +void flowctrl_write_cpu_halt(int cpu, uint32_t val) +{ + write32(tegra_flowctrl_base + flowctrl_offset_halt_cpu[cpu], val); + val = read32(tegra_flowctrl_base + flowctrl_offset_halt_cpu[cpu]); +} + +void flowctrl_write_cc4_ctrl(int cpu, uint32_t val) +{ + write32(tegra_flowctrl_base + flowctrl_offset_cc4_ctrl[cpu], val); + val = read32(tegra_flowctrl_base + flowctrl_offset_cc4_ctrl[cpu]); +} + +void flowctrl_cpu_off(int cpu) +{ + uint32_t val = FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | + FLOW_CTRL_CSR_ENABLE | (FLOW_CTRL_CSR_WFI_CPU0 << cpu); + + flowctrl_write_cpu_csr(cpu, val); + flowctrl_write_cpu_halt(cpu, FLOW_CTRL_WAITEVENT); + flowctrl_write_cc4_ctrl(cpu, 0); +} + +void flowctrl_cpu_on(int cpu) +{ + flowctrl_write_cpu_csr(cpu, FLOW_CTRL_CSR_ENABLE); + flowctrl_write_cpu_halt(cpu, FLOW_CTRL_WAITEVENT | + FLOW_CTRL_HALT_SCLK); +} + +void flowctrl_cpu_suspend(int cpu) +{ + uint32_t val; + + val = FLOW_CTRL_HALT_GIC_IRQ | FLOW_CTRL_HALT_GIC_FIQ | + FLOW_CTRL_HALT_LIC_IRQ | FLOW_CTRL_HALT_LIC_FIQ | + FLOW_CTRL_WAITEVENT; + flowctrl_write_cpu_halt(cpu, val); + + val = FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | + FLOW_CTRL_CSR_ENABLE | (FLOW_CTRL_CSR_WFI_CPU0 << cpu); + flowctrl_write_cpu_csr(cpu, val); +} diff --git a/src/soc/nvidia/tegra210/funitcfg.c b/src/soc/nvidia/tegra210/funitcfg.c new file mode 100644 index 0000000000..96b1204b07 --- /dev/null +++ b/src/soc/nvidia/tegra210/funitcfg.c @@ -0,0 +1,186 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/funitcfg.h> +#include <soc/nvidia/tegra/usb.h> +#include <soc/padconfig.h> +#include <string.h> + +struct clk_dev_control { + uint32_t *clk_enb_set; + uint32_t *rst_dev_clr; +}; + +struct funit_cfg_data { + const char *name; + void *ctlr_base; + uint32_t *clk_src_reg; + const struct clk_dev_control * const dev_control; + uint32_t clk_enb_val; +}; + +enum { + CLK_L_SET = 0, + CLK_H_SET = 1, + CLK_U_SET = 2, + CLK_V_SET = 3, + CLK_W_SET = 4, + CLK_X_SET = 5, + CLK_Y_SET = 6, +}; + +#define CLK_SET_REGS(x) \ + { \ + CLK_RST_REG(clk_enb_##x##_set), \ + CLK_RST_REG(rst_dev_##x##_clr), \ + } + +static const struct clk_dev_control clk_data_arr[] = { + [CLK_L_SET] = CLK_SET_REGS(l), + [CLK_H_SET] = CLK_SET_REGS(h), + [CLK_U_SET] = CLK_SET_REGS(u), + [CLK_V_SET] = CLK_SET_REGS(v), + [CLK_W_SET] = CLK_SET_REGS(w), + [CLK_X_SET] = CLK_SET_REGS(x), + [CLK_Y_SET] = CLK_SET_REGS(y), +}; + +#define FUNIT_DATA(funit_, loname_, clk_set_) \ + [FUNIT_INDEX(funit_)] = { \ + .name = STRINGIFY(loname_), \ + .ctlr_base = (void *)(uintptr_t)TEGRA_##funit_##_BASE, \ + .clk_src_reg = CLK_RST_REG(clk_src_##loname_), \ + .dev_control = &clk_data_arr[CLK_##clk_set_##_SET], \ + .clk_enb_val = CLK_##clk_set_##_##funit_, \ + } + +#define FUNIT_DATA_USB(funit_, clk_set_) \ + [FUNIT_INDEX(funit_)] = { \ + .name = STRINGIFY(funit_), \ + .ctlr_base = (void *)(uintptr_t)TEGRA_##funit_##_BASE, \ + .dev_control = &clk_data_arr[CLK_##clk_set_##_SET], \ + .clk_enb_val = CLK_##clk_set_##_##funit_, \ + } + +static const struct funit_cfg_data funit_data[] = { + FUNIT_DATA(I2C1, i2c1, L), + FUNIT_DATA(I2C2, i2c2, H), + FUNIT_DATA(I2C3, i2c3, U), + FUNIT_DATA(I2C5, i2c5, H), + FUNIT_DATA(I2C6, i2c6, X), + FUNIT_DATA(SDMMC1, sdmmc1, L), + FUNIT_DATA(SDMMC4, sdmmc4, L), + FUNIT_DATA_USB(USBD, L), + FUNIT_DATA_USB(USB2, H), + FUNIT_DATA(QSPI, qspi, Y), + FUNIT_DATA(I2S1, i2s1, L), +}; +_Static_assert(ARRAY_SIZE(funit_data) == FUNIT_INDEX_MAX, + "funit_cfg_data array not filled out!"); + +static inline uint32_t get_clk_src_freq(uint32_t clk_src_freq_id) +{ + uint32_t freq = 0; + + switch (clk_src_freq_id) { + case CLK_M: + freq = TEGRA_CLK_M_KHZ; + break; + case PLLP: + freq = TEGRA_PLLP_KHZ; + break; + default: + printk(BIOS_SPEW, "%s ERROR: Unknown clk_src %d\n", + __func__, clk_src_freq_id); + } + + return freq; +} + +static void configure_clock(const struct funit_cfg * const entry, + const struct funit_cfg_data * const funit) +{ + const char *funit_i2c = "i2c"; + uint32_t clk_div; + uint32_t clk_div_mask; + uint32_t clk_src_freq; + + clk_src_freq = get_clk_src_freq(entry->clk_src_freq_id); + + if (strncmp(funit->name, funit_i2c, strlen(funit_i2c)) == 0) { + /* I2C funit */ + clk_div = get_i2c_clk_div(clk_src_freq, + entry->clk_dev_freq_khz); + clk_div_mask = CLK_DIV_MASK_I2C; + } else { + /* Non I2C */ + clk_div = get_clk_div(clk_src_freq, entry->clk_dev_freq_khz); + clk_div_mask = CLK_DIV_MASK; + } + + _clock_set_div(funit->clk_src_reg, funit->name, clk_div, + clk_div_mask, entry->clk_src_id); +} + +static inline int is_usb(uint32_t idx) +{ + return (idx == FUNIT_USBD || idx == FUNIT_USB2); +} + +void soc_configure_funits(const struct funit_cfg * const entries, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + const struct funit_cfg * const entry = &entries[i]; + const struct funit_cfg_data *funit; + const struct clk_dev_control *dev_control; + int funit_usb = is_usb(entry->funit_index); + + if (entry->funit_index >= FUNIT_INDEX_MAX) { + printk(BIOS_ERR, "Error: Index out of bounds\n"); + continue; + } + + funit = &funit_data[entry->funit_index]; + dev_control = funit->dev_control; + + /* USB controllers have a fixed clock source. */ + if (!funit_usb) + configure_clock(entry, funit); + + clock_grp_enable_clear_reset(funit->clk_enb_val, + dev_control->clk_enb_set, + dev_control->rst_dev_clr); + + if (funit_usb) + usb_setup_utmip(funit->ctlr_base); + + soc_configure_pads(entry->pad_cfg,entry->pad_cfg_size); + } +} + +void __attribute__((weak)) usb_setup_utmip(void *usb_base) +{ + /* default empty implementation required if usb.c is not included */ + printk(BIOS_ERR, "USB setup is not supported in current stage\n"); +} diff --git a/src/soc/nvidia/tegra210/gic.c b/src/soc/nvidia/tegra210/gic.c new file mode 100644 index 0000000000..c8c09fbfde --- /dev/null +++ b/src/soc/nvidia/tegra210/gic.c @@ -0,0 +1,31 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <gic.h> +#include <soc/addressmap.h> + +void *gicd_base(void) +{ + return (void *)(uintptr_t)TEGRA_GICD_BASE; +} + +void *gicc_base(void) +{ + return (void *)(uintptr_t)TEGRA_GICC_BASE; +} diff --git a/src/soc/nvidia/tegra210/i2c.c b/src/soc/nvidia/tegra210/i2c.c new file mode 100644 index 0000000000..04a0c00152 --- /dev/null +++ b/src/soc/nvidia/tegra210/i2c.c @@ -0,0 +1,57 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/nvidia/tegra/i2c.h> + +struct tegra_i2c_bus_info tegra_i2c_info[] = { + { + .base = (void *)TEGRA_I2C1_BASE, + .reset_bit = CLK_L_I2C1, + .reset_func = &clock_reset_l + }, + { + .base = (void *)TEGRA_I2C2_BASE, + .reset_bit = CLK_H_I2C2, + .reset_func = &clock_reset_h + }, + { + .base = (void *)TEGRA_I2C3_BASE, + .reset_bit = CLK_U_I2C3, + .reset_func = &clock_reset_u + }, + { + .base = (void *)TEGRA_I2C4_BASE, + .reset_bit = CLK_V_I2C4, + .reset_func = &clock_reset_v + }, + { + .base = (void *)TEGRA_I2C5_BASE, + .reset_bit = CLK_H_I2C5, + .reset_func = &clock_reset_h + }, + { + .base = (void *)TEGRA_I2C6_BASE, + .reset_bit = CLK_X_I2C6, + .reset_func = &clock_reset_x + } +}; + +unsigned g_num_i2c_buses = ARRAY_SIZE(tegra_i2c_info); diff --git a/src/soc/nvidia/tegra210/i2c6.c b/src/soc/nvidia/tegra210/i2c6.c new file mode 100644 index 0000000000..915a5386e9 --- /dev/null +++ b/src/soc/nvidia/tegra210/i2c6.c @@ -0,0 +1,95 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2014-2015, NVIDIA CORPORATION. + * Copyright 2014 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. + */ + +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/clock.h> +#include <soc/nvidia/tegra/i2c.h> +#include <soc/padconfig.h> +#include <soc/power.h> + +#define I2C6_PADCTL 0xC001 +#define DPAUX_HYBRID_PADCTL 0x545C0124 +#define DPAUX_HYBRID_SPARE 0x545C0134 + +static void enable_sor_periph_clocks(void) +{ + clock_enable(CLK_L_HOST1X, 0, 0, 0, 0, CLK_X_DPAUX, 0); + + /* Give clocks time to stabilize. */ + udelay(IO_STABILIZATION_DELAY); +} + +static void disable_sor_periph_clocks(void) +{ + clock_disable(CLK_L_HOST1X, 0, 0, 0, 0, CLK_X_DPAUX, 0); + + /* Give clocks time to stabilize. */ + udelay(IO_STABILIZATION_DELAY); +} + +static void unreset_sor_periphs(void) +{ + clock_clr_reset(CLK_L_HOST1X, 0, 0, 0, 0, CLK_X_DPAUX, 0); +} + +void soc_configure_i2c6pad(void) +{ + /* + * I2C6 on Tegra1xx requires some special init. + * The SOR block must be unpowergated, and a couple of + * display-based peripherals must be clocked and taken + * out of reset so that a DPAUX register can be + * configured to enable the I2C6 mux routing. + * Afterwards, we can disable clocks to the display blocks + * and put Host1X back in reset. DPAUX must remain out of + * reset and the SOR partition must remained unpowergated. + */ + soc_configure_host1x(); + + /* Now we can write the I2C6 mux in DPAUX */ + write32((void *)DPAUX_HYBRID_PADCTL, I2C6_PADCTL); + /* Finally, power up the pads */ + write32((void *)DPAUX_HYBRID_SPARE, 0); + + /* + * Delay before turning off Host1X/DPAUX clocks. + * This delay is needed to keep the sequence from + * hanging the system. + */ + udelay(CLOCK_PLL_STABLE_DELAY_US); + + /* Stop Host1X/DPAUX clocks and reset Host1X */ + disable_sor_periph_clocks(); + clock_set_reset_l(CLK_L_HOST1X); +} + +void soc_configure_host1x(void) +{ + power_ungate_partition(POWER_PARTID_SOR); + + /* Host1X needs a valid clock source so DPAUX can be accessed. */ + clock_configure_source(host1x, PLLP, 204000); + + enable_sor_periph_clocks(); + remove_clamps(POWER_PARTID_SOR); + unreset_sor_periphs(); +} diff --git a/src/soc/nvidia/tegra210/include/soc/addressmap.h b/src/soc/nvidia/tegra210/include/soc/addressmap.h new file mode 100644 index 0000000000..4a5372da75 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/addressmap.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * (C) Copyright 2010,2011 + * NVIDIA Corporation <www.nvidia.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ADDRESS_MAP_H__ +#define __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ADDRESS_MAP_H__ + +#include <stddef.h> +#include <stdint.h> + +enum { + TEGRA_SRAM_BASE = 0x40000000, + TEGRA_SRAM_SIZE = 0x40000 +}; + +enum { + TEGRA_ARM_PERIPHBASE = 0x50040000, + TEGRA_GICD_BASE = 0x50041000, + TEGRA_GICC_BASE = 0x50042000, + TEGRA_ARM_DISPLAYA = 0x54200000, + TEGRA_ARM_DISPLAYB = 0x54240000, + TEGRA_DSIA_BASE = 0x54300000, + TEGRA_DSIB_BASE = 0x54400000, + TEGRA_ARM_SOR = 0x54540000, + TEGRA_ARM_DPAUX = 0x545c0000, + TEGRA_PG_UP_BASE = 0x60000000, + TEGRA_TMRUS_BASE = 0x60005010, + TEGRA_CLK_RST_BASE = 0x60006000, + TEGRA_FLOW_BASE = 0x60007000, + TEGRA_SB_BASE = 0x6000C200, + TEGRA_GPIO_BASE = 0x6000D000, + TEGRA_EVP_BASE = 0x6000F000, + TEGRA_APB_DMA_BASE = 0x60020000, + TEGRA_APB_MISC_BASE = 0x70000000, + TEGRA_APB_MISC_GP_BASE = TEGRA_APB_MISC_BASE + 0x0800, + TEGRA_APB_PINGROUP_BASE = TEGRA_APB_MISC_BASE + 0x0868, + TEGRA_APB_PINMUX_BASE = TEGRA_APB_MISC_BASE + 0x3000, + TEGRA_APB_UARTA_BASE = TEGRA_APB_MISC_BASE + 0x6000, + TEGRA_APB_UARTB_BASE = TEGRA_APB_MISC_BASE + 0x6040, + TEGRA_APB_UARTC_BASE = TEGRA_APB_MISC_BASE + 0x6200, + TEGRA_APB_UARTD_BASE = TEGRA_APB_MISC_BASE + 0x6300, + TEGRA_APB_UARTE_BASE = TEGRA_APB_MISC_BASE + 0x6400, + TEGRA_NAND_BASE = TEGRA_APB_MISC_BASE + 0x8000, + TEGRA_PWM_BASE = TEGRA_APB_MISC_BASE + 0xA000, + TEGRA_I2C1_BASE = TEGRA_APB_MISC_BASE + 0xC000, + TEGRA_SPI_BASE = TEGRA_APB_MISC_BASE + 0xC380, + TEGRA_I2C2_BASE = TEGRA_APB_MISC_BASE + 0xC400, + TEGRA_I2C3_BASE = TEGRA_APB_MISC_BASE + 0xC500, + TEGRA_I2C4_BASE = TEGRA_APB_MISC_BASE + 0xC700, + TEGRA_I2C5_BASE = TEGRA_APB_MISC_BASE + 0xD000, + TEGRA_I2C6_BASE = TEGRA_APB_MISC_BASE + 0xD100, + TEGRA_SPI1_BASE = TEGRA_APB_MISC_BASE + 0xD400, + TEGRA_SPI2_BASE = TEGRA_APB_MISC_BASE + 0xD600, + TEGRA_SPI3_BASE = TEGRA_APB_MISC_BASE + 0xD800, + TEGRA_SPI4_BASE = TEGRA_APB_MISC_BASE + 0xDA00, + TEGRA_SPI5_BASE = TEGRA_APB_MISC_BASE + 0xDC00, + TEGRA_SPI6_BASE = TEGRA_APB_MISC_BASE + 0xDE00, + TEGRA_SBC1_BASE = TEGRA_SPI1_BASE, + TEGRA_SBC2_BASE = TEGRA_SPI2_BASE, + TEGRA_SBC3_BASE = TEGRA_SPI3_BASE, + TEGRA_SBC4_BASE = TEGRA_SPI4_BASE, + TEGRA_SBC5_BASE = TEGRA_SPI5_BASE, + TEGRA_SBC6_BASE = TEGRA_SPI6_BASE, + TEGRA_PMC_BASE = TEGRA_APB_MISC_BASE + 0xE400, + TEGRA_FUSE_BASE = TEGRA_APB_MISC_BASE + 0xF800, + TEGRA_MC_BASE = 0x70019000, + TEGRA_EMC_BASE = 0x7001B000, + TEGRA_CLUSTER_CLOCK_BASE = 0x70040000, + TEGRA_QSPI_BASE = 0x70410000, + TEGRA_CSITE_BASE = 0x70800000, + TEGRA_SDMMC_BASE = 0x700b0000, + TEGRA_SDMMC1_BASE = TEGRA_SDMMC_BASE + 0x0000, + TEGRA_SDMMC2_BASE = TEGRA_SDMMC_BASE + 0x0200, + TEGRA_SDMMC3_BASE = TEGRA_SDMMC_BASE + 0x0400, + TEGRA_SDMMC4_BASE = TEGRA_SDMMC_BASE + 0x0600, + TEGRA_MIPI_CAL_BASE = 0x700E3000, + TEGRA_SYSCTR0_BASE = 0x700F0000, + TEGRA_I2S1_BASE = 0x70301100, + TEGRA_USBD_BASE = 0x7D000000, + TEGRA_USB2_BASE = 0x7D004000, + TEGRA_USB3_BASE = 0x7D008000, +}; + +enum { + TEGRA_I2C_BASE_COUNT = 6, +}; + +#define GPU_CARVEOUT_SIZE_MB 1 + +/* Return total size of DRAM memory configured on the platform. */ +int sdram_size_mb(void); + +/* Find memory below and above 4GiB boundary repsectively. All units 1MiB. */ +void memory_in_range_below_4gb(uintptr_t *base_mib, uintptr_t *end_mib); +void memory_in_range_above_4gb(uintptr_t *base_mib, uintptr_t *end_mib); + +enum { + CARVEOUT_TZ, + CARVEOUT_SEC, + CARVEOUT_MTS, + CARVEOUT_VPR, + CARVEOUT_GPU, + CARVEOUT_NUM, +}; + +/* Provided the careout id, obtain the base and size in 1MiB units. */ +void carveout_range(int id, uintptr_t *base_mib, size_t *size_mib); +void print_carveouts(void); + +/* + * Add any board-specific memory ranges to the address map when executing + * on aarchv8 core. + */ +struct memranges; +void mainboard_add_memory_ranges(struct memranges *map); + +/* + * There are complications accessing the Trust Zone carveout region. The + * AVP cannot access these registers and the CPU can't access this register + * as a non-secure access. When the page tables live in non-secure memory + * these registers cannot be accessed either. Thus, this function handles + * both the AVP case and non-secured access case by keeping global state. + */ +void trustzone_region_init(void); +void gpu_region_init(void); + +#endif /* __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ADDRESS_MAP_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/ccplex.h b/src/soc/nvidia/tegra210/include/soc/ccplex.h new file mode 100644 index 0000000000..d1aaa8bb2c --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/ccplex.h @@ -0,0 +1,33 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_CCPLEX_H__ +#define __SOC_NVIDIA_TEGRA210_CCPLEX_H__ + +#include <stdint.h> + +#define MTS_LOAD_ADDRESS 0x82000000 + +/* Prepare the clocks and rails to start the cpu. */ +void ccplex_cpu_prepare(void); + +/* Start cpu0 and have it start executing at entry_addr */ +void ccplex_cpu_start(void *entry_addr); + +#endif /* __SOC_NVIDIA_TEGRA210_CCPLEX_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/clk_rst.h b/src/soc/nvidia/tegra210/include/soc/clk_rst.h new file mode 100644 index 0000000000..60ee112d5e --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/clk_rst.h @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef _TEGRA210_CLK_RST_H_ +#define _TEGRA210_CLK_RST_H_ +#include <stdint.h> +#include <stddef.h> + +/* Clock/Reset Controller (CLK_RST_CONTROLLER_) regs */ +struct __attribute__ ((__packed__)) clk_rst_ctlr { + u32 rst_src; /* _RST_SOURCE, 0x000 */ + u32 rst_dev_l; /* _RST_DEVICES_L, 0x004 */ + u32 rst_dev_h; /* _RST_DEVICES_H, 0x008 */ + u32 rst_dev_u; /* _RST_DEVICES_U, 0x00c */ + u32 clk_out_enb_l; /* _CLK_OUT_ENB_L, 0x010 */ + u32 clk_out_enb_h; /* _CLK_OUT_ENB_H, 0x014 */ + u32 clk_out_enb_u; /* _CLK_OUT_ENB_U, 0x018 */ + u32 _rsv0; /* 0x01c */ + u32 cclk_brst_pol; /* _CCLK_BURST_POLICY, 0x020 */ + u32 super_cclk_div; /* _SUPER_CCLK_DIVIDER, 0x024 */ + u32 sclk_brst_pol; /* _SCLK_BURST_POLICY, 0x028 */ + u32 super_sclk_div; /* _SUPER_SCLK_DIVIDER, 0x02C */ + u32 clk_sys_rate; /* _CLK_SYSTEM_RATE, 0x030 */ + u32 _rsv1[3]; /* 0x034-03c */ + u32 cop_clk_skip_plcy; /* _COP_CLK_SKIP_POLICY, 0x040 */ + u32 clk_mask_arm; /* _CLK_MASK_ARM, 0x044 */ + u32 misc_clk_enb; /* _MISC_CLK_ENB, 0x048 */ + u32 clk_cpu_cmplx; /* _CLK_CPU_CMPLX, 0x04C */ + u32 osc_ctrl; /* _OSC_CTRL, 0x050 */ + u32 pll_lfsr; /* _PLL_LFSR, 0x054 */ + u32 osc_freq_det; /* _OSC_FREQ_DET, 0x058 */ + u32 osc_freq_det_stat; /* _OSC_FREQ_DET_STATUS, 0x05C */ + u32 _rsv2[8]; /* 0x060-07C */ + u32 pllc_base; /* _PLLC_BASE, 0x080 */ + u32 pllc_out; /* _PLLC_OUT, 0x084 */ + u32 pllc_misc; /* _PLLC_MISC, 0x088 */ + u32 pllc_misc_1; /* _PLLC_MISC_1, 0x08c */ + u32 pllm_base; /* _PLLM_BASE, 0x090 */ + u32 pllm_out; /* _PLLM_OUT, 0x094 */ + u32 pllm_misc1; /* _PLLM_MISC1, 0x098 */ + u32 pllm_misc2; /* _PLLM_MISC2, 0x09c */ + u32 pllp_base; /* _PLLP_BASE, 0x0a0 */ + u32 pllp_outa; /* _PLLP_OUTA, 0x0a4 */ + u32 pllp_outb; /* _PLLP_OUTB, 0x0a8 */ + u32 pllp_misc; /* _PLLP_MISC, 0x0ac */ + u32 plla_base; /* _PLLA_BASE, 0x0b0 */ + u32 plla_out; /* _PLLA_OUT, 0x0b4 */ + u32 _rsv3; /* 0x0b8 */ + u32 plla_misc; /* _PLLA_MISC, 0x0bc */ + u32 pllu_base; /* _PLLU_BASE, 0x0c0 */ + u32 _rsv4[2]; /* 0x0c4-0c8 */ + u32 pllu_misc; /* _PLLU_MISC, 0x0cc */ + u32 plld_base; /* _PLLD_BASE, 0x0d0 */ + u32 _rsv5[1]; /* 0x0d4 */ + u32 plld_misc1; /* _PLLD_MISC1, 0x0d8 */ + u32 plld_misc; /* _PLLD_MISC, 0x0dc */ + u32 pllx_base; /* _PLLX_BASE, 0x0e0 */ + u32 pllx_misc; /* _PLLX_MISC, 0x0e4 */ + u32 plle_base; /* _PLLE_BASE, 0x0e8 */ + u32 plle_misc; /* _PLLE_MISC, 0x0ec */ + u32 plls_base; /* _PLLS_BASE, 0x0f0 */ + u32 plls_misc; /* _PLLS_MISC, 0x0f4 */ + u32 _rsv6[2]; /* 0x0f8-0fc */ + u32 clk_src_i2s1; /* _CLK_SOURCE_I2S1, 0x100 */ + u32 clk_src_i2s2; /* _CLK_SOURCE_I2S2, 0x104 */ + u32 clk_src_spdif_out; /* _CLK_SOURCE_SPDIF_OUT, 0x108 */ + u32 clk_src_spdif_in; /* _CLK_SOURCE_SPDIF_IN, 0x10c */ + u32 clk_src_pwm; /* _CLK_SOURCE_PWM, 0x110 */ + u32 _rsv7; /* 0x114 */ + u32 clk_src_sbc2; /* _CLK_SOURCE_SBC2, 0x118 */ + u32 clk_src_sbc3; /* _CLK_SOURCE_SBC3, 0x11c */ + u32 _rsv8; /* 0x120 */ + u32 clk_src_i2c1; /* _CLK_SOURCE_I2C1, 0x124 */ + u32 clk_src_i2c5; /* _CLK_SOURCE_I2C5, 0x128 */ + u32 _rsv9[2]; /* 0x12c-130 */ + u32 clk_src_sbc1; /* _CLK_SOURCE_SBC1, 0x134 */ + u32 clk_src_disp1; /* _CLK_SOURCE_DISP1, 0x138 */ + u32 clk_src_disp2; /* _CLK_SOURCE_DISP2, 0x13c */ + u32 _rsv10[2]; /* 0x140-144 */ + u32 clk_src_vi; /* _CLK_SOURCE_VI, 0x148 */ + u32 _rsv11; /* 0x14c */ + u32 clk_src_sdmmc1; /* _CLK_SOURCE_SDMMC1, 0x150 */ + u32 clk_src_sdmmc2; /* _CLK_SOURCE_SDMMC2, 0x154 */ + u32 clk_src_g3d; /* _CLK_SOURCE_G3D, 0x158 */ + u32 clk_src_g2d; /* _CLK_SOURCE_G2D, 0x15c */ + u32 clk_src_ndflash; /* _CLK_SOURCE_NDFLASH, 0x160 */ + u32 clk_src_sdmmc4; /* _CLK_SOURCE_SDMMC4, 0x164 */ + u32 clk_src_vfir; /* _CLK_SOURCE_VFIR, 0x168 */ + u32 clk_src_epp; /* _CLK_SOURCE_EPP, 0x16c */ + u32 clk_src_mpe; /* _CLK_SOURCE_MPE, 0x170 */ + u32 clk_src_hsi; /* _CLK_SOURCE_HSI, 0x174 */ + u32 clk_src_uarta; /* _CLK_SOURCE_UARTA, 0x178 */ + u32 clk_src_uartb; /* _CLK_SOURCE_UARTB, 0x17c */ + u32 clk_src_host1x; /* _CLK_SOURCE_HOST1X, 0x180 */ + u32 _rsv12[2]; /* 0x184-188 */ + u32 clk_src_hdmi; /* _CLK_SOURCE_HDMI, 0x18c */ + u32 _rsv13[2]; /* 0x190-194 */ + u32 clk_src_i2c2; /* _CLK_SOURCE_I2C2, 0x198 */ + u32 clk_src_emc; /* _CLK_SOURCE_EMC, 0x19c */ + u32 clk_src_uartc; /* _CLK_SOURCE_UARTC, 0x1a0 */ + u32 _rsv14; /* 0x1a4 */ + u32 clk_src_vi_sensor; /* _CLK_SOURCE_VI_SENSOR, 0x1a8 */ + u32 _rsv15[2]; /* 0x1ac-1b0 */ + u32 clk_src_sbc4; /* _CLK_SOURCE_SBC4, 0x1b4 */ + u32 clk_src_i2c3; /* _CLK_SOURCE_I2C3, 0x1b8 */ + u32 clk_src_sdmmc3; /* _CLK_SOURCE_SDMMC3, 0x1bc */ + u32 clk_src_uartd; /* _CLK_SOURCE_UARTD, 0x1c0 */ + u32 clk_src_uarte; /* _CLK_SOURCE_UARTE, 0x1c4 */ + u32 clk_src_vde; /* _CLK_SOURCE_VDE, 0x1c8 */ + u32 clk_src_owr; /* _CLK_SOURCE_OWR, 0x1cc */ + u32 clk_src_nor; /* _CLK_SOURCE_NOR, 0x1d0 */ + u32 clk_src_csite; /* _CLK_SOURCE_CSITE, 0x1d4 */ + u32 clk_src_i2s0; /* _CLK_SOURCE_I2S0, 0x1d8 */ + u32 clk_src_dtv; /* _CLK_SOURCE_DTV, 0x1dc */ + u32 _rsv16[4]; /* 0x1e0-1ec */ + u32 clk_src_msenc; /* _CLK_SOURCE_MSENC, 0x1f0 */ + u32 clk_src_tsec; /* _CLK_SOURCE_TSEC, 0x1f4 */ + u32 _rsv17; /* 0x1f8 */ + u32 clk_src_osc; /* _CLK_SOURCE_OSC, 0x1fc */ + u32 _rsv18[32]; /* 0x200-27c */ + u32 clk_out_enb_x; /* _CLK_OUT_ENB_X_0, 0x280 */ + u32 clk_enb_x_set; /* _CLK_ENB_X_SET_0, 0x284 */ + u32 clk_enb_x_clr; /* _CLK_ENB_X_CLR_0, 0x288 */ + u32 rst_dev_x; /* _RST_DEVICES_X_0, 0x28c */ + u32 rst_dev_x_set; /* _RST_DEV_X_SET_0, 0x290 */ + u32 rst_dev_x_clr; /* _RST_DEV_X_CLR_0, 0x294 */ + u32 clk_out_enb_y; /* _CLK_OUT_ENB_Y_0, 0x298 */ + u32 clk_enb_y_set; /* _CLK_ENB_Y_SET_0, 0x29C */ + u32 clk_enb_y_clr; /* _CLK_ENB_Y_CLR_0, 0x2A0 */ + u32 rst_dev_y; /* _RST_DEVICES_Y_0, 0x2A4 */ + u32 rst_dev_y_set; /* _RST_DEV_Y_SET_0, 0x2A8 */ + u32 rst_dev_y_clr; /* _RST_DEV_Y_CLR_0, 0x2AC */ + u32 _rsv19[17]; /* 0x2B0-2f0 */ + u32 dfll_base; /* _DFLL_BASE_0, 0x2f4 */ + u32 _rsv20[2]; /* 0x2f8-2fc */ + u32 rst_dev_l_set; /* _RST_DEV_L_SET 0x300 */ + u32 rst_dev_l_clr; /* _RST_DEV_L_CLR 0x304 */ + u32 rst_dev_h_set; /* _RST_DEV_H_SET 0x308 */ + u32 rst_dev_h_clr; /* _RST_DEV_H_CLR 0x30c */ + u32 rst_dev_u_set; /* _RST_DEV_U_SET 0x310 */ + u32 rst_dev_u_clr; /* _RST_DEV_U_CLR 0x314 */ + u32 _rsv21[2]; /* 0x318-31c */ + u32 clk_enb_l_set; /* _CLK_ENB_L_SET 0x320 */ + u32 clk_enb_l_clr; /* _CLK_ENB_L_CLR 0x324 */ + u32 clk_enb_h_set; /* _CLK_ENB_H_SET 0x328 */ + u32 clk_enb_h_clr; /* _CLK_ENB_H_CLR 0x32c */ + u32 clk_enb_u_set; /* _CLK_ENB_U_SET 0x330 */ + u32 clk_enb_u_clr; /* _CLK_ENB_U_CLR 0x334 */ + u32 _rsv22; /* 0x338 */ + u32 ccplex_pg_sm_ovrd; /* _CCPLEX_PG_SM_OVRD, 0x33c */ + u32 rst_cpu_cmplx_set; /* _RST_CPU_CMPLX_SET, 0x340 */ + u32 rst_cpu_cmplx_clr; /* _RST_CPU_CMPLX_CLR, 0x344 */ + u32 clk_cpu_cmplx_set; /* _CLK_CPU_CMPLX_SET, 0x348 */ + u32 clk_cpu_cmplx_clr; /* _CLK_CPU_CMPLX_SET, 0x34c */ + u32 _rsv23[2]; /* 0x350-354 */ + u32 rst_dev_v; /* _RST_DEVICES_V, 0x358 */ + u32 rst_dev_w; /* _RST_DEVICES_W, 0x35c */ + u32 clk_out_enb_v; /* _CLK_OUT_ENB_V, 0x360 */ + u32 clk_out_enb_w; /* _CLK_OUT_ENB_W, 0x364 */ + u32 cclkg_brst_pol; /* _CCLKG_BURST_POLICY, 0x368 */ + u32 super_cclkg_div; /* _SUPER_CCLKG_DIVIDER, 0x36c */ + u32 cclklp_brst_pol; /* _CCLKLP_BURST_POLICY, 0x370 */ + u32 super_cclkp_div; /* _SUPER_CCLKLP_DIVIDER, 0x374 */ + u32 clk_cpug_cmplx; /* _CLK_CPUG_CMPLX, 0x378 */ + u32 clk_cpulp_cmplx; /* _CLK_CPULP_CMPLX, 0x37c */ + u32 cpu_softrst_ctrl; /* _CPU_SOFTRST_CTRL, 0x380 */ + u32 cpu_softrst_ctrl1; /* _CPU_SOFTRST_CTRL1, 0x384 */ + u32 cpu_softrst_ctrl2; /* _CPU_SOFTRST_CTRL2, 0x388 */ + u32 _rsv24[9]; /* 0x38c-3ac */ + u32 clk_src_g3d2; /* _CLK_SOURCE_G3D2, 0x3b0 */ + u32 clk_src_mselect; /* _CLK_SOURCE_MSELECT, 0x3b4 */ + u32 clk_src_tsensor; /* _CLK_SOURCE_TSENSOR, 0x3b8 */ + u32 clk_src_i2s3; /* _CLK_SOURCE_I2S3, 0x3bc */ + u32 clk_src_i2s4; /* _CLK_SOURCE_I2S4, 0x3c0 */ + u32 clk_src_i2c4; /* _CLK_SOURCE_I2C4, 0x3c4 */ + u32 clk_src_sbc5; /* _CLK_SOURCE_SBC5, 0x3c8 */ + u32 clk_src_sbc6; /* _CLK_SOURCE_SBC6, 0x3cc */ + u32 clk_src_audio; /* _CLK_SOURCE_AUDIO, 0x3d0 */ + u32 _rsv25; /* 0x3d4 */ + u32 clk_src_dam0; /* _CLK_SOURCE_DAM0, 0x3d8 */ + u32 clk_src_dam1; /* _CLK_SOURCE_DAM1, 0x3dc */ + u32 clk_src_dam2; /* _CLK_SOURCE_DAM2, 0x3e0 */ + u32 clk_src_hda2codec_2x; /* _CLK_SOURCE_HDA2CODEC_2X,0x3e4 */ + u32 clk_src_actmon; /* _CLK_SOURCE_ACTMON, 0x3e8 */ + u32 clk_src_extperiph1; /* _CLK_SOURCE_EXTPERIPH1, 0x3ec */ + u32 clk_src_extperiph2; /* _CLK_SOURCE_EXTPERIPH2, 0x3f0 */ + u32 clk_src_extperiph3; /* _CLK_SOURCE_EXTPERIPH3, 0x3f4 */ + u32 clk_src_nand_speed; /* _CLK_SOURCE_NAND_SPEED, 0x3f8 */ + u32 clk_src_i2c_slow; /* _CLK_SOURCE_I2C_SLOW, 0x3fc */ + u32 clk_src_sys; /* _CLK_SOURCE_SYS, 0x400 */ + u32 _rsv26[4]; /* 0x404-410 */ + u32 clk_src_sor; /* _CLK_SOURCE_SOR_0, 0x414 */ + u32 _rsv261[2]; /* 0x404-410 */ + u32 clk_src_sata_oob; /* _CLK_SOURCE_SATA_OOB, 0x420 */ + u32 clk_src_sata; /* _CLK_SOURCE_SATA, 0x424 */ + u32 clk_src_hda; /* _CLK_SOURCE_HDA, 0x428 */ + u32 _rsv27; /* 0x42c */ + u32 rst_dev_v_set; /* _RST_DEV_V_SET, 0x430 */ + u32 rst_dev_v_clr; /* _RST_DEV_V_CLR, 0x434 */ + u32 rst_dev_w_set; /* _RST_DEV_W_SET, 0x438 */ + u32 rst_dev_w_clr; /* _RST_DEV_W_CLR, 0x43c */ + u32 clk_enb_v_set; /* _CLK_ENB_V_SET, 0x440 */ + u32 clk_enb_v_clr; /* _CLK_ENB_V_CLR, 0x444 */ + u32 clk_enb_w_set; /* _CLK_ENB_W_SET, 0x448 */ + u32 clk_enb_w_clr; /* _CLK_ENB_W_CLR, 0x44c */ + u32 rst_cpug_cmplx_set; /* _RST_CPUG_CMPLX_SET, 0x450 */ + u32 rst_cpug_cmplx_clr; /* _RST_CPUG_CMPLX_CLR, 0x454 */ + u32 rst_cpulp_cmplx_set; /* _RST_CPULP_CMPLX_SET, 0x458 */ + u32 rst_cpulp_cmplx_clr; /* _RST_CPULP_CMPLX_CLR, 0x45C */ + u32 clk_cpug_cmplx_set; /* _CLK_CPUG_CMPLX_SET, 0x460 */ + u32 clk_cpug_cmplx_clr; /* _CLK_CPUG_CMPLX_CLR, 0x464 */ + u32 clk_cpulp_cmplx_set; /* _CLK_CPULP_CMPLX_SET, 0x468 */ + u32 clk_cpulp_cmplx_clr; /* _CLK_CPULP_CMPLX_CLR, 0x46c */ + u32 cpu_cmplx_status; /* _CPU_CMPLX_STATUS, 0x470 */ + u32 _rsv28; /* 0x474 */ + u32 intstatus; /* _INTSTATUS, 0x478 */ + u32 intmask; /* _INTMASK, 0x47c */ + u32 utmip_pll_cfg0; /* _UTMIP_PLL_CFG0, 0x480 */ + u32 utmip_pll_cfg1; /* _UTMIP_PLL_CFG1, 0x484 */ + u32 utmip_pll_cfg2; /* _UTMIP_PLL_CFG2, 0x488 */ + u32 plle_aux; /* _PLLE_AUX, 0x48c */ + u32 sata_pll_cfg0; /* _SATA_PLL_CFG0, 0x490 */ + u32 sata_pll_cfg1; /* _SATA_PLL_CFG1, 0x494 */ + u32 pcie_pll_cfg0; /* _PCIE_PLL_CFG0, 0x498 */ + u32 prog_audio_dly_clk; /* _PROG_AUDIO_DLY_CLK, 0x49c */ + u32 audio_sync_clk_i2s0; /* _AUDIO_SYNC_CLK_I2S0, 0x4a0 */ + u32 audio_sync_clk_i2s1; /* _AUDIO_SYNC_CLK_I2S1, 0x4a4 */ + u32 audio_sync_clk_i2s2; /* _AUDIO_SYNC_CLK_I2S2, 0x4a8 */ + u32 audio_sync_clk_i2s3; /* _AUDIO_SYNC_CLK_I2S3, 0x4ac */ + u32 audio_sync_clk_i2s4; /* _AUDIO_SYNC_CLK_I2S4, 0x4b0 */ + u32 audio_sync_clk_spdif; /* _AUDIO_SYNC_CLK_SPDIF, 0x4b4 */ + u32 plld2_base; /* _PLLD2_BASE, 0x4b8 */ + u32 plld2_misc; /* _PLLD2_MISC, 0x4bc */ + u32 utmip_pll_cfg3; /* _UTMIP_PLL_CFG3, 0x4c0 */ + u32 pllrefe_base; /* _PLLREFE_BASE, 0x4c4 */ + u32 pllrefe_misc; /* _PLLREFE_MISC, 0x4c8 */ + u32 _rsv29[7]; /* 0x4cc-4e4 */ + u32 pllc2_base; /* _PLLC2_BASE, 0x4e8 */ + u32 pllc2_misc0; /* _PLLC2_MISC_0, 0x4ec */ + u32 pllc2_misc1; /* _PLLC2_MISC_1, 0x4f0 */ + u32 pllc2_misc2; /* _PLLC2_MISC_2, 0x4f4 */ + u32 pllc2_misc3; /* _PLLC2_MISC_3, 0x4f8 */ + u32 pllc3_base; /* _PLLC3_BASE, 0x4fc */ + u32 pllc3_misc0; /* _PLLC3_MISC_0, 0x500 */ + u32 pllc3_misc1; /* _PLLC3_MISC_1, 0x504 */ + u32 pllc3_misc2; /* _PLLC3_MISC_2, 0x508 */ + u32 pllc3_misc3; /* _PLLC3_MISC_3, 0x50c */ + u32 pllx_misc1; /* _PLLX_MISC_1, 0x510 */ + u32 pllx_misc2; /* _PLLX_MISC_2, 0x514 */ + u32 pllx_misc3; /* _PLLX_MISC_3, 0x518 */ + u32 xusbio_pll_cfg0; /* _XUSBIO_PLL_CFG0, 0x51c */ + u32 xusbio_pll_cfg1; /* _XUSBIO_PLL_CFG1, 0x520 */ + u32 plle_aux1; /* _PLLE_AUX1, 0x524 */ + u32 pllp_reshift; /* _PLLP_RESHIFT, 0x528 */ + u32 utmipll_hw_pwrdn_cfg0; /* _UTMIPLL_HW_PWRDN_CFG0, 0x52c */ + u32 pllu_hw_pwrdn_cfg0; /* _PLLU_HW_PWRDN_CFG0, 0x530 */ + u32 xusb_pll_cfg0; /* _XUSB_PLL_CFG0, 0x534 */ + u32 _rsv30; /* 0x538 */ + u32 clk_cpu_misc; /* _CLK_CPU_MISC, 0x53c */ + u32 clk_cpug_misc; /* _CLK_CPUG_MISC, 0x540 */ + u32 clk_cpulp_misc; /* _CLK_CPULP_MISC, 0x544 */ + u32 pllx_hw_ctrl_cfg; /* _PLLX_HW_CTRL_CFG, 0x548 */ + u32 pllx_sw_ramp_cfg; /* _PLLX_SW_RAMP_CFG, 0x54c */ + u32 pllx_hw_ctrl_status; /* _PLLX_HW_CTRL_STATUS, 0x550 */ + u32 _rsv31; /* 0x554 */ + u32 super_gr3d_clk_div; /* _SUPER_GR3D_CLK_DIVIDER, 0x558 */ + u32 spare_reg0; /* _SPARE_REG0, 0x55c */ + u32 _rsv32[4]; /* 0x560-0x56c */ + u32 plld2_ss_cfg; /* _PLLD2_SS_CFG 0x570 */ + u32 _rsv32_1[7]; /* 0x574-58c */ + u32 plldp_base; /* _PLLDP_BASE, 0x590 */ + u32 plldp_misc; /* _PLLDP_MISC, 0x594 */ + u32 plldp_ss_cfg; /* _PLLDP_SS_CFG, 0x598 */ + u32 _rsrv32_2[25]; + u32 clk_src_xusb_core_host; /* _CLK_SOURCE_XUSB_CORE_HOST 0x600 */ + u32 clk_src_xusb_falcon; /* _CLK_SOURCE_XUSB_FALCON 0x604 */ + u32 clk_src_xusb_fs; /* _CLK_SOURCE_XUSB_FS 0x608 */ + u32 clk_src_xusb_core_dev; /* _CLK_SOURCE_XUSB_CORE_DEV 0x60c */ + u32 clk_src_xusb_ss; /* _CLK_SOURCE_XUSB_SS 0x610 */ + u32 clk_src_cilab; /* _CLK_SOURCE_CILAB 0x614 */ + u32 clk_src_cilcd; /* _CLK_SOURCE_CILCD 0x618 */ + u32 clk_src_cile; /* _CLK_SOURCE_CILE 0x61c */ + u32 clk_src_dsia_lp; /* _CLK_SOURCE_DSIA_LP 0x620 */ + u32 clk_src_dsib_lp; /* _CLK_SOURCE_DSIB_LP 0x624 */ + u32 clk_src_entropy; /* _CLK_SOURCE_ENTROPY 0x628 */ + u32 clk_src_dvfs_ref; /* _CLK_SOURCE_DVFS_REF 0x62c */ + u32 clk_src_dvfs_soc; /* _CLK_SOURCE_DVFS_SOC 0x630 */ + u32 clk_src_traceclkin; /* _CLK_SOURCE_TRACECLKIN 0x634 */ + u32 clk_src_adx0; /* _CLK_SOURCE_ADX0 0x638 */ + u32 clk_src_amx0; /* _CLK_SOURCE_AMX0 0x63c */ + u32 clk_src_emc_latency; /* _CLK_SOURCE_EMC_LATENCY 0x640 */ + u32 clk_src_soc_therm; /* _CLK_SOURCE_SOC_THERM 0x644 */ + u32 _rsv33[5]; /* 0x648-658 */ + u32 clk_src_i2c6; /* _CLK_SOURCE_I2C6, 0x65c */ + u32 clk_src_mipibif; /* _CLK_SOURCE_MIPIBIF, 0x660 */ + u32 clk_src_emc_dll; /* _CLK_SOURCE_EMC_DLL, 0x664 */ + u32 _rsv34; /* 0x668 */ + u32 clk_src_uart_fst_mipi_cal; /* _CLK_SOURCE_UART_FST_MIP_CAL, 0x66c */ + u32 _rsv35[21]; /* 0x670-6c0 */ + u32 clk_src_qspi; /* _CLK_SOURCE_QSPI 0x6C4 */ +}; +check_member(clk_rst_ctlr, clk_src_qspi, 0x6C4); + +#define CLK_RST_REG(field_) \ + (&(((struct clk_rst_ctlr *)TEGRA_CLK_RST_BASE)->field_)) + +/* L, H, U, V, W, X, Y */ +#define DEV_CONFIG_BLOCKS 7 + +#define TEGRA_DEV_L 0 +#define TEGRA_DEV_H 1 +#define TEGRA_DEV_U 2 +#define TEGRA_DEV_V 0 +#define TEGRA_DEV_W 1 + +#define SIMPLE_PLLX (CLOCK_ID_XCPU - CLOCK_ID_FIRST_SIMPLE) + +/* Bits to enable/reset modules */ +#define CLK_ENB_CPU (1 << 0) +#define SWR_TRIG_SYS_RST (1 << 2) +#define SWR_CSITE_RST (1 << 9) +#define CLK_ENB_CSITE (1 << 9) +#define CLK_ENB_EMC_DLL (1 << 14) + +/* _CCLK_BURST_POLICY 0x20 */ +#define CCLK_BURST_POLICY_VAL 0x20008888 +/* CLK_M divisor */ +#define CLK_M_DIVISOR_MASK (0x3 << 2) +#define CLK_M_DIVISOR_BY_2 (1 << 2) + +/* CRC_SUPER_CCLK_DIVIDER_0 0x24 */ +#define SUPER_CDIV_ENB_ENABLE (1 << 31) + +/* CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48 */ +#define EN_PPSB_STOPCLK (1 << 0) + +/* CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0 (0x4C) */ +#define CPU3_CLK_STP_SHIFT 11 +#define CPU2_CLK_STP_SHIFT 10 +#define CPU1_CLK_STP_SHIFT 9 +#define CPU0_CLK_STP_SHIFT 8 +#define CPU0_CLK_STP_MASK (1U << CPU0_CLK_STP_SHIFT) + +/* CRC_OSC_CTRL_0 0x50 */ +#define OSC_FREQ_SHIFT 28 +#define OSC_FREQ_MASK (0xf << OSC_FREQ_SHIFT) +#define OSC_PREDIV_SHIFT 26 +#define OSC_PREDIV_MASK (0x3 << OSC_PREDIV_SHIFT) +#define OSC_XOFS_SHIFT 4 +#define OSC_XOFS_MASK (0x3F << OSC_XOFS_SHIFT) +#define OSC_DRIVE_STRENGTH 7 +#define OSC_XOBP (1 << 1) +#define OSC_XOE (1 << 0) + +enum { + OSC_FREQ_12 = 8, /* 12.0MHz */ + OSC_FREQ_13 = 0, /* 13.0MHz */ + OSC_FREQ_16P8 = 1, /* 16.8MHz */ + OSC_FREQ_19P2 = 4, /* 19.2MHz */ + OSC_FREQ_26 = 12, /* 26.0MHz */ + OSC_FREQ_38P4 = 5, /* 38.4MHz */ + OSC_FREQ_48 = 9, /* 48.0MHz */ +}; + +/* CLK_RST_CONTROLLER_PLL*_BASE_0 */ +#define PLL_BASE_BYPASS (1U << 31) +#define PLL_BASE_ENABLE (1U << 30) +#define PLL_BASE_REF_DIS (1U << 29) +#define PLL_BASE_OVRRIDE (1U << 28) +#define PLL_BASE_LOCK (1U << 27) +#define PLLC_BASE_LOCK (1U << 26) + +#define PLL_BASE_DIVP_SHIFT 20 +#define PLL_BASE_DIVP_MASK (7U << PLL_BASE_DIVP_SHIFT) + +#define PLL_BASE_DIVN_SHIFT 8 +#define PLL_BASE_DIVN_MASK (0x3ffU << PLL_BASE_DIVN_SHIFT) + +#define PLL_BASE_DIVM_SHIFT 0 +#define PLL_BASE_DIVM_MASK (0x1f << PLL_BASE_DIVM_SHIFT) + +/* SPECIAL CASE: PLLM, PLLC and PLLX use different-sized fields here */ +#define PLLCX_BASE_DIVP_MASK (0xfU << PLL_BASE_DIVP_SHIFT) +#define PLLM_BASE_DIVP_MASK (0x1fU << PLL_BASE_DIVP_SHIFT) +#define PLLCMX_BASE_DIVN_MASK (0xffU << PLL_BASE_DIVN_SHIFT) +#define PLLCMX_BASE_DIVM_MASK (0xffU << PLL_BASE_DIVM_SHIFT) + +/* Added based on T210 TRM */ +#define PLLC_MISC_RESET (1U << 30) +#define PLLC_MISC_1_IDDQ (1U << 27) +#define PLLD_N_SHIFT 11 +#define PLLD_M_SHIFT 0 +#define PLLD_P_SHIFT 20 +#define PLLD_MISC1_SETUP 0x20 +#define PLLD_MISC_EN_SDM (1 << 16) +#define PLLD_MISC_SDM_DIN 0x9aa + +/* PLLM specific registers */ +#define PLLM_MISC1_SETUP_SHIFT 0 +#define PLLM_MISC1_PD_LSHIFT_PH45_SHIFT 28 +#define PLLM_MISC1_PD_LSHIFT_PH90_SHIFT 29 +#define PLLM_MISC1_PD_LSHIFT_PH135_SHIFT 30 +#define PLLM_MISC2_KCP_SHIFT 1 +#define PLLM_MISC2_KVCO_SHIFT 0 +#define PLLM_OUT1_RSTN_RESET_DISABLE (1 << 0) +#define PLLM_EN_LCKDET (1 << 4) + +/* PLLU specific registers */ +#define PLLU_MISC_IDDQ (1U << 31) + +/* UTMIP PLL specific registers */ +#define UTMIP_CFG0_PLL_MDIV_SHIFT (8) +#define UTMIP_CFG0_PLL_NDIV_SHIFT (16) +#define UTMIP_CFG1_XTAL_FREQ_COUNT_SHIFT (0) +#define UTMIP_CFG1_FORCE_PLL_ACTIVE_POWERDOWN_DISABLE (0 << 12) +#define UTMIP_CFG1_FORCE_PLL_ENABLE_POWERDOWN_DISABLE (0 << 14) +#define UTMIP_CFG1_FORCE_PLL_ENABLE_POWERUP_ENABLE (1 << 15) +#define UTMIP_CFG1_FORCE_PLLU_POWERDOWN_ENABLE (1 << 16) +#define UTMIP_CFG1_PLLU_ENABLE_DLY_COUNT_SHIFT (27) +#define UTMIP_CFG2_FORCE_PD_SAMP_A_POWERDOWN_DISABLE (0 << 0) +#define UTMIP_CFG2_FORCE_PD_SAMP_B_POWERDOWN_DISABLE (0 << 2) +#define UTMIP_CFG2_FORCE_PD_SAMP_C_POWERDOWN_DISABLE (0 << 4) +#define UTMIP_CFG2_PLLU_STABLE_COUNT_SHIFT (6) +#define UTMIP_CFG2_PLL_ACTIVE_DLY_COUNT_SHIFT (18) +#define UTMIP_CFG2_PHY_XTAL_CLOCKEN (1U << 30) + +/* Generic, indiscriminate divisor mask. May catch some innocent bystander bits + * on the side that we don't particularly care about. */ +#define PLL_BASE_DIV_MASK (0xffffff) + +/* CLK_RST_CONTROLLER_PLL*_OUT*_0 */ +#define PLL_OUT_RSTN (1 << 0) +#define PLL_OUT_CLKEN (1 << 1) +#define PLL_OUT_OVR (1 << 2) + +#define PLL_OUT_RATIO_SHIFT 8 +#define PLL_OUT_RATIO_MASK (0xffU << PLL_OUT_RATIO_SHIFT) + +#define PLL_OUT1_SHIFT 0 +#define PLL_OUT2_SHIFT 16 +#define PLL_OUT3_SHIFT 0 +#define PLL_OUT4_SHIFT 16 + +/* This bit is different all over the place. */ +#define PLLDPD2_MISC_LOCK_ENABLE (1 << 30) +#define PLLU_MISC_LOCK_ENABLE (1 << 29) +#define PLLD_MISC_LOCK_ENABLE (1 << 18) +#define PLLD_MISC_CLK_ENABLE (1 << 21) +#define PLLPAXS_MISC_LOCK_ENABLE (1 << 18) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) + +/* PLLX_BASE_0 0xe0 */ +#define PLLX_BASE_PLLX_ENABLE (1 << 30) + +/* CLK_RST_CONTROLLER_PLLX_MISC_3 */ +#define PLLX_IDDQ_SHIFT 3 +#define PLLX_IDDQ_MASK (1U << PLLX_IDDQ_SHIFT) + +#define CLK_DIVISOR_MASK (0xffff) + +#define CLK_SOURCE_SHIFT 29 +#define CLK_SOURCE_MASK (0x7 << CLK_SOURCE_SHIFT) + +#define CLK_SOURCE_EMC_MC_EMC_SAME_FREQ (1 << 16) +#define EMC_2X_CLK_SRC_SHIFT 29 +#define PLLM_UD 4 + +#define CLK_UART_DIV_OVERRIDE (1 << 24) + +/* CLK_RST_CONTROLLER_SCLK_BURST_POLICY */ +#define SCLK_SYS_STATE_SHIFT 28U +#define SCLK_SYS_STATE_MASK (15U << SCLK_SYS_STATE_SHIFT) +enum { + SCLK_SYS_STATE_STDBY, + SCLK_SYS_STATE_IDLE, + SCLK_SYS_STATE_RUN, + SCLK_SYS_STATE_IRQ = 4U, + SCLK_SYS_STATE_FIQ = 8U, +}; +#define SCLK_COP_FIQ_MASK (1 << 27) +#define SCLK_CPU_FIQ_MASK (1 << 26) +#define SCLK_COP_IRQ_MASK (1 << 25) +#define SCLK_CPU_IRQ_MASK (1 << 24) + +#define SCLK_FIQ_SHIFT 12 +#define SCLK_FIQ_MASK (7 << SCLK_FIQ_SHIFT) +#define SCLK_IRQ_SHIFT 8 +#define SCLK_IRQ_MASK (7 << SCLK_FIQ_SHIFT) +#define SCLK_RUN_SHIFT 4 +#define SCLK_RUN_MASK (7 << SCLK_FIQ_SHIFT) +#define SCLK_IDLE_SHIFT 0 +#define SCLK_IDLE_MASK (7 << SCLK_FIQ_SHIFT) +enum { + SCLK_SOURCE_CLKM, + SCLK_SOURCE_PLLC_OUT1, + SCLK_SOURCE_PLLP_OUT4, + SCLK_SOURCE_PLLP_OUT3, + SCLK_SOURCE_PLLP_OUT2, + SCLK_SOURCE_PLLC_OUT0, + SCLK_SOURCE_CLKS, + SCLK_SOURCE_PLLM_OUT1, +}; + +/* CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER 0x2c */ +#define SCLK_DIV_ENB (1 << 31) +#define SCLK_DIVIDEND_SHIFT 8 +#define SCLK_DIVIDEND_MASK (0xff << SCLK_DIVIDEND_SHIFT) +#define SCLK_DIVISOR_SHIFT 0 +#define SCLK_DIVISOR_MASK (0xff << SCLK_DIVISOR_SHIFT) + +/* CLK_RST_CONTROLLER_CLK_SYSTEM_RATE 0x30 */ +#define HCLK_DISABLE (1 << 7) +#define HCLK_DIVISOR_SHIFT 4 +#define HCLK_DIVISOR_MASK (3 << AHB_RATE_SHIFT) +#define PCLK_DISABLE (1 << 3) +#define PCLK_DIVISOR_SHIFT 0 +#define PCLK_DIVISOR_MASK (3 << AHB_RATE_SHIFT) + +/* CRC_CLK_SOURCE_MSELECT_0 0x3b4 */ +#define MSELECT_CLK_SRC_PLLP_OUT0 (0 << 29) + +/* CRC_CLK_ENB_V_SET_0 0x440 */ +#define SET_CLK_ENB_CPUG_ENABLE (1 << 0) +#define SET_CLK_ENB_CPULP_ENABLE (1 << 1) +#define SET_CLK_ENB_MSELECT_ENABLE (1 << 3) + +/* CLK_RST_CONTROLLER_UTMIP_PLL_CFG1_0 0x484 */ +#define PLLU_POWERDOWN (1 << 16) +#define PLL_ENABLE_POWERDOWN (1 << 14) +#define PLL_ACTIVE_POWERDOWN (1 << 12) + +/* CLK_RST_CONTROLLER_UTMIP_PLL_CFG2_0 0x488 */ +#define UTMIP_FORCE_PD_SAMP_C_POWERDOWN (1 << 4) +#define UTMIP_FORCE_PD_SAMP_B_POWERDOWN (1 << 2) +#define UTMIP_FORCE_PD_SAMP_A_POWERDOWN (1 << 0) + +// CCLK_BRST_POL +enum { + CRC_CCLK_BRST_POL_PLLX_OUT0 = 0x8, + CRC_CCLK_BRST_POL_CPU_STATE_RUN = 0x2 +}; + +// SUPER_CCLK_DIVIDER +enum { + CRC_SUPER_CCLK_DIVIDER_SUPER_CDIV_ENB = 1 << 31 +}; + +// CLK_CPU_CMPLX_CLR +enum { + CRC_CLK_CLR_CPU0_STP = 0x1 << 8, + CRC_CLK_CLR_CPU1_STP = 0x1 << 9, + CRC_CLK_CLR_CPU2_STP = 0x1 << 10, + CRC_CLK_CLR_CPU3_STP = 0x1 << 11 +}; + +// RST_CPUG_CMPLX_CLR +enum { + CRC_RST_CPUG_CLR_CPU0 = 0x1 << 0, + CRC_RST_CPUG_CLR_CPU1 = 0x1 << 1, + CRC_RST_CPUG_CLR_CPU2 = 0x1 << 2, + CRC_RST_CPUG_CLR_CPU3 = 0x1 << 3, + CRC_RST_CPUG_CLR_DBG0 = 0x1 << 12, + CRC_RST_CPUG_CLR_DBG1 = 0x1 << 13, + CRC_RST_CPUG_CLR_DBG2 = 0x1 << 14, + CRC_RST_CPUG_CLR_DBG3 = 0x1 << 15, + CRC_RST_CPUG_CLR_CORE0 = 0x1 << 16, + CRC_RST_CPUG_CLR_CORE1 = 0x1 << 17, + CRC_RST_CPUG_CLR_CORE2 = 0x1 << 18, + CRC_RST_CPUG_CLR_CORE3 = 0x1 << 19, + CRC_RST_CPUG_CLR_CX0 = 0x1 << 20, + CRC_RST_CPUG_CLR_CX1 = 0x1 << 21, + CRC_RST_CPUG_CLR_CX2 = 0x1 << 22, + CRC_RST_CPUG_CLR_CX3 = 0x1 << 23, + CRC_RST_CPUG_CLR_L2 = 0x1 << 24, + CRC_RST_CPUG_CLR_NONCPU = 0x1 << 29, + CRC_RST_CPUG_CLR_PDBG = 0x1 << 30, +}; + +// RST_CPULP_CMPLX_CLR +enum { + CRC_RST_CPULP_CLR_CPU0 = 0x1 << 0, + CRC_RST_CPULP_CLR_DBG0 = 0x1 << 12, + CRC_RST_CPULP_CLR_CORE0 = 0x1 << 16, + CRC_RST_CPULP_CLR_CX0 = 0x1 << 20, + CRC_RST_CPULP_CLR_L2 = 0x1 << 24, + CRC_RST_CPULP_CLR_NONCPU = 0x1 << 29, + CRC_RST_CPULP_CLR_PDBG = 0x1 << 30, +}; + +#define TIMERUS_CNTR_1US 0x0 +#define TIMERUS_USEC_CFG 0x4 +#define TIMERUS_USEC_CFG_19P2_CLK_M 0x045F + +#endif /* _TEGRA210_CLK_RST_H_ */ diff --git a/src/soc/nvidia/tegra210/include/soc/clock.h b/src/soc/nvidia/tegra210/include/soc/clock.h new file mode 100644 index 0000000000..ff9b282eaa --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/clock.h @@ -0,0 +1,442 @@ +/* + * Copyright 2014 Google Inc. + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_CLOCK_H__ +#define __SOC_NVIDIA_TEGRA210_CLOCK_H__ + +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <soc/clk_rst.h> +#include <stdint.h> +#include <stdlib.h> + +enum { + CLK_L_CPU = 0x1 << 0, + CLK_L_COP = 0x1 << 1, + CLK_L_TRIG_SYS = 0x1 << 2, + CLK_L_RTC = 0x1 << 4, + CLK_L_TMR = 0x1 << 5, + CLK_L_UARTA = 0x1 << 6, + CLK_L_UARTB = 0x1 << 7, + CLK_L_GPIO = 0x1 << 8, + CLK_L_SDMMC2 = 0x1 << 9, + CLK_L_SPDIF = 0x1 << 10, + CLK_L_I2S2 = 0x1 << 11, + CLK_L_I2C1 = 0x1 << 12, + CLK_L_NDFLASH = 0x1 << 13, + CLK_L_SDMMC1 = 0x1 << 14, + CLK_L_SDMMC4 = 0x1 << 15, + CLK_L_PWM = 0x1 << 17, + CLK_L_I2S3 = 0x1 << 18, + CLK_L_EPP = 0x1 << 19, + CLK_L_VI = 0x1 << 20, + CLK_L_2D = 0x1 << 21, + CLK_L_USBD = 0x1 << 22, + CLK_L_ISP = 0x1 << 23, + CLK_L_3D = 0x1 << 24, + CLK_L_DISP2 = 0x1 << 26, + CLK_L_DISP1 = 0x1 << 27, + CLK_L_HOST1X = 0x1 << 28, + CLK_L_VCP = 0x1 << 29, + CLK_L_I2S1 = 0x1 << 30, + CLK_L_CACHE2 = 0x1 << 31, + + CLK_H_MEM = 0x1 << 0, + CLK_H_AHBDMA = 0x1 << 1, + CLK_H_APBDMA = 0x1 << 2, + CLK_H_KBC = 0x1 << 4, + CLK_H_STAT_MON = 0x1 << 5, + CLK_H_PMC = 0x1 << 6, + CLK_H_FUSE = 0x1 << 7, + CLK_H_KFUSE = 0x1 << 8, + CLK_H_SBC1 = 0x1 << 9, + CLK_H_SNOR = 0x1 << 10, + CLK_H_JTAG2TBC = 0x1 << 11, + CLK_H_SBC2 = 0x1 << 12, + CLK_H_SBC3 = 0x1 << 14, + CLK_H_I2C5 = 0x1 << 15, + CLK_H_DSI = 0x1 << 16, + CLK_H_HSI = 0x1 << 18, + CLK_H_HDMI = 0x1 << 19, + CLK_H_CSI = 0x1 << 20, + CLK_H_I2C2 = 0x1 << 22, + CLK_H_UARTC = 0x1 << 23, + CLK_H_MIPI_CAL = 0x1 << 24, + CLK_H_EMC = 0x1 << 25, + CLK_H_USB2 = 0x1 << 26, + CLK_H_USB3 = 0x1 << 27, + CLK_H_MPE = 0x1 << 28, + CLK_H_VDE = 0x1 << 29, + CLK_H_BSEA = 0x1 << 30, + CLK_H_BSEV = 0x1 << 31, + + CLK_U_UARTD = 0x1 << 1, + CLK_U_UARTE = 0x1 << 2, + CLK_U_I2C3 = 0x1 << 3, + CLK_U_SBC4 = 0x1 << 4, + CLK_U_SDMMC3 = 0x1 << 5, + CLK_U_PCIE = 0x1 << 6, + CLK_U_OWR = 0x1 << 7, + CLK_U_AFI = 0x1 << 8, + CLK_U_CSITE = 0x1 << 9, + CLK_U_PCIEXCLK = 0x1 << 10, + CLK_U_AVPUCQ = 0x1 << 11, + CLK_U_TRACECLKIN = 0x1 << 13, + CLK_U_SOC_THERM = 0x1 << 14, + CLK_U_DTV = 0x1 << 15, + CLK_U_NAND_SPEED = 0x1 << 16, + CLK_U_I2C_SLOW = 0x1 << 17, + CLK_U_DSIB = 0x1 << 18, + CLK_U_TSEC = 0x1 << 19, + CLK_U_IRAMA = 0x1 << 20, + CLK_U_IRAMB = 0x1 << 21, + CLK_U_IRAMC = 0x1 << 22, + + // Clock reset. + CLK_U_EMUCIF = 0x1 << 23, + // Clock enable. + CLK_U_IRAMD = 0x1 << 23, + + CLK_U_CRAM2 = 0x2 << 24, + CLK_U_XUSB_HOST = 0x1 << 25, + CLK_U_MSENC = 0x1 << 27, + CLK_U_SUS_OUT = 0x1 << 28, + CLK_U_DEV2_OUT = 0x1 << 29, + CLK_U_DEV1_OUT = 0x1 << 30, + CLK_U_XUSB_DEV = 0x1 << 31, + + CLK_V_CPUG = 0x1 << 0, + CLK_V_CPULP = 0x1 << 1, + CLK_V_3D2 = 0x1 << 2, + CLK_V_MSELECT = 0x1 << 3, + CLK_V_I2S4 = 0x1 << 5, + CLK_V_I2S5 = 0x1 << 6, + CLK_V_I2C4 = 0x1 << 7, + CLK_V_SBC5 = 0x1 << 8, + CLK_V_SBC6 = 0x1 << 9, + CLK_V_AHUB = 0x1 << 10, + CLK_V_APB2APE = 0x1 << 11, + CLK_V_HDA2CODEC_2X = 0x1 << 15, + CLK_V_ATOMICS = 0x1 << 16, + CLK_V_ACTMON = 0x1 << 23, + CLK_V_EXTPERIPH1 = 0x1 << 24, + CLK_V_SATA = 0x1 << 28, + CLK_V_HDA = 0x1 << 29, + + CLK_W_HDA2HDMICODEC = 0x1 << 0, + CLK_W_SATACOLD = 0x1 << 1, + CLK_W_CEC = 0x1 << 8, + CLK_W_XUSB_PADCTL = 0x1 << 14, + CLK_W_ENTROPY = 0x1 << 21, + CLK_W_DVFS = 0x1 << 27, + CLK_W_XUSB_SS = 0x1 << 28, + + CLK_X_GPU = 0x1 << 24, + CLK_X_SOR1 = 0x1 << 23, + CLK_X_SOR0 = 0x1 << 22, + CLK_X_DPAUX = 0x1 << 21, + CLK_X_VIC = 0x1 << 18, + CLK_X_UART_FST_MIPI_CAL = 0x1 << 17, + CLK_X_MIPIBIF = 0x1 << 13, + CLK_X_I2C6 = 0x1 << 6, + CLK_X_ETR = 0x1 << 3, + CLK_X_SPARE = 0x1 << 0, + + CLK_Y_APE = 0x1 << 6, + CLK_Y_QSPI = 0x1 << 19, +}; + +enum { + PLLP = 0, + PLLC2 = 1, + PLLC = 2, + PLLC_OUT1 = 3, + PLLM = 4, + CLK_M = 5, + CLK_S = 6, + PLLE = 7, + PLLA = 8, + PLLD = 9, + PLLD2 = 10, + PLLC4_OUT0 = 11, + PLLC4_OUT1 = 12, + PLLC4_OUT2 = 13, + PLLC4_OUT3 = 14, + PLLC4_OUT0_L = 15, + PLLC4_OUT1_L = 16, + PLLC4_OUT2_L = 17, + PLLD_OUT0 = 18, + PLLP_OUT3 = 19, + PLLC2_OUT0 = 20, + UNUSED0 = 100, + UNUSED1 = 101, + UNUSED2 = 102, + UNUSED3 = 103, + UNUSED4 = 104, + UNUSED5 = 105, + UNUSED6 = 106, + UNUSED7 = 107, +}; + +#define CLK_SRC_DEV_ID(dev, src) CLK_SRC_##dev##_##src +#define CLK_SRC_FREQ_ID(dev, src) CLK_SRC_FREQ_##dev##_##src + +#define CLK_SRC_DEVICE(dev, a, b, c, d, e, f, g, h) \ + CLK_SRC_DEV_ID(dev, a) = 0, \ + CLK_SRC_DEV_ID(dev, b) = 1, \ + CLK_SRC_DEV_ID(dev, c) = 2, \ + CLK_SRC_DEV_ID(dev, d) = 3, \ + CLK_SRC_DEV_ID(dev, e) = 4, \ + CLK_SRC_DEV_ID(dev, f) = 5, \ + CLK_SRC_DEV_ID(dev, g) = 6, \ + CLK_SRC_DEV_ID(dev, h) = 7, \ + CLK_SRC_FREQ_ID(dev, a) = a, \ + CLK_SRC_FREQ_ID(dev, b) = b, \ + CLK_SRC_FREQ_ID(dev, c) = c, \ + CLK_SRC_FREQ_ID(dev, d) = d, \ + CLK_SRC_FREQ_ID(dev, e) = e, \ + CLK_SRC_FREQ_ID(dev, f) = f, \ + CLK_SRC_FREQ_ID(dev, g) = g, \ + CLK_SRC_FREQ_ID(dev, h) = h + +enum { + CLK_SRC_DEVICE(disp1, PLLP, PLLD, PLLD_OUT0, UNUSED3, UNUSED4, PLLD2, + CLK_M, UNUSED7), + CLK_SRC_DEVICE(host1x, PLLC4_OUT1, PLLC2, PLLC, PLLC4_OUT2, PLLP, CLK_M, + PLLA, PLLC4_OUT0), + CLK_SRC_DEVICE(I2C1, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(I2C2, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(I2C3, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(I2C5, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(I2C6, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(I2S1, PLLA, UNUSED1, CLK_S, UNUSED3, PLLP, UNUSED5, + CLK_M, UNUSED7), + CLK_SRC_DEVICE(mselect, PLLP, PLLC2, PLLC, PLLC4_OUT2, PLLC4_OUT1, + CLK_S, CLK_M, PLLC4_OUT0), + CLK_SRC_DEVICE(SPI1, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(SPI4, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, PLLC4_OUT1, + CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(SDMMC1, PLLP, PLLA, PLLC, PLLC4_OUT2, PLLM, PLLE, CLK_M, + PLLC4_OUT0), + CLK_SRC_DEVICE(SDMMC4, PLLP, PLLC4_OUT2_L, PLLC4_OUT0_L, PLLC4_OUT2, + PLLC4_OUT1, PLLC4_OUT1_L, CLK_M, PLLC4_OUT0), + CLK_SRC_DEVICE(UARTA, PLLP, PLLC2, PLLC, PLLC4_OUT0, UNUSED4, + PLLC4_OUT1, CLK_M, PLLC4_OUT2), + CLK_SRC_DEVICE(i2s1, PLLA, UNUSED1, CLK_S, UNUSED3, PLLP, UNUSED5, + CLK_M, UNUSED7), + CLK_SRC_DEVICE(extperiph1, PLLA, CLK_S, PLLP, CLK_M, PLLE, UNUSED5, + UNUSED6, UNUSED7), + CLK_SRC_DEVICE(QSPI, PLLP, PLLC_OUT1, PLLC, UNUSED3, PLLC4_OUT2, + PLLC4_OUT1, CLK_M, PLLC4_OUT0), + CLK_SRC_DEVICE(uart_fst_mipi_cal, PLLP_OUT3, PLLP, PLLC, UNUSED3, PLLC2_OUT0, + UNUSED5, CLK_M, UNUSED7), +}; + +/* PLL stabilization delay in usec */ +#define CLOCK_PLL_STABLE_DELAY_US 300 + +#define IO_STABILIZATION_DELAY (2) +#define LOGIC_STABILIZATION_DELAY (2) + +/* Calculate clock fractional divider value from ref and target frequencies. + * This is for a U7.1 format. This is not well written up in the book and + * there have been some questions about this macro, so here we go. + * U7.1 format is defined as (ddddddd+1) + (h*.5) + * The lowest order bit is actually a fractional bit. + * Hence, the divider can be thought of as 9 bits. + * So: + * divider = ((ref/freq) << 1 - 1) (upper 7 bits) | + * (ref/freq & 1) (low order half-bit) + * however we can't do fractional arithmetic ... these are integers! + * So we normalize by shifting the result left 1 bit, and extracting + * ddddddd and h directly to the returned u8. + * divider = 2*(ref/freq); + * We want to + * preserve 7 bits of divisor and one bit of fraction, in 8 bits, as well as + * subtract one from ddddddd. Since we computed ref*2, the dddddd is now nicely + * situated in the upper 7 bits, and the h is sitting there in the low order + * bit. To subtract 1 from ddddddd, just subtract 2 from the 8-bit number + * and voila, upper 7 bits are (ref/freq-1), and lowest bit is h. Since you + * will assign this to a u8, it gets nicely truncated for you. + */ +#define CLK_DIVIDER(REF, FREQ) (div_round_up(((REF) * 2), (FREQ)) - 2) + +/* Calculate clock frequency value from reference and clock divider value + * The discussion in the book is pretty lacking. + * The idea is that we need to divide a ref clock by a divisor + * in U7.1 format, where 7 upper bits are the integer + * and lowest order bit is a fraction. + * from the book, U7.1 is (ddddddd+1) + (h*.5) + * To normalize to an actual number, we might do this: + * ((d>>7+1)&0x7f) + (d&1 >> 1) + * but as you might guess, the low order bit would be lost. + * Since we can't express the fractional bit, we need to multiply it all by 2. + * ((d + 2)&0xfe) + (d & 1) + * Since we're just adding +2, the lowest order bit is preserved. Hence + * (d+2) is the same as ((d + 2)&0xfe) + (d & 1) + * + * Since you multiply denominator * 2 (by NOT shifting it), + * you multiply numerator * 2 to cancel it out. + */ +#define CLK_FREQUENCY(REF, REG) (((REF) * 2) / ((REG) + 2)) + +static inline void _clock_set_div(u32 *reg, const char *name, u32 div, + u32 div_mask, u32 src) +{ + // The I2C and UART divisors are 16 bit while all the others are 8 bit. + // The I2C clocks are handled by the specialized macro below, but the + // UART clocks aren't. Don't use this function on UART clocks. + if (div & ~div_mask) { + printk(BIOS_ERR, "%s clock divisor overflow!", name); + hlt(); + } + clrsetbits_le32(reg, CLK_SOURCE_MASK | CLK_DIVISOR_MASK, + src << CLK_SOURCE_SHIFT | div); +} + +#define get_i2c_clk_div(src,freq) (div_round_up(src, (freq) * (0x19 + 1) * 8) - 1) +#define get_clk_div(src,freq) CLK_DIVIDER(src,freq) +#define CLK_DIV_MASK 0xff +#define CLK_DIV_MASK_I2C 0xffff + +#define clock_configure_source(device, src, freq) \ + _clock_set_div(CLK_RST_REG(clk_src_##device), #device, \ + get_clk_div(TEGRA_##src##_KHZ, freq), CLK_DIV_MASK, \ + CLK_SRC_DEV_ID(device, src)) + +/* soc-specific */ +#define TEGRA_CLK_M_KHZ (clock_get_osc_khz()/2) +#define TEGRA_PLLX_KHZ CONFIG_PLLX_KHZ +#define TEGRA_PLLP_KHZ (408000) +#define TEGRA_PLLP_OUT3_KHZ (68000) +#define TEGRA_PLLC_KHZ (600000) +#define TEGRA_PLLD_KHZ (925000) +#define TEGRA_PLLD_OUT0_KHZ (TEGRA_PLLD_KHZ/2) +#define TEGRA_PLLU_KHZ (960000) + +#define clock_enable(l, h, u, v, w, x, y) \ + do { \ + u32 bits[DEV_CONFIG_BLOCKS] = {l, h, u, v, w, x, y}; \ + clock_enable_regs(bits); \ + } while (0) + +#define clock_disable(l, h, u, v, w, x, y) \ + do { \ + u32 bits[DEV_CONFIG_BLOCKS] = {l, h, u, v, w, x, y}; \ + clock_disable_regs(bits); \ + } while (0) + +#define clock_set_reset(l, h, u, v, w, x, y) \ + do { \ + u32 bits[DEV_CONFIG_BLOCKS] = {l, h, u, v, w, x, y}; \ + clock_set_reset_regs(bits); \ + } while (0) + +#define clock_clr_reset(l, h, u, v, w, x, y) \ + do { \ + u32 bits[DEV_CONFIG_BLOCKS] = {l, h, u, v, w, x, y}; \ + clock_clr_reset_regs(bits); \ + } while (0) + +#define clock_enable_l(l) clock_enable(l, 0, 0, 0, 0, 0, 0) +#define clock_enable_h(h) clock_enable(0, h, 0, 0, 0, 0, 0) +#define clock_enable_u(u) clock_enable(0, 0, u, 0, 0, 0, 0) +#define clock_enable_v(v) clock_enable(0, 0, 0, v, 0, 0, 0) +#define clock_enable_w(w) clock_enable(0, 0, 0, 0, w, 0, 0) +#define clock_enable_x(x) clock_enable(0, 0, 0, 0, 0, x, 0) +#define clock_enable_y(y) clock_enable(0, 0, 0, 0, 0, 0, y) + +#define clock_disable_l(l) clock_disable(l, 0, 0, 0, 0, 0, 0) +#define clock_disable_h(h) clock_disable(0, h, 0, 0, 0, 0, 0) +#define clock_disable_u(u) clock_disable(0, 0, u, 0, 0, 0, 0) +#define clock_disable_v(v) clock_disable(0, 0, 0, v, 0, 0, 0) +#define clock_disable_w(w) clock_disable(0, 0, 0, 0, w, 0, 0) +#define clock_disable_x(x) clock_disable(0, 0, 0, 0, 0, x, 0) +#define clock_disable_y(y) clock_disable(0, 0, 0, 0, 0, 0, y) + +#define clock_set_reset_l(l) clock_set_reset(l, 0, 0, 0, 0, 0, 0) +#define clock_set_reset_h(h) clock_set_reset(0, h, 0, 0, 0, 0, 0) +#define clock_set_reset_u(u) clock_set_reset(0, 0, u, 0, 0, 0, 0) +#define clock_set_reset_v(v) clock_set_reset(0, 0, 0, v, 0, 0, 0) +#define clock_set_reset_w(w) clock_set_reset(0, 0, 0, 0, w, 0, 0) +#define clock_set_reset_x(x) clock_set_reset(0, 0, 0, 0, 0, x, 0) +#define clock_set_reset_y(x) clock_set_reset(0, 0, 0, 0, 0, y, 0) + +#define clock_clr_reset_l(l) clock_clr_reset(l, 0, 0, 0, 0, 0, 0) +#define clock_clr_reset_h(h) clock_clr_reset(0, h, 0, 0, 0, 0, 0) +#define clock_clr_reset_u(u) clock_clr_reset(0, 0, u, 0, 0, 0, 0) +#define clock_clr_reset_v(v) clock_clr_reset(0, 0, 0, v, 0, 0, 0) +#define clock_clr_reset_w(w) clock_clr_reset(0, 0, 0, 0, w, 0, 0) +#define clock_clr_reset_x(x) clock_clr_reset(0, 0, 0, 0, 0, x, 0) +#define clock_clr_reset_y(y) clock_clr_reset(0, 0, 0, 0, 0, 0, y) + +#define clock_enable_clear_reset_l(l) \ + clock_enable_clear_reset(l, 0, 0, 0, 0, 0, 0) +#define clock_enable_clear_reset_h(h) \ + clock_enable_clear_reset(0, h, 0, 0, 0, 0, 0) +#define clock_enable_clear_reset_u(u) \ + clock_enable_clear_reset(0, 0, u, 0, 0, 0, 0) +#define clock_enable_clear_reset_v(v) \ + clock_enable_clear_reset(0, 0, 0, v, 0, 0, 0) +#define clock_enable_clear_reset_w(w) \ + clock_enable_clear_reset(0, 0, 0, 0, w, 0, 0) +#define clock_enable_clear_reset_x(x) \ + clock_enable_clear_reset(0, 0, 0, 0, 0, x, 0) +#define clock_enable_clear_reset_y(y) \ + clock_enable_clear_reset(0, 0, 0, 0, 0, 0, y) + +int clock_get_osc_khz(void); +int clock_get_pll_input_khz(void); +/* + * Configure PLLD to requested frequency. Returned value is closest match + * within the PLLD's constraints or 0 if an error. + */ +u32 clock_configure_plld(u32 frequency); +void clock_early_uart(void); +void clock_external_output(int clk_id); +void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 kvco, u32 kcp, + u32 stable_time, u32 emc_source, u32 same_freq); +void clock_cpu0_config(void); +void clock_halt_avp(void); +void clock_enable_regs(u32 bits[DEV_CONFIG_BLOCKS]); +void clock_disable_regs(u32 bits[DEV_CONFIG_BLOCKS]); +void clock_set_reset_regs(u32 bits[DEV_CONFIG_BLOCKS]); +void clock_clr_reset_regs(u32 bits[DEV_CONFIG_BLOCKS]); +void clock_enable_clear_reset(u32 l, u32 h, u32 u, u32 v, u32 w, u32 x, u32 y); +void clock_grp_enable_clear_reset(u32 val, u32* clk_enb_set_reg, u32* rst_dev_clr_reg); +void clock_reset_l(u32 l); +void clock_reset_h(u32 h); +void clock_reset_u(u32 u); +void clock_reset_v(u32 v); +void clock_reset_w(u32 w); +void clock_reset_x(u32 x); +void clock_reset_y(u32 y); +void clock_init(void); +void clock_init_arm_generic_timer(void); +void sor_clock_stop(void); +void sor_clock_start(void); +void clock_enable_audio(void); + +#endif /* __SOC_NVIDIA_TEGRA210_CLOCK_H__ */ + diff --git a/src/soc/nvidia/tegra210/include/soc/clst_clk.h b/src/soc/nvidia/tegra210/include/soc/clst_clk.h new file mode 100644 index 0000000000..e0de867def --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/clst_clk.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef _TEGRA210_CLST_CLK_H_ +#define _TEGRA210_CLST_CLK_H_ + +/* Cluster Clock (CLUSTER_CLOCKS_PUBLIC_) regs */ +struct __attribute__ ((__packed__)) clst_clk_ctlr { + u32 pllx_base; /* _PLLX_BASE, 0x000 */ + u32 pllx_misc; /* _PLLX_MISC, 0x004 */ + u32 pllx_misc1; /* _PLLX_MISC_1, 0x008 */ + u32 pllx_misc2; /* _PLLX_MISC_2, 0x00c */ + u32 pllx_misc3; /* _PLLX_MISC_3, 0x010 */ + u32 pllx_hw_ctrl_cfg; /* _PLLX_HW_CTRL_CFG, 0x014 */ + u32 pllx_sw_ramp_cfg; /* _PLLX_SW_RAMP_CFG, 0x018 */ + u32 pllx_hw_ctrl_status; /* _PLLX_HW_CTRL_STATUS, 0x01c */ + u32 cclk_brst_pol; /* _CCLK_BURST_POLICY, 0x020 */ + u32 super_cclk_div; /* _SUPER_CCLK_DIVIDER, 0x024 */ + u32 _rsv1[10]; /* 0x028-04c */ + u32 shaper; /* _SHAPER, 0x050 */ + u32 shaper1; /* _SHAPER_1, 0x054 */ + u32 _rsv2[80]; /* 0x058-194 */ + u32 misc_ctrl; /* _MISC_CTRL, 0x198 */ +}; +check_member(clst_clk_ctlr, misc_ctrl, 0x198); + +/* CC_CCLK_BRST_POL */ +enum { + CC_CCLK_BRST_POL_PLLX_OUT0_LJ = 0x8, +}; + +/* CC_SUPER_CCLK_DIVIDER */ +enum { + CC_SUPER_CCLK_DIVIDER_SUPER_CDIV_ENB = 1 << 31 +}; + +/* PLLX_MISC3 */ +enum { + PLLX_IDDQ = 1 << 3, +}; + +/* MISC_CTRL */ +enum { + CLK_SWITCH_MATCH = 1 << 5, +}; + +#define CLK_SWITCH_TIMEOUT_US 1000 +#endif /* _TEGRA210_CLST_CLK_H_ */ diff --git a/src/soc/nvidia/tegra210/include/soc/cpu.h b/src/soc/nvidia/tegra210/include/soc/cpu.h new file mode 100644 index 0000000000..f6ae137ef0 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/cpu.h @@ -0,0 +1,33 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_CPU_H__ +#define __SOC_NVIDIA_TEGRA210_CPU_H__ + +/* + * Start a core in 64-bit mode at the entry_64 address. Note that entry_64 + * should be a 32-bit address. + */ +void start_cpu(int cpu, void *entry_64); +/* Start CPU wthout any log messages. */ +void start_cpu_silent(int cpu, void *entry_64); +/* Prepare SoC for starting a CPU. Initialize the global state of the SoC. */ +void cpu_prepare_startup(void *entry_64); + +#endif /* __SOC_NVIDIA_TEGRA210_CPU_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/display.h b/src/soc/nvidia/tegra210/include/soc/display.h new file mode 100644 index 0000000000..fdc630ba97 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/display.h @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_DISPLAY_H__ +#define __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_DISPLAY_H__ + +#define COLOR_WHITE 0xFFFFFF +#define COLOR_BLACK 0x000000 + +#define hsync_start(mode) \ + (mode->xres + mode->hfront_porch) + +#define hsync_end(mode) \ + (mode->xres + mode->hfront_porch + mode->hsync_width) + +#define htotal(mode) \ + (mode->xres + mode->hfront_porch + \ + mode->hsync_width + mode->hback_porch) + +#define vtotal(mode) \ + (mode->yres + mode->vfront_porch + \ + mode->vsync_width + mode->vback_porch) + +enum { + /* norrin64 */ + TEGRA_EDID_I2C_ADDRESS = 0x50, +}; + +/* refresh rate = 60/s */ +#define FRAME_IN_MS 17 + +/* forward declaration */ +struct soc_nvidia_tegra210_config; +struct display_controller; + +void dsi_display_startup(device_t dev); +void dp_display_startup(device_t dev); + +int tegra_dc_init(struct display_controller *disp_ctrl); +int update_display_mode(struct display_controller *disp_ctrl, + struct soc_nvidia_tegra210_config *config); +void update_window(const struct soc_nvidia_tegra210_config *config); +void update_display_shift_clock_divider(struct display_controller *disp_ctrl, + u32 shift_clock_div); +void pass_mode_info_to_payload( + struct soc_nvidia_tegra210_config *config); +#endif /* __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_DISPLAY_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/dma.h b/src/soc/nvidia/tegra210/include/soc/dma.h new file mode 100644 index 0000000000..257d529fe9 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/dma.h @@ -0,0 +1,192 @@ +/* + * (C) Copyright 2010-2015 NVIDIA Corporation <www.nvidia.com> + * Copyright (C) 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __NVIDIA_TEGRA210_DMA_H__ +#define __NVIDIA_TEGRA210_DMA_H__ + +#include <inttypes.h> +#include <soc/addressmap.h> + +/* + * The DMA engine operates on 4 bytes at a time, so make sure any data + * passed via DMA is aligned to avoid underrun/overrun. + */ +#define TEGRA_DMA_ALIGN_BYTES 4 + +/* + * Note: Many APB DMA controller registers are laid out such that each + * bit controls or represents the status for the corresponding channel. + * So we will not bother to list each individual bit in this case. + */ +#define APB_COMMAND_GEN (1 << 31) + +#define APB_CNTRL_REG_COUNT_VALUE_MASK 0xffff +#define APB_CNTRL_REG_COUNT_VALUE_SHIFT 0 + +/* + * Note: Many APB DMA controller registers are laid out such that each + * bit controls or represents the status for the corresponding channel. + * So we will not bother to list each individual bit in this case. + */ +#define APB_COMMAND_GEN (1 << 31) + +#define APB_CNTRL_REG_COUNT_VALUE_MASK 0xffff +#define APB_CNTRL_REG_COUNT_VALUE_SHIFT 0 +struct apb_dma { + u32 command; /* 0x00 */ + u32 status; /* 0x04 */ + u32 rsvd1[2]; + u32 cntrl_reg; /* 0x10 */ + u32 irq_sta_cpu; /* 0x14 */ + u32 irq_sta_cop; /* 0x18 */ + u32 irq_mask; /* 0x1c */ + u32 irq_mask_set; /* 0x20 */ + u32 irq_mask_clr; /* 0x24 */ + u32 trig_reg; /* 0x28 */ + u32 channel_trig_reg; /* 0x2c */ + u32 dma_status; /* 0x30 */ + u32 channel_en_reg; /* 0x34 */ + u32 security_reg; /* 0x38 */ + u32 channel_swid; /* 0x3c */ + u32 rsvd[1]; + u32 chan_wt_reg0; /* 0x44 */ + u32 chan_wt_reg1; /* 0x48 */ + u32 chan_wt_reg2; /* 0x4c */ + u32 chan_wr_reg3; /* 0x50 */ + u32 channel_swid1; /* 0x54 */ +} __attribute__((packed)); +check_member(apb_dma, channel_swid1, 0x54); + +/* Security enable for DMA channel */ +#define SECURITY_EN_BIT(ch) (1 << ch) + +/* + * Naming in the doc included a superfluous _CHANNEL_n_ for + * each entry and was left out for the sake of conciseness. + */ +#define APB_CSR_ENB (1 << 31) +#define APB_CSR_IE_EOC (1 << 30) +#define APB_CSR_HOLD (1 << 29) +#define APB_CSR_DIR (1 << 28) +#define APB_CSR_ONCE (1 << 27) +#define APB_CSR_FLOW (1 << 21) +#define APB_CSR_REQ_SEL_MASK 0x1f +#define APB_CSR_REQ_SEL_SHIFT 16 + +enum apbdmachan_req_sel { + APBDMA_SLAVE_CNTR_REQ = 0, + APBDMA_SLAVE_APBIF_CH0 = 1, + APBDMA_SLAVE_APBIF_CH1 = 2, + APBDMA_SLAVE_APBIF_CH2 = 3, + APBDMA_SLAVE_APBIF_CH3 = 4, + APBDMA_SLAVE_QSPI = 5, + APBDMA_SLAVE_APBIF_CH4 = 6, + APBDMA_SLAVE_APBIF_CH5 = 7, + APBDMA_SLAVE_UART_A = 8, + APBDMA_SLAVE_UART_B = 9, + APBDMA_SLAVE_UART_C = 10, + APBDMA_SLAVE_DTV = 11, + APBDMA_SLAVE_APBIF_CH6 = 12, + APBDMA_SLAVE_APBIF_CH7 = 13, + APBDMA_SLAVE_APBIF_CH8 = 14, + APBDMA_SLAVE_SL2B1 = 15, + APBDMA_SLAVE_SL2B2 = 16, + APBDMA_SLAVE_SL2B3 = 17, + APBDMA_SLAVE_SL2B4 = 18, + APBDMA_SLAVE_UART_D = 19, + APBDMA_SLAVE_UART_E = 20, + APBDMA_SLAVE_I2C = 21, + APBDMA_SLAVE_I2C2 = 22, + APBDMA_SLAVE_I2C3 = 23, + APBDMA_SLAVE_DVC_I2C = 24, + APBDMA_SLAVE_OWR = 25, + APBDMA_SLAVE_I2C4 = 26, + APBDMA_SLAVE_SL2B5 = 27, + APBDMA_SLAVE_SL2B6 = 28, + APBDMA_SLAVE_APBIF_CH9 = 29, + APBDMA_SLAVE_I2C6 = 30, + APBDMA_SLAVE_NA31 = 31, +}; + +#define APB_STA_BSY (1 << 31) +#define APB_STA_ISE_EOC (1 << 30) +#define APB_STA_HALT (1 << 29) +#define APB_STA_PING_PONG_STA (1 << 28) +#define APB_STA_DMA_ACTIVITY (1 << 27) +#define APB_STA_CHANNEL_PAUSE (1 << 26) + +#define APB_CSRE_CHANNEL_PAUSE (1 << 31) +#define APB_CSRE_TRIG_SEL_MASK 0x3f +#define APB_CSRE_TRIG_SEL_SHIFT 14 + +#define AHB_PTR_MASK (0x3fffffff) +#define AHB_PTR_SHIFT 2 + +#define AHB_SEQ_INTR_ENB (1 << 31) +#define AHB_BUS_WIDTH_MASK 0x7 +#define AHB_BUS_WIDTH_SHIFT 28 +#define AHB_DATA_SWAP (1 << 27) +#define AHB_BURST_MASK 0x7 +#define AHB_BURST_SHIFT 24 +#define AHB_SEQ_DBL_BUF (1 << 19) +#define AHB_SEQ_WRAP_MASK 0x7 +#define AHB_SEQ_WRAP_SHIFT 16 + +#define APB_PTR_MASK 0x3fffffff +#define APB_PTR_SHIFT 2 + +#define APB_BUS_WIDTH_MASK 0x7 +#define APB_BUS_WIDTH_SHIFT 28 +#define APB_DATA_SWAP (1 << 27) +#define APB_ADDR_WRAP_MASK 0x7 +#define APB_ADDR_WRAP_SHIFT 16 + +#define APB_WORD_TRANSFER_MASK 0x0fffffff +#define APB_WORD_TRANSFER_SHIFT 2 + +struct apb_dma_channel_regs { + u32 csr; /* 0x00 */ + u32 sta; /* 0x04 */ + u32 dma_byte_sta; /* 0x08 */ + u32 csre; /* 0x0c */ + u32 ahb_ptr; /* 0x10 */ + u32 ahb_seq; /* 0x14 */ + u32 apb_ptr; /* 0x18 */ + u32 apb_seq; /* 0x1c */ + u32 wcount; /* 0x20 */ + u32 word_transfer; /* 0x24 */ +} __attribute__((packed)); +check_member(apb_dma_channel_regs, word_transfer, 0x24); + +struct apb_dma_channel { + const int num; + struct apb_dma_channel_regs *regs; + + /* + * Basic high-level semaphore that can be used to "claim" + * a DMA channel e.g. by SPI, I2C, or other peripheral driver. + */ + int in_use; +}; + +struct apb_dma_channel * const dma_claim(void); +void dma_release(struct apb_dma_channel * const channel); +int dma_start(struct apb_dma_channel * const channel); +int dma_stop(struct apb_dma_channel * const channel); +int dma_busy(struct apb_dma_channel * const channel); + +#endif /* __NVIDIA_TEGRA210_DMA_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/emc.h b/src/soc/nvidia/tegra210/include/soc/emc.h new file mode 100644 index 0000000000..b24c14d9a8 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/emc.h @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_EMC_H__ +#define __SOC_NVIDIA_TEGRA210_EMC_H__ + +#include <stddef.h> +#include <stdint.h> + +enum { + EMC_PIN_RESET_MASK = 1 << 8, + EMC_PIN_RESET_ACTIVE = 0 << 8, + EMC_PIN_RESET_INACTIVE = 1 << 8, + EMC_PIN_DQM_MASK = 1 << 4, + EMC_PIN_DQM_NORMAL = 0 << 4, + EMC_PIN_DQM_INACTIVE = 1 << 4, + EMC_PIN_CKE_MASK = 1 << 0, + EMC_PIN_CKE_POWERDOWN = 0 << 0, + EMC_PIN_CKE_NORMAL = 1 << 0, + + EMC_REF_CMD_MASK = 1 << 0, + EMC_REF_CMD_REFRESH = 1 << 0, + EMC_REF_NORMAL_MASK = 1 << 1, + EMC_REF_NORMAL_INIT = 0 << 1, + EMC_REF_NORMAL_ENABLED = 1 << 1, + EMC_REF_NUM_SHIFT = 8, + EMC_REF_NUM_MASK = 0xFF << EMC_REF_NUM_SHIFT, + EMC_REF_DEV_SELECTN_SHIFT = 30, + EMC_REF_DEV_SELECTN_MASK = 3 << EMC_REF_DEV_SELECTN_SHIFT, + + EMC_REFCTRL_REF_VALID_MASK = 1 << 31, + EMC_REFCTRL_REF_VALID_DISABLED = 0 << 31, + EMC_REFCTRL_REF_VALID_ENABLED = 1 << 31, + + EMC_CFG_EMC2PMACRO_CFG_BYPASS_ADDRPIPE_MASK = 1 << 1, + EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE1_MASK = 1 << 2, + EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE2_MASK = 1 << 3, + + EMC_NOP_CMD_SHIFT = 0, + EMC_NOP_CMD_MASK = 1 << EMC_NOP_CMD_SHIFT, + EMC_NOP_DEV_SELECTN_SHIFT = 30, + EMC_NOP_DEV_SELECTN_MASK = 3 << EMC_NOP_DEV_SELECTN_SHIFT, + + EMC_TIMING_CONTROL_TIMING_UPDATE = 1, + + EMC_PIN_GPIOEN_SHIFT = 16, + EMC_PIN_GPIO_SHIFT = 12, + EMC_PMACRO_BRICK_CTRL_RFU1_RESET_VAL = 0x1FFF1FFF, + + AUTOCAL_MEASURE_STALL_ENABLE = 1 << 9, + WRITE_MUX_ACTIVE = 1 << 1, + CFG_ADR_EN_LOCKED = 1 << 1, +}; + +struct tegra_emc_regs { + uint32_t intstatus; /* 0x0 */ + uint32_t intmask; /* 0x4 */ + uint32_t dbg; /* 0x8 */ + uint32_t cfg; /* 0xc */ + uint32_t adr_cfg; /* 0x10 */ + uint32_t rsvd_0x14[3]; /* 0x14-0x1C */ + + uint32_t refctrl; /* 0x20 */ + uint32_t pin; /* 0x24 */ + uint32_t timing_control; /* 0x28 */ + uint32_t rc; /* 0x2c */ + uint32_t rfc; /* 0x30 */ + uint32_t ras; /* 0x34 */ + uint32_t rp; /* 0x38 */ + uint32_t r2w; /* 0x3c */ + uint32_t w2r; /* 0x40 */ + uint32_t r2p; /* 0x44 */ + uint32_t w2p; /* 0x48 */ + uint32_t rd_rcd; /* 0x4c */ + uint32_t wr_rcd; /* 0x50 */ + uint32_t rrd; /* 0x54 */ + uint32_t rext; /* 0x58 */ + uint32_t wdv; /* 0x5c */ + uint32_t quse; /* 0x60 */ + uint32_t qrst; /* 0x64 */ + uint32_t qsafe; /* 0x68 */ + uint32_t rdv; /* 0x6c */ + uint32_t refresh; /* 0x70 */ + uint32_t burst_refresh_num; /* 0x74 */ + uint32_t pdex2wr; /* 0x78 */ + uint32_t pdex2rd; /* 0x7c */ + uint32_t pchg2pden; /* 0x80 */ + uint32_t act2pden; /* 0x84 */ + uint32_t ar2pden; /* 0x88 */ + uint32_t rw2pden; /* 0x8c */ + uint32_t txsr; /* 0x90 */ + uint32_t tcke; /* 0x94 */ + uint32_t tfaw; /* 0x98 */ + uint32_t trpab; /* 0x9c */ + uint32_t tclkstable; /* 0xa0 */ + uint32_t tclkstop; /* 0xa4 */ + uint32_t trefbw; /* 0xa8 */ + uint32_t tppd; /* 0xac */ + uint32_t odt_write; /* 0xb0 */ + uint32_t pdex2mrr; /* 0xb4 */ + uint32_t wext; /* 0xb8 */ + uint32_t ctt; /* 0xbc */ + uint32_t rfc_slr; /* 0xc0 */ + uint32_t mrs_wait_cnt2; /* 0xc4 */ + uint32_t mrs_wait_cnt; /* 0xc8 */ + uint32_t mrs; /* 0xcc */ + uint32_t emrs; /* 0xd0 */ + uint32_t ref; /* 0xd4 */ + uint32_t pre; /* 0xd8 */ + uint32_t nop; /* 0xdc */ + uint32_t self_ref; /* 0xe0 */ + uint32_t dpd; /* 0xe4 */ + uint32_t mrw; /* 0xe8 */ + uint32_t mrr; /* 0xec */ + uint32_t cmdq; /* 0xf0 */ + uint32_t mc2emcq; /* 0xf4 */ + uint32_t xm2dqspadctrl3; /* 0xf8 */ + uint32_t rsvd_0xfc[1]; /* 0xfc */ + uint32_t fbio_spare; /* 0x100 */ + uint32_t fbio_cfg5; /* 0x104 */ + uint32_t fbio_wrptr_eq_2; /* 0x108 */ + uint32_t rsvd_0x10c[2]; /* 0x10c-0x110 */ + + uint32_t fbio_cfg6; /* 0x114 */ + uint32_t pdex2cke; /* 0x118 */ + uint32_t cke2pden; /* 0x11C */ + uint32_t cfg_rsv; /* 0x120 */ + uint32_t acpd_control; /* 0x124 */ + uint32_t rsvd_0x128[1]; /* 0x128 */ + uint32_t emrs2; /* 0x12c */ + uint32_t emrs3; /* 0x130 */ + uint32_t mrw2; /* 0x134 */ + uint32_t mrw3; /* 0x138 */ + uint32_t mrw4; /* 0x13c */ + uint32_t clken_override; /* 0x140 */ + uint32_t r2r; /* 0x144 */ + uint32_t w2w; /* 0x148 */ + uint32_t einput; /* 0x14c */ + uint32_t einput_duration; /* 0x150 */ + uint32_t puterm_extra; /* 0x154 */ + uint32_t tckesr; /* 0x158 */ + uint32_t tpd; /* 0x15c */ + uint32_t rsvd_0x160[81]; /* 0x160-0x2A0 */ + + uint32_t auto_cal_config; /* 0x2a4 */ + uint32_t auto_cal_interval; /* 0x2a8 */ + uint32_t auto_cal_status; /* 0x2ac */ + uint32_t req_ctrl; /* 0x2b0 */ + uint32_t status; /* 0x2b4 */ + uint32_t cfg_2; /* 0x2b8 */ + uint32_t cfg_dig_dll; /* 0x2bc */ + uint32_t cfg_dig_dll_period; /* 0x2c0 */ + uint32_t dig_dll_status; /* 0x2C4 */ + uint32_t cfg_dig_dll_1; /* 0x2C8 */ + uint32_t rdv_mask; /* 0x2cc */ + uint32_t wdv_mask; /* 0x2d0 */ + uint32_t rdv_early_mask; /* 0x2d4 */ + uint32_t rdv_early; /* 0x2d8 */ + uint32_t auto_cal_config8; /* 0x2DC */ + uint32_t zcal_interval; /* 0x2e0 */ + uint32_t zcal_wait_cnt; /* 0x2e4 */ + uint32_t zcal_mrw_cmd; /* 0x2e8 */ + uint32_t zq_cal; /* 0x2ec */ + uint32_t xm2cmdpadctrl; /* 0x2f0 */ + uint32_t xm2comppadctrl3; /* 0x2f4 */ + uint32_t auto_cal_vref_sel0; /* 0x2f8 */ + uint32_t xm2dqspadctrl2; /* 0x2fc */ + uint32_t auto_cal_vref_sel1; /* 0x300 */ + uint32_t xm2dqpadctrl2; /* 0x304 */ + uint32_t xm2clkpadctrl; /* 0x308 */ + uint32_t xm2comppadctrl; /* 0x30c */ + uint32_t fdpd_ctrl_dq; /* 0x310 */ + uint32_t fdpd_ctrl_cmd; /* 0x314 */ + uint32_t pmacro_cmd_brick_ctrl_fdpd; /* 0x318 */ + uint32_t pmacro_data_brick_ctrl_fdpd; /* 0x31c */ + uint32_t xm2dqspadctrl4; /* 0x320 */ + uint32_t scratch0; /* 0x324 */ + uint32_t rsvd_0x328[2]; /* 0x328-0x32C */ + + uint32_t pmacro_brick_ctrl_rfu1; /* 0x330 */ + uint32_t pmacro_brick_ctrl_rfu2; /* 0x334 */ + uint32_t rsvd_0x338[18]; /* 0x338-0x37C */ + + uint32_t cmd_mapping_cmd0_0; /* 0x380 */ + uint32_t cmd_mapping_cmd0_1; /* 0x384 */ + uint32_t cmd_mapping_cmd0_2; /* 0x388 */ + uint32_t cmd_mapping_cmd1_0; /* 0x38c */ + uint32_t cmd_mapping_cmd1_1; /* 0x390 */ + uint32_t cmd_mapping_cmd1_2; /* 0x394 */ + uint32_t cmd_mapping_cmd2_0; /* 0x398 */ + uint32_t cmd_mapping_cmd2_1; /* 0x39C */ + uint32_t cmd_mapping_cmd2_2; /* 0x3A0 */ + uint32_t cmd_mapping_cmd3_0; /* 0x3A4 */ + uint32_t cmd_mapping_cmd3_1; /* 0x3A8 */ + uint32_t cmd_mapping_cmd3_2; /* 0x3AC */ + uint32_t cmd_mapping_byte; /* 0x3B0 */ + uint32_t tr_timing_0; /* 0x3B4 */ + uint32_t tr_ctrl_0; /* 0x3B8 */ + uint32_t tr_ctrl_1; /* 0x3BC */ + uint32_t switch_back_ctrl; /* 0x3C0 */ + uint32_t tr_rdv; /* 0x3C4 */ + uint32_t stall_then_exe_before_clkchange; /* 0x3c8 */ + uint32_t stall_then_exe_after_clkchange; /* 0x3cc */ + uint32_t unstall_rw_after_clkchange; /* 0x3d0 */ + uint32_t auto_cal_clk_status; /* 0x3d4 */ + uint32_t sel_dpd_ctrl; /* 0x3d8 */ + uint32_t pre_refresh_req_cnt; /* 0x3dc */ + uint32_t dyn_self_ref_control; /* 0x3e0 */ + uint32_t txsrdll; /* 0x3e4 */ + uint32_t ccfifo_addr; /* 0x3e8 */ + uint32_t ccfifo_data; /* 0x3ec */ + uint32_t ccfifo_status; /* 0x3f0 */ + uint32_t cdb_cntl_1; /* 0x3f4 */ + uint32_t cdb_cntl_2; /* 0x3f8 */ + uint32_t xm2clkpadctrl2; /* 0x3fc */ + uint32_t swizzle_rank0_byte_cfg; /* 0x400 */ + uint32_t swizzle_rank0_byte0; /* 0x404 */ + uint32_t swizzle_rank0_byte1; /* 0x408 */ + uint32_t swizzle_rank0_byte2; /* 0x40c */ + uint32_t swizzle_rank0_byte3; /* 0x410 */ + uint32_t swizzle_rank1_byte_cfg; /* 0x414 */ + uint32_t swizzle_rank1_byte0; /* 0x418 */ + uint32_t swizzle_rank1_byte1; /* 0x41c */ + uint32_t swizzle_rank1_byte2; /* 0x420 */ + uint32_t swizzle_rank1_byte3; /* 0x424 */ + uint32_t issue_qrst; /* 0x428 */ + uint32_t rsvd_0x42C[5]; /* 0x42C-0x43C */ + uint32_t pmc_scratch1; /* 0x440 */ + uint32_t pmc_scratch2; /* 0x444 */ + uint32_t pmc_scratch3; /* 0x448 */ + uint32_t rsvd_0x44C[3]; /* 0x44C-0x454 */ + uint32_t auto_cal_config2; /* 0x458 */ + uint32_t auto_cal_config3; /* 0x45c */ + uint32_t auto_cal_status2; /* 0x460 */ + uint32_t auto_cal_channel; /* 0x464 */ + uint32_t ibdly; /* 0x468 */ + uint32_t obdly; /* 0x46c */ + uint32_t rsvd_0x470[3]; /* 0x470-0x478 */ + + uint32_t dsr_vttgen_drv; /* 0x47c */ + uint32_t txdsrvttgen; /* 0x480 */ + uint32_t xm2cmdpadctrl4; /* 0x484 */ + uint32_t xm2cmdpadctrl5; /* 0x488 */ + uint32_t we_duration; /* 0x48C */ + uint32_t ws_duration; /* 0x490 */ + uint32_t wev; /* 0x494 */ + uint32_t wsv; /* 0x498 */ + uint32_t cfg_3; /* 0x49C */ + uint32_t mrw5; /* 0x4A0 */ + uint32_t mrw6; /* 0x4A4 */ + uint32_t mrw7; /* 0x4A8 */ + uint32_t mrw8; /* 0x4AC */ + uint32_t mrw9; /* 0x4B0 */ + uint32_t mrw10; /* 0x4B4 */ + uint32_t mrw11; /* 0x4B8 */ + uint32_t mrw12; /* 0x4BC */ + uint32_t mrw13; /* 0x4C0 */ + uint32_t mrw14; /* 0x4C4 */ + uint32_t rsvd_0x4c8[2]; /* 0x4C8-0x4CC */ + + uint32_t mrw15; /* 0x4D0 */ + uint32_t cfg_sync; /* 0x4D4 */ + uint32_t fdpd_ctrl_cmd_no_ramp; /* 0x4D8 */ + uint32_t rsvd_0x4dc[1]; /* 0x4DC */ + uint32_t wdv_chk; /* 0x4E0 */ + uint32_t rsvd_0x4e4[28]; /* 0x4E4-0x550 */ + + uint32_t cfg_pipe2; /* 0x554 */ + uint32_t cfg_pipe_clk; /* 0x558 */ + uint32_t cfg_pipe1; /* 0x55C */ + uint32_t cfg_pipe; /* 0x560 */ + uint32_t qpop; /* 0x564 */ + uint32_t quse_width; /* 0x568 */ + uint32_t puterm_width; /* 0x56c */ + uint32_t bgbias_ctl0; /* 0x570 */ + uint32_t auto_cal_config7; /* 0x574 */ + uint32_t xm2comppadctrl2; /* 0x578 */ + uint32_t comppadswctrl; /* 0x57C */ + uint32_t refctrl2; /* 0x580 */ + uint32_t fbio_cfg7; /* 0x584 */ + uint32_t data_brlshft_0; /* 0x588 */ + uint32_t data_brlshft_1; /* 0x58C */ + uint32_t rfcpb; /* 0x590 */ + uint32_t dqs_brlshft_0; /* 0x594 */ + uint32_t dqs_brlshft_1; /* 0x598 */ + uint32_t cmd_brlshft_0; /* 0x59C */ + uint32_t cmd_brlshft_1; /* 0x5A0 */ + uint32_t cmd_brlshft_2; /* 0x5A4 */ + uint32_t cmd_brlshft_3; /* 0x5A8 */ + uint32_t quse_brlshft_0; /* 0x5AC */ + uint32_t auto_cal_config4; /* 0x5B0 */ + uint32_t auto_cal_config5; /* 0x5B4 */ + uint32_t quse_brlshft_1; /* 0x5B8 */ + uint32_t quse_brlshft_2; /* 0x5BC */ + uint32_t ccdmw; /* 0x5C0 */ + uint32_t quse_brlshft_3; /* 0x5C4 */ + uint32_t fbio_cfg8; /* 0x5C8 */ + uint32_t auto_cal_config6; /* 0x5CC */ + uint32_t protobist_config_addr_1; /* 0x5D0 */ + uint32_t protobist_config_addr_2; /* 0x5D4 */ + uint32_t protobist_misc; /* 0x5D8 */ + uint32_t protobist_wdata_lower; /* 0x5DC */ + uint32_t protobist_wdata_upper; /* 0x5E0 */ + uint32_t dll_cfg0; /* 0x5E4 */ + uint32_t dll_cfg1; /* 0x5E8 */ + uint32_t protobist_rdata; /* 0x5EC */ + uint32_t config_sample_delay; /* 0x5F0 */ + uint32_t cfg_update; /* 0x5F4 */ + uint32_t rsvd_0x5f8[2]; /* 0x5F8-0x5FC */ + + uint32_t pmacro_quse_ddll_rank0_0; /* 0x600 */ + uint32_t pmacro_quse_ddll_rank0_1; /* 0x604 */ + uint32_t pmacro_quse_ddll_rank0_2; /* 0x608 */ + uint32_t pmacro_quse_ddll_rank0_3; /* 0x60C */ + uint32_t pmacro_quse_ddll_rank0_4; /* 0x610 */ + uint32_t pmacro_quse_ddll_rank0_5; /* 0x614 */ + uint32_t rsvd_0x618[2]; /* 0x618-0x61C */ + + uint32_t pmacro_quse_ddll_rank1_0; /* 0x620 */ + uint32_t pmacro_quse_ddll_rank1_1; /* 0x624 */ + uint32_t pmacro_quse_ddll_rank1_2; /* 0x628 */ + uint32_t pmacro_quse_ddll_rank1_3; /* 0x62C */ + uint32_t pmacro_quse_ddll_rank1_4; /* 0x630 */ + uint32_t pmacro_quse_ddll_rank1_5; /* 0x634 */ + uint32_t rsvd_0x638[2]; /* 0x638-0x63C */ + + uint32_t pmacro_ob_ddll_long_dq_rank0_0; /* 0x640 */ + uint32_t pmacro_ob_ddll_long_dq_rank0_1; /* 0x644 */ + uint32_t pmacro_ob_ddll_long_dq_rank0_2; /* 0x648 */ + uint32_t pmacro_ob_ddll_long_dq_rank0_3; /* 0x64C */ + uint32_t pmacro_ob_ddll_long_dq_rank0_4; /* 0x650 */ + uint32_t pmacro_ob_ddll_long_dq_rank0_5; /* 0x654 */ + uint32_t rsvd_0x658[2]; /* 0x658-0x65C */ + + uint32_t pmacro_ob_ddll_long_dq_rank1_0; /* 0x660 */ + uint32_t pmacro_ob_ddll_long_dq_rank1_1; /* 0x664 */ + uint32_t pmacro_ob_ddll_long_dq_rank1_2; /* 0x668 */ + uint32_t pmacro_ob_ddll_long_dq_rank1_3; /* 0x66C */ + uint32_t pmacro_ob_ddll_long_dq_rank1_4; /* 0x670 */ + uint32_t pmacro_ob_ddll_long_dq_rank1_5; /* 0x674 */ + uint32_t rsvd_0x678[2]; /* 0x678-0x67C */ + + uint32_t pmacro_ob_ddll_long_dqs_rank0_0; /* 0x680 */ + uint32_t pmacro_ob_ddll_long_dqs_rank0_1; /* 0x684 */ + uint32_t pmacro_ob_ddll_long_dqs_rank0_2; /* 0x688 */ + uint32_t pmacro_ob_ddll_long_dqs_rank0_3; /* 0x68C */ + uint32_t pmacro_ob_ddll_long_dqs_rank0_4; /* 0x690 */ + uint32_t pmacro_ob_ddll_long_dqs_rank0_5; /* 0x694 */ + uint32_t rsvd_0x698[2]; /* 0x698-0x69C */ + + uint32_t pmacro_ob_ddll_long_dqs_rank1_0; /* 0x6A0 */ + uint32_t pmacro_ob_ddll_long_dqs_rank1_1; /* 0x6A4 */ + uint32_t pmacro_ob_ddll_long_dqs_rank1_2; /* 0x6A8 */ + uint32_t pmacro_ob_ddll_long_dqs_rank1_3; /* 0x6AC */ + uint32_t pmacro_ob_ddll_long_dqs_rank1_4; /* 0x6B0 */ + uint32_t pmacro_ob_ddll_long_dqs_rank1_5; /* 0x6B4 */ + uint32_t rsvd_0x6B8[2]; /* 0x6B8-0x6BC */ + + uint32_t pmacro_ib_ddll_long_dqs_rank0_0; /* 0x6C0 */ + uint32_t pmacro_ib_ddll_long_dqs_rank0_1; /* 0x6C4 */ + uint32_t pmacro_ib_ddll_long_dqs_rank0_2; /* 0x6C8 */ + uint32_t pmacro_ib_ddll_long_dqs_rank0_3; /* 0x6CC */ + uint32_t pmacro_ib_ddll_long_dqs_rank0_4; /* 0x6D0 */ + uint32_t pmacro_ib_ddll_long_dqs_rank0_5; /* 0x6D4 */ + uint32_t rsvd_0x6D8[2]; /* 0x6D8-0x6DC */ + + uint32_t pmacro_ib_ddll_long_dqs_rank1_0; /* 0x6E0 */ + uint32_t pmacro_ib_ddll_long_dqs_rank1_1; /* 0x6E4 */ + uint32_t pmacro_ib_ddll_long_dqs_rank1_2; /* 0x6E8 */ + uint32_t pmacro_ib_ddll_long_dqs_rank1_3; /* 0x6EC */ + uint32_t pmacro_ib_ddll_long_dqs_rank1_4; /* 0x6F0 */ + uint32_t pmacro_ib_ddll_long_dqs_rank1_5; /* 0x6F4 */ + uint32_t rsvd_0x6F8[2]; /* 0x6F8-0x6FC */ + + uint32_t pmacro_autocal_cfg0; /* 0x700 */ + uint32_t pmacro_autocal_cfg1; /* 0x704 */ + uint32_t pmacro_autocal_cfg2; /* 0x708 */ + uint32_t rsvd_0x70C[5]; /* 0x70C-0x71C */ + + uint32_t pmacro_tx_pwrd_0; /* 0x720 */ + uint32_t pmacro_tx_pwrd_1; /* 0x724 */ + uint32_t pmacro_tx_pwrd_2; /* 0x728 */ + uint32_t pmacro_tx_pwrd_3; /* 0x72C */ + uint32_t pmacro_tx_pwrd_4; /* 0x730 */ + uint32_t pmacro_tx_pwrd_5; /* 0x734 */ + uint32_t rsvd_0x738[2]; /* 0x738-0x73C */ + + uint32_t pmacro_tx_sel_clk_src_0; /* 0x740 */ + uint32_t pmacro_tx_sel_clk_src_1; /* 0x744 */ + uint32_t pmacro_tx_sel_clk_src_2; /* 0x748 */ + uint32_t pmacro_tx_sel_clk_src_3; /* 0x74C */ + uint32_t pmacro_tx_sel_clk_src_4; /* 0x750 */ + uint32_t pmacro_tx_sel_clk_src_5; /* 0x754 */ + uint32_t rsvd_0x758[2]; /* 0x758-0x75C */ + + uint32_t pmacro_ddll_bypass; /* 0x760 */ + uint32_t rsvd_0x764[3]; /* 0x764-0x76C */ + + uint32_t pmacro_ddll_pwrd_0; /* 0x770 */ + uint32_t pmacro_ddll_pwrd_1; /* 0x774 */ + uint32_t pmacro_ddll_pwrd_2; /* 0x778 */ + uint32_t rsvd_0x77C[1]; /* 0x77C */ + uint32_t pmacro_cmd_ctrl_0; /* 0x780 */ + uint32_t pmacro_cmd_ctrl_1; /* 0x784 */ + uint32_t pmacro_cmd_ctrl_2; /* 0x788 */ + uint32_t rsvd_0x78C[277]; /* 0x78C-0xBDC */ + + uint32_t pmacro_ib_vref_dq_0; /* 0xBE0 */ + uint32_t pmacro_ib_vref_dq_1; /* 0xBE4 */ + uint32_t pmacro_ib_vref_dq_2; /* 0xBE8 */ + uint32_t rsvd_0xBEC[1]; /* 0xBEC */ + uint32_t pmacro_ib_vref_dqs_0; /* 0xBF0 */ + uint32_t pmacro_ib_vref_dqs_1; /* 0xBF4 */ + uint32_t pmacro_ib_vref_dqs_2; /* 0xBF8 */ + uint32_t rsvd_0xBFC[1]; /* 0xBFC */ + uint32_t pmacro_ddll_long_cmd_0; /* 0xC00 */ + uint32_t pmacro_ddll_long_cmd_1; /* 0xC04 */ + uint32_t pmacro_ddll_long_cmd_2; /* 0xC08 */ + uint32_t pmacro_ddll_long_cmd_3; /* 0xC0C */ + uint32_t pmacro_ddll_long_cmd_4; /* 0xC10 */ + uint32_t pmacro_ddll_long_cmd_5; /* 0xC14 */ + uint32_t rsvd_0xC18[2]; /* 0xC18-0xC1C */ + + uint32_t pmacro_ddll_short_cmd_0; /* 0xC20 */ + uint32_t pmacro_ddll_short_cmd_1; /* 0xC24 */ + uint32_t pmacro_ddll_short_cmd_2; /* 0xC28 */ + uint32_t rsvd_0xC2C[2]; /* 0xC2C-0xC30 */ + + uint32_t pmacro_vttgen_ctrl0; /* 0xC34 */ + uint32_t pmacro_vttgen_ctrl1; /* 0xC38 */ + uint32_t pmacro_bg_bias_ctrl_0; /* 0xC3C */ + uint32_t pmacro_pad_cfg_ctrl; /* 0xC40 */ + uint32_t pmacro_zctrl; /* 0xC44 */ + uint32_t pmacro_rx_term; /* 0xC48 */ + uint32_t pmacro_cmd_tx_drv; /* 0xC4C */ + uint32_t pmacro_cmd_pad_rx_ctrl; /* 0xC50 */ + uint32_t pmacro_data_pad_rx_ctrl; /* 0xC54 */ + uint32_t pmacro_cmd_rx_term_mode; /* 0xC58 */ + uint32_t pmacro_data_rx_term_mode; /* 0xC5C */ + uint32_t pmacro_cmd_pad_tx_ctrl; /* 0xC60 */ + uint32_t pmacro_data_pad_tx_ctrl; /* 0xC64 */ + uint32_t pmacro_common_pad_tx_ctrl; /* 0xC68 */ + uint32_t rsvd_0xC6C[1]; /* 0xC6C */ + uint32_t pmacro_dq_tx_drv; /* 0xC70 */ + uint32_t pmacro_ca_tx_drv; /* 0xC74 */ + uint32_t pmacro_autocal_cfg_common; /* 0xC78 */ + uint32_t rsvd_0xC7C[1]; /* 0xC7C */ + uint32_t pmacro_brick_mapping0; /* 0xC80 */ + uint32_t pmacro_brick_mapping1; /* 0xC84 */ + uint32_t pmacro_brick_mapping2; /* 0xC88 */ + uint32_t rsvd_0xC8C[25]; /* 0xC8C-0xCEC */ + + uint32_t pmacro_vttgen_ctrl2; /* 0xCF0 */ + uint32_t pmacro_ib_rxrt; /* 0xCF4 */ + uint32_t pmacro_training_ctrl0; /* 0xCF8 */ + uint32_t pmacro_training_ctrl1; /* 0xCFC */ +} __attribute__((packed)); + +check_member(tegra_emc_regs, pmacro_training_ctrl1, 0xCFC); + +#endif /* __SOC_NVIDIA_TEGRA210_EMC_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/flow.h b/src/soc/nvidia/tegra210/include/soc/flow.h new file mode 100644 index 0000000000..a3eccf868b --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/flow.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef _TEGRA210_FLOW_H_ +#define _TEGRA210_FLOW_H_ + +struct flow_ctlr { + u32 halt_cpu_events; /* offset 0x00 */ + u32 halt_cop_events; /* offset 0x04 */ + u32 cpu_csr; /* offset 0x08 */ + u32 cop_csr; /* offset 0x0c */ + u32 xrq_events; /* offset 0x10 */ + u32 halt_cpu1_events; /* offset 0x14 */ + u32 cpu1_csr; /* offset 0x18 */ + u32 halt_cpu2_events; /* offset 0x1c */ + u32 cpu2_csr; /* offset 0x20 */ + u32 halt_cpu3_events; /* offset 0x24 */ + u32 cpu3_csr; /* offset 0x28 */ + u32 cluster_control; /* offset 0x2c */ + u32 halt_cop1_events; /* offset 0x30 */ + u32 halt_cop1_csr; /* offset 0x34 */ + u32 cpu_pwr_csr; /* offset 0x38 */ + u32 mpid; /* offset 0x3c */ + u32 ram_repair; /* offset 0x40 */ + u32 flow_dbg_sel; /* offset 0x44 */ + u32 flow_dbg_cnt0; /* offset 0x48 */ + u32 flow_dbg_cnt1; /* offset 0x4c */ + u32 flow_dbg_qual; /* offset 0x50 */ + u32 flow_ctlr_spare; /* offset 0x54 */ + u32 reserved; /* offset 0x58 */ + u32 fc_seq_intercept; /* offset 0x5c */ +}; +check_member(flow_ctlr, fc_seq_intercept, 0x5c); + +enum { + FLOW_MODE_SHIFT = 29, + FLOW_MODE_MASK = 0x7 << FLOW_MODE_SHIFT, + + FLOW_MODE_NONE = 0 << FLOW_MODE_SHIFT, + FLOW_MODE_RUN_AND_INT = 1 << FLOW_MODE_SHIFT, + FLOW_MODE_WAITEVENT = 2 << FLOW_MODE_SHIFT, + FLOW_MODE_WAITEVENT_AND_INT = 3 << FLOW_MODE_SHIFT, + FLOW_MODE_STOP_UNTIL_IRQ = 4 << FLOW_MODE_SHIFT, + FLOW_MODE_STOP_UNTIL_IRQ_AND_INT = 5 << FLOW_MODE_SHIFT, + FLOW_MODE_STOP_UNTIL_EVENT_AND_IRQ = 6 << FLOW_MODE_SHIFT, +}; + +/* HALT_COP_EVENTS_0, 0x04 */ +enum { + FLOW_EVENT_GIC_FIQ = 1 << 8, + FLOW_EVENT_GIC_IRQ = 1 << 9, + FLOW_EVENT_LIC_FIQ = 1 << 10, + FLOW_EVENT_LIC_IRQ = 1 << 11, + FLOW_EVENT_IBF = 1 << 12, + FLOW_EVENT_IBE = 1 << 13, + FLOW_EVENT_OBF = 1 << 14, + FLOW_EVENT_OBE = 1 << 15, + FLOW_EVENT_XRQ_A = 1 << 16, + FLOW_EVENT_XRQ_B = 1 << 17, + FLOW_EVENT_XRQ_C = 1 << 18, + FLOW_EVENT_XRQ_D = 1 << 19, + FLOW_EVENT_SMP30 = 1 << 20, + FLOW_EVENT_SMP31 = 1 << 21, + FLOW_EVENT_X_RDY = 1 << 22, + FLOW_EVENT_SEC = 1 << 23, + FLOW_EVENT_MSEC = 1 << 24, + FLOW_EVENT_USEC = 1 << 25, + FLOW_EVENT_X32K = 1 << 26, + FLOW_EVENT_SCLK = 1 << 27, + FLOW_EVENT_JTAG = 1 << 28 +}; + +#endif /* _TEGRA210_FLOW_H_ */ diff --git a/src/soc/nvidia/tegra210/include/soc/flow_ctrl.h b/src/soc/nvidia/tegra210/include/soc/flow_ctrl.h new file mode 100644 index 0000000000..47d68b0651 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/flow_ctrl.h @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2014-2015, NVIDIA CORPORATION. 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. + */ + +#ifndef _TEGRA210_FLOW_CTRL_H_ +#define _TEGRA210_FLOW_CTRL_H_ + +void flowctrl_cpu_off(int cpu); +void flowctrl_cpu_on(int cpu); +void flowctrl_cpu_suspend(int cpu); +void flowctrl_write_cc4_ctrl(int cpu, uint32_t val); +void flowctrl_write_cpu_csr(int cpu, uint32_t val); +void flowctrl_write_cpu_halt(int cpu, uint32_t val); + +#endif diff --git a/src/soc/nvidia/tegra210/include/soc/funitcfg.h b/src/soc/nvidia/tegra210/include/soc/funitcfg.h new file mode 100644 index 0000000000..d51b13fe22 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/funitcfg.h @@ -0,0 +1,94 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_FUNIT_CFG_H +#define __SOC_NVIDIA_TEGRA210_FUNIT_CFG_H + +#include <soc/clock.h> +#include <soc/padconfig.h> +#include <soc/pinmux.h> +#include <stdint.h> + +#define FUNIT_INDEX(_name) FUNIT_##_name + +enum { + FUNIT_INDEX(I2C1), + FUNIT_INDEX(I2C2), + FUNIT_INDEX(I2C3), + FUNIT_INDEX(I2C5), + FUNIT_INDEX(I2C6), + FUNIT_INDEX(SDMMC1), + FUNIT_INDEX(SDMMC4), + FUNIT_INDEX(USBD), + FUNIT_INDEX(USB2), + FUNIT_INDEX(QSPI), + FUNIT_INDEX(I2S1), + FUNIT_INDEX_MAX, +}; + +/* + * Note: these bus numbers are dependent on the driver implementations, and + * currently the I2C is 0-based and SPI is 1-based in its indexing. + */ +enum { + + I2C1_BUS = 0, + I2C2_BUS = 1, + I2C3_BUS = 2, + I2C5_BUS = 4, + I2CPWR_BUS = I2C5_BUS, + I2C6_BUS = 5, + QSPI_BUS = 7, + + SPI1_BUS = 1, + SPI4_BUS = 4, +}; + +struct funit_cfg { + uint32_t funit_index; + uint32_t clk_src_id; + uint32_t clk_src_freq_id; + uint32_t clk_dev_freq_khz; + struct pad_config const* pad_cfg; + size_t pad_cfg_size; +}; + +#define FUNIT_CFG(_funit,_clk_src,_clk_freq,_cfg,_cfg_size) \ + { \ + .funit_index = FUNIT_INDEX(_funit), \ + .clk_src_id = CLK_SRC_DEV_ID(_funit, _clk_src), \ + .clk_src_freq_id = CLK_SRC_FREQ_ID(_funit, _clk_src), \ + .clk_dev_freq_khz = _clk_freq, \ + .pad_cfg = _cfg, \ + .pad_cfg_size = _cfg_size, \ + } + +#define FUNIT_CFG_USB(_funit) \ + { \ + .funit_index = FUNIT_INDEX(_funit), \ + .pad_cfg = NULL, \ + .pad_cfg_size = 0, \ + } + +/* + * Configure the funits associated with entry according to the configuration. + */ +void soc_configure_funits(const struct funit_cfg * const entries, size_t num); + +#endif /* __SOC_NVIDIA_TEGRA210_FUNIT_CFG_H */ diff --git a/src/soc/nvidia/tegra210/include/soc/gpio.h b/src/soc/nvidia/tegra210/include/soc/gpio.h new file mode 100644 index 0000000000..f4c236a99f --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/gpio.h @@ -0,0 +1,25 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_GPIO_H__ +#define __SOC_NVIDIA_TEGRA210_GPIO_H__ + +#include <soc/pinmux.h> + +#endif /* __SOC_NVIDIA_TEGRA210_GPIO_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/id.h b/src/soc/nvidia/tegra210/include/soc/id.h new file mode 100644 index 0000000000..5090a31081 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/id.h @@ -0,0 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ID_H__ +#define __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ID_H__ + + +#include <arch/io.h> +#include <soc/addressmap.h> + +static inline int context_avp(void) +{ + const uint32_t avp_id = 0xaaaaaaaa; + void * const uptag = (void *)(uintptr_t)TEGRA_PG_UP_BASE; + + return read32(uptag) == avp_id; +} + +#endif /* define __SOC_NVIDIA_TEGRA210_INCLUDE_SOC_ID_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/maincpu.h b/src/soc/nvidia/tegra210/include/soc/maincpu.h new file mode 100644 index 0000000000..813caba82c --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/maincpu.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_MAINCPU_H__ +#define __SOC_NVIDIA_TEGRA210_MAINCPU_H__ + +#include <stdint.h> + +extern u32 maincpu_stack_pointer; +extern u32 maincpu_entry_point; +void maincpu_setup(void); + +#endif /* __SOC_NVIDIA_TEGRA210_MAINCPU_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/mc.h b/src/soc/nvidia/tegra210/include/soc/mc.h new file mode 100644 index 0000000000..51ae827b93 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mc.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_MC_H__ +#define __SOC_NVIDIA_TEGRA210_MC_H__ + +#include <stddef.h> +#include <stdint.h> + +// Memory Controller registers we need/care about + +struct tegra_mc_regs { + uint32_t rsvd_0x0[4]; /* 0x00 */ + uint32_t smmu_config; /* 0x10 */ + uint32_t smmu_tlb_config; /* 0x14 */ + uint32_t smmu_ptc_config; /* 0x18 */ + uint32_t smmu_ptb_asid; /* 0x1c */ + uint32_t smmu_ptb_data; /* 0x20 */ + uint32_t rsvd_0x24[3]; /* 0x24 */ + uint32_t smmu_tlb_flush; /* 0x30 */ + uint32_t smmu_ptc_flush; /* 0x34 */ + uint32_t rsvd_0x38[6]; /* 0x38 */ + uint32_t emem_cfg; /* 0x50 */ + uint32_t emem_adr_cfg; /* 0x54 */ + uint32_t emem_adr_cfg_dev0; /* 0x58 */ + uint32_t emem_adr_cfg_dev1; /* 0x5c */ + uint32_t emem_adr_cfg_channel_mask; /* 0x60 */ + uint32_t emem_adr_cfg_bank_mask_0; /* 0x64 */ + uint32_t emem_adr_cfg_bank_mask_1; /* 0x68 */ + uint32_t emem_adr_cfg_bank_mask_2; /* 0x6c */ + uint32_t security_cfg0; /* 0x70 */ + uint32_t security_cfg1; /* 0x74 */ + uint32_t rsvd_0x78[6]; /* 0x78 */ + uint32_t emem_arb_cfg; /* 0x90 */ + uint32_t emem_arb_outstanding_req; /* 0x94 */ + uint32_t emem_arb_timing_rcd; /* 0x98 */ + uint32_t emem_arb_timing_rp; /* 0x9c */ + uint32_t emem_arb_timing_rc; /* 0xa0 */ + uint32_t emem_arb_timing_ras; /* 0xa4 */ + uint32_t emem_arb_timing_faw; /* 0xa8 */ + uint32_t emem_arb_timing_rrd; /* 0xac */ + uint32_t emem_arb_timing_rap2pre; /* 0xb0 */ + uint32_t emem_arb_timing_wap2pre; /* 0xb4 */ + uint32_t emem_arb_timing_r2r; /* 0xb8 */ + uint32_t emem_arb_timing_w2w; /* 0xbc */ + uint32_t emem_arb_timing_r2w; /* 0xc0 */ + uint32_t emem_arb_timing_w2r; /* 0xc4 */ + uint32_t emem_arb_misc2; /* 0xC8 */ + uint32_t rsvd_0xcc[1]; /* 0xCC */ + uint32_t emem_arb_da_turns; /* 0xd0 */ + uint32_t emem_arb_da_covers; /* 0xd4 */ + uint32_t emem_arb_misc0; /* 0xd8 */ + uint32_t emem_arb_misc1; /* 0xdc */ + uint32_t emem_arb_ring1_throttle; /* 0xe0 */ + uint32_t emem_arb_ring3_throttle; /* 0xe4 */ + uint32_t emem_arb_override; /* 0xe8 */ + uint32_t emem_arb_rsv; /* 0xec */ + uint32_t rsvd_0xf0[1]; /* 0xf0 */ + uint32_t clken_override; /* 0xf4 */ + uint32_t timing_control_dbg; /* 0xf8 */ + uint32_t timing_control; /* 0xfc */ + uint32_t stat_control; /* 0x100 */ + uint32_t rsvd_0x104[65]; /* 0x104 */ + uint32_t emem_arb_isochronous_0; /* 0x208 */ + uint32_t emem_arb_isochronous_1; /* 0x20c */ + uint32_t emem_arb_isochronous_2; /* 0x210 */ + uint32_t rsvd_0x214[38]; /* 0x214 */ + uint32_t dis_extra_snap_levels; /* 0x2ac */ + uint32_t rsvd_0x2b0[90]; /* 0x2b0 */ + uint32_t video_protect_vpr_override; /* 0x418 */ + uint32_t rsvd_0x41c[93]; /* 0x41c */ + uint32_t video_protect_vpr_override1; /* 0x590 */ + uint32_t rsvd_0x594[29]; /* 0x594 */ + uint32_t display_snap_ring; /* 0x608 */ + uint32_t rsvd_0x60c[15]; /* 0x60c */ + uint32_t video_protect_bom; /* 0x648 */ + uint32_t video_protect_size_mb; /* 0x64c */ + uint32_t video_protect_reg_ctrl; /* 0x650 */ + uint32_t rsvd_0x654[4]; /* 0x654 */ + uint32_t emem_cfg_access_ctrl; /* 0x664 */ + uint32_t rsvd_0x668[2]; /* 0x668 */ + uint32_t sec_carveout_bom; /* 0x670 */ + uint32_t sec_carveout_size_mb; /* 0x674 */ + uint32_t sec_carveout_reg_ctrl; /* 0x678 */ + uint32_t rsvd_0x67c[17]; /* 0x67C-0x6BC */ + + uint32_t emem_arb_timing_rfcpb; /* 0x6C0 */ + uint32_t emem_arb_timing_ccdmw; /* 0x6C4 */ + uint32_t rsvd_0x6c8[10]; /* 0x6C8-0x6EC */ + + uint32_t emem_arb_refpb_hp_ctrl; /* 0x6F0 */ + uint32_t emem_arb_refpb_bank_ctrl; /* 0x6F4 */ + uint32_t rsvd_0x6f8[156]; /* 0x6F8-0x964 */ + + uint32_t emem_arb_override_1; /* 0x968 */ + uint32_t rsvd_0x96c[3]; /* 0x96c */ + uint32_t video_protect_bom_adr_hi; /* 0x978 */ + uint32_t rsvd_0x97c[2]; /* 0x97c */ + uint32_t video_protect_gpu_override_0; /* 0x984 */ + uint32_t video_protect_gpu_override_1; /* 0x988 */ + uint32_t rsvd_0x98c[5]; /* 0x98c */ + uint32_t mts_carveout_bom; /* 0x9a0 */ + uint32_t mts_carveout_size_mb; /* 0x9a4 */ + uint32_t mts_carveout_adr_hi; /* 0x9a8 */ + uint32_t mts_carveout_reg_ctrl; /* 0x9ac */ + uint32_t rsvd_0x9b0[4]; /* 0x9b0 */ + uint32_t emem_bank_swizzle_cfg0; /* 0x9c0 */ + uint32_t emem_bank_swizzle_cfg1; /* 0x9c4 */ + uint32_t emem_bank_swizzle_cfg2; /* 0x9c8 */ + uint32_t emem_bank_swizzle_cfg3; /* 0x9cc */ + uint32_t rsvd_0x9d0[1]; /* 0x9d0 */ + uint32_t sec_carveout_adr_hi; /* 0x9d4 */ + uint32_t rsvd_0x9d8; /* 0x9D8 */ + uint32_t da_config0; /* 0x9DC */ + uint32_t rsvd_0x9c0[138]; /* 0x9E0-0xc04 */ + + uint32_t security_carveout1_cfg0; /* 0xc08 */ + uint32_t security_carveout1_bom; /* 0xc0c */ + uint32_t security_carveout1_bom_hi; /* 0xc10 */ + uint32_t security_carveout1_size_128kb; /* 0xc14 */ + uint32_t security_carveout1_ca0; /* 0xc18 */ + uint32_t security_carveout1_ca1; /* 0xc1c */ + uint32_t security_carveout1_ca2; /* 0xc20 */ + uint32_t security_carveout1_ca3; /* 0xc24 */ + uint32_t security_carveout1_ca4; /* 0xc28 */ + uint32_t security_carveout1_cfia0; /* 0xc2c */ + uint32_t security_carveout1_cfia1; /* 0xc30 */ + uint32_t security_carveout1_cfia2; /* 0xc34 */ + uint32_t security_carveout1_cfia3; /* 0xc38 */ + uint32_t security_carveout1_cfia4; /* 0xc3c */ + uint32_t rsvd_0xc40[6]; /* 0xc40-0xc54 */ + + uint32_t security_carveout2_cfg0; /* 0xc58 */ + uint32_t security_carveout2_bom; /* 0xc5c */ + uint32_t security_carveout2_bom_hi; /* 0xc60 */ + uint32_t security_carveout2_size_128kb; /* 0xc64 */ + uint32_t security_carveout2_ca0; /* 0xc68 */ + uint32_t security_carveout2_ca1; /* 0xc6c */ + uint32_t security_carveout2_ca2; /* 0xc70 */ + uint32_t security_carveout2_ca3; /* 0xc74 */ + uint32_t security_carveout2_ca4; /* 0xc78 */ + uint32_t security_carveout2_cfia0; /* 0xc7c */ + uint32_t security_carveout2_cfia1; /* 0xc80 */ + uint32_t security_carveout2_cfia2; /* 0xc84 */ + uint32_t security_carveout2_cfia3; /* 0xc88 */ + uint32_t security_carveout2_cfia4; /* 0xc8c */ + uint32_t rsvd_0xc90[6]; /* 0xc90-0xca4 */ + + uint32_t security_carveout3_cfg0; /* 0xca8 */ + uint32_t security_carveout3_bom; /* 0xcac */ + uint32_t security_carveout3_bom_hi; /* 0xcb0 */ + uint32_t security_carveout3_size_128kb; /* 0xcb4 */ + uint32_t security_carveout3_ca0; /* 0xcb8 */ + uint32_t security_carveout3_ca1; /* 0xcbc */ + uint32_t security_carveout3_ca2; /* 0xcc0 */ + uint32_t security_carveout3_ca3; /* 0xcc4 */ + uint32_t security_carveout3_ca4; /* 0xcc8 */ + uint32_t security_carveout3_cfia0; /* 0xccc */ + uint32_t security_carveout3_cfia1; /* 0xcd0 */ + uint32_t security_carveout3_cfia2; /* 0xcd4 */ + uint32_t security_carveout3_cfia3; /* 0xcd8 */ + uint32_t security_carveout3_cfia4; /* 0xcdc */ + uint32_t rsvd_0xce0[6]; /* 0xce0-0xcf4 */ + + uint32_t security_carveout4_cfg0; /* 0xcf8 */ + uint32_t security_carveout4_bom; /* 0xcfc */ + uint32_t security_carveout4_bom_hi; /* 0xd00 */ + uint32_t security_carveout4_size_128kb; /* 0xd04 */ + uint32_t security_carveout4_ca0; /* 0xd08 */ + uint32_t security_carveout4_ca1; /* 0xd0c */ + uint32_t security_carveout4_ca2; /* 0xd10 */ + uint32_t security_carveout4_ca3; /* 0xd14 */ + uint32_t security_carveout4_ca4; /* 0xd18 */ + uint32_t security_carveout4_cfia0; /* 0xd1c */ + uint32_t security_carveout4_cfia1; /* 0xd20 */ + uint32_t security_carveout4_cfia2; /* 0xd24 */ + uint32_t security_carveout4_cfia3; /* 0xd28 */ + uint32_t security_carveout4_cfia4; /* 0xd2c */ + uint32_t rsvd_0xd30[6]; /* 0xd30-0xd44 */ + + uint32_t security_carveout5_cfg0; /* 0xd48 */ + uint32_t security_carveout5_bom; /* 0xd4c */ + uint32_t security_carveout5_bom_hi; /* 0xd50 */ + uint32_t security_carveout5_size_128kb; /* 0xd54 */ + uint32_t security_carveout5_ca0; /* 0xd58 */ + uint32_t security_carveout5_ca1; /* 0xd5c */ + uint32_t security_carveout5_ca2; /* 0xd60 */ + uint32_t security_carveout5_ca3; /* 0xd64 */ + uint32_t security_carveout5_ca4; /* 0xd68 */ + uint32_t security_carveout5_cfia0; /* 0xd6c */ + uint32_t security_carveout5_cfia1; /* 0xd70 */ + uint32_t security_carveout5_cfia2; /* 0xd74 */ + uint32_t security_carveout5_cfia3; /* 0xd78 */ + uint32_t security_carveout5_cfia4; /* 0xd7c */ +}; + +enum { + MC_SMMU_CONFIG_ENABLE = 1, + + MC_EMEM_CFG_SIZE_MB_SHIFT = 0, + MC_EMEM_CFG_SIZE_MB_MASK = 0x3fff, + + MC_EMEM_ARB_MISC0_MC_EMC_SAME_FREQ_SHIFT = 27, + MC_EMEM_ARB_MISC0_MC_EMC_SAME_FREQ_MASK = 1 << 27, + + MC_EMEM_CFG_ACCESS_CTRL_WRITE_ACCESS_DISABLED = 1, + + MC_TIMING_CONTROL_TIMING_UPDATE = 1, +}; + +#define MC_SECURITY_CARVEOUT_LOCKED (1 << 1) +#define MC_VPR_WR_ACCESS_DISABLE (1 << 0) +#define MC_VPR_ALLOW_TZ_WR_ACCESS_ENABLE (1 << 1) + +check_member(tegra_mc_regs, security_carveout5_cfia4, 0xd7c); + +#endif /* __SOC_NVIDIA_TEGRA210_MC_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/memlayout.ld b/src/soc/nvidia/tegra210/include/soc/memlayout.ld new file mode 100644 index 0000000000..df9eed5ea1 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/memlayout.ld @@ -0,0 +1,44 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <memlayout.h> + +#include <arch/header.ld> + +/* + * Note: The BootROM uses the address range [0x4000_0000:0x4000_E000) itself, + * so the bootblock loading address must be placed after that. After the + * handoff that area may be reclaimed for other uses, e.g. CBFS cache. + * TODO: Did this change on Tegra210? What's the new valid range? + */ + +SECTIONS +{ + SRAM_START(0x40000000) + PRERAM_CBMEM_CONSOLE(0x40000000, 8K) + PRERAM_CBFS_CACHE(0x40002000, 84K) + STACK(0x40017000, 16K) + BOOTBLOCK(0x4001B000, 26K) + ROMSTAGE(0x40022000, 120K) + SRAM_END(0x40040000) + + DRAM_START(0x80000000) + POSTRAM_CBFS_CACHE(0x80100000, 1M) + RAMSTAGE(0x80200000, 256K) +} diff --git a/src/soc/nvidia/tegra210/include/soc/memlayout_vboot2.ld b/src/soc/nvidia/tegra210/include/soc/memlayout_vboot2.ld new file mode 100644 index 0000000000..7aa98e0e02 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/memlayout_vboot2.ld @@ -0,0 +1,47 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <memlayout.h> +#include <vendorcode/google/chromeos/memlayout.h> + +#include <arch/header.ld> + +/* + * Note: The BootROM uses the address range [0x4000_0000:0x4000_E000) itself, + * so the bootblock loading address must be placed after that. After the + * handoff that area may be reclaimed for other uses, e.g. CBFS cache. + * TODO: Did this change on Tegra210? What's the new valid range? + */ + +SECTIONS +{ + SRAM_START(0x40000000) + PRERAM_CBMEM_CONSOLE(0x40000000, 8K) + PRERAM_CBFS_CACHE(0x40002000, 72K) + VBOOT2_WORK(0x40014000, 16K) + STACK(0x40018000, 2K) + BOOTBLOCK(0x40019000, 24K) + VERSTAGE(0x4001F000, 56K) + ROMSTAGE(0x4002D000, 76K) + SRAM_END(0x40040000) + + DRAM_START(0x80000000) + POSTRAM_CBFS_CACHE(0x80100000, 1M) + RAMSTAGE(0x80200000, 256K) +} diff --git a/src/soc/nvidia/tegra210/include/soc/mipi-phy.h b/src/soc/nvidia/tegra210/include/soc/mipi-phy.h new file mode 100644 index 0000000000..eb81719ace --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mipi-phy.h @@ -0,0 +1,46 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#ifndef _TEGRA_MIPI_PHY_H +#define _TEGRA_MIPI_PHY_H + +#include <stdlib.h> + +/* + * Macros for calculating the phy timings + */ +/* Period of one bit time in nano seconds */ +#define DSI_TBIT_Factorized(Freq) (((1000) * (1000))/(Freq)) +#define DSI_TBIT(Freq) (DSI_TBIT_Factorized(Freq)/(1000)) + +//#define NV_MAX(a,b) (((a) > (b)) ? (a) : (b)) + +/* Period of one byte time in nano seconds */ +#define DSI_TBYTE(Freq) ((DSI_TBIT_Factorized(Freq)) * (8)) +#define DSI_PHY_TIMING_DIV(X, Freq) ((X*1000) / (DSI_TBYTE(Freq))) + +/* + * As per Mipi spec (minimum): + * (3 + MAX(8 * DSI_TBIT, 60 + 4 * DSI_TBIT) / DSI_TBYTE) + */ +#define DSI_THSTRAIL_VAL(Freq) \ + (MAX(((8) * (DSI_TBIT(Freq))), ((60) + ((4) * (DSI_TBIT(Freq)))))) + +int mipi_dphy_set_timing(struct tegra_dsi *dsi); + +#endif diff --git a/src/soc/nvidia/tegra210/include/soc/mipi_display.h b/src/soc/nvidia/tegra210/include/soc/mipi_display.h new file mode 100644 index 0000000000..2e2e453432 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mipi_display.h @@ -0,0 +1,148 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +/* + * Defines for Mobile Industry Processor Interface (MIPI(R)) + * Display Working Group standards: DSI, DCS, DBI, DPI + * + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef MIPI_DISPLAY_H +#define MIPI_DISPLAY_H + +/* MIPI DSI Processor-to-Peripheral transaction types */ +enum { + MIPI_DSI_V_SYNC_START = 0x01, + MIPI_DSI_V_SYNC_END = 0x11, + MIPI_DSI_H_SYNC_START = 0x21, + MIPI_DSI_H_SYNC_END = 0x31, + + MIPI_DSI_COLOR_MODE_OFF = 0x02, + MIPI_DSI_COLOR_MODE_ON = 0x12, + MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22, + MIPI_DSI_TURN_ON_PERIPHERAL = 0x32, + + MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03, + MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13, + MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23, + + MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04, + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14, + MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24, + + MIPI_DSI_DCS_SHORT_WRITE = 0x05, + MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15, + + MIPI_DSI_DCS_READ = 0x06, + + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37, + + MIPI_DSI_END_OF_TRANSMISSION = 0x08, + + MIPI_DSI_NULL_PACKET = 0x09, + MIPI_DSI_BLANKING_PACKET = 0x19, + MIPI_DSI_GENERIC_LONG_WRITE = 0x29, + MIPI_DSI_DCS_LONG_WRITE = 0x39, + + MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c, + + MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d, + MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d, + + MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e, + MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e, + MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e, + MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e, +}; + +/* MIPI DSI Peripheral-to-Processor transaction types */ +enum { + MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02, + MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12, + MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a, + MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22, +}; + +/* MIPI DCS commands */ +enum { + MIPI_DCS_NOP = 0x00, + MIPI_DCS_SOFT_RESET = 0x01, + MIPI_DCS_GET_DISPLAY_ID = 0x04, + MIPI_DCS_GET_RED_CHANNEL = 0x06, + MIPI_DCS_GET_GREEN_CHANNEL = 0x07, + MIPI_DCS_GET_BLUE_CHANNEL = 0x08, + MIPI_DCS_GET_DISPLAY_STATUS = 0x09, + MIPI_DCS_GET_POWER_MODE = 0x0A, + MIPI_DCS_GET_ADDRESS_MODE = 0x0B, + MIPI_DCS_GET_PIXEL_FORMAT = 0x0C, + MIPI_DCS_GET_DISPLAY_MODE = 0x0D, + MIPI_DCS_GET_SIGNAL_MODE = 0x0E, + MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F, + MIPI_DCS_ENTER_SLEEP_MODE = 0x10, + MIPI_DCS_EXIT_SLEEP_MODE = 0x11, + MIPI_DCS_ENTER_PARTIAL_MODE = 0x12, + MIPI_DCS_ENTER_NORMAL_MODE = 0x13, + MIPI_DCS_EXIT_INVERT_MODE = 0x20, + MIPI_DCS_ENTER_INVERT_MODE = 0x21, + MIPI_DCS_SET_GAMMA_CURVE = 0x26, + MIPI_DCS_SET_DISPLAY_OFF = 0x28, + MIPI_DCS_SET_DISPLAY_ON = 0x29, + MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A, + MIPI_DCS_SET_PAGE_ADDRESS = 0x2B, + MIPI_DCS_WRITE_MEMORY_START = 0x2C, + MIPI_DCS_WRITE_LUT = 0x2D, + MIPI_DCS_READ_MEMORY_START = 0x2E, + MIPI_DCS_SET_PARTIAL_AREA = 0x30, + MIPI_DCS_SET_SCROLL_AREA = 0x33, + MIPI_DCS_SET_TEAR_OFF = 0x34, + MIPI_DCS_SET_TEAR_ON = 0x35, + MIPI_DCS_SET_ADDRESS_MODE = 0x36, + MIPI_DCS_SET_SCROLL_START = 0x37, + MIPI_DCS_EXIT_IDLE_MODE = 0x38, + MIPI_DCS_ENTER_IDLE_MODE = 0x39, + MIPI_DCS_SET_PIXEL_FORMAT = 0x3A, + MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C, + MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E, + MIPI_DCS_SET_TEAR_SCANLINE = 0x44, + MIPI_DCS_GET_SCANLINE = 0x45, + MIPI_DCS_READ_DDB_START = 0xA1, + MIPI_DCS_READ_DDB_CONTINUE = 0xA8, +}; + +/* MIPI DCS pixel formats */ +#define MIPI_DCS_PIXEL_FMT_24BIT 7 +#define MIPI_DCS_PIXEL_FMT_18BIT 6 +#define MIPI_DCS_PIXEL_FMT_16BIT 5 +#define MIPI_DCS_PIXEL_FMT_12BIT 3 +#define MIPI_DCS_PIXEL_FMT_8BIT 2 +#define MIPI_DCS_PIXEL_FMT_3BIT 1 + +#endif diff --git a/src/soc/nvidia/tegra210/include/soc/mipi_dsi.h b/src/soc/nvidia/tegra210/include/soc/mipi_dsi.h new file mode 100644 index 0000000000..2d1e8b15d1 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mipi_dsi.h @@ -0,0 +1,316 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +/* + * MIPI DSI Bus + * + * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Andrzej Hajda <a.hajda@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MIPI_DSI_H__ +#define __MIPI_DSI_H__ + +struct mipi_dsi_host; +struct mipi_dsi_device; + +/* request ACK from peripheral */ +#define MIPI_DSI_MSG_REQ_ACK BIT(0) +/* use Low Power Mode to transmit message */ +#define MIPI_DSI_MSG_USE_LPM BIT(1) + +/** + * struct mipi_dsi_msg - read/write DSI buffer + * @channel: virtual channel id + * @type: payload data type + * @flags: flags controlling this message transmission + * @tx_len: length of @tx_buf + * @tx_buf: data to be written + * @rx_len: length of @rx_buf + * @rx_buf: data to be read, or NULL + */ +struct mipi_dsi_msg { + u8 channel; + u8 type; + u16 flags; + + size_t tx_len; + const void *tx_buf; + + size_t rx_len; + void *rx_buf; +}; + +/** + * struct mipi_dsi_host_ops - DSI bus operations + * @attach: attach DSI device to DSI host + * @detach: detach DSI device from DSI host + * @transfer: transmit a DSI packet + * + * DSI packets transmitted by .transfer() are passed in as mipi_dsi_msg + * structures. This structure contains information about the type of packet + * being transmitted as well as the transmit and receive buffers. When an + * error is encountered during transmission, this function will return a + * negative error code. On success it shall return the number of bytes + * transmitted for write packets or the number of bytes received for read + * packets. + * + * Note that typically DSI packet transmission is atomic, so the .transfer() + * function will seldomly return anything other than the number of bytes + * contained in the transmit buffer on success. + */ +struct mipi_dsi_host_ops { + int (*attach)(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi); + int (*detach)(struct mipi_dsi_host *host, + struct mipi_dsi_device *dsi); + ssize_t (*transfer)(struct mipi_dsi_host *host, + const struct mipi_dsi_msg *msg); +}; + +/** + * struct mipi_dsi_host - DSI host device + * @dev: driver model device node for this DSI host + * @ops: DSI host operations + */ +struct mipi_dsi_host { + //struct device *dev; + void *dev; + const struct mipi_dsi_host_ops *ops; +}; + +int mipi_dsi_host_register(struct mipi_dsi_host *host); + +/* DSI mode flags */ + +/* video mode */ +#define MIPI_DSI_MODE_VIDEO BIT(0) +/* video burst mode */ +#define MIPI_DSI_MODE_VIDEO_BURST BIT(1) +/* video pulse mode */ +#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2) +/* enable auto vertical count mode */ +#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3) +/* enable hsync-end packets in vsync-pulse and v-porch area */ +#define MIPI_DSI_MODE_VIDEO_HSE BIT(4) +/* disable hfront-porch area */ +#define MIPI_DSI_MODE_VIDEO_HFP BIT(5) +/* disable hback-porch area */ +#define MIPI_DSI_MODE_VIDEO_HBP BIT(6) +/* disable hsync-active area */ +#define MIPI_DSI_MODE_VIDEO_HSA BIT(7) +/* flush display FIFO on vsync pulse */ +#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) +/* disable EoT packets in HS mode */ +#define MIPI_DSI_MODE_EOT_PACKET BIT(9) +/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ +#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) + +enum mipi_dsi_pixel_format { + MIPI_DSI_FMT_RGB888, + MIPI_DSI_FMT_RGB666, + MIPI_DSI_FMT_RGB666_PACKED, + MIPI_DSI_FMT_RGB565, +}; + +struct mipi_dsi_master_ops { + int (*enslave)(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave); + int (*liberate)(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave); +}; + +/** + * struct mipi_dsi_device - DSI peripheral device + * @host: DSI host for this peripheral + * @dev: driver model device node for this peripheral + * @channel: virtual channel assigned to the peripheral + * @format: pixel format for video mode + * @lanes: number of active data lanes + * @mode_flags: DSI operation mode related flags + * @ops: callbacks for master/slave setup + * @master: master interface for dual-channel peripherals + * @slave: slave interface for dual-channel peripherals + * + * For dual-channel interfaces, the master interface can be identified by the + * fact that it's .slave field is set to non-NULL. The slave interface will + * have the .master field set to non-NULL. + */ +struct mipi_dsi_device { + struct mipi_dsi_host *host; + + unsigned int channel; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + + const struct mipi_dsi_master_ops *ops; + struct mipi_dsi_device *master; + struct mipi_dsi_device *slave; +}; + +int mipi_dsi_attach(struct mipi_dsi_device *dsi); +int mipi_dsi_detach(struct mipi_dsi_device *dsi); +int mipi_dsi_enslave(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave); +int mipi_dsi_liberate(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave); + +/** + * enum mipi_dsi_dcs_tear_mode - Tearing Effect Output Line mode + * @MIPI_DSI_DCS_TEAR_MODE_VBLANK: the TE output line consists of V-Blanking + * information only + * @MIPI_DSI_DCS_TEAR_MODE_VHBLANK : the TE output line consists of both + * V-Blanking and H-Blanking information + */ +enum mipi_dsi_dcs_tear_mode { + MIPI_DSI_DCS_TEAR_MODE_VBLANK, + MIPI_DSI_DCS_TEAR_MODE_VHBLANK, +}; + +#define MIPI_DSI_DCS_POWER_MODE_DISPLAY (1 << 2) +#define MIPI_DSI_DCS_POWER_MODE_NORMAL (1 << 3) +#define MIPI_DSI_DCS_POWER_MODE_SLEEP (1 << 4) +#define MIPI_DSI_DCS_POWER_MODE_PARTIAL (1 << 5) +#define MIPI_DSI_DCS_POWER_MODE_IDLE (1 << 6) + +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, + const void *data, size_t len); +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi); +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi); +int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, + u16 end); +int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, + u16 end); +int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi, + bool reverse_page_address, + bool reverse_col_address, + bool reverse_page_col_address, + bool refresh_from_bottom, + bool reverse_rgb, + bool latch_right_to_left, + bool flip_horizontal, + bool flip_vertical); +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, + enum mipi_dsi_dcs_tear_mode mode); +int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format); + +#define MIPI_CAL_CTRL 0x00 +#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) +#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) +#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4) +#define MIPI_CAL_CTRL_START (1 << 0) + +#define MIPI_CAL_AUTOCAL_CTRL 0x01 + +#define MIPI_CAL_STATUS 0x02 +#define MIPI_CAL_STATUS_DONE (1 << 16) +#define MIPI_CAL_STATUS_ACTIVE (1 << 0) + +#define MIPI_CAL_CONFIG_CSIA 0x05 +#define MIPI_CAL_CONFIG_CSIB 0x06 +#define MIPI_CAL_CONFIG_CSIC 0x07 +#define MIPI_CAL_CONFIG_CSID 0x08 +#define MIPI_CAL_CONFIG_CSIE 0x09 +#define MIPI_CAL_CONFIG_CSIF 0x0a +#define MIPI_CAL_CONFIG_DSIA 0x0e +#define MIPI_CAL_CONFIG_DSIB 0x0f +#define MIPI_CAL_CONFIG_DSIC 0x10 +#define MIPI_CAL_CONFIG_DSID 0x11 + +#define MIPI_CAL_CONFIG_DSIA_CLK 0x19 +#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a +#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b +#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c +#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c +#define MIPI_CAL_CONFIG_DSID_CLK 0x1d +#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d + +/* for data and clock lanes */ +#define MIPI_CAL_CONFIG_SELECT (1 << 21) + +/* for data lanes */ +#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16) +#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0) + +/* for clock lanes */ +#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0) + +#define MIPI_CAL_BIAS_PAD_CFG0 0x16 +#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1) +#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0) + +#define MIPI_CAL_BIAS_PAD_CFG1 0x17 +#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) + +#define MIPI_CAL_BIAS_PAD_CFG2 0x18 +#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) +#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) +#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1) + +struct tegra_mipi_pad { + unsigned long data; + unsigned long clk; +}; + +struct tegra_mipi_soc { + int has_clk_lane; + const struct tegra_mipi_pad *pads; + unsigned int num_pads; + + int clock_enable_override; + int needs_vclamp_ref; + + /* bias pad configuration settings */ + u8 pad_drive_down_ref; + u8 pad_drive_up_ref; + + u8 pad_vclamp_level; + u8 pad_vauxp_level; + + /* calibration settings for data lanes */ + u8 hspdos; + u8 hspuos; + u8 termos; + + /* calibration settings for clock lanes */ + u8 hsclkpdos; + u8 hsclkpuos; +}; + +struct tegra_mipi { + const struct tegra_mipi_soc *soc; + void *regs; +}; + +struct tegra_mipi_device { + struct tegra_mipi *mipi; + unsigned long pads; +}; + +struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device, + int device_index); +int tegra_mipi_calibrate(struct tegra_mipi_device *device); +#endif /* __MIPI_DSI_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/mmu_operations.h b/src/soc/nvidia/tegra210/include/soc/mmu_operations.h new file mode 100644 index 0000000000..6a81e7c321 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mmu_operations.h @@ -0,0 +1,28 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_MMU_OPERATIONS_H__ +#define __SOC_NVIDIA_TEGRA210_MMU_OPERATIONS_H__ + +void tegra210_mmu_init(void); + +/* Default ttb size of 4MiB */ +#define TTB_SIZE 0x4 + +#endif //__SOC_NVIDIA_TEGRA210_MMU_OPERATIONS_H__ diff --git a/src/soc/nvidia/tegra210/include/soc/mtc.h b/src/soc/nvidia/tegra210/include/soc/mtc.h new file mode 100644 index 0000000000..c94d79b988 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/mtc.h @@ -0,0 +1,37 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_MTC_H__ +#define __SOC_NVIDIA_TEGRA210_MTC_H__ + +#include <boot/coreboot_tables.h> + +#if CONFIG_HAVE_MTC + +int tegra210_run_mtc(void); +void soc_add_mtc(struct lb_header *header); + +#else + +static inline int tegra210_run_mtc(void) { return -1; } +static inline void soc_add_mtc(struct lb_header *header) {} + +#endif /* CONFIG_HAVE_MTC */ + +#endif /* __SOC_NVIDIA_TEGRA210_MTC_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/padconfig.h b/src/soc/nvidia/tegra210/include/soc/padconfig.h new file mode 100644 index 0000000000..01cd72a4a9 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/padconfig.h @@ -0,0 +1,94 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_PAD_CFG_H +#define __SOC_NVIDIA_TEGRA210_PAD_CFG_H + +#include <stdint.h> +#include <soc/pinmux.h> + +struct pad_config { + uint16_t pinmux_flags; /* PU/PU, OD, INPUT, SFIO, etc */ + uint8_t gpio_index; /* bank, port, index */ + uint16_t pinmux_index:9; + uint16_t unused:1; + uint16_t sfio:1; + uint16_t gpio_out0:1; + uint16_t gpio_out1:1; + uint16_t pad_has_gpio:1; + uint16_t por_pullup:1; +}; + +#define PAD_CFG_GPIO_INPUT(ball_, pinmux_flgs_) \ + { \ + .pinmux_flags = pinmux_flgs_ | PINMUX_INPUT_ENABLE, \ + .gpio_index = PAD_TO_GPIO_##ball_, \ + .pinmux_index = PINMUX_##ball_##_INDEX, \ + .sfio = 0, \ + .pad_has_gpio = PAD_HAS_GPIO_##ball_, \ + } + +#define PAD_CFG_GPIO_OUT0(ball_, pinmux_flgs_) \ + { \ + .pinmux_flags = pinmux_flgs_, \ + .gpio_index = PAD_TO_GPIO_##ball_, \ + .pinmux_index = PINMUX_##ball_##_INDEX, \ + .sfio = 0, \ + .gpio_out0 = 1, \ + .pad_has_gpio = PAD_HAS_GPIO_##ball_, \ + } + +#define PAD_CFG_GPIO_OUT1(ball_, pinmux_flgs_) \ + { \ + .pinmux_flags = pinmux_flgs_, \ + .gpio_index = PAD_TO_GPIO_##ball_, \ + .pinmux_index = PINMUX_##ball_##_INDEX, \ + .sfio = 0, \ + .gpio_out1 = 1, \ + .pad_has_gpio = PAD_HAS_GPIO_##ball_, \ + } + +#define PAD_CFG_SFIO(ball_, pinmux_flgs_, sfio_) \ + { \ + .pinmux_flags = pinmux_flgs_ | \ + PINMUX_##ball_##_FUNC_##sfio_, \ + .gpio_index = PAD_TO_GPIO_##ball_, \ + .pinmux_index = PINMUX_##ball_##_INDEX, \ + .sfio = 1, \ + .pad_has_gpio = PAD_HAS_GPIO_##ball_, \ + } + +#define PAD_CFG_UNUSED(ball_) \ + { \ + .gpio_index = PAD_TO_GPIO_##ball_, \ + .pinmux_index = PINMUX_##ball_##_INDEX, \ + .unused = 1, \ + .pad_has_gpio = PAD_HAS_GPIO_##ball_, \ + } +/* + * Configure the pads associated with entry according to the configuration. + */ +void soc_configure_pads(const struct pad_config * const entries, size_t num); +/* I2C6 requires special init as its pad lives int the SOR/DPAUX block */ +void soc_configure_i2c6pad(void); +void soc_configure_host1x(void); +/* APE (Audio Processing Engine) requires special init */ +void soc_configure_ape(void); + +#endif /* __SOC_NVIDIA_TEGRA210_PAD_CFG_H */ diff --git a/src/soc/nvidia/tegra210/include/soc/pinmux.h b/src/soc/nvidia/tegra210/include/soc/pinmux.h new file mode 100644 index 0000000000..ab59fbf076 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/pinmux.h @@ -0,0 +1,285 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_PINMUX_H__ +#define __SOC_NVIDIA_TEGRA210_PINMUX_H__ + +#include <stdint.h> +#include <soc/nvidia/tegra/gpio.h> +#include <soc/nvidia/tegra/pinmux.h> + +enum { + PINMUX_FUNC_MASK = 3 << 0, + + PINMUX_PULL_MASK = 3 << 2, + PINMUX_PULL_NONE = 0 << 2, + PINMUX_PULL_DOWN = 1 << 2, + PINMUX_PULL_UP = 2 << 2, + + PINMUX_TRISTATE = 1 << 4, + PINMUX_PARKED = 1 << 5, + PINMUX_INPUT_ENABLE = 1 << 6, + PINMUX_LOCK = 1 << 7, + PINMUX_LPDR = 1 << 8, + PINMUX_HSM = 1 << 9, + PINMUX_IO_HV = 1 << 10, + PINMUX_OPEN_DRAIN = 1 << 11, + PINMUX_SCHMT = 1 << 12, +}; + +/* GPIO index constants. */ + +#define GPIO_PORT_CONSTANTS(port) \ + GPIO_##port##0_INDEX, GPIO_##port##1_INDEX, GPIO_##port##2_INDEX, \ + GPIO_##port##3_INDEX, GPIO_##port##4_INDEX, GPIO_##port##5_INDEX, \ + GPIO_##port##6_INDEX, GPIO_##port##7_INDEX + +enum { + GPIO_PORT_CONSTANTS(A), + GPIO_PORT_CONSTANTS(B), + GPIO_PORT_CONSTANTS(C), + GPIO_PORT_CONSTANTS(D), + GPIO_PORT_CONSTANTS(E), + GPIO_PORT_CONSTANTS(F), + GPIO_PORT_CONSTANTS(G), + GPIO_PORT_CONSTANTS(H), + GPIO_PORT_CONSTANTS(I), + GPIO_PORT_CONSTANTS(J), + GPIO_PORT_CONSTANTS(K), + GPIO_PORT_CONSTANTS(L), + GPIO_PORT_CONSTANTS(M), + GPIO_PORT_CONSTANTS(N), + GPIO_PORT_CONSTANTS(O), + GPIO_PORT_CONSTANTS(P), + GPIO_PORT_CONSTANTS(Q), + GPIO_PORT_CONSTANTS(R), + GPIO_PORT_CONSTANTS(S), + GPIO_PORT_CONSTANTS(T), + GPIO_PORT_CONSTANTS(U), + GPIO_PORT_CONSTANTS(V), + GPIO_PORT_CONSTANTS(W), + GPIO_PORT_CONSTANTS(X), + GPIO_PORT_CONSTANTS(Y), + GPIO_PORT_CONSTANTS(Z), + GPIO_PORT_CONSTANTS(AA), + GPIO_PORT_CONSTANTS(BB), + GPIO_PORT_CONSTANTS(CC), + GPIO_PORT_CONSTANTS(DD), + GPIO_PORT_CONSTANTS(EE), + GPIO_PORT_CONSTANTS(FF), + GPIO_NONE_INDEX = 0, +}; + +#define PINMUX_CONSTANTS_GPIO(name, gpio) \ + PINMUX_GPIO_##gpio = PINMUX_##name##_INDEX + +#define PINMUX_CONSTANTS(index, name, por_pu, gpio, has_gpio, \ + func0, func1, func2, func3) \ + PINMUX_##name##_INDEX = index, \ + PINMUX_##name##_FUNC_##func0 = 0, \ + PINMUX_##name##_FUNC_##func1 = 1, \ + PINMUX_##name##_FUNC_##func2 = 2, \ + PINMUX_##name##_FUNC_##func3 = 3, \ + PAD_TO_GPIO_##name = GPIO_##gpio##_INDEX, \ + PAD_HAS_GPIO_##name = has_gpio, \ + PAD_POR_PU_##name = por_pu + +#define PAD_GPIO(index, name, por_pu, gpio, func0, func1, func2, func3) \ + PINMUX_CONSTANTS(index, name, por_pu, gpio, 1, \ + func0, func1, func2, func3), \ + PINMUX_CONSTANTS_GPIO(name, gpio) + +#define PAD_NO_GPIO(index, name, por_pu, func0, func1, func2, func3) \ + PINMUX_CONSTANTS(index, name, por_pu, NONE, 0, \ + func0, func1, func2, func3) + +enum { + /* Power-on-reset pull states. */ + POR_PU = 2, + POR_PD = 1, + POR_NP = 0, + + PAD_GPIO(0, SDMMC1_CLK, POR_PD, M0, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(1, SDMMC1_CMD, POR_PU, M1, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(2, SDMMC1_DAT3, POR_PU, M2, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(3, SDMMC1_DAT2, POR_PU, M3, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(4, SDMMC1_DAT1, POR_PU, M4, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(5, SDMMC1_DAT0, POR_PU, M5, SDMMC1, RES1, RES2, RES3), + PAD_GPIO(6, SDMMC3_CLK, POR_PD, P0, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(7, SDMMC3_CMD, POR_PU, P1, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(8, SDMMC3_DAT0, POR_PU, P2, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(9, SDMMC3_DAT1, POR_PU, P3, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(10, SDMMC3_DAT2, POR_PU, P4, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(11, SDMMC3_DAT3, POR_PU, P5, SDMMC3, RES1, RES2, RES3), + /* GPIO12 - unused */ + /* GPIO13 - unused */ + PAD_GPIO(14, PEX_L0_RST_N, POR_NP, A0, PE0, RES1, RES2, RES3), + PAD_GPIO(15, PEX_L0_CLKREQ_N, POR_NP, A1, PE0, RES1, RES2, RES3), + PAD_GPIO(16, PEX_WAKE_N, POR_NP, A2, PE, RES1, RES2, RES3), + PAD_GPIO(17, PEX_L1_RST_N, POR_NP, A3, PE1, RES1, RES2, RES3), + PAD_GPIO(18, PEX_L1_CLKREQ_N, POR_NP, A4, PE1, RES1, RES2, RES3), + PAD_GPIO(19, SATA_LED_ACTIVE, POR_NP, A5, SATA, RES1, RES2, RES3), + PAD_GPIO(20, SPI1_MOSI, POR_PD, C0, SPI1, RES1, RES2, RES3), + PAD_GPIO(21, SPI1_MISO, POR_PD, C1, SPI1, RES1, RES2, RES3), + PAD_GPIO(22, SPI1_SCK, POR_PD, C2, SPI1, RES1, RES2, RES3), + PAD_GPIO(23, SPI1_CS0, POR_PU, C3, SPI1, RES1, RES2, RES3), + PAD_GPIO(24, SPI1_CS1, POR_PU, C4, SPI1, RES1, RES2, RES3), + PAD_GPIO(25, SPI2_MOSI, POR_PD, B4, SPI2, DTV, RES2, RES3), + PAD_GPIO(26, SPI2_MISO, POR_PD, B5, SPI2, DTV, RES2, RES3), + PAD_GPIO(27, SPI2_SCK, POR_PD, B6, SPI2, DTV, RES2, RES3), + PAD_GPIO(28, SPI2_CS0, POR_PU, B7, SPI2, DTV, RES2, RES3), + PAD_GPIO(29, SPI2_CS1, POR_PU, DD0, SPI2, RES1, RES2, RES3), + PAD_GPIO(30, SPI4_MOSI, POR_PD, C7, SPI4, RES1, RES2, RES3), + PAD_GPIO(31, SPI4_MISO, POR_PD, D0, SPI4, RES1, RES2, RES3), + PAD_GPIO(32, SPI4_SCK, POR_PD, C5, SPI4, RES1, RES2, RES3), + PAD_GPIO(33, SPI4_CS0, POR_PU, C6, SPI4, RES1, RES2, RES3), + PAD_GPIO(34, QSPI_SCK, POR_PU, EE0, QSPI, RES1, RES2, RES3), + PAD_GPIO(35, QSPI_CS_N, POR_PU, EE1, QSPI, RES1, RES2, RES3), + PAD_GPIO(36, QSPI_IO0, POR_PU, EE2, QSPI, RES1, RES2, RES3), + PAD_GPIO(37, QSPI_IO1, POR_PU, EE3, QSPI, RES1, RES2, RES3), + PAD_GPIO(38, QSPI_IO2, POR_PU, EE4, QSPI, RES1, RES2, RES3), + PAD_GPIO(39, QSPI_IO3, POR_PU, EE5, QSPI, RES1, RES2, RES3), + /* GPIO40 - unused */ + PAD_GPIO(41, DMIC1_CLK, POR_PD, E0, DMIC1, I2S3, RES2, RES3), + PAD_GPIO(42, DMIC1_DAT, POR_PD, E1, DMIC1, I2S3, RES2, RES3), + PAD_GPIO(43, DMIC2_CLK, POR_PD, E2, DMIC2, I2S3, RES2, RES3), + PAD_GPIO(44, DMIC2_DAT, POR_PD, E3, DMIC2, I2S3, RES2, RES3), + PAD_GPIO(45, DMIC3_CLK, POR_PD, E4, DMIC3, I2S5A, RES2, RES3), + PAD_GPIO(46, DMIC3_DAT, POR_PD, E5, DMIC3, I2S5A, RES2, RES3), + PAD_GPIO(47, GEN1_I2C_SDA, POR_NP, J0, I2C1, RES1, RES2, RES3), + PAD_GPIO(48, GEN1_I2C_SCL, POR_NP, J1, I2C1, RES1, RES2, RES3), + PAD_GPIO(49, GEN2_I2C_SCL, POR_NP, J2, I2C2, RES1, RES2, RES3), + PAD_GPIO(50, GEN2_I2C_SDA, POR_NP, J3, I2C2, RES1, RES2, RES3), + PAD_GPIO(51, GEN3_I2C_SCL, POR_NP, F0, I2C3, RES1, RES2, RES3), + PAD_GPIO(52, GEN3_I2C_SDA, POR_NP, F1, I2C3, RES1, RES2, RES3), + PAD_GPIO(53, CAM_I2C_SCL, POR_NP, S2, I2C3, I2CVI, RES2, RES3), + PAD_GPIO(54, CAM_I2C_SDA, POR_NP, S3, I2C3, I2CVI, RES2, RES3), + PAD_GPIO(55, PWR_I2C_SCL, POR_NP, Y3, I2CPMU, RES1, RES2, RES3), + PAD_GPIO(56, PWR_I2C_SDA, POR_NP, Y4, I2CPMU, RES1, RES2, RES3), + PAD_GPIO(57, UART1_TX, POR_PD, U0, UARTA, RES1, RES2, RES3), + PAD_GPIO(58, UART1_RX, POR_PD, U1, UARTA, RES1, RES2, RES3), + PAD_GPIO(59, UART1_RTS, POR_PD, U2, UARTA, RES1, RES2, RES3), + PAD_GPIO(60, UART1_CTS, POR_PD, U3, UARTA, RES1, RES2, RES3), + PAD_GPIO(61, UART2_TX, POR_PD, G0, UARTB, I2S4A, SPDIF, UART), + PAD_GPIO(62, UART2_RX, POR_PD, G1, UARTB, I2S4A, SPDIF, UART), + PAD_GPIO(63, UART2_RTS, POR_PD, G2, UARTB, I2S4A, RES2, UART), + PAD_GPIO(64, UART2_CTS, POR_PD, G3, UARTB, I2S4A, RES2, UART), + PAD_GPIO(65, UART3_TX, POR_PD, D1, UARTC, SPI4, RES2, RES3), + PAD_GPIO(66, UART3_RX, POR_PD, D2, UARTC, SPI4, RES2, RES3), + PAD_GPIO(67, UART3_RTS, POR_PD, D3, UARTC, SPI4, RES2, RES3), + PAD_GPIO(68, UART3_CTS, POR_PD, D4, UARTC, SPI4, RES2, RES3), + PAD_GPIO(69, UART4_TX, POR_PD, I4, UARTD, UART, RES2, RES3), + PAD_GPIO(70, UART4_RX, POR_PD, I5, UARTD, UART, RES2, RES3), + PAD_GPIO(71, UART4_RTS, POR_PD, I6, UARTD, UART, RES2, RES3), + PAD_GPIO(72, UART4_CTS, POR_PD, I7, UARTD, UART, RES2, RES3), + PAD_GPIO(73, DAP1_FS, POR_PD, B0, I2S1, RES1, RES2, RES3), + PAD_GPIO(74, DAP1_DIN, POR_PD, B1, I2S1, RES1, RES2, RES3), + PAD_GPIO(75, DAP1_DOUT, POR_PD, B2, I2S1, RES1, RES2, RES3), + PAD_GPIO(76, DAP1_SCLK, POR_PD, B3, I2S1, RES1, RES2, RES3), + PAD_GPIO(77, DAP2_FS, POR_PD, AA0, I2S2, RES1, RES2, RES3), + PAD_GPIO(78, DAP2_DIN, POR_PD, AA1, I2S2, RES1, RES2, RES3), + PAD_GPIO(79, DAP2_DOUT, POR_PD, AA2, I2S2, RES1, RES2, RES3), + PAD_GPIO(80, DAP2_SCLK, POR_PD, AA3, I2S2, RES1, RES2, RES3), + PAD_GPIO(81, DAP4_FS, POR_PD, J4, I2S4B, RES1, RES2, RES3), + PAD_GPIO(82, DAP4_DIN, POR_PD, J5, I2S4B, RES1, RES2, RES3), + PAD_GPIO(83, DAP4_DOUT, POR_PD, J6, I2S4B, RES1, RES2, RES3), + PAD_GPIO(84, DAP4_SCLK, POR_PD, J7, I2S4B, RES1, RES2, RES3), + PAD_GPIO(85, CAM1_MCLK, POR_PD, S0, EXTPERIPH3, RES1, RES2, RES3), + PAD_GPIO(86, CAM2_MCLK, POR_PD, S1, EXTPERIPH3, RES1, RES2, RES3), + PAD_NO_GPIO(87, JTAG_RTCK, POR_PU, JTAG, RES1, RES2, RES3), + PAD_NO_GPIO(88, CLK_32K_IN, POR_NP, CLK_32K_IN, RES1, RES2, RES3), + PAD_GPIO(89, CLK_32K_OUT, POR_PD, Y5, SOC, BLINK, RES2, RES3), + PAD_NO_GPIO(90, BATT_BCL, POR_NP, BCL, RES1, RES2, RES3), + PAD_NO_GPIO(91, CLK_REQ, POR_NP, CLK_REQ, RES1, RES2, RES3), + PAD_NO_GPIO(92, CPU_PWR_REQ, POR_NP, CPU, RES1, RES2, RES3), + PAD_NO_GPIO(93, PWR_INT_N, POR_NP, PMI, RES1, RES2, RES3), + PAD_NO_GPIO(94, SHUTDOWN, POR_NP, SHUTDOWN, RES1, RES2, RES3), + PAD_NO_GPIO(95, CORE_PWR_REQ, POR_NP, PWRON, RES1, RES2, RES3), + PAD_GPIO(96, AUD_MCLK, POR_PD, BB0, AUD, RES1, RES2, RES3), + PAD_GPIO(97, DVFS_PWM, POR_PD, BB1, RES0, CLDVFS, SPI3, RES3), + PAD_GPIO(98, DVFS_CLK, POR_PU, BB2, RES0, CLDVFS, SPI3, RES3), + PAD_GPIO(99, GPIO_X1_AUD, POR_PD, BB3, RES0, RES1, SPI3, RES3), + PAD_GPIO(100, GPIO_X3_AUD, POR_PU, BB4, RES0, RES1, SPI3, RES3), + PAD_NO_GPIO(101, GPIO_PCC7, POR_NP, RES0, RES1, RES2, RES3), + PAD_GPIO(102, HDMI_CEC, POR_NP, CC0, CEC, RES1, RES2, RES3), + PAD_GPIO(103, HDMI_INT_DP_HPD, POR_PD, CC1, DP, RES1, RES2, RES3), + PAD_GPIO(104, SPDIF_OUT, POR_PU, CC2, SPDIF, RES1, RES2, I2C3), + PAD_GPIO(105, SPDIF_IN, POR_PD, CC3, SPDIF, RES1, RES2, I2C3), + PAD_GPIO(106, USB_VBUS_EN0, POR_NP, CC4, USB, RES1, RES2, RES3), + PAD_GPIO(107, USB_VBUS_EN1, POR_NP, CC5, USB, RES1, RES2, RES3), + PAD_GPIO(108, DP_HPD0, POR_PD, CC6, DP, RES1, RES2, RES3), + PAD_GPIO(109, WIFI_EN, POR_PD, H0, RES0, RES1, RES2, RES3), + PAD_GPIO(110, WIFI_RST, POR_PD, H1, RES0, RES1, RES2, RES3), + PAD_GPIO(111, WIFI_WAKE_AP, POR_PD, H2, RES0, RES1, RES2, RES3), + PAD_GPIO(112, AP_WAKE_BT, POR_PD, H3, RES0, UARTB, SPDIF, RES3), + PAD_GPIO(113, BT_RST, POR_PD, H4, RES0, UARTB, SPDIF, RES3), + PAD_GPIO(114, BT_WAKE_AP, POR_PD, H5, RES0, RES1, RES2, RES3), + PAD_GPIO(115, AP_WAKE_NFC, POR_PD, H7, RES0, RES1, RES2, RES3), + PAD_GPIO(116, NFC_EN, POR_PD, I0, RES0, RES1, RES2, RES3), + PAD_GPIO(117, NFC_INT, POR_PD, I1, RES0, RES1, RES2, RES3), + PAD_GPIO(118, GPS_EN, POR_PD, I2, RES0, RES1, RES2, RES3), + PAD_GPIO(119, GPS_RST, POR_PD, I3, RES0, RES1, RES2, RES3), + PAD_GPIO(120, CAM_RST, POR_PD, S4, VGP1, RES1, RES2, RES3), + PAD_GPIO(121, CAM_AF_EN, POR_PD, S5, VIMCLK, VGP2, RES2, RES3), + PAD_GPIO(122, CAM_FLASH_EN, POR_PD, S6, VIMCLK, VGP3, RES2, RES3), + PAD_GPIO(123, CAM1_PWDN, POR_PD, S7, VGP4, RES1, RES2, RES3), + PAD_GPIO(124, CAM2_PWDN, POR_PD, T0, VGP5, RES1, RES2, RES3), + PAD_GPIO(125, CAM1_STROBE, POR_PD, T1, VGP6, RES1, RES2, RES3), + PAD_GPIO(126, LCD_TE, POR_PD, Y2, DISPLAYA, RES1, RES2, RES3), + PAD_GPIO(127, LCD_BL_PWM, POR_PD, V0, DISPLAYA, PWM0, SOR0, RES3), + PAD_GPIO(128, LCD_BL_EN, POR_PD, V1, RES0, RES1, RES2, RES3), + PAD_GPIO(129, LCD_RST, POR_PD, V2, RES0, RES1, RES2, RES3), + PAD_GPIO(130, LCD_GPIO1, POR_PD, V3, DISPLAYB, RES1, RES2, RES3), + PAD_GPIO(131, LCD_GPIO2, POR_PD, V4, DISPLAYB, PWM1, RES2, SOR1), + PAD_GPIO(132, AP_READY, POR_PD, V5, RES0, RES1, RES2, RES3), + PAD_GPIO(133, TOUCH_RST, POR_PD, V6, RES0, RES1, RES2, RES3), + PAD_GPIO(134, TOUCH_CLK, POR_PD, V7, TOUCH, RES1, RES2, RES3), + PAD_GPIO(135, MODEM_WAKE_AP, POR_PD, X0, RES0, RES1, RES2, RES3), + PAD_GPIO(136, TOUCH_INT, POR_PD, X1, RES0, RES1, RES2, RES3), + PAD_GPIO(137, MOTION_INT, POR_PD, X2, RES0, RES1, RES2, RES3), + PAD_GPIO(138, ALS_PROX_INT, POR_PD, X3, RES0, RES1, RES2, RES3), + PAD_GPIO(139, TEMP_ALERT, POR_PD, X4, RES0, RES1, RES2, RES3), + PAD_GPIO(140, BUTTON_POWER_ON, POR_PU, X5, RES0, RES1, RES2, RES3), + PAD_GPIO(141, BUTTON_VOL_UP, POR_PU, X6, RES0, RES1, RES2, RES3), + PAD_GPIO(142, BUTTON_VOL_DOWN, POR_PU, X7, RES0, RES1, RES2, RES3), + PAD_GPIO(143, BUTTON_SLIDE_SW, POR_PU, Y0, RES0, RES1, RES2, RES3), + PAD_GPIO(144, BUTTON_HOME, POR_PU, Y1, RES0, RES1, RES2, RES3), + PAD_NO_GPIO(145, GPIO_PA6, POR_NP, SATA, RES1, RES2, RES3), + PAD_NO_GPIO(146, GPIO_PE6, POR_PD, RES0, I2S5A, PWM2, RES3), + PAD_NO_GPIO(147, GPIO_PE7, POR_PD, RES0, I2S5A, PWM3, RES3), + PAD_NO_GPIO(148, GPIO_PH6, POR_PD, RES0, RES1, RES2, RES3), + PAD_GPIO(149, GPIO_PK0, POR_PD, K0, IQC0, I2S5B, RES2, RES3), + PAD_GPIO(150, GPIO_PK1, POR_PD, K1, IQC0, I2S5B, RES2, RES3), + PAD_GPIO(151, GPIO_PK2, POR_PD, K2, IQC0, I2S5B, RES2, RES3), + PAD_NO_GPIO(152, GPIO_PK3, POR_PD, IQC0, I2S5B, RES2, RES3), + PAD_NO_GPIO(153, GPIO_PK4, POR_PD, IQC1, RES1, RES2, RES3), + PAD_NO_GPIO(154, GPIO_PK5, POR_PD, IQC1, RES1, RES2, RES3), + PAD_NO_GPIO(155, GPIO_PK6, POR_PD, IQC1, RES1, RES2, RES3), + PAD_NO_GPIO(156, GPIO_PK7, POR_PD, IQC1, RES1, RES2, RES3), + PAD_NO_GPIO(157, GPIO_PL0, POR_PD, RES0, RES1, RES2, RES3), + PAD_NO_GPIO(158, GPIO_PL1, POR_PD, SOC, RES1, RES2, RES3), + PAD_NO_GPIO(159, GPIO_PZ0, POR_PD, VIMCLK2, RES1, RES2, RES3), + PAD_GPIO(160, GPIO_PZ1, POR_PD, Z1, VIMCLK2, SDMMC1, RES2, RES3), + PAD_NO_GPIO(161, GPIO_PZ2, POR_PD, SDMMC3, CCLA, RES2, RES3), + PAD_NO_GPIO(162, GPIO_PZ3, POR_PD, SDMMC3, RES1, RES2, RES3), + PAD_GPIO(163, GPIO_PZ4, POR_PD, Z4, SDMMC1, RES1, RES2, RES3), + PAD_NO_GPIO(164, GPIO_PZ5, POR_PD, SOC, RES1, RES2, RES3), +}; + +#endif /* __SOC_NVIDIA_TEGRA210_PINMUX_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/pmc.h b/src/soc/nvidia/tegra210/include/soc/pmc.h new file mode 100644 index 0000000000..77a7396d56 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/pmc.h @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef _TEGRA210_PMC_H_ +#define _TEGRA210_PMC_H_ + +#include <stdint.h> + +enum { + POWER_PARTID_CRAIL = 0, + POWER_PARTID_TD = 1, + POWER_PARTID_VE = 2, + POWER_PARTID_PCX = 3, + POWER_PARTID_C0L2 = 5, + POWER_PARTID_MPE = 6, + POWER_PARTID_HEG = 7, + POWER_PARTID_SAX = 8, + POWER_PARTID_CE1 = 9, + POWER_PARTID_CE2 = 10, + POWER_PARTID_CE3 = 11, + POWER_PARTID_CELP = 12, + POWER_PARTID_CE0 = 14, + POWER_PARTID_C0NC = 15, + POWER_PARTID_C1NC = 16, + POWER_PARTID_SOR = 17, + POWER_PARTID_DIS = 18, + POWER_PARTID_DISB = 19, + POWER_PARTID_XUSBA = 20, + POWER_PARTID_XUSBB = 21, + POWER_PARTID_XUSBC = 22, + POWER_PARTID_VIC = 23, + POWER_PARTID_IRAM = 24, + POWER_PARTID_NVDEC = 25, + POWER_PARTID_NVJPG = 26, + POWER_PARTID_APE = 27, + POWER_PARTID_DFD = 28, + POWER_PARTID_VE2 = 29, +}; + +struct tegra_pmc_regs { + u32 cntrl; + u32 sec_disable; + u32 pmc_swrst; + u32 wake_mask; + u32 wake_lvl; + u32 wake_status; + u32 sw_wake_status; + u32 dpd_pads_oride; + u32 dpd_sample; + u32 dpd_enable; + u32 pwrgate_timer_off; + u32 clamp_status; + u32 pwrgate_toggle; + u32 remove_clamping_cmd; + u32 pwrgate_status; + u32 pwrgood_timer; + u32 blink_timer; + u32 no_iopower; + u32 pwr_det; + u32 pwr_det_latch; + u32 scratch0; + u32 scratch1; + u32 scratch2; + u32 scratch3; + u32 scratch4; + u32 scratch5; + u32 scratch6; + u32 scratch7; + u32 scratch8; + u32 scratch9; + u32 scratch10; + u32 scratch11; + u32 scratch12; + u32 scratch13; + u32 scratch14; + u32 scratch15; + u32 scratch16; + u32 scratch17; + u32 scratch18; + u32 scratch19; + u32 odmdata; + u32 scratch21; + u32 scratch22; + u32 scratch23; + u32 secure_scratch0; + u32 secure_scratch1; + u32 secure_scratch2; + u32 secure_scratch3; + u32 secure_scratch4; + u32 secure_scratch5; + u32 cpupwrgood_timer; + u32 cpupwroff_timer; + u32 pg_mask; + u32 pg_mask_1; + u32 auto_wake_lvl; + u32 auto_wake_lvl_mask; + u32 wake_delay; + u32 pwr_det_val; + u32 ddr_pwr; + u32 usb_debounce_del; + u32 usb_a0; + u32 crypto_op; + u32 pllp_wb0_override; + u32 scratch24; + u32 scratch25; + u32 scratch26; + u32 scratch27; + u32 scratch28; + u32 scratch29; + u32 scratch30; + u32 scratch31; + u32 scratch32; + u32 scratch33; + u32 scratch34; + u32 scratch35; + u32 scratch36; + u32 scratch37; + u32 scratch38; + u32 scratch39; + u32 scratch40; + u32 scratch41; + u32 scratch42; + u32 bondout_mirror[3]; + u32 sys_33v_en; + u32 bondout_mirror_access; + u32 gate; + u32 wake2_mask; + u32 wake2_lvl; + u32 wake2_status; + u32 sw_wake2_status; + u32 auto_wake2_lvl_mask; + u32 pg_mask_2; + u32 pg_mask_ce1; + u32 pg_mask_ce2; + u32 pg_mask_ce3; + u32 pwrgate_timer_ce[7]; + u32 pcx_edpd_cntrl; + u32 osc_edpd_over; + u32 clk_out_cntrl; + u32 sata_pwrgt; + u32 sensor_ctrl; + u32 rst_status; + u32 io_dpd_req; + u32 io_dpd_status; + u32 io_dpd2_req; + u32 io_dpd2_status; + u32 sel_dpd_tim; + u32 vddp_sel; + u32 ddr_cfg; + u32 e_no_vttgen; + u8 _rsv0[4]; + u32 pllm_wb0_override_freq; + u32 test_pwrgate; + u32 pwrgate_timer_mult; + u32 dis_sel_dpd; + u32 utmip_uhsic_triggers; + u32 utmip_uhsic_saved_state; + u32 utmip_pad_cfg; + u32 utmip_term_pad_cfg; + u32 utmip_uhsic_sleep_cfg; + u32 utmip_uhsic_sleepwalk_cfg; + u32 utmip_sleepwalk_p[3]; + u32 uhsic_sleepwalk_p0; + u32 utmip_uhsic_status; + u32 utmip_uhsic_fake; + u32 bondout_mirror3[5 - 3]; + u32 secure_scratch6; + u32 secure_scratch7; + u32 scratch43; + u32 scratch44; + u32 scratch45; + u32 scratch46; + u32 scratch47; + u32 scratch48; + u32 scratch49; + u32 scratch50; + u32 scratch51; + u32 scratch52; + u32 scratch53; + u32 scratch54; + u32 scratch55; + u32 scratch0_eco; + u32 por_dpd_ctrl; + u32 scratch2_eco; + u32 utmip_uhsic_line_wakeup; + u32 utmip_bias_master_cntrl; + u32 utmip_master_config; + u32 td_pwrgate_inter_part_timer; + u32 utmip_uhsic2_triggers; + u32 utmip_uhsic2_saved_state; + u32 utmip_uhsic2_sleep_cfg; + u32 utmip_uhsic2_sleepwalk_cfg; + u32 uhsic2_sleepwalk_p1; + u32 utmip_uhsic2_status; + u32 utmip_uhsic2_fake; + u32 utmip_uhsic2_line_wakeup; + u32 utmip_master2_config; + u32 utmip_uhsic_rpd_cfg; + u32 pg_mask_ce0; + u32 pg_mask3[5 - 3]; + u32 pllm_wb0_override2; + u32 tsc_mult; + u32 cpu_vsense_override; + u32 glb_amap_cfg; + u32 sticky_bits; + u32 sec_disable2; + u32 weak_bias; + u32 reg_short; + u32 pg_mask_andor; + u8 _rsv1[0x2c]; + u32 secure_scratch8; /* offset 0x300 */ + u32 secure_scratch9; + u32 secure_scratch10; + u32 secure_scratch11; + u32 secure_scratch12; + u32 secure_scratch13; + u32 secure_scratch14; + u32 secure_scratch15; + u32 secure_scratch16; + u32 secure_scratch17; + u32 secure_scratch18; + u32 secure_scratch19; + u32 secure_scratch20; + u32 secure_scratch21; + u32 secure_scratch22; + u32 secure_scratch23; + u32 secure_scratch24; + u32 secure_scratch25; + u32 secure_scratch26; + u32 secure_scratch27; + u32 secure_scratch28; + u32 secure_scratch29; + u32 secure_scratch30; + u32 secure_scratch31; + u32 secure_scratch32; + u32 secure_scratch33; + u32 secure_scratch34; + u32 secure_scratch35; + u32 secure_scratch36; + u32 secure_scratch37; + u32 secure_scratch38; + u32 secure_scratch39; + u32 secure_scratch40; + u32 secure_scratch41; + u32 secure_scratch42; + u32 secure_scratch43; + u32 secure_scratch44; + u32 secure_scratch45; + u32 secure_scratch46; + u32 secure_scratch47; + u32 secure_scratch48; + u32 secure_scratch49; + u32 secure_scratch50; + u32 secure_scratch51; + u32 secure_scratch52; + u32 secure_scratch53; + u32 secure_scratch54; + u32 secure_scratch55; + u32 secure_scratch56; + u32 secure_scratch57; + u32 secure_scratch58; + u32 secure_scratch59; + u32 secure_scratch60; + u32 secure_scratch61; + u32 secure_scratch62; + u32 secure_scratch63; + u32 secure_scratch64; + u32 secure_scratch65; + u32 secure_scratch66; + u32 secure_scratch67; + u32 secure_scratch68; + u32 secure_scratch69; + u32 secure_scratch70; + u32 secure_scratch71; + u32 secure_scratch72; + u32 secure_scratch73; + u32 secure_scratch74; + u32 secure_scratch75; + u32 secure_scratch76; + u32 secure_scratch77; + u32 secure_scratch78; + u32 secure_scratch79; + u32 _rsv0x420[8]; + u32 cntrl2; /* 0x440 */ + u32 _rsv0x444[2]; + u32 event_counter; /* 0x44C */ + u32 fuse_control; + u32 scratch1_eco; + u32 _rsv0x458[1]; + u32 io_dpd3_req; /* 0x45C */ + u32 io_dpd3_status; + u32 io_dpd4_req; + u32 io_dpd4_status; + u32 _rsv0x46C[30]; + u32 ddr_cntrl; /* 0x4E4 */ + u32 _rsv0x4E8[70]; + u32 scratch56; /* 0x600 */ + u32 scratch57; + u32 scratch58; + u32 scratch59; + u32 scratch60; + u32 scratch61; + u32 scratch62; + u32 scratch63; + u32 scratch64; + u32 scratch65; + u32 scratch66; + u32 scratch67; + u32 scratch68; + u32 scratch69; + u32 scratch70; + u32 scratch71; + u32 scratch72; + u32 scratch73; + u32 scratch74; + u32 scratch75; + u32 scratch76; + u32 scratch77; + u32 scratch78; + u32 scratch79; + u32 scratch80; + u32 scratch81; + u32 scratch82; + u32 scratch83; + u32 scratch84; + u32 scratch85; + u32 scratch86; + u32 scratch87; + u32 scratch88; + u32 scratch89; + u32 scratch90; + u32 scratch91; + u32 scratch92; + u32 scratch93; + u32 scratch94; + u32 scratch95; + u32 scratch96; + u32 scratch97; + u32 scratch98; + u32 scratch99; + u32 scratch100; + u32 scratch101; + u32 scratch102; + u32 scratch103; + u32 scratch104; + u32 scratch105; + u32 scratch106; + u32 scratch107; + u32 scratch108; + u32 scratch109; + u32 scratch110; + u32 scratch111; + u32 scratch112; + u32 scratch113; + u32 scratch114; + u32 scratch115; + u32 scratch116; + u32 scratch117; + u32 scratch118; + u32 scratch119; + u32 scratch120; /* 0x700 */ + u32 scratch121; + u32 scratch122; + u32 scratch123; + u32 scratch124; + u32 scratch125; + u32 scratch126; + u32 scratch127; + u32 scratch128; + u32 scratch129; + u32 scratch130; + u32 scratch131; + u32 scratch132; + u32 scratch133; + u32 scratch134; + u32 scratch135; + u32 scratch136; + u32 scratch137; + u32 scratch138; + u32 scratch139; + u32 scratch140; + u32 scratch141; + u32 scratch142; + u32 scratch143; + u32 scratch144; + u32 scratch145; + u32 scratch146; + u32 scratch147; + u32 scratch148; + u32 scratch149; + u32 scratch150; + u32 scratch151; + u32 scratch152; + u32 scratch153; + u32 scratch154; + u32 scratch155; + u32 scratch156; + u32 scratch157; + u32 scratch158; + u32 scratch159; + u32 scratch160; + u32 scratch161; + u32 scratch162; + u32 scratch163; + u32 scratch164; + u32 scratch165; + u32 scratch166; + u32 scratch167; + u32 scratch168; + u32 scratch169; + u32 scratch170; + u32 scratch171; + u32 scratch172; + u32 scratch173; + u32 scratch174; + u32 scratch175; + u32 scratch176; + u32 scratch177; + u32 scratch178; + u32 scratch179; + u32 scratch180; + u32 scratch181; + u32 scratch182; + u32 scratch183; + u32 scratch184; + u32 scratch185; + u32 scratch186; + u32 scratch187; + u32 scratch188; + u32 scratch189; + u32 scratch190; + u32 scratch191; + u32 scratch192; + u32 scratch193; + u32 scratch194; + u32 scratch195; + u32 scratch196; + u32 scratch197; + u32 scratch198; + u32 scratch199; + u32 scratch200; + u32 scratch201; + u32 scratch202; + u32 scratch203; + u32 scratch204; + u32 scratch205; + u32 scratch206; + u32 scratch207; + u32 scratch208; + u32 scratch209; + u32 scratch210; + u32 scratch211; + u32 scratch212; + u32 scratch213; + u32 scratch214; + u32 scratch215; + u32 scratch216; + u32 scratch217; + u32 scratch218; + u32 scratch219; + u32 scratch220; + u32 scratch221; + u32 scratch222; + u32 scratch223; + u32 scratch224; + u32 scratch225; + u32 scratch226; + u32 scratch227; + u32 scratch228; + u32 scratch229; + u32 scratch230; + u32 scratch231; + u32 scratch232; + u32 scratch233; + u32 scratch234; + u32 scratch235; + u32 scratch236; + u32 scratch237; + u32 scratch238; + u32 scratch239; + u32 scratch240; + u32 scratch241; + u32 scratch242; + u32 scratch243; + u32 scratch244; + u32 scratch245; + u32 scratch246; + u32 scratch247; + u32 scratch248; + u32 scratch249; + u32 scratch250; + u32 scratch251; + u32 scratch252; + u32 scratch253; + u32 scratch254; + u32 scratch255; + u32 scratch256; + u32 scratch257; + u32 scratch258; + u32 scratch259; + u32 scratch260; + u32 scratch261; + u32 scratch262; + u32 scratch263; + u32 scratch264; + u32 scratch265; + u32 scratch266; + u32 scratch267; + u32 scratch268; + u32 scratch269; + u32 scratch270; + u32 scratch271; + u32 scratch272; + u32 scratch273; + u32 scratch274; + u32 scratch275; + u32 scratch276; + u32 scratch277; + u32 scratch278; + u32 scratch279; + u32 scratch280; + u32 scratch281; + u32 scratch282; + u32 scratch283; + u32 scratch284; + u32 scratch285; + u32 scratch286; + u32 scratch287; + u32 scratch288; + u32 scratch289; + u32 scratch290; + u32 scratch291; + u32 scratch292; + u32 scratch293; + u32 scratch294; + u32 scratch295; + u32 scratch296; + u32 scratch297; + u32 scratch298; + u32 scratch299; /* 0x9CC */ + u32 _rsv0x9D0[50]; + u32 secure_scratch80; /* 0xa98 */ + u32 secure_scratch81; + u32 secure_scratch82; + u32 secure_scratch83; + u32 secure_scratch84; + u32 secure_scratch85; + u32 secure_scratch86; + u32 secure_scratch87; + u32 secure_scratch88; + u32 secure_scratch89; + u32 secure_scratch90; + u32 secure_scratch91; + u32 secure_scratch92; + u32 secure_scratch93; + u32 secure_scratch94; + u32 secure_scratch95; + u32 secure_scratch96; + u32 secure_scratch97; + u32 secure_scratch98; + u32 secure_scratch99; + u32 secure_scratch100; + u32 secure_scratch101; + u32 secure_scratch102; + u32 secure_scratch103; + u32 secure_scratch104; + u32 secure_scratch105; + u32 secure_scratch106; + u32 secure_scratch107; + u32 secure_scratch108; + u32 secure_scratch109; + u32 secure_scratch110; + u32 secure_scratch111; + u32 secure_scratch112; + u32 secure_scratch113; + u32 secure_scratch114; + u32 secure_scratch115; + u32 secure_scratch116; + u32 secure_scratch117; + u32 secure_scratch118; + u32 secure_scratch119; +}; + +check_member(tegra_pmc_regs, secure_scratch119, 0xB34); + +enum { + PMC_RST_STATUS_SOURCE_MASK = 0x7, + PMC_RST_STATUS_SOURCE_POR = 0x0, + PMC_RST_STATUS_SOURCE_WATCHDOG = 0x1, + PMC_RST_STATUS_SOURCE_SENSOR = 0x2, + PMC_RST_STATUS_SOURCE_SW_MAIN = 0x3, + PMC_RST_STATUS_SOURCE_LP0 = 0x4, + PMC_RST_STATUS_NUM_SOURCES = 0x5, +}; + +enum { + PMC_PWRGATE_TOGGLE_PARTID_MASK = 0x1f, + PMC_PWRGATE_TOGGLE_PARTID_SHIFT = 0, + PMC_PWRGATE_TOGGLE_START = 0x1 << 8 +}; + +enum { + PMC_CNTRL_KBC_CLK_DIS = 0x1 << 0, + PMC_CNTRL_RTC_CLK_DIS = 0x1 << 1, + PMC_CNTRL_RTC_RST = 0x1 << 2, + PMC_CNTRL_KBC_RST = 0x1 << 3, + PMC_CNTRL_MAIN_RST = 0x1 << 4, + PMC_CNTRL_LATCHWAKE_EN = 0x1 << 5, + PMC_CNTRL_GLITCHDET_DIS = 0x1 << 6, + PMC_CNTRL_BLINK_EN = 0x1 << 7, + PMC_CNTRL_PWRREQ_POLARITY = 0x1 << 8, + PMC_CNTRL_PWRREQ_OE = 0x1 << 9, + PMC_CNTRL_SYSCLK_POLARITY = 0x1 << 10, + PMC_CNTRL_SYSCLK_OE = 0x1 << 11, + PMC_CNTRL_PWRGATE_DIS = 0x1 << 12, + PMC_CNTRL_AOINIT = 0x1 << 13, + PMC_CNTRL_SIDE_EFFECT_LP0 = 0x1 << 14, + PMC_CNTRL_CPUPWRREQ_POLARITY = 0x1 << 15, + PMC_CNTRL_CPUPWRREQ_OE = 0x1 << 16, + PMC_CNTRL_INTR_POLARITY = 0x1 << 17, + PMC_CNTRL_FUSE_OVERRIDE = 0x1 << 18, + PMC_CNTRL_CPUPWRGOOD_EN = 0x1 << 19, + PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT = 20, + PMC_CNTRL_CPUPWRGOOD_SEL_MASK = + 0x3 << PMC_CNTRL_CPUPWRGOOD_SEL_SHIFT +}; + +enum { + PMC_DDR_PWR_EMMC_MASK = 1 << 1, + PMC_DDR_PWR_VAL_MASK = 1 << 0, +}; + +enum { + PMC_DDR_CFG_PKG_MASK = 1 << 0, + PMC_DDR_CFG_IF_MASK = 1 << 1, + PMC_DDR_CFG_XM0_RESET_TRI_MASK = 1 << 12, + PMC_DDR_CFG_XM0_RESET_DPDIO_MASK = 1 << 13, +}; + +enum { + PMC_NO_IOPOWER_MEM_MASK = 1 << 7, + PMC_NO_IOPOWER_MEM_COMP_MASK = 1 << 16, +}; + +enum { + PMC_POR_DPD_CTRL_MEM0_ADDR0_CLK_SEL_DPD_MASK = 1 << 0, + PMC_POR_DPD_CTRL_MEM0_ADDR1_CLK_SEL_DPD_MASK = 1 << 1, + PMC_POR_DPD_CTRL_MEM0_HOLD_CKE_LOW_OVR_MASK = 1 << 31, +}; + +enum { + PMC_CNTRL2_HOLD_CKE_LOW_EN = 0x1 << 12 +}; + +enum { + PMC_OSC_EDPD_OVER_XOFS_SHIFT = 1, + PMC_OSC_EDPD_OVER_XOFS_MASK = + 0x3f << PMC_OSC_EDPD_OVER_XOFS_SHIFT +}; + +enum { + PMC_CMD_HOLD_LOW_BR00_11_MASK = 0x0007FF80, + DPD_OFF = 1 << 30, + DPD_ON = 2 << 30, +}; + +enum { + PMC_GPIO_RAIL_AO_SHIFT = 21, + PMC_GPIO_RAIL_AO_MASK = (1 << PMC_GPIO_RAIL_AO_SHIFT), + PMC_GPIO_RAIL_AO_DISABLE = (0 << PMC_GPIO_RAIL_AO_SHIFT), + PMC_GPIO_RAIL_AO_ENABLE = (1 << PMC_GPIO_RAIL_AO_SHIFT), + + PMC_AUDIO_RAIL_AO_SHIFT = 18, + PMC_AUDIO_RAIL_AO_MASK = (1 << PMC_AUDIO_RAIL_AO_SHIFT), + PMC_AUDIO_RAIL_AO_DISABLE = (0 << PMC_AUDIO_RAIL_AO_SHIFT), + PMC_AUDIO_RAIL_AO_ENABLE = (1 << PMC_AUDIO_RAIL_AO_SHIFT), + + PMC_SDMMC3_RAIL_AO_SHIFT = 13, + PMC_SDMMC3_RAIL_AO_MASK = (1 << PMC_SDMMC3_RAIL_AO_SHIFT), + PMC_SDMMC3_RAIL_AO_DISABLE = (0 << PMC_SDMMC3_RAIL_AO_SHIFT), + PMC_SDMMC3_RAIL_AO_ENABLE = (1 << PMC_SDMMC3_RAIL_AO_SHIFT), +}; + +#endif /* _TEGRA210_PMC_H_ */ diff --git a/src/soc/nvidia/tegra210/include/soc/power.h b/src/soc/nvidia/tegra210/include/soc/power.h new file mode 100644 index 0000000000..c40609666a --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/power.h @@ -0,0 +1,33 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_POWER_H__ +#define __SOC_NVIDIA_TEGRA210_POWER_H__ + +#include <soc/pmc.h> + +void power_ungate_partition(uint32_t id); +void power_gate_partition(uint32_t id); + +uint8_t pmc_rst_status(void); +void pmc_print_rst_status(void); +void remove_clamps(int id); +void pmc_override_pwr_det(uint32_t bits, uint32_t override); + +#endif /* __SOC_NVIDIA_TEGRA210_POWER_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/romstage.h b/src/soc/nvidia/tegra210/include/soc/romstage.h new file mode 100644 index 0000000000..d4ab982382 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/romstage.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SOC_ROMSTAGE_H__ +#define __SOC_NVIDIA_TEGRA210_SOC_ROMSTAGE_H__ + +void romstage(void); +void romstage_mainboard_init(void); + +void mainboard_configure_pmc(void); +void mainboard_enable_vdd_cpu(void); + +#endif /* __SOC_NVIDIA_TEGRA210_SOC_ROMSTAGE_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/sdram.h b/src/soc/nvidia/tegra210/include/soc/sdram.h new file mode 100644 index 0000000000..a8dec820c5 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/sdram.h @@ -0,0 +1,31 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SDRAM_H__ +#define __SOC_NVIDIA_TEGRA210_SDRAM_H__ + +#include <soc/sdram_param.h> + +uint32_t sdram_get_ram_code(void); +void sdram_init(const struct sdram_params *param); + +/* Save params to PMC scratch registers for use by BootROM on LP0 resume. */ +void sdram_lp0_save_params(const struct sdram_params *sdram); + +#endif /* __SOC_NVIDIA_TEGRA210_SDRAM_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/sdram_configs.h b/src/soc/nvidia/tegra210/include/soc/sdram_configs.h new file mode 100644 index 0000000000..905ff9c50f --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/sdram_configs.h @@ -0,0 +1,28 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SDRAM_CONFIGS_H__ +#define __SOC_NVIDIA_TEGRA210_SDRAM_CONFIGS_H__ + +#include <soc/sdram.h> + +/* Loads SDRAM configurations for current system. */ +const struct sdram_params *get_sdram_config(void); + +#endif /* __SOC_NVIDIA_TEGRA210_SDRAM_CONFIGS_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/sdram_param.h b/src/soc/nvidia/tegra210/include/soc/sdram_param.h new file mode 100644 index 0000000000..018101efe8 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/sdram_param.h @@ -0,0 +1,979 @@ +/* + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + * + * See file CREDITS for list of people who contributed to this + * project. + */ + +/** + * Defines the SDRAM parameter structure. + * + * Note that PLLM is used by EMC. The field names are in camel case to ease + * directly converting BCT config files (*.cfg) into C structure. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ +#define __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ + +#include <stddef.h> +#include <stdint.h> + +enum { + /* Specifies the memory type to be undefined */ + NvBootMemoryType_None = 0, + + /* Specifies the memory type to be DDR SDRAM */ + NvBootMemoryType_Ddr = 0, + + /* Specifies the memory type to be LPDDR SDRAM */ + NvBootMemoryType_LpDdr = 0, + + /* Specifies the memory type to be DDR2 SDRAM */ + NvBootMemoryType_Ddr2 = 0, + + /* Specifies the memory type to be LPDDR2 SDRAM */ + NvBootMemoryType_LpDdr2, + + /* Specifies the memory type to be DDR3 SDRAM */ + NvBootMemoryType_Ddr3, + + /* Specifies the memory type to be LPDDR4 SDRAM */ + NvBootMemoryType_LpDdr4, + + NvBootMemoryType_Num, + + /* Specifies an entry in the ram_code table that's not in use */ + NvBootMemoryType_Unused = 0X7FFFFFF, +}; + +enum { + BOOT_ROM_PATCH_CONTROL_ENABLE_MASK = 0x1 << 31, + BOOT_ROM_PATCH_CONTROL_OFFSET_SHIFT = 0, + BOOT_ROM_PATCH_CONTROL_OFFSET_MASK = 0x7FFFFFFF << 0, + BOOT_ROM_PATCH_CONTROL_BASE_ADDRESS = 0x70000000, + + EMC_ZCAL_WARM_COLD_BOOT_ENABLES_COLDBOOT_MASK = 1 << 0, +}; + +/** + * Defines the SDRAM parameter structure + */ +struct sdram_params { + + /* Specifies the type of memory device */ + uint32_t MemoryType; + + /* MC/EMC clock source configuration */ + + /* Specifies the M value for PllM */ + uint32_t PllMInputDivider; + /* Specifies the N value for PllM */ + uint32_t PllMFeedbackDivider; + /* Specifies the time to wait for PLLM to lock (in microseconds) */ + uint32_t PllMStableTime; + /* Specifies misc. control bits */ + uint32_t PllMSetupControl; + /* Specifies the P value for PLLM */ + uint32_t PllMPostDivider; + /* Specifies value for Charge Pump Gain Control */ + uint32_t PllMKCP; + /* Specifies VCO gain */ + uint32_t PllMKVCO; + /* Spare BCT param */ + uint32_t EmcBctSpare0; + /* Spare BCT param */ + uint32_t EmcBctSpare1; + /* Spare BCT param */ + uint32_t EmcBctSpare2; + /* Spare BCT param */ + uint32_t EmcBctSpare3; + /* Spare BCT param */ + uint32_t EmcBctSpare4; + /* Spare BCT param */ + uint32_t EmcBctSpare5; + /* Spare BCT param */ + uint32_t EmcBctSpare6; + /* Spare BCT param */ + uint32_t EmcBctSpare7; + /* Spare BCT param */ + uint32_t EmcBctSpare8; + /* Spare BCT param */ + uint32_t EmcBctSpare9; + /* Spare BCT param */ + uint32_t EmcBctSpare10; + /* Spare BCT param */ + uint32_t EmcBctSpare11; + /* Spare BCT param */ + uint32_t EmcBctSpare12; + /* Spare BCT param */ + uint32_t EmcBctSpare13; + + /* Defines EMC_2X_CLK_SRC, EMC_2X_CLK_DIVISOR, EMC_INVERT_DCD */ + uint32_t EmcClockSource; + uint32_t EmcClockSourceDll; + + /* Defines possible override for PLLLM_MISC2 */ + uint32_t ClkRstControllerPllmMisc2Override; + /* enables override for PLLLM_MISC2 */ + uint32_t ClkRstControllerPllmMisc2OverrideEnable; + /* defines CLK_ENB_MC1 in register clk_rst_controller_clk_enb_w_clr */ + uint32_t ClearClk2Mc1; + + /* Auto-calibration of EMC pads */ + + /* Specifies the value for EMC_AUTO_CAL_INTERVAL */ + uint32_t EmcAutoCalInterval; + /* + * Specifies the value for EMC_AUTO_CAL_CONFIG + * Note: Trigger bits are set by the SDRAM code. + */ + uint32_t EmcAutoCalConfig; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG2 */ + uint32_t EmcAutoCalConfig2; + + /* Specifies the value for EMC_AUTO_CAL_CONFIG3 */ + uint32_t EmcAutoCalConfig3; + + /* Specifies the values for EMC_AUTO_CAL_CONFIG4-8 */ + uint32_t EmcAutoCalConfig4; + uint32_t EmcAutoCalConfig5; + uint32_t EmcAutoCalConfig6; + uint32_t EmcAutoCalConfig7; + uint32_t EmcAutoCalConfig8; + + /* Specifies the value for EMC_AUTO_CAL_VREF_SEL_0 */ + uint32_t EmcAutoCalVrefSel0; + uint32_t EmcAutoCalVrefSel1; + + /* Specifies the value for EMC_AUTO_CAL_CHANNEL */ + uint32_t EmcAutoCalChannel; + + /* Specifies the value for EMC_PMACRO_AUTOCAL_CFG_0 */ + uint32_t EmcPmacroAutocalCfg0; + uint32_t EmcPmacroAutocalCfg1; + uint32_t EmcPmacroAutocalCfg2; + uint32_t EmcPmacroRxTerm; + uint32_t EmcPmacroDqTxDrv; + uint32_t EmcPmacroCaTxDrv; + uint32_t EmcPmacroCmdTxDrv; + uint32_t EmcPmacroAutocalCfgCommon; + uint32_t EmcPmacroZctrl; + + /* + * Specifies the time for the calibration + * to stabilize (in microseconds) + */ + uint32_t EmcAutoCalWait; + + uint32_t EmcXm2CompPadCtrl; + uint32_t EmcXm2CompPadCtrl2; + uint32_t EmcXm2CompPadCtrl3; + + /* + * DRAM size information + * Specifies the value for EMC_ADR_CFG + */ + uint32_t EmcAdrCfg; + + /* + * Specifies the time to wait after asserting pin + * CKE (in microseconds) + */ + uint32_t EmcPinProgramWait; + /* Specifies the extra delay before/after pin RESET/CKE command */ + uint32_t EmcPinExtraWait; + + uint32_t EmcPinGpioEn; + uint32_t EmcPinGpio; + + /* + * Specifies the extra delay after the first writing + * of EMC_TIMING_CONTROL + */ + uint32_t EmcTimingControlWait; + + /* Timing parameters required for the SDRAM */ + + /* Specifies the value for EMC_RC */ + uint32_t EmcRc; + /* Specifies the value for EMC_RFC */ + uint32_t EmcRfc; + /* Specifies the value for EMC_RFC_PB */ + uint32_t EmcRfcPb; + /* Specifies the value for EMC_RFC_CTRL2 */ + uint32_t EmcRefctrl2; + /* Specifies the value for EMC_RFC_SLR */ + uint32_t EmcRfcSlr; + /* Specifies the value for EMC_RAS */ + uint32_t EmcRas; + /* Specifies the value for EMC_RP */ + uint32_t EmcRp; + /* Specifies the value for EMC_R2R */ + uint32_t EmcR2r; + /* Specifies the value for EMC_W2W */ + uint32_t EmcW2w; + /* Specifies the value for EMC_R2W */ + uint32_t EmcR2w; + /* Specifies the value for EMC_W2R */ + uint32_t EmcW2r; + /* Specifies the value for EMC_R2P */ + uint32_t EmcR2p; + /* Specifies the value for EMC_W2P */ + uint32_t EmcW2p; + + uint32_t EmcTppd; + uint32_t EmcCcdmw; + + /* Specifies the value for EMC_RD_RCD */ + uint32_t EmcRdRcd; + /* Specifies the value for EMC_WR_RCD */ + uint32_t EmcWrRcd; + /* Specifies the value for EMC_RRD */ + uint32_t EmcRrd; + /* Specifies the value for EMC_REXT */ + uint32_t EmcRext; + /* Specifies the value for EMC_WEXT */ + uint32_t EmcWext; + /* Specifies the value for EMC_WDV */ + uint32_t EmcWdv; + + uint32_t EmcWdvChk; + uint32_t EmcWsv; + uint32_t EmcWev; + + /* Specifies the value for EMC_WDV_MASK */ + uint32_t EmcWdvMask; + + uint32_t EmcWsDuration; + uint32_t EmcWeDuration; + + /* Specifies the value for EMC_QUSE */ + uint32_t EmcQUse; + /* Specifies the value for EMC_QUSE_WIDTH */ + uint32_t EmcQuseWidth; + /* Specifies the value for EMC_IBDLY */ + uint32_t EmcIbdly; + /* Specifies the value for EMC_OBDLY */ + uint32_t EmcObdly; + /* Specifies the value for EMC_EINPUT */ + uint32_t EmcEInput; + /* Specifies the value for EMC_EINPUT_DURATION */ + uint32_t EmcEInputDuration; + /* Specifies the value for EMC_PUTERM_EXTRA */ + uint32_t EmcPutermExtra; + /* Specifies the value for EMC_PUTERM_WIDTH */ + uint32_t EmcPutermWidth; + /* Specifies the value for EMC_PUTERM_ADJ */ + uint32_t EmcPutermAdj; + + /* Specifies the value for EMC_QRST */ + uint32_t EmcQRst; + /* Specifies the value for EMC_QSAFE */ + uint32_t EmcQSafe; + /* Specifies the value for EMC_RDV */ + uint32_t EmcRdv; + /* Specifies the value for EMC_RDV_MASK */ + uint32_t EmcRdvMask; + /* Specifies the value for EMC_RDV_EARLY */ + uint32_t EmcRdvEarly; + /* Specifies the value for EMC_RDV_EARLY_MASK */ + uint32_t EmcRdvEarlyMask; + /* Specifies the value for EMC_QPOP */ + uint32_t EmcQpop; + + /* Specifies the value for EMC_REFRESH */ + uint32_t EmcRefresh; + /* Specifies the value for EMC_BURST_REFRESH_NUM */ + uint32_t EmcBurstRefreshNum; + /* Specifies the value for EMC_PRE_REFRESH_REQ_CNT */ + uint32_t EmcPreRefreshReqCnt; + /* Specifies the value for EMC_PDEX2WR */ + uint32_t EmcPdEx2Wr; + /* Specifies the value for EMC_PDEX2RD */ + uint32_t EmcPdEx2Rd; + /* Specifies the value for EMC_PCHG2PDEN */ + uint32_t EmcPChg2Pden; + /* Specifies the value for EMC_ACT2PDEN */ + uint32_t EmcAct2Pden; + /* Specifies the value for EMC_AR2PDEN */ + uint32_t EmcAr2Pden; + /* Specifies the value for EMC_RW2PDEN */ + uint32_t EmcRw2Pden; + /* Specifies the value for EMC_CKE2PDEN */ + uint32_t EmcCke2Pden; + /* Specifies the value for EMC_PDEX2CKE */ + uint32_t EmcPdex2Cke; + /* Specifies the value for EMC_PDEX2MRR */ + uint32_t EmcPdex2Mrr; + /* Specifies the value for EMC_TXSR */ + uint32_t EmcTxsr; + /* Specifies the value for EMC_TXSRDLL */ + uint32_t EmcTxsrDll; + /* Specifies the value for EMC_TCKE */ + uint32_t EmcTcke; + /* Specifies the value for EMC_TCKESR */ + uint32_t EmcTckesr; + /* Specifies the value for EMC_TPD */ + uint32_t EmcTpd; + /* Specifies the value for EMC_TFAW */ + uint32_t EmcTfaw; + /* Specifies the value for EMC_TRPAB */ + uint32_t EmcTrpab; + /* Specifies the value for EMC_TCLKSTABLE */ + uint32_t EmcTClkStable; + /* Specifies the value for EMC_TCLKSTOP */ + uint32_t EmcTClkStop; + /* Specifies the value for EMC_TREFBW */ + uint32_t EmcTRefBw; + + /* FBIO configuration values */ + + /* Specifies the value for EMC_FBIO_CFG5 */ + uint32_t EmcFbioCfg5; + /* Specifies the value for EMC_FBIO_CFG7 */ + uint32_t EmcFbioCfg7; + /* Specifies the value for EMC_FBIO_CFG8 */ + uint32_t EmcFbioCfg8; + + /* Command mapping for CMD brick 0 */ + uint32_t EmcCmdMappingCmd0_0; + uint32_t EmcCmdMappingCmd0_1; + uint32_t EmcCmdMappingCmd0_2; + uint32_t EmcCmdMappingCmd1_0; + uint32_t EmcCmdMappingCmd1_1; + uint32_t EmcCmdMappingCmd1_2; + uint32_t EmcCmdMappingCmd2_0; + uint32_t EmcCmdMappingCmd2_1; + uint32_t EmcCmdMappingCmd2_2; + uint32_t EmcCmdMappingCmd3_0; + uint32_t EmcCmdMappingCmd3_1; + uint32_t EmcCmdMappingCmd3_2; + uint32_t EmcCmdMappingByte; + + /* Specifies the value for EMC_FBIO_SPARE */ + uint32_t EmcFbioSpare; + + /* Specifies the value for EMC_CFG_RSV */ + uint32_t EmcCfgRsv; + + /* MRS command values */ + + /* Specifies the value for EMC_MRS */ + uint32_t EmcMrs; + /* Specifies the MP0 command to initialize mode registers */ + uint32_t EmcEmrs; + /* Specifies the MP2 command to initialize mode registers */ + uint32_t EmcEmrs2; + /* Specifies the MP3 command to initialize mode registers */ + uint32_t EmcEmrs3; + /* Specifies the programming to LPDDR2 Mode Register 1 at cold boot */ + uint32_t EmcMrw1; + /* Specifies the programming to LPDDR2 Mode Register 2 at cold boot */ + uint32_t EmcMrw2; + /* Specifies the programming to LPDDR2 Mode Register 3 at cold boot */ + uint32_t EmcMrw3; + /* Specifies the programming to LPDDR2 Mode Register 11 at cold boot */ + uint32_t EmcMrw4; + /* Specifies the programming to LPDDR2 Mode Register 3? at cold boot */ + uint32_t EmcMrw6; + /* Specifies the programming to LPDDR2 Mode Register 11 at cold boot */ + uint32_t EmcMrw8; + /* Specifies the programming to LPDDR2 Mode Register 11? at cold boot */ + uint32_t EmcMrw9; + /* Specifies the programming to LPDDR2 Mode Register 12 at cold boot */ + uint32_t EmcMrw10; + /* Specifies the programming to LPDDR2 Mode Register 14 at cold boot */ + uint32_t EmcMrw12; + /* Specifies the programming to LPDDR2 Mode Register 14? at cold boot */ + uint32_t EmcMrw13; + /* Specifies the programming to LPDDR2 Mode Register 22 at cold boot */ + uint32_t EmcMrw14; + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at cold boot + */ + uint32_t EmcMrwExtra; + /* + * Specifies the programming to extra LPDDR2 Mode Register + * at warm boot + */ + uint32_t EmcWarmBootMrwExtra; + /* + * Specify the enable of extra Mode Register programming at + * warm boot + */ + uint32_t EmcWarmBootExtraModeRegWriteEnable; + /* + * Specify the enable of extra Mode Register programming at + * cold boot + */ + uint32_t EmcExtraModeRegWriteEnable; + + /* Specifies the EMC_MRW reset command value */ + uint32_t EmcMrwResetCommand; + /* Specifies the EMC Reset wait time (in microseconds) */ + uint32_t EmcMrwResetNInitWait; + /* Specifies the value for EMC_MRS_WAIT_CNT */ + uint32_t EmcMrsWaitCnt; + /* Specifies the value for EMC_MRS_WAIT_CNT2 */ + uint32_t EmcMrsWaitCnt2; + + /* EMC miscellaneous configurations */ + + /* Specifies the value for EMC_CFG */ + uint32_t EmcCfg; + /* Specifies the value for EMC_CFG_2 */ + uint32_t EmcCfg2; + /* Specifies the pipe bypass controls */ + uint32_t EmcCfgPipe; + uint32_t EmcCfgPipeClk; + uint32_t EmcFdpdCtrlCmdNoRamp; + uint32_t EmcCfgUpdate; + + /* Specifies the value for EMC_DBG */ + uint32_t EmcDbg; + uint32_t EmcDbgWriteMux; + + /* Specifies the value for EMC_CMDQ */ + uint32_t EmcCmdQ; + /* Specifies the value for EMC_MC2EMCQ */ + uint32_t EmcMc2EmcQ; + /* Specifies the value for EMC_DYN_SELF_REF_CONTROL */ + uint32_t EmcDynSelfRefControl; + + /* Specifies the value for MEM_INIT_DONE */ + uint32_t AhbArbitrationXbarCtrlMemInitDone; + + /* Specifies the value for EMC_CFG_DIG_DLL */ + uint32_t EmcCfgDigDll; + uint32_t EmcCfgDigDll_1; + /* Specifies the value for EMC_CFG_DIG_DLL_PERIOD */ + uint32_t EmcCfgDigDllPeriod; + /* Specifies the value of *DEV_SELECTN of various EMC registers */ + uint32_t EmcDevSelect; + + /* Specifies the value for EMC_SEL_DPD_CTRL */ + uint32_t EmcSelDpdCtrl; + + /* Pads trimmer delays */ + uint32_t EmcFdpdCtrlDq; + uint32_t EmcFdpdCtrlCmd; + uint32_t EmcPmacroIbVrefDq_0; + uint32_t EmcPmacroIbVrefDq_1; + uint32_t EmcPmacroIbVrefDqs_0; + uint32_t EmcPmacroIbVrefDqs_1; + uint32_t EmcPmacroIbRxrt; + uint32_t EmcCfgPipe1; + uint32_t EmcCfgPipe2; + + /* Specifies the value for EMC_PMACRO_QUSE_DDLL_RANK0_0 */ + uint32_t EmcPmacroQuseDdllRank0_0; + uint32_t EmcPmacroQuseDdllRank0_1; + uint32_t EmcPmacroQuseDdllRank0_2; + uint32_t EmcPmacroQuseDdllRank0_3; + uint32_t EmcPmacroQuseDdllRank0_4; + uint32_t EmcPmacroQuseDdllRank0_5; + uint32_t EmcPmacroQuseDdllRank1_0; + uint32_t EmcPmacroQuseDdllRank1_1; + uint32_t EmcPmacroQuseDdllRank1_2; + uint32_t EmcPmacroQuseDdllRank1_3; + uint32_t EmcPmacroQuseDdllRank1_4; + uint32_t EmcPmacroQuseDdllRank1_5; + + uint32_t EmcPmacroObDdllLongDqRank0_0; + uint32_t EmcPmacroObDdllLongDqRank0_1; + uint32_t EmcPmacroObDdllLongDqRank0_2; + uint32_t EmcPmacroObDdllLongDqRank0_3; + uint32_t EmcPmacroObDdllLongDqRank0_4; + uint32_t EmcPmacroObDdllLongDqRank0_5; + uint32_t EmcPmacroObDdllLongDqRank1_0; + uint32_t EmcPmacroObDdllLongDqRank1_1; + uint32_t EmcPmacroObDdllLongDqRank1_2; + uint32_t EmcPmacroObDdllLongDqRank1_3; + uint32_t EmcPmacroObDdllLongDqRank1_4; + uint32_t EmcPmacroObDdllLongDqRank1_5; + + uint32_t EmcPmacroObDdllLongDqsRank0_0; + uint32_t EmcPmacroObDdllLongDqsRank0_1; + uint32_t EmcPmacroObDdllLongDqsRank0_2; + uint32_t EmcPmacroObDdllLongDqsRank0_3; + uint32_t EmcPmacroObDdllLongDqsRank0_4; + uint32_t EmcPmacroObDdllLongDqsRank0_5; + uint32_t EmcPmacroObDdllLongDqsRank1_0; + uint32_t EmcPmacroObDdllLongDqsRank1_1; + uint32_t EmcPmacroObDdllLongDqsRank1_2; + uint32_t EmcPmacroObDdllLongDqsRank1_3; + uint32_t EmcPmacroObDdllLongDqsRank1_4; + uint32_t EmcPmacroObDdllLongDqsRank1_5; + + uint32_t EmcPmacroIbDdllLongDqsRank0_0; + uint32_t EmcPmacroIbDdllLongDqsRank0_1; + uint32_t EmcPmacroIbDdllLongDqsRank0_2; + uint32_t EmcPmacroIbDdllLongDqsRank0_3; + uint32_t EmcPmacroIbDdllLongDqsRank1_0; + uint32_t EmcPmacroIbDdllLongDqsRank1_1; + uint32_t EmcPmacroIbDdllLongDqsRank1_2; + uint32_t EmcPmacroIbDdllLongDqsRank1_3; + + uint32_t EmcPmacroDdllLongCmd_0; + uint32_t EmcPmacroDdllLongCmd_1; + uint32_t EmcPmacroDdllLongCmd_2; + uint32_t EmcPmacroDdllLongCmd_3; + uint32_t EmcPmacroDdllLongCmd_4; + uint32_t EmcPmacroDdllShortCmd_0; + uint32_t EmcPmacroDdllShortCmd_1; + uint32_t EmcPmacroDdllShortCmd_2; + + /* + * Specifies the delay after asserting CKE pin during a WarmBoot0 + * sequence (in microseconds) + */ + uint32_t WarmBootWait; + + /* Specifies the value for EMC_ODT_WRITE */ + uint32_t EmcOdtWrite; + + /* Periodic ZQ calibration */ + + /* + * Specifies the value for EMC_ZCAL_INTERVAL + * Value 0 disables ZQ calibration + */ + uint32_t EmcZcalInterval; + /* Specifies the value for EMC_ZCAL_WAIT_CNT */ + uint32_t EmcZcalWaitCnt; + /* Specifies the value for EMC_ZCAL_MRW_CMD */ + uint32_t EmcZcalMrwCmd; + + /* DRAM initialization sequence flow control */ + + /* Specifies the MRS command value for resetting DLL */ + uint32_t EmcMrsResetDll; + /* Specifies the command for ZQ initialization of device 0 */ + uint32_t EmcZcalInitDev0; + /* Specifies the command for ZQ initialization of device 1 */ + uint32_t EmcZcalInitDev1; + /* + * Specifies the wait time after programming a ZQ initialization + * command (in microseconds) + */ + uint32_t EmcZcalInitWait; + /* + * Specifies the enable for ZQ calibration at cold boot [bit 0] + * and warm boot [bit 1] + */ + uint32_t EmcZcalWarmColdBootEnables; + + /* + * Specifies the MRW command to LPDDR2 for ZQ calibration + * on warmboot + */ + /* Is issued to both devices separately */ + uint32_t EmcMrwLpddr2ZcalWarmBoot; + /* + * Specifies the ZQ command to DDR3 for ZQ calibration on warmboot + * Is issued to both devices separately + */ + uint32_t EmcZqCalDdr3WarmBoot; + uint32_t EmcZqCalLpDdr4WarmBoot; + /* + * Specifies the wait time for ZQ calibration on warmboot + * (in microseconds) + */ + uint32_t EmcZcalWarmBootWait; + /* + * Specifies the enable for DRAM Mode Register programming + * at warm boot + */ + uint32_t EmcMrsWarmBootEnable; + /* + * Specifies the wait time after sending an MRS DLL reset command + * in microseconds) + */ + uint32_t EmcMrsResetDllWait; + /* Specifies the extra MRS command to initialize mode registers */ + uint32_t EmcMrsExtra; + /* Specifies the extra MRS command at warm boot */ + uint32_t EmcWarmBootMrsExtra; + /* Specifies the EMRS command to enable the DDR2 DLL */ + uint32_t EmcEmrsDdr2DllEnable; + /* Specifies the MRS command to reset the DDR2 DLL */ + uint32_t EmcMrsDdr2DllReset; + /* Specifies the EMRS command to set OCD calibration */ + uint32_t EmcEmrsDdr2OcdCalib; + /* + * Specifies the wait between initializing DDR and setting OCD + * calibration (in microseconds) + */ + uint32_t EmcDdr2Wait; + /* Specifies the value for EMC_CLKEN_OVERRIDE */ + uint32_t EmcClkenOverride; + + /* + * Specifies LOG2 of the extra refresh numbers after booting + * Program 0 to disable + */ + uint32_t EmcExtraRefreshNum; + /* Specifies the master override for all EMC clocks */ + uint32_t EmcClkenOverrideAllWarmBoot; + /* Specifies the master override for all MC clocks */ + uint32_t McClkenOverrideAllWarmBoot; + /* Specifies digital dll period, choosing between 4 to 64 ms */ + uint32_t EmcCfgDigDllPeriodWarmBoot; + + /* Pad controls */ + + /* Specifies the value for PMC_VDDP_SEL */ + uint32_t PmcVddpSel; + /* Specifies the wait time after programming PMC_VDDP_SEL */ + uint32_t PmcVddpSelWait; + /* Specifies the value for PMC_DDR_PWR */ + uint32_t PmcDdrPwr; + /* Specifies the value for PMC_DDR_CFG */ + uint32_t PmcDdrCfg; + /* Specifies the value for PMC_IO_DPD3_REQ */ + uint32_t PmcIoDpd3Req; + /* Specifies the wait time after programming PMC_IO_DPD3_REQ */ + uint32_t PmcIoDpd3ReqWait; + uint32_t PmcIoDpd4ReqWait; + + /* Specifies the value for PMC_REG_SHORT */ + uint32_t PmcRegShort; + /* Specifies the value for PMC_NO_IOPOWER */ + uint32_t PmcNoIoPower; + + uint32_t PmcDdrCntrlWait; + uint32_t PmcDdrCntrl; + + /* Specifies the value for EMC_ACPD_CONTROL */ + uint32_t EmcAcpdControl; + + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE_CFG */ + uint32_t EmcSwizzleRank0ByteCfg; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE0 */ + uint32_t EmcSwizzleRank0Byte0; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE1 */ + uint32_t EmcSwizzleRank0Byte1; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE2 */ + uint32_t EmcSwizzleRank0Byte2; + /* Specifies the value for EMC_SWIZZLE_RANK0_BYTE3 */ + uint32_t EmcSwizzleRank0Byte3; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE_CFG */ + uint32_t EmcSwizzleRank1ByteCfg; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE0 */ + uint32_t EmcSwizzleRank1Byte0; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE1 */ + uint32_t EmcSwizzleRank1Byte1; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE2 */ + uint32_t EmcSwizzleRank1Byte2; + /* Specifies the value for EMC_SWIZZLE_RANK1_BYTE3 */ + uint32_t EmcSwizzleRank1Byte3; + + /* Specifies the value for EMC_TXDSRVTTGEN */ + uint32_t EmcTxdsrvttgen; + + /* Specifies the value for EMC_DATA_BRLSHFT_0 */ + uint32_t EmcDataBrlshft0; + uint32_t EmcDataBrlshft1; + + uint32_t EmcDqsBrlshft0; + uint32_t EmcDqsBrlshft1; + + uint32_t EmcCmdBrlshft0; + uint32_t EmcCmdBrlshft1; + uint32_t EmcCmdBrlshft2; + uint32_t EmcCmdBrlshft3; + + uint32_t EmcQuseBrlshft0; + uint32_t EmcQuseBrlshft1; + uint32_t EmcQuseBrlshft2; + uint32_t EmcQuseBrlshft3; + + uint32_t EmcDllCfg0; + uint32_t EmcDllCfg1; + + uint32_t EmcPmcScratch1; + uint32_t EmcPmcScratch2; + uint32_t EmcPmcScratch3; + + uint32_t EmcPmacroPadCfgCtrl; + + uint32_t EmcPmacroVttgenCtrl0; + uint32_t EmcPmacroVttgenCtrl1; + uint32_t EmcPmacroVttgenCtrl2; + + uint32_t EmcPmacroBrickCtrlRfu1; + uint32_t EmcPmacroCmdBrickCtrlFdpd; + uint32_t EmcPmacroBrickCtrlRfu2; + uint32_t EmcPmacroDataBrickCtrlFdpd; + uint32_t EmcPmacroBgBiasCtrl0; + uint32_t EmcPmacroDataPadRxCtrl; + uint32_t EmcPmacroCmdPadRxCtrl; + uint32_t EmcPmacroDataRxTermMode; + uint32_t EmcPmacroCmdRxTermMode; + uint32_t EmcPmacroDataPadTxCtrl; + uint32_t EmcPmacroCommonPadTxCtrl; + uint32_t EmcPmacroCmdPadTxCtrl; + uint32_t EmcCfg3; + + uint32_t EmcPmacroTxPwrd0; + uint32_t EmcPmacroTxPwrd1; + uint32_t EmcPmacroTxPwrd2; + uint32_t EmcPmacroTxPwrd3; + uint32_t EmcPmacroTxPwrd4; + uint32_t EmcPmacroTxPwrd5; + + uint32_t EmcConfigSampleDelay; + + uint32_t EmcPmacroBrickMapping0; + uint32_t EmcPmacroBrickMapping1; + uint32_t EmcPmacroBrickMapping2; + + uint32_t EmcPmacroTxSelClkSrc0; + uint32_t EmcPmacroTxSelClkSrc1; + uint32_t EmcPmacroTxSelClkSrc2; + uint32_t EmcPmacroTxSelClkSrc3; + uint32_t EmcPmacroTxSelClkSrc4; + uint32_t EmcPmacroTxSelClkSrc5; + + uint32_t EmcPmacroDdllBypass; + + uint32_t EmcPmacroDdllPwrd0; + uint32_t EmcPmacroDdllPwrd1; + uint32_t EmcPmacroDdllPwrd2; + + uint32_t EmcPmacroCmdCtrl0; + uint32_t EmcPmacroCmdCtrl1; + uint32_t EmcPmacroCmdCtrl2; + + /* DRAM size information */ + + /* Specifies the value for MC_EMEM_ADR_CFG */ + uint32_t McEmemAdrCfg; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV0 */ + uint32_t McEmemAdrCfgDev0; + /* Specifies the value for MC_EMEM_ADR_CFG_DEV1 */ + uint32_t McEmemAdrCfgDev1; + uint32_t McEmemAdrCfgChannelMask; + + /* Specifies the value for MC_EMEM_BANK_SWIZZLECfg0 */ + uint32_t McEmemAdrCfgBankMask0; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG1 */ + uint32_t McEmemAdrCfgBankMask1; + /* Specifies the value for MC_EMEM_BANK_SWIZZLE_CFG2 */ + uint32_t McEmemAdrCfgBankMask2; + + /* + * Specifies the value for MC_EMEM_CFG which holds the external memory + * size (in KBytes) + */ + uint32_t McEmemCfg; + + /* MC arbitration configuration */ + + /* Specifies the value for MC_EMEM_ARB_CFG */ + uint32_t McEmemArbCfg; + /* Specifies the value for MC_EMEM_ARB_OUTSTANDING_REQ */ + uint32_t McEmemArbOutstandingReq; + + uint32_t McEmemArbRefpbHpCtrl; + uint32_t McEmemArbRefpbBankCtrl; + + /* Specifies the value for MC_EMEM_ARB_TIMING_RCD */ + uint32_t McEmemArbTimingRcd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RP */ + uint32_t McEmemArbTimingRp; + /* Specifies the value for MC_EMEM_ARB_TIMING_RC */ + uint32_t McEmemArbTimingRc; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAS */ + uint32_t McEmemArbTimingRas; + /* Specifies the value for MC_EMEM_ARB_TIMING_FAW */ + uint32_t McEmemArbTimingFaw; + /* Specifies the value for MC_EMEM_ARB_TIMING_RRD */ + uint32_t McEmemArbTimingRrd; + /* Specifies the value for MC_EMEM_ARB_TIMING_RAP2PRE */ + uint32_t McEmemArbTimingRap2Pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_WAP2PRE */ + uint32_t McEmemArbTimingWap2Pre; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2R */ + uint32_t McEmemArbTimingR2R; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2W */ + uint32_t McEmemArbTimingW2W; + /* Specifies the value for MC_EMEM_ARB_TIMING_R2W */ + uint32_t McEmemArbTimingR2W; + /* Specifies the value for MC_EMEM_ARB_TIMING_W2R */ + uint32_t McEmemArbTimingW2R; + + uint32_t McEmemArbTimingRFCPB; + + /* Specifies the value for MC_EMEM_ARB_DA_TURNS */ + uint32_t McEmemArbDaTurns; + /* Specifies the value for MC_EMEM_ARB_DA_COVERS */ + uint32_t McEmemArbDaCovers; + /* Specifies the value for MC_EMEM_ARB_MISC0 */ + uint32_t McEmemArbMisc0; + /* Specifies the value for MC_EMEM_ARB_MISC1 */ + uint32_t McEmemArbMisc1; + uint32_t McEmemArbMisc2; + + /* Specifies the value for MC_EMEM_ARB_RING1_THROTTLE */ + uint32_t McEmemArbRing1Throttle; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE */ + uint32_t McEmemArbOverride; + /* Specifies the value for MC_EMEM_ARB_OVERRIDE_1 */ + uint32_t McEmemArbOverride1; + /* Specifies the value for MC_EMEM_ARB_RSV */ + uint32_t McEmemArbRsv; + + uint32_t McDaCfg0; + uint32_t McEmemArbTimingCcdmw; + + /* Specifies the value for MC_CLKEN_OVERRIDE */ + uint32_t McClkenOverride; + + /* Specifies the value for MC_STAT_CONTROL */ + uint32_t McStatControl; + + /* Specifies the value for MC_VIDEO_PROTECT_BOM */ + uint32_t McVideoProtectBom; + /* Specifies the value for MC_VIDEO_PROTECT_BOM_ADR_HI */ + uint32_t McVideoProtectBomAdrHi; + /* Specifies the value for MC_VIDEO_PROTECT_SIZE_MB */ + uint32_t McVideoProtectSizeMb; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE */ + uint32_t McVideoProtectVprOverride; + /* Specifies the value for MC_VIDEO_PROTECT_VPR_OVERRIDE1 */ + uint32_t McVideoProtectVprOverride1; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_0 */ + uint32_t McVideoProtectGpuOverride0; + /* Specifies the value for MC_VIDEO_PROTECT_GPU_OVERRIDE_1 */ + uint32_t McVideoProtectGpuOverride1; + /* Specifies the value for MC_SEC_CARVEOUT_BOM */ + uint32_t McSecCarveoutBom; + /* Specifies the value for MC_SEC_CARVEOUT_ADR_HI */ + uint32_t McSecCarveoutAdrHi; + /* Specifies the value for MC_SEC_CARVEOUT_SIZE_MB */ + uint32_t McSecCarveoutSizeMb; + /* Specifies the value for MC_VIDEO_PROTECT_REG_CTRL. + VIDEO_PROTECT_WRITEAccess */ + uint32_t McVideoProtectWriteAccess; + /* Specifies the value for MC_SEC_CARVEOUT_REG_CTRL. + SEC_CARVEOUT_WRITEAccess */ + uint32_t McSecCarveoutProtectWriteAccess; + + /* Write-Protect Regions (WPR) */ + uint32_t McGeneralizedCarveout1Bom; + uint32_t McGeneralizedCarveout1BomHi; + uint32_t McGeneralizedCarveout1Size128kb; + uint32_t McGeneralizedCarveout1Access0; + uint32_t McGeneralizedCarveout1Access1; + uint32_t McGeneralizedCarveout1Access2; + uint32_t McGeneralizedCarveout1Access3; + uint32_t McGeneralizedCarveout1Access4; + uint32_t McGeneralizedCarveout1ForceInternalAccess0; + uint32_t McGeneralizedCarveout1ForceInternalAccess1; + uint32_t McGeneralizedCarveout1ForceInternalAccess2; + uint32_t McGeneralizedCarveout1ForceInternalAccess3; + uint32_t McGeneralizedCarveout1ForceInternalAccess4; + uint32_t McGeneralizedCarveout1Cfg0; + + uint32_t McGeneralizedCarveout2Bom; + uint32_t McGeneralizedCarveout2BomHi; + uint32_t McGeneralizedCarveout2Size128kb; + uint32_t McGeneralizedCarveout2Access0; + uint32_t McGeneralizedCarveout2Access1; + uint32_t McGeneralizedCarveout2Access2; + uint32_t McGeneralizedCarveout2Access3; + uint32_t McGeneralizedCarveout2Access4; + uint32_t McGeneralizedCarveout2ForceInternalAccess0; + uint32_t McGeneralizedCarveout2ForceInternalAccess1; + uint32_t McGeneralizedCarveout2ForceInternalAccess2; + uint32_t McGeneralizedCarveout2ForceInternalAccess3; + uint32_t McGeneralizedCarveout2ForceInternalAccess4; + uint32_t McGeneralizedCarveout2Cfg0; + + uint32_t McGeneralizedCarveout3Bom; + uint32_t McGeneralizedCarveout3BomHi; + uint32_t McGeneralizedCarveout3Size128kb; + uint32_t McGeneralizedCarveout3Access0; + uint32_t McGeneralizedCarveout3Access1; + uint32_t McGeneralizedCarveout3Access2; + uint32_t McGeneralizedCarveout3Access3; + uint32_t McGeneralizedCarveout3Access4; + uint32_t McGeneralizedCarveout3ForceInternalAccess0; + uint32_t McGeneralizedCarveout3ForceInternalAccess1; + uint32_t McGeneralizedCarveout3ForceInternalAccess2; + uint32_t McGeneralizedCarveout3ForceInternalAccess3; + uint32_t McGeneralizedCarveout3ForceInternalAccess4; + uint32_t McGeneralizedCarveout3Cfg0; + + uint32_t McGeneralizedCarveout4Bom; + uint32_t McGeneralizedCarveout4BomHi; + uint32_t McGeneralizedCarveout4Size128kb; + uint32_t McGeneralizedCarveout4Access0; + uint32_t McGeneralizedCarveout4Access1; + uint32_t McGeneralizedCarveout4Access2; + uint32_t McGeneralizedCarveout4Access3; + uint32_t McGeneralizedCarveout4Access4; + uint32_t McGeneralizedCarveout4ForceInternalAccess0; + uint32_t McGeneralizedCarveout4ForceInternalAccess1; + uint32_t McGeneralizedCarveout4ForceInternalAccess2; + uint32_t McGeneralizedCarveout4ForceInternalAccess3; + uint32_t McGeneralizedCarveout4ForceInternalAccess4; + uint32_t McGeneralizedCarveout4Cfg0; + + uint32_t McGeneralizedCarveout5Bom; + uint32_t McGeneralizedCarveout5BomHi; + uint32_t McGeneralizedCarveout5Size128kb; + uint32_t McGeneralizedCarveout5Access0; + uint32_t McGeneralizedCarveout5Access1; + uint32_t McGeneralizedCarveout5Access2; + uint32_t McGeneralizedCarveout5Access3; + uint32_t McGeneralizedCarveout5Access4; + uint32_t McGeneralizedCarveout5ForceInternalAccess0; + uint32_t McGeneralizedCarveout5ForceInternalAccess1; + uint32_t McGeneralizedCarveout5ForceInternalAccess2; + uint32_t McGeneralizedCarveout5ForceInternalAccess3; + uint32_t McGeneralizedCarveout5ForceInternalAccess4; + uint32_t McGeneralizedCarveout5Cfg0; + + /* Specifies enable for CA training */ + uint32_t EmcCaTrainingEnable; + + /* Set if bit 6 select is greater than bit 7 select; uses aremc. + spec packet SWIZZLE_BIT6_GT_BIT7 */ + uint32_t SwizzleRankByteEncode; + /* Specifies enable and offset for patched boot rom write */ + uint32_t BootRomPatchControl; + /* Specifies data for patched boot rom write */ + uint32_t BootRomPatchData; + + /* Specifies the value for MC_MTS_CARVEOUT_BOM */ + uint32_t McMtsCarveoutBom; + /* Specifies the value for MC_MTS_CARVEOUT_ADR_HI */ + uint32_t McMtsCarveoutAdrHi; + /* Specifies the value for MC_MTS_CARVEOUT_SIZE_MB */ + uint32_t McMtsCarveoutSizeMb; + /* Specifies the value for MC_MTS_CARVEOUT_REG_CTRL */ + uint32_t McMtsCarveoutRegCtrl; + + /* End */ +}; + +check_member(sdram_params, McMtsCarveoutRegCtrl, 0x770); + +#endif /* __SOC_NVIDIA_TEGRA210_SDRAM_PARAM_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/secure_boot.h b/src/soc/nvidia/tegra210/include/soc/secure_boot.h new file mode 100644 index 0000000000..306eac782c --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/secure_boot.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef _TEGRA210_SECURE_BOOT_H_ +#define _TEGRA210_SECURE_BOOT_H_ + +struct tegra_secure_boot { + u32 sb_csr; /* offset 0x00 */ + u32 sb_pirom_start; /* offset 0x04 */ + u32 sb_pfcfg; /* offset 0x08 */ + u32 sb_secure_spare_0; /* offset 0x0c */ + u32 sb_secure_spare_1; /* offset 0x10 */ + u32 sb_secure_spare_2; /* offset 0x14 */ + u32 sb_secure_spare_3; /* offset 0x18 */ + u32 sb_secure_spare_4; /* offset 0x1c */ + u32 sb_secure_spare_5; /* offset 0x20 */ + u32 sb_secure_spare_6; /* offset 0x24 */ + u32 sb_secure_spare_7; /* offset 0x28 */ + u32 rsvd; /* offset 0x2c */ + u32 sb_aa64_reset_low; /* offset 0x30 */ + u32 sb_aa64_reset_high; /* offset 0x3c */ +}; + +#endif /* _TEGRA210_SECURE_BOOT_H_ */ diff --git a/src/soc/nvidia/tegra210/include/soc/sor.h b/src/soc/nvidia/tegra210/include/soc/sor.h new file mode 100644 index 0000000000..5d1d6632bb --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/sor.h @@ -0,0 +1,930 @@ +/* + * drivers/video/tegra/dc/sor_regs.h + * + * Copyright (c) 2011-2015, NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __TEGRA210_SOR_H__ +#define __TEGRA210_SOR_H__ + +#define NV_SOR_SUPER_STATE0 (0x1) +#define NV_SOR_SUPER_STATE0_UPDATE_SHIFT (0) +#define NV_SOR_SUPER_STATE0_UPDATE_DEFAULT_MASK (0x1) +#define NV_SOR_SUPER_STATE1 (0x2) +#define NV_SOR_SUPER_STATE1_ATTACHED_SHIFT (3) +#define NV_SOR_SUPER_STATE1_ATTACHED_NO (0 << 3) +#define NV_SOR_SUPER_STATE1_ATTACHED_YES (1 << 3) +#define NV_SOR_SUPER_STATE1_ASY_ORMODE_SHIFT (2) +#define NV_SOR_SUPER_STATE1_ASY_ORMODE_SAFE (0 << 2) +#define NV_SOR_SUPER_STATE1_ASY_ORMODE_NORMAL (1 << 2) +#define NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SHIFT (0) +#define NV_SOR_SUPER_STATE1_ASY_HEAD_OP_DEFAULT_MASK (0x3) +#define NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SLEEP (0) +#define NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SNOOZE (1) +#define NV_SOR_SUPER_STATE1_ASY_HEAD_OP_AWAKE (2) +#define NV_SOR_STATE0 (0x3) +#define NV_SOR_STATE0_UPDATE_SHIFT (0) +#define NV_SOR_STATE0_UPDATE_DEFAULT_MASK (0x1) +#define NV_SOR_STATE1 (0x4) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_SHIFT (17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_DEFAULT_MASK (0xf << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_16_422 (1 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_18_444 (2 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_20_422 (3 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_24_422 (4 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_24_444 (5 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_30_444 (6 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_32_422 (7 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_36_444 (8 << 17) +#define NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_48_444 (9 << 17) +#define NV_SOR_STATE1_ASY_REPLICATE_SHIFT (15) +#define NV_SOR_STATE1_ASY_REPLICATE_DEFAULT_MASK (0x3 << 15) +#define NV_SOR_STATE1_ASY_REPLICATE_OFF (0 << 15) +#define NV_SOR_STATE1_ASY_REPLICATE_X2 (1 << 15) +#define NV_SOR_STATE1_ASY_REPLICATE_X4 (2 << 15) +#define NV_SOR_STATE1_ASY_DEPOL_SHIFT (14) +#define NV_SOR_STATE1_ASY_DEPOL_DEFAULT_MASK (0x1 << 14) +#define NV_SOR_STATE1_ASY_DEPOL_POSITIVE_TRUE (0 << 14) +#define NV_SOR_STATE1_ASY_DEPOL_NEGATIVE_TRUE (1 << 14) +#define NV_SOR_STATE1_ASY_VSYNCPOL_SHIFT (13) +#define NV_SOR_STATE1_ASY_VSYNCPOL_DEFAULT_MASK (0x1 << 13) +#define NV_SOR_STATE1_ASY_VSYNCPOL_POSITIVE_TRUE (0 << 13) +#define NV_SOR_STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE (1 << 13) +#define NV_SOR_STATE1_ASY_HSYNCPOL_SHIFT (12) +#define NV_SOR_STATE1_ASY_HSYNCPOL_DEFAULT_MASK (0x1 << 12) +#define NV_SOR_STATE1_ASY_HSYNCPOL_POSITIVE_TRUE (0 << 12) +#define NV_SOR_STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE (1 << 12) +#define NV_SOR_STATE1_ASY_PROTOCOL_SHIFT (8) +#define NV_SOR_STATE1_ASY_PROTOCOL_DEFAULT_MASK (0xf << 8) +#define NV_SOR_STATE1_ASY_PROTOCOL_LVDS_CUSTOM (0 << 8) +#define NV_SOR_STATE1_ASY_PROTOCOL_DP_A (8 << 8) +#define NV_SOR_STATE1_ASY_PROTOCOL_DP_B (9 << 8) +#define NV_SOR_STATE1_ASY_PROTOCOL_CUSTOM (15 << 8) +#define NV_SOR_STATE1_ASY_CRCMODE_SHIFT (6) +#define NV_SOR_STATE1_ASY_CRCMODE_DEFAULT_MASK (0x3 << 6) +#define NV_SOR_STATE1_ASY_CRCMODE_ACTIVE_RASTER (0 << 6) +#define NV_SOR_STATE1_ASY_CRCMODE_COMPLETE_RASTER (1 << 6) +#define NV_SOR_STATE1_ASY_CRCMODE_NON_ACTIVE_RASTER (2 << 6) +#define NV_SOR_STATE1_ASY_SUBOWNER_SHIFT (4) +#define NV_SOR_STATE1_ASY_SUBOWNER_DEFAULT_MASK (0x3 << 4) +#define NV_SOR_STATE1_ASY_SUBOWNER_NONE (0 << 4) +#define NV_SOR_STATE1_ASY_SUBOWNER_SUBHEAD0 (1 << 4) +#define NV_SOR_STATE1_ASY_SUBOWNER_SUBHEAD1 (2 << 4) +#define NV_SOR_STATE1_ASY_SUBOWNER_BOTH (3 << 4) +#define NV_SOR_STATE1_ASY_OWNER_SHIFT (0) +#define NV_SOR_STATE1_ASY_OWNER_DEFAULT_MASK (0xf) +#define NV_SOR_STATE1_ASY_OWNER_NONE (0) +#define NV_SOR_STATE1_ASY_OWNER_HEAD0 (1) +#define NV_SOR_STATE1_ASY_OWNER_HEAD1 (2) +#define NV_HEAD_STATE0(i) (0x5) +#define NV_HEAD_STATE0_INTERLACED_SHIFT (4) +#define NV_HEAD_STATE0_INTERLACED_DEFAULT_MASK (0x3 << 4) +#define NV_HEAD_STATE0_INTERLACED_PROGRESSIVE (0 << 4) +#define NV_HEAD_STATE0_INTERLACED_INTERLACED (1 << 4) +#define NV_HEAD_STATE0_RANGECOMPRESS_SHIFT (3) +#define NV_HEAD_STATE0_RANGECOMPRESS_DEFAULT_MASK (0x1 << 3) +#define NV_HEAD_STATE0_RANGECOMPRESS_DISABLE (0 << 3) +#define NV_HEAD_STATE0_RANGECOMPRESS_ENABLE (1 << 3) +#define NV_HEAD_STATE0_DYNRANGE_SHIFT (2) +#define NV_HEAD_STATE0_DYNRANGE_DEFAULT_MASK (0x1 << 2) +#define NV_HEAD_STATE0_DYNRANGE_VESA (0 << 2) +#define NV_HEAD_STATE0_DYNRANGE_CEA (1 << 2) +#define NV_HEAD_STATE0_COLORSPACE_SHIFT (0) +#define NV_HEAD_STATE0_COLORSPACE_DEFAULT_MASK (0x3) +#define NV_HEAD_STATE0_COLORSPACE_RGB (0) +#define NV_HEAD_STATE0_COLORSPACE_YUV_601 (1) +#define NV_HEAD_STATE0_COLORSPACE_YUV_709 (2) +#define NV_HEAD_STATE1(i) (0x7 + i) +#define NV_HEAD_STATE1_VTOTAL_SHIFT (16) +#define NV_HEAD_STATE1_VTOTAL_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE1_HTOTAL_SHIFT (0) +#define NV_HEAD_STATE1_HTOTAL_DEFAULT_MASK (0x7fff) +#define NV_HEAD_STATE2(i) (0x9 + i) +#define NV_HEAD_STATE2_VSYNC_END_SHIFT (16) +#define NV_HEAD_STATE2_VSYNC_END_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE2_HSYNC_END_SHIFT (0) +#define NV_HEAD_STATE2_HSYNC_END_DEFAULT_MASK (0x7fff) +#define NV_HEAD_STATE3(i) (0xb + i) +#define NV_HEAD_STATE3_VBLANK_END_SHIFT (16) +#define NV_HEAD_STATE3_VBLANK_END_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE3_HBLANK_END_SHIFT (0) +#define NV_HEAD_STATE3_HBLANK_END_DEFAULT_MASK (0x7fff) +#define NV_HEAD_STATE4(i) (0xd + i) +#define NV_HEAD_STATE4_VBLANK_START_SHIFT (16) +#define NV_HEAD_STATE4_VBLANK_START_DEFAULT_MASK (0x7fff << 16) +#define NV_HEAD_STATE4_HBLANK_START_SHIFT (0) +#define NV_HEAD_STATE4_HBLANK_START_DEFAULT_MASK (0x7fff) +#define NV_HEAD_STATE5(i) (0xf + i) +#define NV_SOR_CRC_CNTRL (0x11) +#define NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_SHIFT (0) +#define NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_NO (0) +#define NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_YES (1) +#define NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_DIS (0) +#define NV_SOR_CRC_CNTRL_ARM_CRC_ENABLE_EN (1) +#define NV_SOR_CLK_CNTRL (0x13) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_SHIFT (0) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_MASK (0x3) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK (0) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK (1) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK (2) +#define NV_SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK (3) +#define NV_SOR_CLK_CNTRL_DP_LINK_SPEED_SHIFT (2) +#define NV_SOR_CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2) +#define NV_SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 (6 << 2) +#define NV_SOR_CLK_CNTRL_DP_LINK_SPEED_G2_7 (10 << 2) +#define NV_SOR_CLK_CNTRL_DP_LINK_SPEED_LVDS (7 << 2) +#define NV_SOR_CAP (0x14) +#define NV_SOR_CAP_DP_A_SHIFT (24) +#define NV_SOR_CAP_DP_A_DEFAULT_MASK (0x1 << 24) +#define NV_SOR_CAP_DP_A_FALSE (0 << 24) +#define NV_SOR_CAP_DP_A_TRUE (1 << 24) +#define NV_SOR_CAP_DP_B_SHIFT (25) +#define NV_SOR_CAP_DP_B_DEFAULT_MASK (0x1 << 24) +#define NV_SOR_CAP_DP_B_FALSE (0 << 24) +#define NV_SOR_CAP_DP_B_TRUE (1 << 24) +#define NV_SOR_PWR (0x15) +#define NV_SOR_PWR_SETTING_NEW_SHIFT (31) +#define NV_SOR_PWR_SETTING_NEW_DEFAULT_MASK (0x1 << 31) +#define NV_SOR_PWR_SETTING_NEW_DONE (0 << 31) +#define NV_SOR_PWR_SETTING_NEW_PENDING (1 << 31) +#define NV_SOR_PWR_SETTING_NEW_TRIGGER (1 << 31) +#define NV_SOR_PWR_MODE_SHIFT (28) +#define NV_SOR_PWR_MODE_DEFAULT_MASK (0x1 << 28) +#define NV_SOR_PWR_MODE_NORMAL (0 << 28) +#define NV_SOR_PWR_MODE_SAFE (1 << 28) +#define NV_SOR_PWR_HALT_DELAY_SHIFT (24) +#define NV_SOR_PWR_HALT_DELAY_DEFAULT_MASK (0x1 << 24) +#define NV_SOR_PWR_HALT_DELAY_DONE (0 << 24) +#define NV_SOR_PWR_HALT_DELAY_ACTIVE (1 << 24) +#define NV_SOR_PWR_SAFE_START_SHIFT (17) +#define NV_SOR_PWR_SAFE_START_DEFAULT_MASK (0x1 << 17) +#define NV_SOR_PWR_SAFE_START_NORMAL (0 << 17) +#define NV_SOR_PWR_SAFE_START_ALT (1 << 17) +#define NV_SOR_PWR_SAFE_STATE_SHIFT (16) +#define NV_SOR_PWR_SAFE_STATE_DEFAULT_MASK (0x1 << 16) +#define NV_SOR_PWR_SAFE_STATE_PD (0 << 16) +#define NV_SOR_PWR_SAFE_STATE_PU (1 << 16) +#define NV_SOR_PWR_NORMAL_START_SHIFT (1) +#define NV_SOR_PWR_NORMAL_START_DEFAULT_MASK (0x1 << 1) +#define NV_SOR_PWR_NORMAL_START_NORMAL (0 << 16) +#define NV_SOR_PWR_NORMAL_START_ALT (1 << 16) +#define NV_SOR_PWR_NORMAL_STATE_SHIFT (0) +#define NV_SOR_PWR_NORMAL_STATE_DEFAULT_MASK (0x1) +#define NV_SOR_PWR_NORMAL_STATE_PD (0) +#define NV_SOR_PWR_NORMAL_STATE_PU (1) +#define NV_SOR_TEST (0x16) +#define NV_SOR_TEST_TESTMUX_SHIFT (24) +#define NV_SOR_TEST_TESTMUX_DEFAULT_MASK (0xff << 24) +#define NV_SOR_TEST_TESTMUX_AVSS (0 << 24) +#define NV_SOR_TEST_TESTMUX_CLOCKIN (2 << 24) +#define NV_SOR_TEST_TESTMUX_PLL_VOL (4 << 24) +#define NV_SOR_TEST_TESTMUX_SLOWCLKINT (8 << 24) +#define NV_SOR_TEST_TESTMUX_AVDD (16 << 24) +#define NV_SOR_TEST_TESTMUX_VDDREG (32 << 24) +#define NV_SOR_TEST_TESTMUX_REGREF_VDDREG (64 << 24) +#define NV_SOR_TEST_TESTMUX_REGREF_AVDD (128 << 24) +#define NV_SOR_TEST_CRC_SHIFT (23) +#define NV_SOR_TEST_CRC_PRE_SERIALIZE (0 << 23) +#define NV_SOR_TEST_CRC_POST_DESERIALIZE (1 << 23) +#define NV_SOR_TEST_TPAT_SHIFT (20) +#define NV_SOR_TEST_TPAT_DEFAULT_MASK (0x7 << 20) +#define NV_SOR_TEST_TPAT_LO (0 << 20) +#define NV_SOR_TEST_TPAT_TDAT (1 << 20) +#define NV_SOR_TEST_TPAT_RAMP (2 << 20) +#define NV_SOR_TEST_TPAT_WALK (3 << 20) +#define NV_SOR_TEST_TPAT_MAXSTEP (4 << 20) +#define NV_SOR_TEST_TPAT_MINSTEP (5 << 20) +#define NV_SOR_TEST_DSRC_SHIFT (16) +#define NV_SOR_TEST_DSRC_DEFAULT_MASK (0x3 << 16) +#define NV_SOR_TEST_DSRC_NORMAL (0 << 16) +#define NV_SOR_TEST_DSRC_DEBUG (1 << 16) +#define NV_SOR_TEST_DSRC_TGEN (2 << 16) +#define NV_SOR_TEST_HEAD_NUMBER_SHIFT (12) +#define NV_SOR_TEST_HEAD_NUMBER_DEFAULT_MASK (0x3 << 12) +#define NV_SOR_TEST_HEAD_NUMBER_NONE (0 << 12) +#define NV_SOR_TEST_HEAD_NUMBER_HEAD0 (1 << 12) +#define NV_SOR_TEST_HEAD_NUMBER_HEAD1 (2 << 12) +#define NV_SOR_TEST_ATTACHED_SHIFT (10) +#define NV_SOR_TEST_ATTACHED_DEFAULT_MASK (0x1 << 10) +#define NV_SOR_TEST_ATTACHED_FALSE (0 << 10) +#define NV_SOR_TEST_ATTACHED_TRUE (1 << 10) +#define NV_SOR_TEST_ACT_HEAD_OPMODE_SHIFT (8) +#define NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK (0x3 << 8) +#define NV_SOR_TEST_ACT_HEAD_OPMODE_SLEEP (0 << 8) +#define NV_SOR_TEST_ACT_HEAD_OPMODE_SNOOZE (1 << 8) +#define NV_SOR_TEST_ACT_HEAD_OPMODE_AWAKE (2 << 8) +#define NV_SOR_TEST_INVD_SHIFT (6) +#define NV_SOR_TEST_INVD_DISABLE (0 << 6) +#define NV_SOR_TEST_INVD_ENABLE (1 << 6) +#define NV_SOR_TEST_TEST_ENABLE_SHIFT (1) +#define NV_SOR_TEST_TEST_ENABLE_DISABLE (0 << 1) +#define NV_SOR_TEST_TEST_ENABLE_ENABLE (1 << 1) +#define NV_SOR_PLL0 (0x17) +#define NV_SOR_PLL0_ICHPMP_SHFIT (24) +#define NV_SOR_PLL0_ICHPMP_DEFAULT_MASK (0xf << 24) +#define NV_SOR_PLL0_VCOCAP_SHIFT (8) +#define NV_SOR_PLL0_VCOCAP_DEFAULT_MASK (0xf << 8) +#define NV_SOR_PLL0_PLLREG_LEVEL_SHIFT (6) +#define NV_SOR_PLL0_PLLREG_LEVEL_DEFAULT_MASK (0x3 << 6) +#define NV_SOR_PLL0_PLLREG_LEVEL_V25 (0 << 6) +#define NV_SOR_PLL0_PLLREG_LEVEL_V15 (1 << 6) +#define NV_SOR_PLL0_PLLREG_LEVEL_V35 (2 << 6) +#define NV_SOR_PLL0_PLLREG_LEVEL_V45 (3 << 6) +#define NV_SOR_PLL0_PULLDOWN_SHIFT (5) +#define NV_SOR_PLL0_PULLDOWN_DEFAULT_MASK (0x1 << 5) +#define NV_SOR_PLL0_PULLDOWN_DISABLE (0 << 5) +#define NV_SOR_PLL0_PULLDOWN_ENABLE (1 << 5) +#define NV_SOR_PLL0_RESISTORSEL_SHIFT (4) +#define NV_SOR_PLL0_RESISTORSEL_DEFAULT_MASK (0x1 << 4) +#define NV_SOR_PLL0_RESISTORSEL_INT (0 << 4) +#define NV_SOR_PLL0_RESISTORSEL_EXT (1 << 4) +#define NV_SOR_PLL0_VCOPD_SHIFT (2) +#define NV_SOR_PLL0_VCOPD_MASK (1 << 2) +#define NV_SOR_PLL0_VCOPD_RESCIND (0 << 2) +#define NV_SOR_PLL0_VCOPD_ASSERT (1 << 2) +#define NV_SOR_PLL0_PWR_SHIFT (0) +#define NV_SOR_PLL0_PWR_MASK (1) +#define NV_SOR_PLL0_PWR_ON (0) +#define NV_SOR_PLL0_PWR_OFF (1) +#define NV_SOR_PLL1_TMDS_TERM_SHIFT (8) +#define NV_SOR_PLL1_TMDS_TERM_DISABLE (0 << 8) +#define NV_SOR_PLL1_TMDS_TERM_ENABLE (1 << 8) +#define NV_SOR_PLL1 (0x18) +#define NV_SOR_PLL1_TERM_COMPOUT_SHIFT (15) +#define NV_SOR_PLL1_TERM_COMPOUT_LOW (0 << 15) +#define NV_SOR_PLL1_TERM_COMPOUT_HIGH (1 << 15) +#define NV_SOR_PLL2 (0x19) +#define NV_SOR_PLL2_DCIR_PLL_RESET_SHIFT (0) +#define NV_SOR_PLL2_DCIR_PLL_RESET_OVERRIDE (0 << 0) +#define NV_SOR_PLL2_DCIR_PLL_RESET_ALLOW (1 << 0) +#define NV_SOR_PLL2_AUX1_SHIFT (17) +#define NV_SOR_PLL2_AUX1_SEQ_MASK (1 << 17) +#define NV_SOR_PLL2_AUX1_SEQ_PLLCAPPD_ALLOW (0 << 17) +#define NV_SOR_PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE (1 << 17) +#define NV_SOR_PLL2_AUX2_SHIFT (18) +#define NV_SOR_PLL2_AUX2_MASK (1 << 18) +#define NV_SOR_PLL2_AUX2_OVERRIDE_POWERDOWN (0 << 18) +#define NV_SOR_PLL2_AUX2_ALLOW_POWERDOWN (1 << 18) +#define NV_SOR_PLL2_AUX6_SHIFT (22) +#define NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK (1 << 22) +#define NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE (0 << 22) +#define NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE (1 << 22) +#define NV_SOR_PLL2_AUX7_SHIFT (23) +#define NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK (1 << 23) +#define NV_SOR_PLL2_AUX7_PORT_POWERDOWN_DISABLE (0 << 23) +#define NV_SOR_PLL2_AUX7_PORT_POWERDOWN_ENABLE (1 << 23) +#define NV_SOR_PLL2_AUX8_SHIFT (24) +#define NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK (1 << 24) +#define NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE (0 << 24) +#define NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE (1 << 24) +#define NV_SOR_PLL2_AUX9_SHIFT (25) +#define NV_SOR_PLL2_AUX9_LVDSEN_ALLOW (0 << 25) +#define NV_SOR_PLL2_AUX9_LVDSEN_OVERRIDE (1 << 25) +#define NV_SOR_PLL3 (0x1a) +#define NV_SOR_PLL3_PLLVDD_MODE_SHIFT (13) +#define NV_SOR_PLL3_PLLVDD_MODE_MASK (1 << 13) +#define NV_SOR_PLL3_PLLVDD_MODE_V1_8 (0 << 13) +#define NV_SOR_PLL3_PLLVDD_MODE_V3_3 (1 << 13) +#define NV_SOR_CSTM (0x1b) +#define NV_SOR_CSTM_ROTDAT_SHIFT (28) +#define NV_SOR_CSTM_ROTDAT_DEFAULT_MASK (0x7 << 28) +#define NV_SOR_CSTM_ROTCLK_SHIFT (24) +#define NV_SOR_CSTM_ROTCLK_DEFAULT_MASK (0xf << 24) +#define NV_SOR_CSTM_LVDS_EN_SHIFT (16) +#define NV_SOR_CSTM_LVDS_EN_DISABLE (0 << 16) +#define NV_SOR_CSTM_LVDS_EN_ENABLE (1 << 16) +#define NV_SOR_CSTM_LINKACTB_SHIFT (15) +#define NV_SOR_CSTM_LINKACTB_DISABLE (0 << 15) +#define NV_SOR_CSTM_LINKACTB_ENABLE (1 << 15) +#define NV_SOR_CSTM_LINKACTA_SHIFT (14) +#define NV_SOR_CSTM_LINKACTA_DISABLE (0 << 14) +#define NV_SOR_CSTM_LINKACTA_ENABLE (1 << 14) +#define NV_SOR_LVDS (0x1c) +#define NV_SOR_LVDS_ROTDAT_SHIFT (28) +#define NV_SOR_LVDS_ROTDAT_DEFAULT_MASK (0x7 << 28) +#define NV_SOR_LVDS_ROTDAT_RST (0 << 28) +#define NV_SOR_LVDS_ROTCLK_SHIFT (24) +#define NV_SOR_LVDS_ROTCLK_DEFAULT_MASK (0xf << 24) +#define NV_SOR_LVDS_ROTCLK_RST (0 << 24) +#define NV_SOR_LVDS_PLLDIV_SHIFT (21) +#define NV_SOR_LVDS_PLLDIV_DEFAULT_MASK (0x1 << 21) +#define NV_SOR_LVDS_PLLDIV_BY_7 (0 << 21) +#define NV_SOR_LVDS_BALANCED_SHIFT (19) +#define NV_SOR_LVDS_BALANCED_DEFAULT_MASK (0x1 << 19) +#define NV_SOR_LVDS_BALANCED_DISABLE (0 << 19) +#define NV_SOR_LVDS_BALANCED_ENABLE (1 << 19) +#define NV_SOR_LVDS_NEW_MODE_SHIFT (18) +#define NV_SOR_LVDS_NEW_MODE_DEFAULT_MASK (0x1 << 18) +#define NV_SOR_LVDS_NEW_MODE_DISABLE (0 << 18) +#define NV_SOR_LVDS_NEW_MODE_ENABLE (1 << 18) +#define NV_SOR_LVDS_DUP_SYNC_SHIFT (17) +#define NV_SOR_LVDS_DUP_SYNC_DEFAULT_MASK (0x1 << 17) +#define NV_SOR_LVDS_DUP_SYNC_DISABLE (0 << 17) +#define NV_SOR_LVDS_DUP_SYNC_ENABLE (1 << 17) +#define NV_SOR_LVDS_LVDS_EN_SHIFT (16) +#define NV_SOR_LVDS_LVDS_EN_DEFAULT_MASK (0x1 << 16) +#define NV_SOR_LVDS_LVDS_EN_ENABLE (1 << 16) +#define NV_SOR_LVDS_LINKACTB_SHIFT (15) +#define NV_SOR_LVDS_LINKACTB_DEFAULT_MASK (0x1 << 15) +#define NV_SOR_LVDS_LINKACTB_DISABLE (0 << 15) +#define NV_SOR_LVDS_LINKACTB_ENABLE (1 << 15) +#define NV_SOR_LVDS_LINKACTA_SHIFT (14) +#define NV_SOR_LVDS_LINKACTA_DEFAULT_MASK (0x1 << 14) +#define NV_SOR_LVDS_LINKACTA_ENABLE (1 << 14) +#define NV_SOR_LVDS_MODE_SHIFT (12) +#define NV_SOR_LVDS_MODE_DEFAULT_MASK (0x3 << 12) +#define NV_SOR_LVDS_MODE_LVDS (0 << 12) +#define NV_SOR_LVDS_UPPER_SHIFT (11) +#define NV_SOR_LVDS_UPPER_DEFAULT_MASK (0x1 << 11) +#define NV_SOR_LVDS_UPPER_FALSE (0 << 11) +#define NV_SOR_LVDS_UPPER_TRUE (1 << 11) +#define NV_SOR_LVDS_PD_TXCB_SHIFT (9) +#define NV_SOR_LVDS_PD_TXCB_DEFAULT_MASK (0x1 << 9) +#define NV_SOR_LVDS_PD_TXCB_ENABLE (0 << 9) +#define NV_SOR_LVDS_PD_TXCB_DISABLE (1 << 9) +#define NV_SOR_LVDS_PD_TXCA_SHIFT (8) +#define NV_SOR_LVDS_PD_TXCA_DEFAULT_MASK (0x1 << 8) +#define NV_SOR_LVDS_PD_TXCA_ENABLE (0 << 8) +#define NV_SOR_LVDS_PD_TXDB_3_SHIFT (7) +#define NV_SOR_LVDS_PD_TXDB_3_DEFAULT_MASK (0x1 << 7) +#define NV_SOR_LVDS_PD_TXDB_3_ENABLE (0 << 7) +#define NV_SOR_LVDS_PD_TXDB_3_DISABLE (1 << 7) +#define NV_SOR_LVDS_PD_TXDB_2_SHIFT (6) +#define NV_SOR_LVDS_PD_TXDB_2_DEFAULT_MASK (0x1 << 6) +#define NV_SOR_LVDS_PD_TXDB_2_ENABLE (0 << 6) +#define NV_SOR_LVDS_PD_TXDB_2_DISABLE (1 << 6) +#define NV_SOR_LVDS_PD_TXDB_1_SHIFT (5) +#define NV_SOR_LVDS_PD_TXDB_1_DEFAULT_MASK (0x1 << 5) +#define NV_SOR_LVDS_PD_TXDB_1_ENABLE (0 << 5) +#define NV_SOR_LVDS_PD_TXDB_1_DISABLE (1 << 5) +#define NV_SOR_LVDS_PD_TXDB_0_SHIFT (4) +#define NV_SOR_LVDS_PD_TXDB_0_DEFAULT_MASK (0x1 << 4) +#define NV_SOR_LVDS_PD_TXDB_0_ENABLE (0 << 4) +#define NV_SOR_LVDS_PD_TXDB_0_DISABLE (1 << 4) +#define NV_SOR_LVDS_PD_TXDA_3_SHIFT (3) +#define NV_SOR_LVDS_PD_TXDA_3_DEFAULT_MASK (0x1 << 3) +#define NV_SOR_LVDS_PD_TXDA_3_ENABLE (0 << 3) +#define NV_SOR_LVDS_PD_TXDA_3_DISABLE (1 << 3) +#define NV_SOR_LVDS_PD_TXDA_2_SHIFT (2) +#define NV_SOR_LVDS_PD_TXDA_2_DEFAULT_MASK (0x1 << 2) +#define NV_SOR_LVDS_PD_TXDA_2_ENABLE (0 << 2) +#define NV_SOR_LVDS_PD_TXDA_1_SHIFT (1) +#define NV_SOR_LVDS_PD_TXDA_1_DEFAULT_MASK (0x1 << 1) +#define NV_SOR_LVDS_PD_TXDA_1_ENABLE (0 << 1) +#define NV_SOR_LVDS_PD_TXDA_0_SHIFT (0) +#define NV_SOR_LVDS_PD_TXDA_0_DEFAULT_MASK (0x1) +#define NV_SOR_LVDS_PD_TXDA_0_ENABLE (0) +#define NV_SOR_CRCA (0x1d) +#define NV_SOR_CRCA_VALID_FALSE (0) +#define NV_SOR_CRCA_VALID_TRUE (1) +#define NV_SOR_CRCA_VALID_RST (1) +#define NV_SOR_CRCB (0x1e) +#define NV_SOR_CRCB_CRC_DEFAULT_MASK (0xffffffff) +#define NV_SOR_SEQ_CTL (0x20) +#define NV_SOR_SEQ_CTL_SWITCH_SHIFT (30) +#define NV_SOR_SEQ_CTL_SWITCH_MASK (0x1 << 30) +#define NV_SOR_SEQ_CTL_SWITCH_WAIT (0 << 30) +#define NV_SOR_SEQ_CTL_SWITCH_FORCE (1 << 30) +#define NV_SOR_SEQ_CTL_STATUS_SHIFT (28) +#define NV_SOR_SEQ_CTL_STATUS_MASK (0x1 << 28) +#define NV_SOR_SEQ_CTL_STATUS_STOPPED (0 << 28) +#define NV_SOR_SEQ_CTL_STATUS_RUNNING (1 << 28) +#define NV_SOR_SEQ_CTL_PC_SHIFT (16) +#define NV_SOR_SEQ_CTL_PC_MASK (0xf << 16) +#define NV_SOR_SEQ_CTL_PD_PC_ALT_SHIFT (12) +#define NV_SOR_SEQ_CTL_PD_PC_ALT_MASK (0xf << 12) +#define NV_SOR_SEQ_CTL_PD_PC_SHIFT (8) +#define NV_SOR_SEQ_CTL_PD_PC_MASK (0xf << 8) +#define NV_SOR_SEQ_CTL_PU_PC_ALT_SHIFT (4) +#define NV_SOR_SEQ_CTL_PU_PC_ALT_MASK (0xf << 4) +#define NV_SOR_SEQ_CTL_PU_PC_SHIFT (0) +#define NV_SOR_SEQ_CTL_PU_PC_MASK (0xf) +#define NV_SOR_LANE_SEQ_CTL (0x21) +#define NV_SOR_LANE_SEQ_CTL_SETTING_NEW_SHIFT (31) +#define NV_SOR_LANE_SEQ_CTL_SETTING_MASK (1 << 31) +#define NV_SOR_LANE_SEQ_CTL_SETTING_NEW_DONE (0 << 31) +#define NV_SOR_LANE_SEQ_CTL_SETTING_NEW_PENDING (1 << 31) +#define NV_SOR_LANE_SEQ_CTL_SETTING_NEW_TRIGGER (1 << 31) +#define NV_SOR_LANE_SEQ_CTL_SEQ_STATE_SHIFT (28) +#define NV_SOR_LANE_SEQ_CTL_SEQ_STATE_IDLE (0 << 28) +#define NV_SOR_LANE_SEQ_CTL_SEQ_STATE_BUSY (1 << 28) +#define NV_SOR_LANE_SEQ_CTL_SEQUENCE_SHIFT (20) +#define NV_SOR_LANE_SEQ_CTL_SEQUENCE_UP (0 << 20) +#define NV_SOR_LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20) +#define NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_SHIFT (16) +#define NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PU (0 << 16) +#define NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PD (1 << 16) +#define NV_SOR_LANE_SEQ_CTL_DELAY_SHIFT (12) +#define NV_SOR_LANE_SEQ_CTL_DELAY_DEFAULT_MASK (0xf << 12) +#define NV_SOR_LANE_SEQ_CTL_LANE9_STATE_SHIFT (9) +#define NV_SOR_LANE_SEQ_CTL_LANE9_STATE_POWERUP (0 << 9) +#define NV_SOR_LANE_SEQ_CTL_LANE9_STATE_POWERDOWN (1 << 9) +#define NV_SOR_LANE_SEQ_CTL_LANE8_STATE_SHIFT (8) +#define NV_SOR_LANE_SEQ_CTL_LANE8_STATE_POWERUP (0 << 8) +#define NV_SOR_LANE_SEQ_CTL_LANE8_STATE_POWERDOWN (1 << 8) +#define NV_SOR_LANE_SEQ_CTL_LANE7_STATE_SHIFT (7) +#define NV_SOR_LANE_SEQ_CTL_LANE7_STATE_POWERUP (0 << 7) +#define NV_SOR_LANE_SEQ_CTL_LANE7_STATE_POWERDOWN (1 << 7) +#define NV_SOR_LANE_SEQ_CTL_LANE6_STATE_SHIFT (6) +#define NV_SOR_LANE_SEQ_CTL_LANE6_STATE_POWERUP (0 << 6) +#define NV_SOR_LANE_SEQ_CTL_LANE6_STATE_POWERDOWN (1 << 6) +#define NV_SOR_LANE_SEQ_CTL_LANE5_STATE_SHIFT (5) +#define NV_SOR_LANE_SEQ_CTL_LANE5_STATE_POWERUP (0 << 5) +#define NV_SOR_LANE_SEQ_CTL_LANE5_STATE_POWERDOWN (1 << 5) +#define NV_SOR_LANE_SEQ_CTL_LANE4_STATE_SHIFT (4) +#define NV_SOR_LANE_SEQ_CTL_LANE4_STATE_POWERUP (0 << 4) +#define NV_SOR_LANE_SEQ_CTL_LANE4_STATE_POWERDOWN (1 << 4) +#define NV_SOR_LANE_SEQ_CTL_LANE3_STATE_SHIFT (3) +#define NV_SOR_LANE_SEQ_CTL_LANE3_STATE_POWERUP (0 << 3) +#define NV_SOR_LANE_SEQ_CTL_LANE3_STATE_POWERDOWN (1 << 3) +#define NV_SOR_LANE_SEQ_CTL_LANE2_STATE_SHIFT (2) +#define NV_SOR_LANE_SEQ_CTL_LANE2_STATE_POWERUP (0 << 2) +#define NV_SOR_LANE_SEQ_CTL_LANE2_STATE_POWERDOWN (1 << 2) +#define NV_SOR_LANE_SEQ_CTL_LANE1_STATE_SHIFT (1) +#define NV_SOR_LANE_SEQ_CTL_LANE1_STATE_POWERUP (0 << 1) +#define NV_SOR_LANE_SEQ_CTL_LANE1_STATE_POWERDOWN (1 << 1) +#define NV_SOR_LANE_SEQ_CTL_LANE0_STATE_SHIFT (0) +#define NV_SOR_LANE_SEQ_CTL_LANE0_STATE_POWERUP (0) +#define NV_SOR_LANE_SEQ_CTL_LANE0_STATE_POWERDOWN (1) +#define NV_SOR_SEQ_INST(i) (0x22 + i) +#define NV_SOR_SEQ_INST_PLL_PULLDOWN_SHIFT (31) +#define NV_SOR_SEQ_INST_PLL_PULLDOWN_DISABLE (0 << 31) +#define NV_SOR_SEQ_INST_PLL_PULLDOWN_ENABLE (1 << 31) +#define NV_SOR_SEQ_INST_POWERDOWN_MACRO_SHIFT (30) +#define NV_SOR_SEQ_INST_POWERDOWN_MACRO_NORMAL (0 << 30) +#define NV_SOR_SEQ_INST_POWERDOWN_MACRO_POWERDOWN (1 << 30) +#define NV_SOR_SEQ_INST_ASSERT_PLL_RESET_SHIFT (29) +#define NV_SOR_SEQ_INST_ASSERT_PLL_RESET_NORMAL (0 << 29) +#define NV_SOR_SEQ_INST_ASSERT_PLL_RESET_RST (1 << 29) +#define NV_SOR_SEQ_INST_BLANK_V_SHIFT (28) +#define NV_SOR_SEQ_INST_BLANK_V_NORMAL (0 << 28) +#define NV_SOR_SEQ_INST_BLANK_V_INACTIVE (1 << 28) +#define NV_SOR_SEQ_INST_BLANK_H_SHIFT (27) +#define NV_SOR_SEQ_INST_BLANK_H_NORMAL (0 << 27) +#define NV_SOR_SEQ_INST_BLANK_H_INACTIVE (1 << 27) +#define NV_SOR_SEQ_INST_BLANK_DE_SHIFT (26) +#define NV_SOR_SEQ_INST_BLANK_DE_NORMAL (0 << 26) +#define NV_SOR_SEQ_INST_BLANK_DE_INACTIVE (1 << 26) +#define NV_SOR_SEQ_INST_BLACK_DATA_SHIFT (25) +#define NV_SOR_SEQ_INST_BLACK_DATA_NORMAL (0 << 25) +#define NV_SOR_SEQ_INST_BLACK_DATA_BLACK (1 << 25) +#define NV_SOR_SEQ_INST_TRISTATE_IOS_SHIFT (24) +#define NV_SOR_SEQ_INST_TRISTATE_IOS_ENABLE_PINS (0 << 24) +#define NV_SOR_SEQ_INST_TRISTATE_IOS_TRISTATE (1 << 24) +#define NV_SOR_SEQ_INST_DRIVE_PWM_OUT_LO_SHIFT (23) +#define NV_SOR_SEQ_INST_DRIVE_PWM_OUT_LO_FALSE (0 << 23) +#define NV_SOR_SEQ_INST_DRIVE_PWM_OUT_LO_TRUE (1 << 23) +#define NV_SOR_SEQ_INST_PIN_B_SHIFT (22) +#define NV_SOR_SEQ_INST_PIN_B_LOW (0 << 22) +#define NV_SOR_SEQ_INST_PIN_B_HIGH (1 << 22) +#define NV_SOR_SEQ_INST_PIN_A_SHIFT (21) +#define NV_SOR_SEQ_INST_PIN_A_LOW (0 << 21) +#define NV_SOR_SEQ_INST_PIN_A_HIGH (1 << 21) +#define NV_SOR_SEQ_INST_SEQUENCE_SHIFT (19) +#define NV_SOR_SEQ_INST_SEQUENCE_UP (0 << 19) +#define NV_SOR_SEQ_INST_SEQUENCE_DOWN (1 << 19) +#define NV_SOR_SEQ_INST_LANE_SEQ_SHIFT (18) +#define NV_SOR_SEQ_INST_LANE_SEQ_STOP (0 << 18) +#define NV_SOR_SEQ_INST_LANE_SEQ_RUN (1 << 18) +#define NV_SOR_SEQ_INST_PDPORT_SHIFT (17) +#define NV_SOR_SEQ_INST_PDPORT_NO (0 << 17) +#define NV_SOR_SEQ_INST_PDPORT_YES (1 << 17) +#define NV_SOR_SEQ_INST_PDPLL_SHIFT (16) +#define NV_SOR_SEQ_INST_PDPLL_NO (0 << 16) +#define NV_SOR_SEQ_INST_PDPLL_YES (1 << 16) +#define NV_SOR_SEQ_INST_HALT_SHIFT (15) +#define NV_SOR_SEQ_INST_HALT_FALSE (0 << 15) +#define NV_SOR_SEQ_INST_HALT_TRUE (1 << 15) +#define NV_SOR_SEQ_INST_WAIT_UNITS_SHIFT (12) +#define NV_SOR_SEQ_INST_WAIT_UNITS_DEFAULT_MASK (0x3 << 12) +#define NV_SOR_SEQ_INST_WAIT_UNITS_US (0 << 12) +#define NV_SOR_SEQ_INST_WAIT_UNITS_MS (1 << 12) +#define NV_SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) +#define NV_SOR_SEQ_INST_WAIT_TIME_SHIFT (0) +#define NV_SOR_SEQ_INST_WAIT_TIME_DEFAULT_MASK (0x3ff) +#define NV_SOR_PWM_DIV (0x32) +#define NV_SOR_PWM_DIV_DIVIDE_DEFAULT_MASK (0xffffff) +#define NV_SOR_PWM_CTL (0x33) +#define NV_SOR_PWM_CTL_SETTING_NEW_SHIFT (31) +#define NV_SOR_PWM_CTL_SETTING_NEW_DONE (0 << 31) +#define NV_SOR_PWM_CTL_SETTING_NEW_PENDING (1 << 31) +#define NV_SOR_PWM_CTL_SETTING_NEW_TRIGGER (1 << 31) +#define NV_SOR_PWM_CTL_CLKSEL_SHIFT (30) +#define NV_SOR_PWM_CTL_CLKSEL_PCLK (0 << 30) +#define NV_SOR_PWM_CTL_CLKSEL_XTAL (1 << 30) +#define NV_SOR_PWM_CTL_DUTY_CYCLE_SHIFT (0) +#define NV_SOR_PWM_CTL_DUTY_CYCLE_MASK (0xffffff) +#define NV_SOR_MSCHECK (0x49) +#define NV_SOR_MSCHECK_CTL_SHIFT (31) +#define NV_SOR_MSCHECK_CTL_CLEAR (0 << 31) +#define NV_SOR_MSCHECK_CTL_RUN (1 << 31) +#define NV_SOR_XBAR_CTRL (0x4a) +#define NV_SOR_DP_LINKCTL(i) (0x4c + (i)) +#define NV_SOR_DP_LINKCTL_FORCE_IDLEPTTRN_SHIFT (31) +#define NV_SOR_DP_LINKCTL_FORCE_IDLEPTTRN_NO (0 << 31) +#define NV_SOR_DP_LINKCTL_FORCE_IDLEPTTRN_YES (1 << 31) +#define NV_SOR_DP_LINKCTL_COMPLIANCEPTTRN_SHIFT (28) +#define NV_SOR_DP_LINKCTL_COMPLIANCEPTTRN_NOPATTERN (0 << 28) +#define NV_SOR_DP_LINKCTL_COMPLIANCEPTTRN_COLORSQARE (1 << 28) +#define NV_SOR_DP_LINKCTL_LANECOUNT_SHIFT (16) +#define NV_SOR_DP_LINKCTL_LANECOUNT_MASK (0x1f << 16) +#define NV_SOR_DP_LINKCTL_LANECOUNT_ZERO (0 << 16) +#define NV_SOR_DP_LINKCTL_LANECOUNT_ONE (1 << 16) +#define NV_SOR_DP_LINKCTL_LANECOUNT_TWO (3 << 16) +#define NV_SOR_DP_LINKCTL_LANECOUNT_FOUR (15 << 16) +#define NV_SOR_DP_LINKCTL_ENHANCEDFRAME_SHIFT (14) +#define NV_SOR_DP_LINKCTL_ENHANCEDFRAME_DISABLE (0 << 14) +#define NV_SOR_DP_LINKCTL_ENHANCEDFRAME_ENABLE (1 << 14) +#define NV_SOR_DP_LINKCTL_SYNCMODE_SHIFT (10) +#define NV_SOR_DP_LINKCTL_SYNCMODE_DISABLE (0 << 10) +#define NV_SOR_DP_LINKCTL_SYNCMODE_ENABLE (1 << 10) +#define NV_SOR_DP_LINKCTL_TUSIZE_SHIFT (2) +#define NV_SOR_DP_LINKCTL_TUSIZE_MASK (0x7f << 2) +#define NV_SOR_DP_LINKCTL_ENABLE_SHIFT (0) +#define NV_SOR_DP_LINKCTL_ENABLE_NO (0) +#define NV_SOR_DP_LINKCTL_ENABLE_YES (1) +#define NV_SOR_DC(i) (0x4e + (i)) +#define NV_SOR_DC_LANE3_DP_LANE3_SHIFT (24) +#define NV_SOR_DC_LANE3_DP_LANE3_MASK (0xff << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P0_LEVEL0 (17 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P1_LEVEL0 (21 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P2_LEVEL0 (26 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P3_LEVEL0 (34 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P0_LEVEL1 (26 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P1_LEVEL1 (32 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P2_LEVEL1 (39 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P0_LEVEL2 (34 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P1_LEVEL2 (43 << 24) +#define NV_SOR_DC_LANE3_DP_LANE3_P0_LEVEL3 (51 << 24) +#define NV_SOR_DC_LANE2_DP_LANE0_SHIFT (16) +#define NV_SOR_DC_LANE2_DP_LANE0_MASK (0xff << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P0_LEVEL0 (17 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P1_LEVEL0 (21 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P2_LEVEL0 (26 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P3_LEVEL0 (34 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P0_LEVEL1 (26 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P1_LEVEL1 (32 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P2_LEVEL1 (39 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P0_LEVEL2 (34 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P1_LEVEL2 (43 << 16) +#define NV_SOR_DC_LANE2_DP_LANE0_P0_LEVEL3 (51 << 16) +#define NV_SOR_DC_LANE1_DP_LANE1_SHIFT (8) +#define NV_SOR_DC_LANE1_DP_LANE1_MASK (0xff << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P0_LEVEL0 (17 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P1_LEVEL0 (21 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P2_LEVEL0 (26 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P3_LEVEL0 (34 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P0_LEVEL1 (26 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P1_LEVEL1 (32 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P2_LEVEL1 (39 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P0_LEVEL2 (34 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P1_LEVEL2 (43 << 8) +#define NV_SOR_DC_LANE1_DP_LANE1_P0_LEVEL3 (51 << 8) +#define NV_SOR_DC_LANE0_DP_LANE2_SHIFT (0) +#define NV_SOR_DC_LANE0_DP_LANE2_MASK (0xff) +#define NV_SOR_DC_LANE0_DP_LANE2_P0_LEVEL0 (17) +#define NV_SOR_DC_LANE0_DP_LANE2_P1_LEVEL0 (21) +#define NV_SOR_DC_LANE0_DP_LANE2_P2_LEVEL0 (26) +#define NV_SOR_DC_LANE0_DP_LANE2_P3_LEVEL0 (34) +#define NV_SOR_DC_LANE0_DP_LANE2_P0_LEVEL1 (26) +#define NV_SOR_DC_LANE0_DP_LANE2_P1_LEVEL1 (32) +#define NV_SOR_DC_LANE0_DP_LANE2_P2_LEVEL1 (39) +#define NV_SOR_DC_LANE0_DP_LANE2_P0_LEVEL2 (34) +#define NV_SOR_DC_LANE0_DP_LANE2_P1_LEVEL2 (43) +#define NV_SOR_DC_LANE0_DP_LANE2_P0_LEVEL3 (51) +#define NV_SOR_LANE_DRIVE_CURRENT(i) (0x4e + (i)) +#define NV_SOR_PR(i) (0x52 + (i)) +#define NV_SOR_PR_LANE3_DP_LANE3_SHIFT (24) +#define NV_SOR_PR_LANE3_DP_LANE3_MASK (0xff << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D0_LEVEL0 (0 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D1_LEVEL0 (0 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D2_LEVEL0 (0 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D3_LEVEL0 (0 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D0_LEVEL1 (4 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D1_LEVEL1 (6 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D2_LEVEL1 (17 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D0_LEVEL2 (8 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D1_LEVEL2 (13 << 24) +#define NV_SOR_PR_LANE3_DP_LANE3_D0_LEVEL3 (17 << 24) +#define NV_SOR_PR_LANE2_DP_LANE0_SHIFT (16) +#define NV_SOR_PR_LANE2_DP_LANE0_MASK (0xff << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D0_LEVEL0 (0 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D1_LEVEL0 (0 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D2_LEVEL0 (0 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D3_LEVEL0 (0 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D0_LEVEL1 (4 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D1_LEVEL1 (6 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D2_LEVEL1 (17 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D0_LEVEL2 (8 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D1_LEVEL2 (13 << 16) +#define NV_SOR_PR_LANE2_DP_LANE0_D0_LEVEL3 (17 << 16) +#define NV_SOR_PR_LANE1_DP_LANE1_SHIFT (8) +#define NV_SOR_PR_LANE1_DP_LANE1_MASK (0xff >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D0_LEVEL0 (0 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D1_LEVEL0 (0 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D2_LEVEL0 (0 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D3_LEVEL0 (0 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D0_LEVEL1 (4 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D1_LEVEL1 (6 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D2_LEVEL1 (17 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D0_LEVEL2 (8 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D1_LEVEL2 (13 >> 8) +#define NV_SOR_PR_LANE1_DP_LANE1_D0_LEVEL3 (17 >> 8) +#define NV_SOR_PR_LANE0_DP_LANE2_SHIFT (0) +#define NV_SOR_PR_LANE0_DP_LANE2_MASK (0xff) +#define NV_SOR_PR_LANE0_DP_LANE2_D0_LEVEL0 (0) +#define NV_SOR_PR_LANE0_DP_LANE2_D1_LEVEL0 (0) +#define NV_SOR_PR_LANE0_DP_LANE2_D2_LEVEL0 (0) +#define NV_SOR_PR_LANE0_DP_LANE2_D3_LEVEL0 (0) +#define NV_SOR_PR_LANE0_DP_LANE2_D0_LEVEL1 (4) +#define NV_SOR_PR_LANE0_DP_LANE2_D1_LEVEL1 (6) +#define NV_SOR_PR_LANE0_DP_LANE2_D2_LEVEL1 (17) +#define NV_SOR_PR_LANE0_DP_LANE2_D0_LEVEL2 (8) +#define NV_SOR_PR_LANE0_DP_LANE2_D1_LEVEL2 (13) +#define NV_SOR_PR_LANE0_DP_LANE2_D0_LEVEL3 (17) +#define NV_SOR_LANE4_PREEMPHASIS(i) (0x54 + (i)) +#define NV_SOR_POSTCURSOR(i) (0x56 + (i)) +#define NV_SOR_DP_CONFIG(i) (0x58 + (i)) +#define NV_SOR_DP_CONFIG_RD_RESET_VAL_SHIFT (31) +#define NV_SOR_DP_CONFIG_RD_RESET_VAL_POSITIVE (0 << 31) +#define NV_SOR_DP_CONFIG_RD_RESET_VAL_NEGATIVE (1 << 31) +#define NV_SOR_DP_CONFIG_IDLE_BEFORE_ATTACH_SHIFT (28) +#define NV_SOR_DP_CONFIG_IDLE_BEFORE_ATTACH_DISABLE (0 << 28) +#define NV_SOR_DP_CONFIG_IDLE_BEFORE_ATTACH_ENABLE (1 << 28) +#define NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_SHIFT (26) +#define NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_DISABLE (0 << 26) +#define NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_ENABLE (1 << 26) +#define NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_SHIFT (24) +#define NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_NEGATIVE (0 << 24) +#define NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE (1 << 24) +#define NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_SHIFT (16) +#define NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_MASK (0xf << 16) +#define NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_SHIFT (8) +#define NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_MASK (0x7f << 8) +#define NV_SOR_DP_CONFIG_WATERMARK_SHIFT (0) +#define NV_SOR_DP_CONFIG_WATERMARK_MASK (0x3f) +#define NV_SOR_DP_MN(i) (0x5a + i) +#define NV_SOR_DP_MN_M_MOD_SHIFT (30) +#define NV_SOR_DP_MN_M_MOD_DEFAULT_MASK (0x3 << 30) +#define NV_SOR_DP_MN_M_MOD_NONE (0 << 30) +#define NV_SOR_DP_MN_M_MOD_INC (1 << 30) +#define NV_SOR_DP_MN_M_MOD_DEC (2 << 30) +#define NV_SOR_DP_MN_M_DELTA_SHIFT (24) +#define NV_SOR_DP_MN_M_DELTA_DEFAULT_MASK (0xf << 24) +#define NV_SOR_DP_MN_N_VAL_SHIFT (0) +#define NV_SOR_DP_MN_N_VAL_DEFAULT_MASK (0xffffff) +#define NV_SOR_DP_PADCTL(i) (0x5c + (i)) +#define NV_SOR_DP_PADCTL_SPARE_SHIFT (25) +#define NV_SOR_DP_PADCTL_SPARE_DEFAULT_MASK (0x7f << 25) +#define NV_SOR_DP_PADCTL_VCO_2X_SHIFT (24) +#define NV_SOR_DP_PADCTL_VCO_2X_DISABLE (0 << 24) +#define NV_SOR_DP_PADCTL_VCO_2X_ENABLE (1 << 24) +#define NV_SOR_DP_PADCTL_PAD_CAL_PD_SHIFT (23) +#define NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERUP (0 << 23) +#define NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN (1 << 23) +#define NV_SOR_DP_PADCTL_TX_PU_SHIFT (22) +#define NV_SOR_DP_PADCTL_TX_PU_DISABLE (0 << 22) +#define NV_SOR_DP_PADCTL_TX_PU_ENABLE (1 << 22) +#define NV_SOR_DP_PADCTL_TX_PU_MASK (1 << 22) +#define NV_SOR_DP_PADCTL_REG_CTRL_SHIFT (20) +#define NV_SOR_DP_PADCTL_REG_CTRL_DEFAULT_MASK (0x3 << 20) +#define NV_SOR_DP_PADCTL_VCMMODE_SHIFT (16) +#define NV_SOR_DP_PADCTL_VCMMODE_DEFAULT_MASK (0xf << 16) +#define NV_SOR_DP_PADCTL_VCMMODE_TRISTATE (0 << 16) +#define NV_SOR_DP_PADCTL_VCMMODE_TEST_MUX (1 << 16) +#define NV_SOR_DP_PADCTL_VCMMODE_WEAK_PULLDOWN (2 << 16) +#define NV_SOR_DP_PADCTL_VCMMODE_STRONG_PULLDOWN (4 << 16) +#define NV_SOR_DP_PADCTL_TX_PU_VALUE_SHIFT (8) +#define NV_SOR_DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK (0xff << 8) +#define NV_SOR_DP_PADCTL_COMODE_TXD_3_DP_TXD_3_SHIFT (7) +#define NV_SOR_DP_PADCTL_COMODE_TXD_3_DP_TXD_3_DISABLE (0 << 7) +#define NV_SOR_DP_PADCTL_COMODE_TXD_3_DP_TXD_3_ENABLE (1 << 7) +#define NV_SOR_DP_PADCTL_COMODE_TXD_2_DP_TXD_0_SHIFT (6) +#define NV_SOR_DP_PADCTL_COMODE_TXD_2_DP_TXD_0_DISABLE (0 << 6) +#define NV_SOR_DP_PADCTL_COMODE_TXD_2_DP_TXD_0_ENABLE (1 << 6) +#define NV_SOR_DP_PADCTL_COMODE_TXD_1_DP_TXD_1_SHIFT (5) +#define NV_SOR_DP_PADCTL_COMODE_TXD_1_DP_TXD_1_DISABLE (0 << 5) +#define NV_SOR_DP_PADCTL_COMODE_TXD_1_DP_TXD_1_ENABLE (1 << 5) +#define NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT (4) +#define NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_DISABLE (0 << 4) +#define NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_ENABLE (1 << 4) +#define NV_SOR_DP_PADCTL_PD_TXD_3_SHIFT (3) +#define NV_SOR_DP_PADCTL_PD_TXD_3_YES (0 << 3) +#define NV_SOR_DP_PADCTL_PD_TXD_3_NO (1 << 3) +#define NV_SOR_DP_PADCTL_PD_TXD_0_SHIFT (2) +#define NV_SOR_DP_PADCTL_PD_TXD_0_YES (0 << 2) +#define NV_SOR_DP_PADCTL_PD_TXD_0_NO (1 << 2) +#define NV_SOR_DP_PADCTL_PD_TXD_1_SHIFT (1) +#define NV_SOR_DP_PADCTL_PD_TXD_1_YES (0 << 1) +#define NV_SOR_DP_PADCTL_PD_TXD_1_NO (1 << 1) +#define NV_SOR_DP_PADCTL_PD_TXD_2_SHIFT (0) +#define NV_SOR_DP_PADCTL_PD_TXD_2_YES (0) +#define NV_SOR_DP_PADCTL_PD_TXD_2_NO (1) +#define NV_SOR_DP_DEBUG(i) (0x5e + i) +#define NV_SOR_DP_SPARE(i) (0x60 + (i)) +#define NV_SOR_DP_SPARE_REG_SHIFT (3) +#define NV_SOR_DP_SPARE_REG_DEFAULT_MASK (0x1fffffff << 3) +#define NV_SOR_DP_SPARE_SOR_CLK_SEL_SHIFT (2) +#define NV_SOR_DP_SPARE_SOR_CLK_SEL_DEFAULT_MASK (0x1 << 2) +#define NV_SOR_DP_SPARE_SOR_CLK_SEL_SAFE_SORCLK (0 << 2) +#define NV_SOR_DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK (1 << 2) +#define NV_SOR_DP_SPARE_PANEL_SHIFT (1) +#define NV_SOR_DP_SPARE_PANEL_EXTERNAL (0 << 1) +#define NV_SOR_DP_SPARE_PANEL_INTERNAL (1 << 1) +#define NV_SOR_DP_SPARE_SEQ_ENABLE_SHIFT (0) +#define NV_SOR_DP_SPARE_SEQ_ENABLE_NO (0) +#define NV_SOR_DP_SPARE_SEQ_ENABLE_YES (1) +#define NV_SOR_DP_AUDIO_CTRL (0x62) +#define NV_SOR_DP_AUDIO_HBLANK_SYMBOLS (0x63) +#define NV_SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK (0x1ffff) +#define NV_SOR_DP_AUDIO_HBLANK_SYMBOLS_VALUE_SHIFT (0) +#define NV_SOR_DP_AUDIO_VBLANK_SYMBOLS (0x64) +#define NV_SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK (0x1ffff) +#define NV_SOR_DP_AUDIO_VBLANK_SYMBOLS_SHIFT (0) +#define NV_SOR_DP_GENERIC_INFOFRAME_HEADER (0x65) +#define NV_SOR_DP_GENERIC_INFOFRAME_SUBPACK(i) (0x66 + (i)) +#define NV_SOR_DP_TPG (0x6d) +#define NV_SOR_DP_TPG_LANE3_CHANNELCODING_SHIFT (30) +#define NV_SOR_DP_TPG_LANE3_CHANNELCODING_DISABLE (0 << 30) +#define NV_SOR_DP_TPG_LANE3_CHANNELCODING_ENABLE (1 << 30) +#define NV_SOR_DP_TPG_LANE3_SCRAMBLEREN_SHIFT (28) +#define NV_SOR_DP_TPG_LANE3_SCRAMBLEREN_ENABLE_GALIOS (1 << 28) +#define NV_SOR_DP_TPG_LANE3_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 28) +#define NV_SOR_DP_TPG_LANE3_PATTERN_SHIFT (24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_DEFAULT_MASK (0xf << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_NOPATTERN (0 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_TRAINING1 (1 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_TRAINING2 (2 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_TRAINING3 (3 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_D102 (4 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_SBLERRRATE (5 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_PRBS7 (6 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_CSTM (7 << 24) +#define NV_SOR_DP_TPG_LANE3_PATTERN_HBR2_COMPLIANCE (8 << 24) +#define NV_SOR_DP_TPG_LANE2_CHANNELCODING_SHIFT (22) +#define NV_SOR_DP_TPG_LANE2_CHANNELCODING_DISABLE (0 << 22) +#define NV_SOR_DP_TPG_LANE2_CHANNELCODING_ENABLE (1 << 22) +#define NV_SOR_DP_TPG_LANE2_SCRAMBLEREN_SHIFT (20) +#define NV_SOR_DP_TPG_LANE2_SCRAMBLEREN_DEFAULT_MASK (0x3 << 20) +#define NV_SOR_DP_TPG_LANE2_SCRAMBLEREN_DISABLE (0 << 20) +#define NV_SOR_DP_TPG_LANE2_SCRAMBLEREN_ENABLE_GALIOS (1 << 20) +#define NV_SOR_DP_TPG_LANE2_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 20) +#define NV_SOR_DP_TPG_LANE2_PATTERN_SHIFT (16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_DEFAULT_MASK (0xf << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_NOPATTERN (0 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_TRAINING1 (1 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_TRAINING2 (2 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_TRAINING3 (3 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_D102 (4 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_SBLERRRATE (5 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_PRBS7 (6 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_CSTM (7 << 16) +#define NV_SOR_DP_TPG_LANE2_PATTERN_HBR2_COMPLIANCE (8 << 16) +#define NV_SOR_DP_TPG_LANE1_CHANNELCODING_SHIFT (14) +#define NV_SOR_DP_TPG_LANE1_CHANNELCODING_DISABLE (0 << 14) +#define NV_SOR_DP_TPG_LANE1_CHANNELCODING_ENABLE (1 << 14) +#define NV_SOR_DP_TPG_LANE1_SCRAMBLEREN_SHIFT (12) +#define NV_SOR_DP_TPG_LANE1_SCRAMBLEREN_DEFAULT_MASK (0x3 << 12) +#define NV_SOR_DP_TPG_LANE1_SCRAMBLEREN_DISABLE (0 << 12) +#define NV_SOR_DP_TPG_LANE1_SCRAMBLEREN_ENABLE_GALIOS (1 << 12) +#define NV_SOR_DP_TPG_LANE1_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 12) +#define NV_SOR_DP_TPG_LANE1_PATTERN_SHIFT (8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_DEFAULT_MASK (0xf << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_NOPATTERN (0 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_TRAINING1 (1 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_TRAINING2 (2 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_TRAINING3 (3 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_D102 (4 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_SBLERRRATE (5 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_PRBS7 (6 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_CSTM (7 << 8) +#define NV_SOR_DP_TPG_LANE1_PATTERN_HBR2_COMPLIANCE (8 << 8) +#define NV_SOR_DP_TPG_LANE0_CHANNELCODING_SHIFT (6) +#define NV_SOR_DP_TPG_LANE0_CHANNELCODING_DISABLE (0 << 6) +#define NV_SOR_DP_TPG_LANE0_CHANNELCODING_ENABLE (1 << 6) +#define NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_SHIFT (4) +#define NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_DEFAULT_MASK (0x3 << 4) +#define NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_DISABLE (0 << 4) +#define NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_ENABLE_GALIOS (1 << 4) +#define NV_SOR_DP_TPG_LANE0_SCRAMBLEREN_ENABLE_FIBONACCI (2 << 4) +#define NV_SOR_DP_TPG_LANE0_PATTERN_SHIFT (0) +#define NV_SOR_DP_TPG_LANE0_PATTERN_DEFAULT_MASK (0xf) +#define NV_SOR_DP_TPG_LANE0_PATTERN_NOPATTERN (0) +#define NV_SOR_DP_TPG_LANE0_PATTERN_TRAINING1 (1) +#define NV_SOR_DP_TPG_LANE0_PATTERN_TRAINING2 (2) +#define NV_SOR_DP_TPG_LANE0_PATTERN_TRAINING3 (3) +#define NV_SOR_DP_TPG_LANE0_PATTERN_D102 (4) +#define NV_SOR_DP_TPG_LANE0_PATTERN_SBLERRRATE (5) +#define NV_SOR_DP_TPG_LANE0_PATTERN_PRBS7 (6) +#define NV_SOR_DP_TPG_LANE0_PATTERN_CSTM (7) +#define NV_SOR_DP_TPG_LANE0_PATTERN_HBR2_COMPLIANCE (8) + +enum { + training_pattern_disabled = 0, + training_pattern_1 = 1, + training_pattern_2 = 2, + training_pattern_3 = 3, + training_pattern_none = 0xff +}; + +enum tegra_dc_sor_protocol { + SOR_DP, + SOR_LVDS, +}; + +#define SOR_LINK_SPEED_G1_62 6 +#define SOR_LINK_SPEED_G2_7 10 +#define SOR_LINK_SPEED_G5_4 20 +#define SOR_LINK_SPEED_LVDS 7 + +/* todo: combine this and the intel_dp struct into one struct. */ +struct tegra_dc_dp_link_config { + int is_valid; + + /* Supported configuration */ + u8 max_link_bw; + u8 max_lane_count; + int downspread; + int support_enhanced_framing; + u32 bits_per_pixel; + int alt_scramber_reset_cap; /* true for eDP */ + int only_enhanced_framing; /* enhanced_frame_en ignored */ + + /* Actual configuration */ + u8 link_bw; + u8 lane_count; + int enhanced_framing; + int scramble_ena; + + u32 activepolarity; + u32 active_count; + u32 tu_size; + u32 active_frac; + u32 watermark; + + s32 hblank_sym; + s32 vblank_sym; + + /* Training data */ + u32 drive_current; + u32 preemphasis; + u32 postcursor; + u8 aux_rd_interval; + u8 tps3_supported; +}; + +/* TODO: just pull these up into one struct? Need to see how this impacts + * having two channels. + */ +struct tegra_dc_sor_data { + struct tegra_dc *dc; + void *base; + void *pmc_base; + u8 portnum; /* 0 or 1 */ + struct tegra_dc_dp_link_config *link_cfg; + int power_is_up; +}; + +#define TEGRA_SOR_TIMEOUT_MS 1000 +#define TEGRA_SOR_ATTACH_TIMEOUT_MS 1000 +#define TEGRA_DC_POLL_TIMEOUT_MS 50 + +#define CHECK_RET(x) \ + do { \ + ret = (x); \ + if (ret != 0) \ + return ret; \ + } while (0) + +void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor); +int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd); +void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena, + u8 training_pattern, const struct tegra_dc_dp_link_config *link_cfg); +void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw); +void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count); +void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor, + int power_up); +void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int); +void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw, + u8 *lane_count); +void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor); +void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor, + const struct tegra_dc_dp_link_config *link_cfg); +void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor); +void tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor); +void tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor); +void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor); +void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, + u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported); +void tegra_dc_detach(struct tegra_dc_sor_data *sor); +#endif /*__TEGRA210_SOR_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/spi.h b/src/soc/nvidia/tegra210/include/soc/spi.h new file mode 100644 index 0000000000..b76622a848 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/spi.h @@ -0,0 +1,67 @@ +/* + * Copyright 2014 Google Inc. + * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + */ + +#ifndef __NVIDIA_TEGRA210_SPI_H__ +#define __NVIDIA_TEGRA210_SPI_H__ + +#include <soc/dma.h> +#include <spi-generic.h> +#include <stddef.h> + +struct tegra_spi_regs { + u32 command1; /* 0x000: SPI_COMMAND1 */ + u32 command2; /* 0x004: SPI_COMMAND2 */ + u32 timing1; /* 0x008: SPI_CS_TIM1 */ + u32 timing2; /* 0x00c: SPI_CS_TIM2 */ + u32 trans_status; /* 0x010: SPI_TRANS_STATUS */ + u32 fifo_status; /* 0x014: SPI_FIFO_STATUS */ + u32 tx_data; /* 0x018: SPI_TX_DATA */ + u32 rx_data; /* 0x01c: SPI_RX_DATA */ + u32 dma_ctl; /* 0x020: SPI_DMA_CTL */ + u32 dma_blk; /* 0x024: SPI_DMA_BLK */ + u32 rsvd[56]; /* 0x028-0x107: reserved */ + u32 tx_fifo; /* 0x108: SPI_FIFO1 */ + u32 rsvd2[31]; /* 0x10c-0x187 reserved */ + u32 rx_fifo; /* 0x188: SPI_FIFO2 */ + u32 spare_ctl; /* 0x18c: SPI_SPARE_CTRL */ +} __attribute__((packed)); +check_member(tegra_spi_regs, spare_ctl, 0x18c); + +enum spi_xfer_mode { + XFER_MODE_NONE = 0, + XFER_MODE_PIO, + XFER_MODE_DMA, +}; + +struct tegra_spi_channel { + struct tegra_spi_regs *regs; + + /* static configuration */ + struct spi_slave slave; + unsigned int req_sel; + + int dual_mode; /* for x2 transfers with bit interleaving */ + + /* context (used internally) */ + u8 *in_buf, *out_buf; + struct apb_dma_channel *dma_out, *dma_in; + enum spi_xfer_mode xfer_mode; +}; + +struct tegra_spi_channel *tegra_spi_init(unsigned int bus); + +#endif /* __NVIDIA_TEGRA210_SPI_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/sysctr.h b/src/soc/nvidia/tegra210/include/soc/sysctr.h new file mode 100644 index 0000000000..e5a894c389 --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/sysctr.h @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SYSCTR_H__ +#define __SOC_NVIDIA_TEGRA210_SYSCTR_H__ + +#include <stdint.h> + +enum { + SYSCTR_CNTCR_EN = 1 << 0, + SYSCTR_CNTCR_HDBG = 1 << 1, + SYSCTR_CNTCR_FCREQ = 1 << 8 +}; + +struct sysctr_regs { + uint32_t cntcr; + uint32_t cntsr; + uint32_t cntcv0; + uint32_t cntcv1; + uint8_t _rsv0[0x10]; + uint32_t cntfid0; + uint32_t cntfid1; + uint8_t _rsv1[0xfa8]; + uint32_t counterid4; + uint32_t counterid5; + uint32_t counterid6; + uint32_t counterid7; + uint32_t counterid0; + uint32_t counterid1; + uint32_t counterid2; + uint32_t counterid3; + uint32_t counterid8; + uint32_t counterid9; + uint32_t counterid10; + uint32_t counterid11; +}; +check_member(sysctr_regs, counterid11, 0xffc); + +#endif /* __SOC_NVIDIA_TEGRA210_SYSCTR_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/tegra_dsi.h b/src/soc/nvidia/tegra210/include/soc/tegra_dsi.h new file mode 100644 index 0000000000..058e26faca --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/tegra_dsi.h @@ -0,0 +1,223 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#ifndef __TEGRA_DSI_H__ +#define __TEGRA_DSI_H__ + +#define DSI_INCR_SYNCPT 0x00 +#define DSI_INCR_SYNCPT_CONTROL 0x01 +#define DSI_INCR_SYNCPT_ERROR 0x02 +#define DSI_CTXSW 0x08 +#define DSI_RD_DATA 0x09 +#define DSI_WR_DATA 0x0a +#define DSI_POWER_CONTROL 0x0b +#define DSI_POWER_CONTROL_ENABLE (1 << 0) +#define DSI_INT_ENABLE 0x0c +#define DSI_INT_STATUS 0x0d +#define DSI_INT_MASK 0x0e +#define DSI_HOST_CONTROL 0x0f +#define DSI_HOST_CONTROL_FIFO_RESET (1 << 21) +#define DSI_HOST_CONTROL_CRC_RESET (1 << 20) +#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12) +#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12) +#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12) +#define DSI_HOST_CONTROL_RAW (1 << 6) +#define DSI_HOST_CONTROL_HS (1 << 5) +#define DSI_HOST_CONTROL_FIFO_SEL (1 << 4) +#define DSI_HOST_CONTROL_IMM_BTA (1 << 3) +#define DSI_HOST_CONTROL_PKT_BTA (1 << 2) +#define DSI_HOST_CONTROL_CS (1 << 1) +#define DSI_HOST_CONTROL_ECC (1 << 0) +#define DSI_CONTROL 0x10 +#define DSI_CONTROL_HS_CLK_CTRL (1 << 20) +#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16) +#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12) +#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8) +#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4) +#define DSI_CONTROL_DCS_ENABLE (1 << 3) +#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2) +#define DSI_CONTROL_VIDEO_ENABLE (1 << 1) +#define DSI_CONTROL_HOST_ENABLE (1 << 0) +#define DSI_SOL_DELAY 0x11 +#define DSI_MAX_THRESHOLD 0x12 +#define DSI_TRIGGER 0x13 +#define DSI_TRIGGER_HOST (1 << 1) +#define DSI_TRIGGER_VIDEO (1 << 0) +#define DSI_TX_CRC 0x14 +#define DSI_STATUS 0x15 +#define DSI_STATUS_IDLE (1 << 10) +#define DSI_STATUS_UNDERFLOW (1 << 9) +#define DSI_STATUS_OVERFLOW (1 << 8) +#define DSI_INIT_SEQ_CONTROL 0x1a +#define DSI_INIT_SEQ_DATA_0 0x1b +#define DSI_INIT_SEQ_DATA_1 0x1c +#define DSI_INIT_SEQ_DATA_2 0x1d +#define DSI_INIT_SEQ_DATA_3 0x1e +#define DSI_INIT_SEQ_DATA_4 0x1f +#define DSI_INIT_SEQ_DATA_5 0x20 +#define DSI_INIT_SEQ_DATA_6 0x21 +#define DSI_INIT_SEQ_DATA_7 0x22 +#define DSI_PKT_SEQ_0_LO 0x23 +#define DSI_PKT_SEQ_0_HI 0x24 +#define DSI_PKT_SEQ_1_LO 0x25 +#define DSI_PKT_SEQ_1_HI 0x26 +#define DSI_PKT_SEQ_2_LO 0x27 +#define DSI_PKT_SEQ_2_HI 0x28 +#define DSI_PKT_SEQ_3_LO 0x29 +#define DSI_PKT_SEQ_3_HI 0x2a +#define DSI_PKT_SEQ_4_LO 0x2b +#define DSI_PKT_SEQ_4_HI 0x2c +#define DSI_PKT_SEQ_5_LO 0x2d +#define DSI_PKT_SEQ_5_HI 0x2e +#define DSI_DCS_CMDS 0x33 +#define DSI_PKT_LEN_0_1 0x34 +#define DSI_PKT_LEN_2_3 0x35 +#define DSI_PKT_LEN_4_5 0x36 +#define DSI_PKT_LEN_6_7 0x37 +#define DSI_PHY_TIMING_0 0x3c +#define DSI_PHY_TIMING_1 0x3d +#define DSI_PHY_TIMING_2 0x3e +#define DSI_BTA_TIMING 0x3f + +#define DSI_TIMING_FIELD(value, period, hwinc) \ + ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff) + +#define DSI_TIMEOUT_0 0x44 +#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16) +#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0) +#define DSI_TIMEOUT_1 0x45 +#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16) +#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0) +#define DSI_TO_TALLY 0x46 +#define DSI_TALLY_TA(x) (((x) & 0xff) << 16) +#define DSI_TALLY_LRX(x) (((x) & 0xff) << 8) +#define DSI_TALLY_HTX(x) (((x) & 0xff) << 0) +#define DSI_PAD_CONTROL_0 0x4b +#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) +#define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8) +#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) +#define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24) +#define DSI_PAD_CONTROL_CD 0x4c +#define DSI_PAD_CD_STATUS 0x4d +#define DSI_VIDEO_MODE_CONTROL 0x4e +#define DSI_PAD_CONTROL_1 0x4f +#define DSI_PAD_CONTROL_2 0x50 +#define DSI_PAD_OUT_CLK(x) (((x) & 0x7) << 0) +#define DSI_PAD_LP_DN(x) (((x) & 0x7) << 4) +#define DSI_PAD_LP_UP(x) (((x) & 0x7) << 8) +#define DSI_PAD_SLEW_DN(x) (((x) & 0x7) << 12) +#define DSI_PAD_SLEW_UP(x) (((x) & 0x7) << 16) +#define DSI_PAD_CONTROL_3 0x51 +#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12) +#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8) +#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4) +#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0) +#define DSI_PAD_CONTROL_4 0x52 +#define DSI_GANGED_MODE_CONTROL 0x53 +#define DSI_GANGED_MODE_CONTROL_ENABLE (1 << 0) +#define DSI_GANGED_MODE_START 0x54 +#define DSI_GANGED_MODE_SIZE 0x55 +#define DSI_RAW_DATA_BYTE_COUNT 0x56 +#define DSI_ULTRA_LOW_POWER_CONTROL 0x57 +#define DSI_INIT_SEQ_DATA_8 0x58 +#define DSI_INIT_SEQ_DATA_9 0x59 +#define DSI_INIT_SEQ_DATA_10 0x5a +#define DSI_INIT_SEQ_DATA_11 0x5b +#define DSI_INIT_SEQ_DATA_12 0x5c +#define DSI_INIT_SEQ_DATA_13 0x5d +#define DSI_INIT_SEQ_DATA_14 0x5e +#define DSI_INIT_SEQ_DATA_15 0x5f + +#define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) +#define PKT_LEN0(len) (((len) & 0x07) << 0) +#define PKT_ID1(id) ((((id) & 0x3f) << 13) | (1 << 19)) +#define PKT_LEN1(len) (((len) & 0x07) << 10) +#define PKT_ID2(id) ((((id) & 0x3f) << 23) | (1 << 29)) +#define PKT_LEN2(len) (((len) & 0x07) << 20) + +#define PKT_LP (1 << 30) +#define NUM_PKT_SEQ 12 + +#define APB_MISC_GP_MIPI_PAD_CTRL_0 (TEGRA_APB_MISC_GP_BASE + 0x20) +#define DSIB_MODE_SHIFT 1 +#define DSIB_MODE_CSI (0 << DSIB_MODE_SHIFT) +#define DSIB_MODE_DSI (1 << DSIB_MODE_SHIFT) + +/* + * pixel format as used in the DSI_CONTROL_FORMAT field + */ +enum tegra_dsi_format { + TEGRA_DSI_FORMAT_16P, + TEGRA_DSI_FORMAT_18NP, + TEGRA_DSI_FORMAT_18P, + TEGRA_DSI_FORMAT_24P, +}; + +enum dsi_dev { + DSI_A = 0, + DSI_B, + NUM_DSI, +}; + +struct panel_jdi; +struct tegra_mipi_device; +struct mipi_dsi_host; +struct mipi_dsi_msg; + +#define MAX_DSI_VIDEO_FIFO_DEPTH 96 +#define MAX_DSI_HOST_FIFO_DEPTH 64 + +struct tegra_dsi { + struct panel_jdi *panel; + //struct tegra_output output; + void *regs; + u8 channel; + unsigned long clk_rate; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; + + struct tegra_mipi_device *mipi; + struct mipi_dsi_host host; + bool enabled; + + unsigned int video_fifo_depth; + unsigned int host_fifo_depth; + + /* for ganged-mode support */ + unsigned int ganged_lanes; + struct tegra_dsi *slave; + int ganged_mode; + + struct tegra_dsi *master; +}; + +static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi, + unsigned long reg) +{ + return read32(dsi->regs + (reg << 2)); +} + +static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value, + unsigned long reg) +{ + write32(dsi->regs + (reg << 2), value); +} + +#endif /* __TEGRA_DSI_H__ */ diff --git a/src/soc/nvidia/tegra210/include/soc/verstage.h b/src/soc/nvidia/tegra210/include/soc/verstage.h new file mode 100644 index 0000000000..6c37218e9e --- /dev/null +++ b/src/soc/nvidia/tegra210/include/soc/verstage.h @@ -0,0 +1,25 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#ifndef __SOC_NVIDIA_TEGRA210_SOC_VERSTAGE_H__ +#define __SOC_NVIDIA_TEGRA210_SOC_VERSTAGE_H__ + +void verstage_mainboard_init(void); + +#endif /* __SOC_NVIDIA_TEGRA210_SOC_VERSTAGE_H__ */ diff --git a/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.c b/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.c new file mode 100644 index 0000000000..2149ca936e --- /dev/null +++ b/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.c @@ -0,0 +1,215 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 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. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <device/device.h> +#include <soc/nvidia/tegra/types.h> +#include "../chip.h" +#include <soc/display.h> +#include <soc/mipi_dsi.h> +#include <soc/tegra_dsi.h> +#include "panel-jdi-lpm102a188a.h" + +struct panel_jdi jdi_data[NUM_DSI]; + +int panel_jdi_prepare(struct panel_jdi *jdi) +{ + int ret; + u8 data; + + if (jdi->enabled) + return 0; + + ret = mipi_dsi_dcs_set_column_address(jdi->dsi, 0, + jdi->mode->xres / 2 - 1); // 2560/2 + if (ret < 0) + printk(BIOS_ERR, "failed to set column address: %d\n", ret); + + ret = mipi_dsi_dcs_set_column_address(jdi->dsi->slave, 0, + jdi->mode->xres / 2 - 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set column address: %d\n", ret); + + ret = mipi_dsi_dcs_set_page_address(jdi->dsi, 0, + jdi->mode->yres - 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set page address: %d\n", ret); + + ret = mipi_dsi_dcs_set_page_address(jdi->dsi->slave, 0, + jdi->mode->yres - 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set page address: %d\n", ret); + + ret = mipi_dsi_dcs_set_tear_on(jdi->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) + printk(BIOS_ERR, "failed to set tear on: %d\n", ret); + + ret = mipi_dsi_dcs_set_tear_on(jdi->dsi->slave, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) + printk(BIOS_ERR, "failed to set tear on: %d\n", ret); + + ret = mipi_dsi_dcs_set_address_mode(jdi->dsi, false, false, false, + false, false, false, false, false); + if (ret < 0) + printk(BIOS_ERR, "failed to set address mode: %d\n", ret); + + ret = mipi_dsi_dcs_set_address_mode(jdi->dsi->slave, false, false, + false, false, false, false, false, false); + if (ret < 0) + printk(BIOS_ERR, "failed to set address mode: %d\n", ret); + + ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi, 0x77); + if (ret < 0) + printk(BIOS_ERR, "failed to set pixel format: %d\n", ret); + + ret = mipi_dsi_dcs_set_pixel_format(jdi->dsi->slave, 0x77); + if (ret < 0) + printk(BIOS_ERR, "failed to set pixel format: %d\n", ret); + + data = 0xFF; + ret = mipi_dsi_dcs_write(jdi->dsi, 0x51, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x51: %d\n", ret); + + data = 0xFF; + ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x51, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x51: %d\n", ret); + + data = 0x24; + ret = mipi_dsi_dcs_write(jdi->dsi, 0x53, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x53: %d\n", ret); + + data = 0x24; + ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x53, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x53: %d\n", ret); + + data = 0x00; + ret = mipi_dsi_dcs_write(jdi->dsi, 0x55, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x55: %d\n", ret); + + data = 0x00; + ret = mipi_dsi_dcs_write(jdi->dsi->slave, 0x55, &data, 1); + if (ret < 0) + printk(BIOS_ERR, "failed to set 0x55: %d\n", ret); + + ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi); + if (ret < 0) + printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret); + + ret = mipi_dsi_dcs_exit_sleep_mode(jdi->dsi->slave); + if (ret < 0) + printk(BIOS_ERR, "failed to exit sleep mode: %d\n", ret); + mdelay(150); + + ret = mipi_dsi_dcs_set_display_on(jdi->dsi); + if (ret < 0) + printk(BIOS_ERR, "failed to set display on: %d\n", ret); + + ret = mipi_dsi_dcs_set_display_on(jdi->dsi->slave); + if (ret < 0) + printk(BIOS_ERR, "failed to set display on: %d\n", ret); + mdelay(50); + + jdi->enabled = true; + + return 0; +} + +static int panel_jdi_enslave(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave) +{ + int ret; + + ret = mipi_dsi_attach(master); + if (ret < 0) + return ret; + + return ret; +} + +static int panel_jdi_liberate(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave) +{ + int ret; + + ret = mipi_dsi_detach(master); + if (ret < 0) + return ret; + + return 0; +} + +static const struct mipi_dsi_master_ops panel_jdi_master_ops = { + .enslave = panel_jdi_enslave, + .liberate = panel_jdi_liberate, +}; + +struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi) +{ + static int index = 0; + struct panel_jdi *jdi; + int ret; + + if (index >= NUM_DSI) + return (void *)-EPTR; + + jdi = &jdi_data[index++]; + + jdi->dsi = dsi; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = 0; + + if (dsi->master) { + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + printk(BIOS_ERR, "mipi_dsi_attach() failed: %d\n", ret); + return (void *)-EPTR; + } + + ret = mipi_dsi_enslave(dsi->master, dsi); + if (ret < 0) { + printk(BIOS_ERR, "mipi_dsi_enslave() failed: %d\n", + ret); + return (void *)-EPTR; + } + + return jdi; + } + + dsi->ops = &panel_jdi_master_ops; + + jdi->enabled = 0; + jdi->width_mm = 211; + jdi->height_mm = 148; + + return jdi; +} diff --git a/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.h b/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.h new file mode 100644 index 0000000000..b345971060 --- /dev/null +++ b/src/soc/nvidia/tegra210/jdi_25x18_display/panel-jdi-lpm102a188a.h @@ -0,0 +1,131 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 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. + */ +#ifndef _PANEL_JDI_LPM102A188A_H_ +#define _PANEL_JDI_LPM102A188A_H_ + +#define LP8557_MAX_BRIGHTNESS 0xFFF; + +#define LP8557_COMMAND 0x00 +#define LP8557_COMMAND_ON (1 << 0) + +#define LP8557_BRIGHTNESS_LOW 0x03 +#define LP8557_BRIGHTNESS_LOW_MASK(x) (((x) & 0xF) << 4) + +#define LP8557_BRIGHTNESS_HIGH 0x04 +#define LP8557_BRIGHTNESS_HIGH_MASK(x) (((x) & 0xFF0) >> 4) + +enum lp8557_config_brightness_mode { + LP8557_CONFIG_BRTMODE_PWM = 0x0, + LP8557_CONFIG_BRTMODE_REG, + LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_PWM, + LP8557_CONFIG_BRTMODE_PWM_REG_SHAPE_BRIGHTNESS, + LP8557_CONFIG_BRTMODE_MAX, +}; +#define LP8557_CONFIG 0x10 +#define LP8557_CONFIG_BRTMODE(x) (((x) & 0x3) << 0) +#define LP8557_CONFIG_AUTO_DETECT_LED (1 << 2) +#define LP8557_CONFIG_PWM_STANDBY (1 << 7) + +enum lp8557_current { + LP8557_CURRENT_5_MA = 0x0, + LP8557_CURRENT_10_MA, + LP8557_CURRENT_13_MA, + LP8557_CURRENT_15_MA, + LP8557_CURRENT_18_MA, + LP8557_CURRENT_20_MA, + LP8557_CURRENT_23_MA, + LP8557_CURRENT_25_MA, + LP8557_CURRENT_MAX, +}; +#define LP8557_CURRENT 0x11 +#define LP8557_CURRENT_MAXCURR(x) (((x) & 0x7) << 0) +#define LP8557_CURRENT_ISET (1 << 7) + +enum lp8557_pgen_frequency { + LP8557_PGEN_FREQ_4_9_KHZ = 0x0, + LP8557_PGEN_FREQ_9_8_KHZ, + LP8557_PGEN_FREQ_14_6_KHZ, + LP8557_PGEN_FREQ_19_5_KHZ, + LP8557_PGEN_FREQ_24_4_KHZ, + LP8557_PGEN_FREQ_29_3_KHZ, + LP8557_PGEN_FREQ_34_2_KHZ, + LP8557_PGEN_FREQ_39_1_KHZ, + LP8557_PGEN_FREQ_MAX, +}; +#define LP8557_PGEN 0x12 +#define LP8557_PGEN_FREQ(x) (((x) & 0x7) << 0) +#define LP8557_PGEN_MAGIC (5 << 3) +#define LP8557_PGEN_FSET (1 << 7) + +enum lp8557_boost_freq { + LP8557_BOOST_FREQ_500_KHZ = 0x0, + LP8557_BOOST_FREQ_1_MHZ, + LP8557_BOOST_FREQ_MAX, +}; +enum lp8557_boost_bcomp { + LP8557_BOOST_BCOMP_OPTION_0 = 0x0, + LP8557_BOOST_BCOMP_OPTION_1, + LP8557_BOOST_BCOMP_MAX, +}; +#define LP8557_BOOST 0x13 +#define LP8557_BOOST_FREQ(x) (((x) & 0x1) << 0) +#define LP8557_BOOST_BCOMP(x) (((x) & 0x1) << 1) +#define LP8557_BOOST_BCSET (1 << 6) +#define LP8557_BOOST_BFSET (1 << 7) + +#define LP8557_LED_ENABLE 0x14 +#define LP8557_LED_ENABLE_SINKS(x) (((x) & 0x3F) << 0) +#define LP8557_LED_ENABLE_MAGIC (2 << 6) + +enum lp8557_step_ramp { + LP8557_STEP_RAMP_0_MS = 0x0, + LP8557_STEP_RAMP_50_MS, + LP8557_STEP_RAMP_100_MS, + LP8557_STEP_RAMP_200_MS, + LP8557_STEP_RAMP_MAX, +}; +enum lp8557_step_smoothing { + LP8557_STEP_SMOOTHING_NONE = 0x0, + LP8557_STEP_SMOOTHING_LIGHT, + LP8557_STEP_SMOOTHING_MEDIUM, + LP8557_STEP_SMOOTHING_HEAVY, + LP8557_STEP_SMOOTHING_MAX, +}; +#define LP8557_STEP 0x15 +#define LP8557_STEP_RAMP(x) (((x) & 0x3) << 0) +#define LP8557_STEP_SMOOTHING(x) (((x) & 0x3) << 6) + +struct mipi_dsi_device; +struct soc_nvidia_tegra210_config; + +struct panel_jdi { + struct mipi_dsi_device *dsi; + const struct soc_nvidia_tegra210_config *mode; + + /* Physical size */ + unsigned int width_mm; + unsigned int height_mm; + + int enabled; +}; + +struct panel_jdi *panel_jdi_dsi_probe(struct mipi_dsi_device *dsi); +int panel_jdi_prepare(struct panel_jdi *jdi); + +#endif diff --git a/src/soc/nvidia/tegra210/lp0/Makefile b/src/soc/nvidia/tegra210/lp0/Makefile new file mode 100644 index 0000000000..9dfe4b41f3 --- /dev/null +++ b/src/soc/nvidia/tegra210/lp0/Makefile @@ -0,0 +1,58 @@ +################################################################################ +## +## Copyright 2014 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. +## +################################################################################ + +CC = $(GCC_PREFIX)gcc +NM = $(GCC_PREFIX)nm +OBJCOPY = $(GCC_PREFIX)objcopy + +OPENSSL = openssl +DD = dd +CP = cp +MV = mv +RM = rm + +SIGKEY = 00000000000000000000000000000000 + +.PHONY: all +all: tegra_lp0_resume.fw + +tegra_lp0_resume.elf: tegra_lp0_resume.ld tegra_lp0_resume.c + $(CC) -marm -march=armv4t -mno-unaligned-access -nostdlib -static \ + -Os -fpie -Wl,--build-id=none -ggdb3 -T tegra_lp0_resume.ld \ + -o $@ $(filter %.c,$+) + +tegra_lp0_resume.fw: tegra_lp0_resume.elf + @# Get rid of any files we're about to create. + $(RM) -f $@.nosig $@.sig $@.tosig + @# Convert the ELF image into a binary image. + $(OBJCOPY) -O binary $< $@.nosig + @# Extract the part of the binary which needs to be signed. + $(DD) bs=1 skip=544 if=$@.nosig of=$@.tosig + @# Calculate a signature for that part. + $(OPENSSL) dgst -mac cmac -macopt cipher:aes-128-cbc \ + -macopt hexkey:$(SIGKEY) -md5 -binary \ + $@.tosig > $@.sig + @# Inject the signature into the binary image's header. + $(DD) conv=notrunc bs=1 seek=272 count=16 if=$@.sig of=$@.nosig + @# Copy the signed binary to the target file name. + $(MV) $@.nosig $@ + +clean: + $(RM) -f tegra_lp0_resume.fw tegra_lp0_resume.fw.sig + $(RM) -f tegra_lp0_resume.fw.tosig tegra_lp0_resume.elf diff --git a/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.c b/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.c new file mode 100644 index 0000000000..2011529332 --- /dev/null +++ b/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.c @@ -0,0 +1,691 @@ +/* + * Copyright 2014 Google Inc. + * Copyright 2013-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 <stdint.h> + +/* Function unit addresses. */ +enum { + UP_TAG_BASE = 0X60000000, + TIMER_BASE = 0X60005000, + CLK_RST_BASE = 0X60006000, + FLOW_CTLR_BASE = 0X60007000, + TEGRA_EVP_BASE = 0x6000f000, + APB_MISC_BASE = 0x70000000, + PMC_CTLR_BASE = 0X7000e400, + MC_CTLR_BASE = 0X70019000, +}; + + + +/* UP tag registers. */ +static uint32_t *up_tag_ptr = (void *)(UP_TAG_BASE + 0x0); +enum { + UP_TAG_AVP = 0xaaaaaaaa +}; + + +/* APB Misc JTAG Configuration Register */ +static uint32_t *misc_pp_config_ctl_ptr = (void *)(APB_MISC_BASE + 0x24); +enum { + PP_CONFIG_CTL_JTAG = 0x1 << 6 +}; + + +/* Timer registers. */ +static uint32_t *timer_us_ptr = (void *)(TIMER_BASE + 0x10); + + + +/* Clock and reset controller registers. */ +static uint32_t *clk_rst_rst_devices_l_ptr = (void *)(CLK_RST_BASE + 0x4); +enum { + SWR_TRIG_SYS_RST = 0x1 << 2 +}; + +static uint32_t *clk_rst_cclk_burst_policy_ptr = (void *)(CLK_RST_BASE + 0x20); +enum { + CCLK_PLLP_BURST_POLICY = 0x20004444 +}; + +static uint32_t *clk_rst_super_cclk_div_ptr = (void *)(CLK_RST_BASE + 0x24); +enum { + SUPER_CDIV_ENB = 0x1 << 31 +}; + +static uint32_t *clk_rst_osc_ctrl_ptr = (void *)(CLK_RST_BASE + 0x50); +enum { + OSC_XOE = 0x1 << 0, + OSC_XOFS_SHIFT = 4, + OSC_XOFS_MASK = 0x3f << OSC_XOFS_SHIFT, + OSC_FREQ_SHIFT = 28, + OSC_FREQ_MASK = 0xf << OSC_FREQ_SHIFT +}; +enum { + OSC_FREQ_13 = 0, + OSC_FREQ_16P8 = 1, + OSC_FREQ_19P2 = 4, + OSC_FREQ_38P4 = 5, + OSC_FREQ_12 = 8, + OSC_FREQ_48 = 9, + OSC_FREQ_26 = 12 +}; + +static uint32_t *clk_rst_pllu_base_ptr = (void *)(CLK_RST_BASE + 0xc0); +enum { + PLLU_DIVM_SHIFT = 0, + PLLU_DIVN_SHIFT = 8, + PLLU_OVERRIDE = 0x1 << 24, + PLLU_ENABLE = 0x1 << 30, + PLLU_BYPASS = 0x1 << 31 +}; + +static uint32_t *clk_rst_pllu_misc_ptr = (void *)(CLK_RST_BASE + 0xcc); +enum { + PLLU_LFCON_SHIFT = 4, + PLLU_CPCON_SHIFT = 8, + PLLU_LOCK_ENABLE = 22 +}; + +static uint32_t *clk_rst_pllx_base_ptr = (void *)(CLK_RST_BASE + 0xe0); +enum { + PLLX_ENABLE = 0x1 << 30 +}; + +static uint32_t *clk_rst_rst_dev_u_clr_ptr = (void *)(CLK_RST_BASE + 0x314); +enum { + SWR_CSITE_RST = 0x1 << 9 +}; + +static uint32_t *clk_rst_clk_enb_l_set_ptr = (void *)(CLK_RST_BASE + 0x320); +enum { + CLK_ENB_CPU = 0x1 << 0 +}; + +static uint32_t *clk_rst_clk_out_enb_u_set_ptr = + (void *)(CLK_RST_BASE + 0x330); +enum { + CLK_ENB_CSITE = 0x1 << 9 +}; + +static uint32_t *clk_rst_cpu_softrst_ctrl2_ptr = + (void *)(CLK_RST_BASE + 0x388); +enum { + CAR2PMC_CPU_ACK_WIDTH_SHIFT = 0, + CAR2PMC_CPU_ACK_WIDTH_MASK = 0xfff << CAR2PMC_CPU_ACK_WIDTH_SHIFT +}; + +static uint32_t *clk_rst_clk_enb_v_set_ptr = (void *)(CLK_RST_BASE + 0x440); +enum { + CLK_ENB_CPUG = 0x1 << 0, + CLK_ENB_CPULP = 0x1 << 1, +}; + +static uint32_t *clk_rst_rst_cpulp_cmplx_set_ptr = + (void *)(CLK_RST_BASE + 0x450); +enum { + SET_CXRESET0 = 0x1 << 20, + SET_CXRESET1 = 0x1 << 21 +}; +static uint32_t *clk_rst_rst_cpug_cmplx_clr_ptr = + (void *)(CLK_RST_BASE + 0x454); +enum { + CLR_CPURESET0 = 0x1 << 0, + CLR_CPURESET1 = 0x1 << 1, + CLR_CPURESET2 = 0x1 << 2, + CLR_CPURESET3 = 0x1 << 3, + CLR_DBGRESET0 = 0x1 << 12, + CLR_DBGRESET1 = 0x1 << 13, + CLR_DBGRESET2 = 0x1 << 14, + CLR_DBGRESET3 = 0x1 << 15, + CLR_CORERESET0 = 0x1 << 16, + CLR_CORERESET1 = 0x1 << 17, + CLR_CORERESET2 = 0x1 << 18, + CLR_CORERESET3 = 0x1 << 19, + CLR_CXRESET0 = 0x1 << 20, + CLR_CXRESET1 = 0x1 << 21, + CLR_CXRESET2 = 0x1 << 22, + CLR_CXRESET3 = 0x1 << 23, + CLR_L2RESET = 0x1 << 24, + CLR_NONCPURESET = 0x1 << 29, + CLR_PRESETDBG = 0x1 << 30 +}; + + +/* Reset vector. */ + +static uint32_t *evp_cpu_reset_ptr = (void *)(TEGRA_EVP_BASE + 0x100); + + + +/* Flow controller registers. */ +static uint32_t *flow_ctlr_halt_cop_events_ptr = + (void *)(FLOW_CTLR_BASE + 0x4); +enum { + EVENT_MSEC = 0x1 << 24, + EVENT_JTAG = 0x1 << 28, + FLOW_MODE_SHIFT = 29, + FLOW_MODE_STOP = 2 << FLOW_MODE_SHIFT, +}; + +static uint32_t *flow_ctlr_cluster_control_ptr = + (void *)(FLOW_CTLR_BASE + 0x2c); +enum { + FLOW_CLUSTER_ACTIVE_LP = 0x1 << 0 +}; + +static uint32_t *flow_ctlr_ram_repair_ptr = + (void *)(FLOW_CTLR_BASE + 0x40); +static uint32_t *flow_ctlr_ram_repair_cluster1_ptr = + (void *)(FLOW_CTLR_BASE + 0x58); +enum { + RAM_REPAIR_REQ = 0x1 << 0, + RAM_REPAIR_STS = 0x1 << 1, +}; + + +/* Power management controller registers. */ +enum { + PARTID_CRAIL = 0, + PARTID_CE0 = 14, + PARTID_C0NC = 15, +}; + +static uint32_t *pmc_ctlr_clamp_status_ptr = (void *)(PMC_CTLR_BASE + 0x2c); + +static uint32_t *pmc_ctlr_pwrgate_toggle_ptr = (void *)(PMC_CTLR_BASE + 0x30); +enum { + PWRGATE_TOGGLE_START = 0x1 << 8 +}; + +static uint32_t *pmc_ctlr_pwrgate_status_ptr = (void *)(PMC_CTLR_BASE + 0x38); + +static uint32_t *pmc_ctlr_cpupwrgood_timer_ptr = + (void *)(PMC_CTLR_BASE + 0xc8); + +static uint32_t *pmc_odmdata_ptr = (void *)(PMC_CTLR_BASE + 0xa0); + +static uint32_t *pmc_ctlr_scratch41_ptr = (void *)(PMC_CTLR_BASE + 0x140); +static uint32_t *pmc_ctlr_secure_scratch34_ptr = (void *)(PMC_CTLR_BASE + 0x368); +static uint32_t *pmc_ctlr_secure_scratch35_ptr = (void *)(PMC_CTLR_BASE + 0x36c); + +static uint32_t *pmc_ctlr_osc_edpd_over_ptr = (void *)(PMC_CTLR_BASE + 0x1a4); +enum { + PMC_XOFS_SHIFT = 1, + PMC_XOFS_MASK = 0x3f << PMC_XOFS_SHIFT +}; + + + +/* Memory controller registers. */ +static uint32_t *mc_video_protect_size_mb_ptr = (void *)(MC_CTLR_BASE + 0x64c); + +static uint32_t *mc_video_protect_reg_ctrl_ptr = + (void *)(MC_CTLR_BASE + 0x650); +enum { + VIDEO_PROTECT_WRITE_ACCESS_DISABLE = 0x1 << 0, + VIDEO_PROTECT_ALLOW_TZ_WRITE_ACCESS = 0x1 << 1 +}; + + +/* Utility functions. */ + +static inline void __attribute__((always_inline)) + __attribute__((noreturn)) halt(void) +{ + for (;;); +} + +inline static uint32_t read32(const void *addr) +{ + return *(volatile uint32_t *)addr; +} + +inline static void write32(uint32_t val, void *addr) +{ + *(volatile uint32_t *)addr = val; +} + +inline static void setbits32(uint32_t bits, void *addr) +{ + write32(addr, read32(addr) | bits); +} + +inline static void clrbits32(uint32_t bits, void *addr) +{ + write32(addr, read32(addr) & ~bits); +} + +static void __attribute__((noreturn)) reset(void) +{ + write32(clk_rst_rst_devices_l_ptr, SWR_TRIG_SYS_RST); + halt(); +} + +static void udelay(unsigned usecs) +{ + uint32_t start = read32(timer_us_ptr); + while (read32(timer_us_ptr) - start < usecs) + ; +} + +/* UART related defines */ +static uint32_t *uart_clk_out_enb_regs[4] = { + (uint32_t *)0x60006010, + (uint32_t *)0x60006010, + (uint32_t *)0x60006014, + (uint32_t *)0x60006018 +}; + +static uint32_t *uart_rst_devices_regs[4] = { + (uint32_t *)0x60006004, + (uint32_t *)0x60006004, + (uint32_t *)0x60006008, + (uint32_t *)0x6000600c +}; + +static uint32_t uart_enable_mask[4] = { + 1 << 6, + 1 << 7, + 1 << 23, + 1 << 1 +}; + +static uint32_t *uart_clk_source_regs[4] = { + (uint32_t *)0x60006178, + (uint32_t *)0x6000617c, + (uint32_t *)0x600061a0, + (uint32_t *)0x600061c0, +}; + +static uint32_t *uart_base_regs[4] = { + (uint32_t *)0x70006000, + (uint32_t *)0x70006040, + (uint32_t *)0x70006200, + (uint32_t *)0x70006300, +}; +enum { + UART_THR_DLAB = 0x0, + UART_IER_DLAB = 0x1, + UART_IIR_FCR = 0x2, + UART_LCR = 0x3 +}; +enum { + UART_RATE_115200 = (408000000/115200/16), /* based on 408000000 PLLP */ + FCR_TX_CLR = 0x4, /* bit 2 of FCR : clear TX FIFO */ + FCR_RX_CLR = 0x2, /* bit 1 of FCR : clear RX FIFO */ + FCR_EN_FIFO = 0x1, /* bit 0 of FCR : enable TX & RX FIFO */ + LCR_DLAB = 0x80, /* bit 7 of LCR : Divisor Latch Access Bit */ + LCR_WD_SIZE_8 = 0x3, /* bit 1:0 of LCR : word length of 8 */ +}; + +static void enable_uart(void) +{ + uint32_t *uart_clk_enb_reg; + uint32_t *uart_rst_reg; + uint32_t *uart_clk_source; + uint32_t uart_port; + uint32_t uart_mask; + uint32_t *uart_base; + + /* + * Read odmdata (stored in pmc->odmdata) to determine debug uart port. + * + * Bits 15-17 of odmdata contains debug uart port. + * 0 : UARTA + * 1 : UARTB + * 2 : UARTC + * 3 : UARTD + */ + uart_port = (read32(pmc_odmdata_ptr) >> 15) & 0x7; + + /* Default to UARTA if uart_port is out of range */ + if (uart_port >= 4) + uart_port = 0; + + uart_clk_enb_reg = uart_clk_out_enb_regs[uart_port]; + uart_rst_reg = uart_rst_devices_regs[uart_port]; + uart_mask = uart_enable_mask[uart_port]; + uart_clk_source = uart_clk_source_regs[uart_port]; + uart_base = uart_base_regs[uart_port]; + + /* Enable UART clock */ + setbits32(uart_mask, uart_clk_enb_reg); + + /* Reset and unreset UART */ + setbits32(uart_mask, uart_rst_reg); + clrbits32(uart_mask, uart_rst_reg); + + /* Program UART clock source: PLLP (408000000) */ + write32(uart_clk_source, 0); + + /* Program 115200n8 to the uart port */ + /* baud-rate of 115200 */ + write32((uart_base + UART_LCR), LCR_DLAB); + write32((uart_base + UART_THR_DLAB), (UART_RATE_115200 & 0xff)); + write32((uart_base + UART_IER_DLAB), (UART_RATE_115200 >> 8)); + /* 8-bit and no parity */ + write32((uart_base + UART_LCR), LCR_WD_SIZE_8); + /* enable and clear RX/TX FIFO */ + write32((uart_base + UART_IIR_FCR), + (FCR_TX_CLR + FCR_RX_CLR + FCR_EN_FIFO)); +} + +/* Accessors. */ + +static uint32_t get_wakeup_vector(void) +{ + return read32(pmc_ctlr_scratch41_ptr); +} + +static unsigned get_osc_freq(void) +{ + return (read32(clk_rst_osc_ctrl_ptr) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + + +/* Jtag configuration. */ + +static void enable_jtag(void) +{ + write32(misc_pp_config_ctl_ptr, PP_CONFIG_CTL_JTAG); +} + +/* Clock configuration. */ + +static void config_oscillator(void) +{ + // Read oscillator drive strength from OSC_EDPD_OVER.XOFS and copy + // to OSC_CTRL.XOFS and set XOE. + uint32_t xofs = (read32(pmc_ctlr_osc_edpd_over_ptr) & + PMC_XOFS_MASK) >> PMC_XOFS_SHIFT; + + uint32_t osc_ctrl = read32(clk_rst_osc_ctrl_ptr); + osc_ctrl &= ~OSC_XOFS_MASK; + osc_ctrl |= (xofs << OSC_XOFS_SHIFT); + osc_ctrl |= OSC_XOE; + write32(clk_rst_osc_ctrl_ptr, osc_ctrl); +} + +static void config_pllu(void) +{ + // Figure out what parameters to use for PLLU. + uint32_t divm, divn, cpcon, lfcon; + switch (get_osc_freq()) { + case OSC_FREQ_12: + case OSC_FREQ_48: + divm = 0x0c; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + break; + case OSC_FREQ_16P8: + divm = 0x07; + divn = 0x190; + cpcon = 0x05; + lfcon = 0x02; + break; + case OSC_FREQ_19P2: + case OSC_FREQ_38P4: + divm = 0x04; + divn = 0xc8; + cpcon = 0x03; + lfcon = 0x02; + break; + case OSC_FREQ_26: + divm = 0x1a; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + break; + default: + // Map anything that's not recognized to 13MHz. + divm = 0x0d; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + } + + // Configure PLLU. + uint32_t base = PLLU_BYPASS | PLLU_OVERRIDE | + (divn << PLLU_DIVN_SHIFT) | (divm << PLLU_DIVM_SHIFT); + write32(clk_rst_pllu_base_ptr, base); + uint32_t misc = (cpcon << PLLU_CPCON_SHIFT) | + (lfcon << PLLU_LFCON_SHIFT); + write32(clk_rst_pllu_misc_ptr, misc); + + // Enable PLLU. + base &= ~PLLU_BYPASS; + base |= PLLU_ENABLE; + write32(clk_rst_pllu_base_ptr, base); + misc |= PLLU_LOCK_ENABLE; + write32(clk_rst_pllu_misc_ptr, misc); +} + +static void enable_cpu_clocks(void) +{ + // Enable the CPU complex clock. + write32(clk_rst_clk_enb_l_set_ptr, CLK_ENB_CPU); + write32(clk_rst_clk_enb_v_set_ptr, CLK_ENB_CPUG | CLK_ENB_CPULP); +} + + + +/* Function unit configuration. */ + +static void config_core_sight(void) +{ + // Enable the CoreSight clock. + write32(clk_rst_clk_out_enb_u_set_ptr, CLK_ENB_CSITE); + + /* + * De-assert CoreSight reset. + * NOTE: We're leaving the CoreSight clock on the oscillator for + * now. It will be restored to its original clock source + * when the CPU-side restoration code runs. + */ + write32(clk_rst_rst_dev_u_clr_ptr, SWR_CSITE_RST); +} + + +/* Resets. */ + +static void clear_cpu_resets(void) +{ + /* Hold CPU1 in reset */ + setbits32(SET_CXRESET1, clk_rst_rst_cpulp_cmplx_set_ptr); + + write32(clk_rst_rst_cpug_cmplx_clr_ptr, + CLR_NONCPURESET | CLR_L2RESET | CLR_PRESETDBG); + + write32(clk_rst_rst_cpug_cmplx_clr_ptr, + CLR_CPURESET0 | CLR_DBGRESET0 | CLR_CORERESET0 | CLR_CXRESET0); +} + + + +/* RAM repair */ + +void ram_repair(void) +{ + // Request Cluster0 RAM repair. + setbits32(RAM_REPAIR_REQ, flow_ctlr_ram_repair_ptr); + // Poll for Cluster0 RAM repair status. + while (!(read32(flow_ctlr_ram_repair_ptr) & RAM_REPAIR_STS)) + ; + + // Request Cluster1 RAM repair. + setbits32(RAM_REPAIR_REQ, flow_ctlr_ram_repair_cluster1_ptr); + // Poll for Cluster1 RAM repair status. + while (!(read32(flow_ctlr_ram_repair_cluster1_ptr) & RAM_REPAIR_STS)) + ; +} + + +/* Power. */ + +static void power_on_partition(unsigned id) +{ + uint32_t bit = 0x1 << id; + if (!(read32(pmc_ctlr_pwrgate_status_ptr) & bit)) { + // Partition is not on. Turn it on. + write32(pmc_ctlr_pwrgate_toggle_ptr, + id | PWRGATE_TOGGLE_START); + + // Wait until the partition is powerd on. + while (!(read32(pmc_ctlr_pwrgate_status_ptr) & bit)) + ; + + // Wait until clamp is off. + while (read32(pmc_ctlr_clamp_status_ptr) & bit) + ; + } +} + +static void power_on_main_cpu(void) +{ + /* + * Reprogram PMC_CPUPWRGOOD_TIMER register: + * + * XXX This is a fragile assumption. XXX + * The kernel prepares PMC_CPUPWRGOOD_TIMER based on a 32768Hz clock. + * Note that PMC_CPUPWRGOOD_TIMER is running at pclk. + * + * We need to reprogram PMC_CPUPWRGOOD_TIMER based on the current pclk + * which is at 204Mhz (pclk = sclk = pllp_out2) after BootROM. Multiply + * PMC_CPUPWRGOOD_TIMER by 204M / 32K. + * + * Save the original PMC_CPUPWRGOOD_TIMER register which we need to + * restore after the CPU is powered up. + */ + uint32_t orig_timer = read32(pmc_ctlr_cpupwrgood_timer_ptr); + + write32(pmc_ctlr_cpupwrgood_timer_ptr, + orig_timer * (204000000 / 32768)); + + power_on_partition(PARTID_CRAIL); + power_on_partition(PARTID_C0NC); + power_on_partition(PARTID_CE0); + + // Restore the original PMC_CPUPWRGOOD_TIMER. + write32(pmc_ctlr_cpupwrgood_timer_ptr, orig_timer); +} + + +static void aarch64_trampoline(void) +{ + uint32_t val = 3; /* bit1: to warm reset; bit0: AARCH64*/ + + asm volatile ("mcr p15, 0, %0, c12, c0, 2" : : "r" (val)); + + /* unreachable */ + halt(); +} + + +/* Entry point. */ + +void lp0_resume(void) +{ + // If not on the AVP, reset. + if (read32(up_tag_ptr) != UP_TAG_AVP) + reset(); + + // Enable JTAG + enable_jtag(); + + config_oscillator(); + + // Program SUPER_CCLK_DIVIDER. + write32(clk_rst_super_cclk_div_ptr, SUPER_CDIV_ENB); + + config_core_sight(); + + enable_uart(); + + config_pllu(); + + /* + * Set the CPU reset vector. + * + * T210 always resets to AARCH32 and SW needs to write RMR_EL3 + * to bootstrap into AARCH64. + */ + write32(pmc_ctlr_secure_scratch34_ptr, get_wakeup_vector()); + write32(pmc_ctlr_secure_scratch35_ptr, 0); + write32(evp_cpu_reset_ptr, (uint32_t)aarch64_trampoline); + + // Select CPU complex clock source. + write32(clk_rst_cclk_burst_policy_ptr, CCLK_PLLP_BURST_POLICY); + + // Disable PLLX since it isn't used as CPU clock source. + clrbits32(PLLX_ENABLE, clk_rst_pllx_base_ptr); + + // Set CAR2PMC_CPU_ACK_WIDTH to 408. + uint32_t ack_width = read32(clk_rst_cpu_softrst_ctrl2_ptr); + ack_width &= ~CAR2PMC_CPU_ACK_WIDTH_MASK; + ack_width |= 408 << CAR2PMC_CPU_ACK_WIDTH_SHIFT; + write32(clk_rst_cpu_softrst_ctrl2_ptr, ack_width); + + // Disable VPR. + write32(mc_video_protect_size_mb_ptr, 0); + write32(mc_video_protect_reg_ctrl_ptr, + VIDEO_PROTECT_WRITE_ACCESS_DISABLE); + + enable_cpu_clocks(); + + power_on_main_cpu(); + + // Perform ram repair after cpu is powered on. + ram_repair(); + + clear_cpu_resets(); + + // Halt the AVP. + while (1) + write32(flow_ctlr_halt_cop_events_ptr, + FLOW_MODE_STOP | EVENT_JTAG); +} + + + +/* Header. */ + +extern uint8_t blob_data; +extern uint8_t blob_data_size; +extern uint8_t blob_total_size; + +struct lp0_header { + uint32_t length_insecure; // Insecure total length. + uint32_t reserved[3]; + uint8_t rsa_modulus[256]; // RSA key modulus. + uint8_t aes_signature[16]; // AES signature. + uint8_t rsa_signature[256]; // RSA-PSS signature. + uint8_t random_aes_block[16]; // Random data, may be zero. + uint32_t length_secure; // Secure total length. + uint32_t destination; // Where to load the blob in iRAM. + uint32_t entry_point; // Entry point for the blob. + uint32_t code_length; // Length of just the data. +} __attribute__((packed)); + +struct lp0_header header __attribute__((section(".header"))) = +{ + .length_insecure = (uintptr_t)&blob_total_size, + .length_secure = (uintptr_t)&blob_total_size, + .destination = (uintptr_t)&blob_data, + .entry_point = (uintptr_t)&lp0_resume, + .code_length = (uintptr_t)&blob_data_size +}; diff --git a/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.ld b/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.ld new file mode 100644 index 0000000000..26cacbd8ee --- /dev/null +++ b/src/soc/nvidia/tegra210/lp0/tegra_lp0_resume.ld @@ -0,0 +1,73 @@ +/* + * Copyright 2014 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. + */ + +/* 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) + +PHDRS +{ + to_load PT_LOAD; +} + +ENTRY(lp0_resume) +SECTIONS +{ + . = 0x40020000 - 0x240; + + /* + * The lp0 blob header is built as a static data structure and put + * in the .header section. + */ + .header_start = .; + .header . : { + *(.header); + } : to_load = 0xff + .header_end = .; + + . = 0x40020000; + + /* The actual lp0 blob code. */ + .data_start = .; + .data . : { + *(.text); + *(.text.*); + *(.rodata); + *(.rodata.*); + *(.data); + *(.data.*); + *(.bss); + *(.bss.*); + *(.sbss); + *(.sbss.*); + . = ALIGN(16); + } + .data_end = .; + + /* Some values we need in the header. */ + blob_data = .data_start; + blob_data_size = .data_end - .data_start; + blob_total_size = .data_end - .header_start; + + /DISCARD/ : { + *(.comment) + *(.note) + *(.comment.*) + *(.note.*) + *(.ARM.*) + } +} diff --git a/src/soc/nvidia/tegra210/maincpu.S b/src/soc/nvidia/tegra210/maincpu.S new file mode 100644 index 0000000000..898d821d7e --- /dev/null +++ b/src/soc/nvidia/tegra210/maincpu.S @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <arch/asm.h> + +ENTRY(maincpu_setup) + /* + * Set the cpu to System mode with IRQ and FIQ disabled. Prefetch/Data + * aborts may happen early and crash before the abort handlers are + * installed, but at least the problem will show up near the code that + * causes it. + */ + msr cpsr, #0xdf + + ldr sp, maincpu_stack_pointer + eor lr, lr + ldr r0, maincpu_entry_point + bx r0 +ENDPROC(maincpu_setup) + + .align 2 + + .global maincpu_stack_pointer +maincpu_stack_pointer: + .word 0 + + .global maincpu_entry_point +maincpu_entry_point: + .word 0 diff --git a/src/soc/nvidia/tegra210/mipi-phy.c b/src/soc/nvidia/tegra210/mipi-phy.c new file mode 100644 index 0000000000..fc9d9de930 --- /dev/null +++ b/src/soc/nvidia/tegra210/mipi-phy.c @@ -0,0 +1,92 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> + +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <device/device.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/display.h> +#include <soc/mipi_dsi.h> +#include <soc/mipi_display.h> +#include <soc/tegra_dsi.h> +#include <soc/mipi-phy.h> + +int mipi_dphy_set_timing(struct tegra_dsi *dsi) +{ + + u32 freq = (dsi->clk_rate * 2) / 1000000; + + u32 thsdexit = (DSI_PHY_TIMING_DIV(120, (freq))); + u32 thstrial = (((3) + (DSI_PHY_TIMING_DIV((DSI_THSTRAIL_VAL(freq)), + freq)))); + u32 tdatzero = DSI_PHY_TIMING_DIV(((145) + (5 * (DSI_TBIT(freq)))), + (freq)); + u32 thsprepare = DSI_PHY_TIMING_DIV((65 + (5*(DSI_TBIT(freq)))), freq); + u32 tclktrial = (DSI_PHY_TIMING_DIV(80, freq)); + u32 tclkpost = ((DSI_PHY_TIMING_DIV(((70) + ((52) * (DSI_TBIT(freq)))), + freq))); + u32 tclkzero = (DSI_PHY_TIMING_DIV(260, freq)); + u32 ttlpx = (DSI_PHY_TIMING_DIV(60, freq)) ; + u32 tclkprepare = (DSI_PHY_TIMING_DIV(60, freq)); + u32 tclkpre = 1; //min = 8*UI per mipi spec, tclk_pre=0 should be ok, but using 1 value + u32 twakeup = 0x7F; //min = 1ms + + u32 ttaget; + u32 ttassure; + u32 ttago; + u32 value; + + if (!ttlpx) { + ttaget = 5; + ttassure = 2; + ttago = 4; + } else { + ttaget = 5 * ttlpx; + ttassure = 2 * ttlpx; + ttago = 4 * ttlpx; + } + + value = (thsdexit << 24) | + (thstrial << 16) | + (tdatzero << 8) | + (thsprepare << 0); + tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0); + + value = (tclktrial << 24) | + (tclkpost << 16) | + (tclkzero << 8) | + (ttlpx << 0); + tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1); + + value = (tclkprepare << 16) | + (tclkpre << 8) | + (twakeup << 0); + tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2); + + value = (ttaget << 16) | + (ttassure << 8) | + (ttago << 0), + tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); + return 0; +} diff --git a/src/soc/nvidia/tegra210/mipi.c b/src/soc/nvidia/tegra210/mipi.c new file mode 100644 index 0000000000..003530883c --- /dev/null +++ b/src/soc/nvidia/tegra210/mipi.c @@ -0,0 +1,204 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <device/device.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/display.h> +#include <soc/mipi_dsi.h> +#include <soc/tegra_dsi.h> +#include "jdi_25x18_display/panel-jdi-lpm102a188a.h" + +static unsigned long dsi_pads[] = { + 0x0c0, /* DSIA channel A & B pads */ + 0x300, /* DSIB channel A & B pads */ +}; + +static struct tegra_mipi mipi_data = { + .regs = (void *)TEGRA_MIPI_CAL_BASE, +}; + +static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi, + unsigned long reg) +{ + return read32(mipi->regs + (reg << 2)); +} + +static inline void tegra_mipi_writel(struct tegra_mipi *mipi, + unsigned long value, unsigned long reg) +{ + write32(mipi->regs + (reg << 2), value); +} + +static const struct tegra_mipi_pad tegra210_mipi_pads[] = { + { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 }, + { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK }, + { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK }, + { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK }, + { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK }, +}; + +static const struct tegra_mipi_soc tegra210_mipi_soc = { + .has_clk_lane = 1, + .pads = tegra210_mipi_pads, + .num_pads = ARRAY_SIZE(tegra210_mipi_pads), + .clock_enable_override = 1, + .needs_vclamp_ref = 0, + .pad_drive_down_ref = 0x0, + .pad_drive_up_ref = 0x3, + .pad_vclamp_level = 0x1, + .pad_vauxp_level = 0x1, + .hspdos = 0x0, + .hspuos = 0x2, + .termos = 0x0, + .hsclkpdos = 0x0, + .hsclkpuos = 0x2, +}; + +struct tegra_mipi_device *tegra_mipi_request(struct tegra_mipi_device *device, + int device_index) +{ + device->mipi = &mipi_data; + device->mipi->soc = &tegra210_mipi_soc; + device->pads = dsi_pads[device_index]; + + return device; +} + +static int tegra_mipi_wait(struct tegra_mipi *mipi) +{ + u32 poll_interval_us = 1000; + u32 timeout_us = 250 * 1000; + unsigned long value; + + do { + value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS); + if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 && + (value & MIPI_CAL_STATUS_DONE) != 0) + return 0; + + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + + udelay(poll_interval_us); + } while (1); + + printk(BIOS_ERR, "%s: ERROR: timeout\n", __func__); + return -ETIMEDOUT; +} + +int tegra_mipi_calibrate(struct tegra_mipi_device *device) +{ + const struct tegra_mipi_soc *soc = device->mipi->soc; + unsigned int i; + u32 value; + int err; + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0); + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; + + if (soc->needs_vclamp_ref) + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; + + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0); + + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) | + MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref); + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); + value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level); + value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level); + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2); + + for (i = 0; i < soc->num_pads; i++) { + u32 clk = 0, data = 0; + + if (device->pads & BIT(i)) { + data = MIPI_CAL_CONFIG_SELECT | + MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) | + MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) | + MIPI_CAL_CONFIG_TERMOS(soc->termos); + clk = MIPI_CAL_CONFIG_SELECT | + MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) | + MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos); + } + + tegra_mipi_writel(device->mipi, data, soc->pads[i].data); + + if (soc->has_clk_lane && soc->pads[i].clk != 0) + tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk); + } + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL); + value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); + value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); + value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa); + value |= MIPI_CAL_CTRL_PRESCALE(0x2); + + if (!soc->clock_enable_override) + value &= ~MIPI_CAL_CTRL_CLKEN_OVR; + else + value |= MIPI_CAL_CTRL_CLKEN_OVR; + + tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL); + + /* clear any pending status bits */ + value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS); + tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL); + value |= MIPI_CAL_CTRL_START; + tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL); + + err = tegra_mipi_wait(device->mipi); + if (err < 0) + printk(BIOS_ERR, "failed to calibrate MIPI pads: %d\n", err); + else + printk(BIOS_INFO, "MIPI calibration done\n"); + + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0); + + if (soc->needs_vclamp_ref) + value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; + + value |= MIPI_CAL_BIAS_PAD_PDVCLAMP; + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0); + + return err; +} diff --git a/src/soc/nvidia/tegra210/mipi_dsi.c b/src/soc/nvidia/tegra210/mipi_dsi.c new file mode 100644 index 0000000000..5e4cf55602 --- /dev/null +++ b/src/soc/nvidia/tegra210/mipi_dsi.c @@ -0,0 +1,431 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ +/* + * MIPI DSI Bus + * + * Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd. + * Andrzej Hajda <a.hajda@samsung.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> +#include <delay.h> +#include <string.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <device/device.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/display.h> +#include <soc/mipi_dsi.h> +#include <soc/mipi_display.h> +#include <soc/tegra_dsi.h> + +struct mipi_dsi_device mipi_dsi_device_data[NUM_DSI] = { + { + .master = NULL, + .slave = &mipi_dsi_device_data[DSI_B], + }, + { + .master = &mipi_dsi_device_data[DSI_A], + .slave = NULL, + }, +}; + +static struct mipi_dsi_device * +mipi_dsi_device_alloc(struct mipi_dsi_host *host) +{ + static int index = 0; + struct mipi_dsi_device *dsi; + + if (index >= NUM_DSI) + return (void *)-EPTR; + + dsi = &mipi_dsi_device_data[index++]; + dsi->host = host; + return dsi; +} + +static struct mipi_dsi_device * +of_mipi_dsi_device_add(struct mipi_dsi_host *host) +{ + struct mipi_dsi_device *dsi; + u32 reg = 0; + + dsi = mipi_dsi_device_alloc(host); + if (IS_ERR_PTR(dsi)) { + printk(BIOS_ERR, "failed to allocate DSI device\n"); + return dsi; + } + + dsi->channel = reg; + host->dev = (void *)dsi; + + return dsi; +} + +int mipi_dsi_host_register(struct mipi_dsi_host *host) +{ + of_mipi_dsi_device_add(host); + return 0; +} + +/** + * mipi_dsi_attach - attach a DSI device to its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_attach(struct mipi_dsi_device *dsi) +{ + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (!ops || !ops->attach) + return -ENOSYS; + + return ops->attach(dsi->host, dsi); +} + +/** + * mipi_dsi_detach - detach a DSI device from its DSI host + * @dsi: DSI peripheral + */ +int mipi_dsi_detach(struct mipi_dsi_device *dsi) +{ + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (!ops || !ops->detach) + return -ENOSYS; + + return ops->detach(dsi->host, dsi); +} + +/** + * mipi_dsi_enslave() - use a MIPI DSI peripheral as slave for dual-channel + * operation + * @master: master DSI peripheral device + * @slave: slave DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_enslave(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave) +{ + int err = 0; + + slave->master = master; + master->slave = slave; + + if (master->ops && master->ops->enslave) + err = master->ops->enslave(master, slave); + + return err; +} + +/** + * mipi_dsi_liberate() - stop using a MIPI DSI peripheral as slave for dual- + * channel operation + * @master: master DSI peripheral device + * @slave: slave DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_liberate(struct mipi_dsi_device *master, + struct mipi_dsi_device *slave) +{ + int err = 0; + + if (master->ops && master->ops->liberate) + err = master->ops->liberate(master, slave); + + master->slave = NULL; + slave->master = NULL; + + return err; +} + +/** + * mipi_dsi_dcs_write() - send DCS write command + * @dsi: DSI peripheral device + * @cmd: DCS command + * @data: buffer containing the command payload + * @len: command payload length + * + * This function will automatically choose the right data type depending on + * the command payload length. + * + * Return: The number of bytes successfully transmitted or a negative error + * code on failure. + */ +ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd, + const void *data, size_t len) +{ + struct mipi_dsi_msg msg; + ssize_t err; + size_t size; + + u8 buffer[MAX_DSI_HOST_FIFO_DEPTH + 4]; + u8 *tx = buffer; + + if (len > MAX_DSI_HOST_FIFO_DEPTH) { + printk(BIOS_ERR, "%s: Error: too large payload length: %zu\n", + __func__, len); + + return -EINVAL; + } + + if (len > 0) { + unsigned int offset = 0; + + /* + * DCS long write packets contain the word count in the header + * bytes 1 and 2 and have a payload containing the DCS command + * byte folowed by word count minus one bytes. + * + * DCS short write packets encode the DCS command and up to + * one parameter in header bytes 1 and 2. + */ + if (len > 1) + size = 3 + len; + else + size = 1 + len; + + /* write word count to header for DCS long write packets */ + if (len > 1) { + tx[offset++] = ((1 + len) >> 0) & 0xff; + tx[offset++] = ((1 + len) >> 8) & 0xff; + } + + /* write the DCS command byte followed by the payload */ + tx[offset++] = cmd; + memcpy(tx + offset, data, len); + } else { + tx = &cmd; + size = 1; + } + + memset(&msg, 0, sizeof(msg)); + msg.flags = MIPI_DSI_MSG_USE_LPM; + msg.channel = dsi->channel; + msg.tx_len = size; + msg.tx_buf = tx; + + switch (len) { + case 0: + msg.type = MIPI_DSI_DCS_SHORT_WRITE; + break; + case 1: + msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; + break; + default: + msg.type = MIPI_DSI_DCS_LONG_WRITE; + break; + } + + err = dsi->host->ops->transfer(dsi->host, &msg); + + return err; +} + +/** + * mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display + * module + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_display_on() - start displaying the image data on the + * display device + * @dsi: DSI peripheral device + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_column_address() - define the column extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first column of frame memory + * @end: last column of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start, + u16 end) +{ + u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload, + sizeof(payload)); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_page_address() - define the page extent of the frame + * memory accessed by the host processor + * @dsi: DSI peripheral device + * @start: first page of frame memory + * @end: last page of frame memory + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start, + u16 end) +{ + u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff }; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload, + sizeof(payload)); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect + * output signal on the TE signal line. + * @dsi: DSI peripheral device + * @mode: the Tearing Effect Output Line mode + * + * Return: 0 on success or a negative error code on failure + */ +int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi, + enum mipi_dsi_dcs_tear_mode mode) +{ + u8 value = mode; + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value, + sizeof(value)); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image + * data used by the interface + * @dsi: DSI peripheral device + * @format: pixel format + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format) +{ + ssize_t err; + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format, + sizeof(format)); + if (err < 0) + return err; + + return 0; +} + +/** + * mipi_dsi_dcs_set_address_mode() - sets the data order for forward transfers + * from the host to the peripheral + * @dsi: DSI peripheral device + * @reverse_page_address: reverses the page addressing to bottom->top + * @reverse_col_address: reverses the column addressing to right->left + * @reverse_page_col_address: reverses the page/column addressing order + * @refresh_from_bottom: refresh the display bottom to top + * @reverse_rgb: send pixel data bgr instead of rgb + * @latch_right_to_left: latch the incoming display data right to left + * @flip_horizontal: flip the image horizontally, left to right + * @flip_vertical: flip the image vertically, top to bottom + * + * Return: 0 on success or a negative error code on failure. + */ +int mipi_dsi_dcs_set_address_mode(struct mipi_dsi_device *dsi, + bool reverse_page_address, + bool reverse_col_address, + bool reverse_page_col_address, + bool refresh_from_bottom, + bool reverse_rgb, + bool latch_right_to_left, + bool flip_horizontal, + bool flip_vertical) +{ + ssize_t err; + u8 data; + + data = ((flip_vertical ? 1 : 0) << 0) | + ((flip_horizontal ? 1 : 0) << 1) | + ((latch_right_to_left ? 1 : 0) << 2) | + ((reverse_rgb ? 1 : 0) << 3) | + ((refresh_from_bottom ? 1 : 0) << 4) | + ((reverse_page_col_address ? 1 : 0) << 5) | + ((reverse_col_address ? 1 : 0) << 6) | + ((reverse_page_address ? 1 : 0) << 7); + + err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_ADDRESS_MODE, &data, 1); + if (err < 0) + return err; + + return 0; +} diff --git a/src/soc/nvidia/tegra210/mmu_operations.c b/src/soc/nvidia/tegra210/mmu_operations.c new file mode 100644 index 0000000000..2ee6b80ac4 --- /dev/null +++ b/src/soc/nvidia/tegra210/mmu_operations.c @@ -0,0 +1,83 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/mmu.h> +#include <memrange.h> +#include <soc/addressmap.h> +#include <soc/mmu_operations.h> +#include <stdlib.h> +#include <stdint.h> + +/* This structure keeps track of all the mmap memory ranges for t210 */ +static struct memranges t210_mmap_ranges; + +static void tegra210_memrange_init(struct memranges *map) +{ + uint64_t start,end; + const unsigned long devmem = MA_DEV | MA_S | MA_RW; + const unsigned long cachedmem = MA_MEM | MA_NS | MA_RW; + const unsigned long secure_mem = MA_MEM | MA_S | MA_RW; + uintptr_t tz_base_mib; + size_t tz_size_mib; + + print_carveouts(); + + memranges_init_empty(map); + + memory_in_range_below_4gb(&start,&end); + + /* Device memory below DRAM */ + memranges_insert(map, 0, start * MiB, devmem); + + /* DRAM */ + memranges_insert(map, start * MiB, (end-start) * MiB, cachedmem); + + memory_in_range_above_4gb(&start,&end); + + memranges_insert(map, start * MiB, (end-start) * MiB, cachedmem); + + /* SRAM */ + memranges_insert(map, TEGRA_SRAM_BASE, TEGRA_SRAM_SIZE, cachedmem); + + /* Add TZ carveout. */ + carveout_range(CARVEOUT_TZ, &tz_base_mib, &tz_size_mib); + memranges_insert(map, tz_base_mib * MiB, tz_size_mib * MiB, secure_mem); +} + +void __attribute__((weak)) mainboard_add_memory_ranges(struct memranges *map) +{ + /* Don't add any ranges by default. */ +} + +void tegra210_mmu_init(void) +{ + uintptr_t tz_base_mib; + size_t tz_size_mib; + size_t ttb_size_mib; + struct memranges *map = &t210_mmap_ranges; + + tegra210_memrange_init(map); + mainboard_add_memory_ranges(map); + /* Place page tables at the base of the trust zone region. */ + carveout_range(CARVEOUT_TZ, &tz_base_mib, &tz_size_mib); + tz_base_mib *= MiB; + ttb_size_mib = TTB_SIZE * MiB; + mmu_init(map, (void *)tz_base_mib, ttb_size_mib); + mmu_enable(); +} diff --git a/src/soc/nvidia/tegra210/monotonic_timer.c b/src/soc/nvidia/tegra210/monotonic_timer.c new file mode 100644 index 0000000000..560a7a5f31 --- /dev/null +++ b/src/soc/nvidia/tegra210/monotonic_timer.c @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <soc/addressmap.h> +#include <timer.h> + +void timer_monotonic_get(struct mono_time *mt) +{ + mono_time_set_usecs(mt, read32((void *)TEGRA_TMRUS_BASE)); +} diff --git a/src/soc/nvidia/tegra210/mtc.c b/src/soc/nvidia/tegra210/mtc.c new file mode 100644 index 0000000000..fb6c9cbdd7 --- /dev/null +++ b/src/soc/nvidia/tegra210/mtc.c @@ -0,0 +1,89 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 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. + */ + +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <soc/mtc.h> +#include <string.h> + +static size_t mtc_table_size; + +#define MAX_MTC_TABLE_ENTRIES 20 +#define MTC_TABLE_ENTRY_SIZE 4880 +#define MTC_TABLE_MAX_SIZE (MAX_MTC_TABLE_ENTRIES * MTC_TABLE_ENTRY_SIZE) + +int tegra210_run_mtc(void) +{ + ssize_t nread; + struct region_device fh; + + void * const mtc = (void *)(uintptr_t)CONFIG_MTC_ADDRESS; + void *dvfs_table; + size_t (*mtc_fw)(void **dvfs_table) = (void *)mtc; + + if (cbfs_boot_locate(&fh, "tegra_mtc.bin", NULL)) { + printk(BIOS_ERR, "MTC file not found: tegra_mtc.bin\n"); + return -1; + } + + /* Read MTC file into predefined region. */ + nread = rdev_readat(&fh, mtc, 0, region_device_sz(&fh)); + + if (nread != region_device_sz(&fh)) { + printk(BIOS_ERR, "MTC bytes read (%zu) != file length(%zu)!\n", + nread, region_device_sz(&fh)); + return -1; + } + + printk(BIOS_INFO, "MTC: %zu bytes loaded @ %p\n", nread, mtc); + + mtc_table_size = (*mtc_fw)(&dvfs_table); + + if ((mtc_table_size == 0) || (mtc_table_size > MTC_TABLE_MAX_SIZE)) { + printk(BIOS_ERR, "MTC Training table size is invalid.!\n"); + return -1; + } + + printk(BIOS_INFO, "MTC: Done. Entries size 0x%zx located at %p\n", + mtc_table_size, dvfs_table); + + void *cbmem_tab = cbmem_add(CBMEM_ID_MTC, mtc_table_size); + if (cbmem_tab == NULL) { + printk(BIOS_ERR, "MTC table allocation in cbmem failed!\n"); + return -1; + } + + memcpy(cbmem_tab, dvfs_table, mtc_table_size); + printk(BIOS_INFO, "MTC: Copied 0x%zx bytes from %p to %p\n", + mtc_table_size, dvfs_table, cbmem_tab); + + return 0; +} + +void soc_add_mtc(struct lb_header *header) +{ + struct lb_range *mtc; + mtc = (struct lb_range *)lb_new_record(header); + mtc->tag = LB_TAG_MTC; + mtc->size = sizeof(*mtc); + + mtc->range_start = (uintptr_t)cbmem_find(CBMEM_ID_MTC); + mtc->range_size = mtc_table_size; +} diff --git a/src/soc/nvidia/tegra210/padconfig.c b/src/soc/nvidia/tegra210/padconfig.c new file mode 100644 index 0000000000..ab93454343 --- /dev/null +++ b/src/soc/nvidia/tegra210/padconfig.c @@ -0,0 +1,137 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <soc/addressmap.h> +#include <soc/padconfig.h> + +static uint32_t * const pinmux_regs = (void *)(uintptr_t)TEGRA_APB_PINMUX_BASE; +static struct gpio_bank * const gpio_regs = (void *)(uintptr_t)TEGRA_GPIO_BASE; + +static inline struct gpio_bank * const get_gpio_bank_regs(int index) +{ + return &gpio_regs[gpio_index_to_bank(index)]; +} + +static inline uint32_t pad_get_pinmux(int index) +{ + return read32(&pinmux_regs[index]); +} + +static inline void pad_set_pinmux(int index, uint32_t reg) +{ + return write32(&pinmux_regs[index], reg); +} + +static inline void pad_set_gpio_out(int gpio_index, int val) +{ + struct gpio_bank * const regs = get_gpio_bank_regs(gpio_index); + int port = gpio_index_to_port(gpio_index); + int bit = gpio_to_bit(gpio_index); + + write32(®s->out_value_mask[port], + (1 << (bit + GPIO_GPIOS_PER_PORT)) | (val << bit)); + write32(®s->out_enable_mask[port], + (1 << (bit + GPIO_GPIOS_PER_PORT)) | (1 << bit)); +} + +static inline void pad_set_mode(int gpio_index, int sfio_or_gpio) +{ + struct gpio_bank * const regs = get_gpio_bank_regs(gpio_index); + int port = gpio_index_to_port(gpio_index); + int bit = gpio_to_bit(gpio_index); + + write32(®s->config_mask[port], + (1 << (bit + GPIO_GPIOS_PER_PORT)) | (sfio_or_gpio << bit)); +} + +static inline void pad_set_gpio_mode(int gpio_index) +{ + pad_set_mode(gpio_index, 1); +} + +static inline void pad_set_sfio_mode(int gpio_index) +{ + pad_set_mode(gpio_index, 0); +} + +static void configure_unused_pad(const struct pad_config * const entry) +{ + uint32_t reg; + + /* + * Tristate the pad and disable input. If power-on-reset state is a + * pullup maintain that. Otherwise enable pulldown. + */ + reg = pad_get_pinmux(entry->pinmux_index); + reg &= ~PINMUX_INPUT_ENABLE; + reg |= PINMUX_TRISTATE; + reg &= ~PINMUX_PULL_MASK; + if (entry->por_pullup) + reg |= PINMUX_PULL_UP; + else + reg |= PINMUX_PULL_DOWN; + pad_set_pinmux(entry->pinmux_index, reg); + + /* + * Set to GPIO mode if GPIO available to bypass collisions of + * controller signals going to more than one pad. + */ + if (entry->pad_has_gpio) + pad_set_gpio_mode(entry->gpio_index); +} + +static void configure_sfio_pad(const struct pad_config * const entry) +{ + pad_set_pinmux(entry->pinmux_index, entry->pinmux_flags); + pad_set_sfio_mode(entry->gpio_index); +} + +static void configure_gpio_pad(const struct pad_config * const entry) +{ + uint32_t reg; + + if (entry->gpio_out0 || entry->gpio_out1) + pad_set_gpio_out(entry->gpio_index, entry->gpio_out1 ? 1 : 0); + + /* Keep the original SFIO selection. */ + reg = pinmux_get_config(entry->pinmux_index); + reg &= PINMUX_FUNC_MASK; + reg |= entry->pinmux_flags; + + pad_set_pinmux(entry->pinmux_index, reg); + pad_set_gpio_mode(entry->gpio_index); +} + +void soc_configure_pads(const struct pad_config * const entries, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + const struct pad_config * const entry = &entries[i]; + + if (entry->unused) { + configure_unused_pad(entry); + } else if (entry->sfio) { + configure_sfio_pad(entry); + } else { + configure_gpio_pad(entry); + } + } +} diff --git a/src/soc/nvidia/tegra210/power.c b/src/soc/nvidia/tegra210/power.c new file mode 100644 index 0000000000..7802a65451 --- /dev/null +++ b/src/soc/nvidia/tegra210/power.c @@ -0,0 +1,137 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. 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. + */ + +#include <arch/io.h> +#include <assert.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/pmc.h> +#include <soc/power.h> + +static struct tegra_pmc_regs * const pmc = (void *)TEGRA_PMC_BASE; + +enum { + POWER_GATE = 0, + POWER_UNGATE = 1, +}; + +static int partition_powered(int id) +{ + if (read32(&pmc->pwrgate_status) & (0x1 << id)) + return POWER_UNGATE; + + return POWER_GATE; +} + +static const char * const power_gate_string[] = { + [POWER_GATE] = "Gat", + [POWER_UNGATE] = "Ungat", +}; + +static void power_gate_toggle_request(uint32_t id, int request) +{ + printk(BIOS_INFO, "%sing power partition %d.\n", + power_gate_string[request], id); + + int part_powered = partition_powered(id); + + if (request == part_powered) { + printk(BIOS_INFO, "Partition %d already %sed.\n", id, + power_gate_string[request]); + return; + } + + uint32_t pwrgate_toggle = read32(&pmc->pwrgate_toggle); + pwrgate_toggle &= ~(PMC_PWRGATE_TOGGLE_PARTID_MASK); + pwrgate_toggle |= (id << PMC_PWRGATE_TOGGLE_PARTID_SHIFT); + pwrgate_toggle |= PMC_PWRGATE_TOGGLE_START; + write32(&pmc->pwrgate_toggle, pwrgate_toggle); + + // Wait for the request to be accepted. + while (read32(&pmc->pwrgate_toggle) & PMC_PWRGATE_TOGGLE_START) + ; + printk(BIOS_INFO, "Power gate toggle request accepted.\n"); + + while (1) { + part_powered = partition_powered(id); + if (request == part_powered) { + printk(BIOS_INFO, "Partition %d %sed.\n", id, + power_gate_string[request]); + return; + } + } +} + +void power_gate_partition(uint32_t id) +{ + power_gate_toggle_request(id, POWER_GATE); +} + +void power_ungate_partition(uint32_t id) +{ + power_gate_toggle_request(id, POWER_UNGATE); +} + +uint8_t pmc_rst_status(void) +{ + return read32(&pmc->rst_status) & PMC_RST_STATUS_SOURCE_MASK; +} + +static const char *pmc_rst_status_str[PMC_RST_STATUS_NUM_SOURCES] = { + [PMC_RST_STATUS_SOURCE_POR] = "POR", + [PMC_RST_STATUS_SOURCE_WATCHDOG] = "Watchdog", + [PMC_RST_STATUS_SOURCE_SENSOR] = "Sensor", + [PMC_RST_STATUS_SOURCE_SW_MAIN] = "SW Main", + [PMC_RST_STATUS_SOURCE_LP0] = "LP0", +}; + +void pmc_print_rst_status(void) +{ + uint8_t rst_status = pmc_rst_status(); + assert(rst_status < PMC_RST_STATUS_NUM_SOURCES); + printk(BIOS_INFO, "PMC Reset Status: %s\n", + pmc_rst_status_str[rst_status]); +} + +static int partition_clamp_on(int id) +{ + return read32(&pmc->clamp_status) & (1 << id); +} + +void remove_clamps(int id) +{ + if (!partition_clamp_on(id)) + return; + + /* Remove clamp */ + write32(&pmc->remove_clamping_cmd, (1 << id)); + + /* Wait for clamp off */ + while (partition_clamp_on(id)) + ; +} + +void pmc_override_pwr_det(uint32_t bits, uint32_t override) +{ + uint32_t val = read32(&pmc->pwr_det_val); + val &= ~bits; + val |= (override & bits); + write32(&pmc->pwr_det_val, val); +} diff --git a/src/soc/nvidia/tegra210/psci.c b/src/soc/nvidia/tegra210/psci.c new file mode 100644 index 0000000000..25a899e31c --- /dev/null +++ b/src/soc/nvidia/tegra210/psci.c @@ -0,0 +1,194 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <assert.h> +#include <arch/cpu.h> +#include <arch/io.h> +#include <arch/psci.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/cpu.h> +#include <soc/flow_ctrl.h> +#include <soc/power.h> +#include <stdlib.h> + +#include <console/console.h> + +extern void tegra210_reset_handler(void); + +#define TEGRA210_PM_STATE_C7 7 + +static void *cpu_on_entry_point; + +void psci_soc_init(uintptr_t cpu_on_entry) +{ + /* + * Stash secmon entry point for CPUs starting up. The 32-bit reset + * vector register is accessible in < EL3 so one has to attempt to + * plug the potential race for that register being changed out from + * under us. Therefore, we set the appropriate registers here, but + * it is also done on each CPU_ON request. + */ + cpu_on_entry_point = tegra210_reset_handler; + cpu_prepare_startup(cpu_on_entry_point); +} + +static size_t children_at_level(int parent_level, uint64_t mpidr) +{ + if (mpidr != 0) + return 0; + + /* + * T210 has 2 clusters. Each cluster has 4 cores. Currently we are + * concentrating only on one of the clusters i.e. A57 cluster. For A53 + * bringup, correct the cluster details for A53 cluster as well. + * Since, A57 cluster has 4 cores, level 1 has 4 children at level 0. + * TODO(furquan): Update for A53. + */ + switch (parent_level) { + case PSCI_AFFINITY_ROOT: + return 1; + case PSCI_AFFINITY_LEVEL_3: + return 1; + case PSCI_AFFINITY_LEVEL_2: + return 1; + case PSCI_AFFINITY_LEVEL_1: + return 4; + case PSCI_AFFINITY_LEVEL_0: + return 0; + default: + return 0; + } +} + +static void prepare_cpu_on(int cpu) +{ + cpu_prepare_startup(cpu_on_entry_point); +} + +static void prepare_cpu_suspend(int cpu, uint32_t state_id) +{ + flowctrl_write_cc4_ctrl(cpu, 0xffffffff); + switch (state_id) { + case TEGRA210_PM_STATE_C7: + flowctrl_cpu_suspend(cpu); + break; + default: + return; + } +} + +static void prepare_cpu_resume(int cpu) +{ + flowctrl_write_cpu_csr(cpu, 0); + flowctrl_write_cpu_halt(cpu, 0); + flowctrl_write_cc4_ctrl(cpu, 0); +} + +static void cpu_suspend_commit(int cpu, uint32_t state_id) +{ + int l2_flush; + + switch (state_id) { + case TEGRA210_PM_STATE_C7: + l2_flush = NO_L2_FLUSH; + break; + default: + return; + } + + cortex_a57_cpu_power_down(l2_flush); + /* should never be here */ +} + +static int cmd_prepare(struct psci_cmd *cmd) +{ + int ret; + struct cpu_info *ci; + + ci = cmd->target->cpu_state.ci; + + switch (cmd->type) { + case PSCI_CMD_SUSPEND: + cmd->state_id = cmd->state->id; + prepare_cpu_on(ci->id); + prepare_cpu_suspend(ci->id, cmd->state_id); + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_RESUME: + prepare_cpu_resume(ci->id); + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_ON: + prepare_cpu_on(ci->id); + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_OFF: + if (cmd->state_id != -1) { + ret = PSCI_RET_INVALID_PARAMETERS; + break; + } + ret = PSCI_RET_SUCCESS; + break; + default: + ret = PSCI_RET_NOT_SUPPORTED; + break; + } + return ret; +} + +static int cmd_commit(struct psci_cmd *cmd) +{ + int ret; + struct cpu_info *ci; + + ci = cmd->target->cpu_state.ci; + + switch (cmd->type) { + case PSCI_CMD_SUSPEND: + cpu_suspend_commit(ci->id, cmd->state_id); + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_RESUME: + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_ON: + /* Take CPU out of reset */ + flowctrl_cpu_on(ci->id); + ret = PSCI_RET_SUCCESS; + break; + case PSCI_CMD_OFF: + flowctrl_cpu_off(ci->id); + cortex_a57_cpu_power_down(NO_L2_FLUSH); + /* Never reach here */ + ret = PSCI_RET_NOT_SUPPORTED; + printk(BIOS_ERR, "t210 CPU%d PSCI_CMD_OFF fail\n", ci->id); + break; + default: + ret = PSCI_RET_NOT_SUPPORTED; + break; + } + return ret; +} + +struct psci_soc_ops soc_psci_ops = { + .children_at_level = &children_at_level, + .cmd_prepare = &cmd_prepare, + .cmd_commit = &cmd_commit, +}; diff --git a/src/soc/nvidia/tegra210/ram_code.c b/src/soc/nvidia/tegra210/ram_code.c new file mode 100644 index 0000000000..8715410e76 --- /dev/null +++ b/src/soc/nvidia/tegra210/ram_code.c @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + * + */ + +#include <arch/io.h> +#include <soc/addressmap.h> +#include <soc/sdram.h> +#include <soc/nvidia/tegra/apbmisc.h> + +uint32_t sdram_get_ram_code(void) +{ + struct apbmisc *misc = (struct apbmisc *)TEGRA_APB_MISC_BASE; + + return (read32(&misc->pp_strapping_opt_a) & + PP_STRAPPING_OPT_A_RAM_CODE_MASK) >> + PP_STRAPPING_OPT_A_RAM_CODE_SHIFT; +} diff --git a/src/soc/nvidia/tegra210/ramstage.c b/src/soc/nvidia/tegra210/ramstage.c new file mode 100644 index 0000000000..7838c69ab9 --- /dev/null +++ b/src/soc/nvidia/tegra210/ramstage.c @@ -0,0 +1,39 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/clock.h> +#include <arch/cpu.h> +#include <arch/stages.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/mmu_operations.h> + +void arm64_arch_timer_init(void) +{ + uint32_t freq = clock_get_osc_khz() * 1000; + // Set the cntfrq register. + set_cntfrq(freq); +} + +void arm64_soc_init(void) +{ + trustzone_region_init(); + + tegra210_mmu_init(); +} diff --git a/src/soc/nvidia/tegra210/reset.c b/src/soc/nvidia/tegra210/reset.c new file mode 100644 index 0000000000..dee2786b8c --- /dev/null +++ b/src/soc/nvidia/tegra210/reset.c @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <reset.h> + +/* + * Promote cpu_reset() to a hard_reset(). A shallower reset can be added, + * if needed, at a later time. + */ +void cpu_reset(void) +{ + hard_reset(); +} diff --git a/src/soc/nvidia/tegra210/reset_handler.S b/src/soc/nvidia/tegra210/reset_handler.S new file mode 100644 index 0000000000..53622b13bf --- /dev/null +++ b/src/soc/nvidia/tegra210/reset_handler.S @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. 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. + */ + +#include <arch/asm.h> + +#define CPUACTLR_EL1 s3_1_c15_c2_0 + +CPU_RESET_ENTRY(tegra210_reset_handler) + /* + * Invalidate BTB along with I$ to remove any stale entries + * from the branch predictor array. + */ + mrs x0, CPUACTLR_EL1 + orr x0, x0, #1 + msr CPUACTLR_EL1, x0 /* invalidate BTB and I$ together */ + dsb sy + isb + ic iallu /* invalidate */ + dsb sy + isb + + bic x0, x0, #1 + msr CPUACTLR_EL1, x0 /* restore original CPUACTLR_EL1 */ + dsb sy + isb + + .rept 7 + nop /* wait */ + .endr + + /* + * Extract OSLK bit and check if it is '1'. This bit remains '0' + * for A53. If '1', turn off regional clock gating and request + * warm reset. + */ + mrs x0, oslsr_el1 + and x0, x0, #2 /* extract oslk bit */ + mrs x1, mpidr_el1 + bics xzr, x0, x1, lsr #7 /* 0 if slow cluster */ + b.eq __restore_oslock + mov x0, xzr + msr oslar_el1, x0 /* os lock stays 0 across warm reset */ + mov x3, #3 + movz x4, #0x8000, lsl #48 + msr CPUACTLR_EL1, x4 /* turn off RCG */ + isb + msr rmr_el3, x3 /* request warm reset */ + isb + dsb sy + wfi + + /* + * These nops are here so that speculative execution won't harm us + * before we are done warm reset. + */ + .rept 65 + nop + .endr + +__restore_oslock: + mov x0, #1 + msr oslar_el1, x0 + + b arm64_cpu_startup_resume +ENDPROC(tegra210_reset_handler) diff --git a/src/soc/nvidia/tegra210/romstage.c b/src/soc/nvidia/tegra210/romstage.c new file mode 100644 index 0000000000..c029e937b3 --- /dev/null +++ b/src/soc/nvidia/tegra210/romstage.c @@ -0,0 +1,92 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/exception.h> +#include <arch/stages.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/cbmem_console.h> +#include <console/console.h> +#include <program_loading.h> +#include <soc/addressmap.h> +#include <soc/ccplex.h> +#include <soc/clock.h> +#include <soc/sdram.h> +#include <soc/sdram_configs.h> +#include <soc/romstage.h> +#include <soc/nvidia/tegra/apbmisc.h> +#include <timer.h> +#include <timestamp.h> +#include <vendorcode/google/chromeos/chromeos.h> + +void __attribute__((weak)) romstage_mainboard_init(void) +{ + /* Default empty implementation. */ +} + +void romstage(void) +{ + console_init(); + exception_init(); + + printk(BIOS_INFO, "T210: romstage here\n"); + +#if CONFIG_BOOTROM_SDRAM_INIT + printk(BIOS_INFO, "T210 romstage: SDRAM init done by BootROM, RAMCODE = %d\n", + sdram_get_ram_code()); +#else + sdram_init(get_sdram_config()); + printk(BIOS_INFO, "T210 romstage: sdram_init done\n"); +#endif + + /* + * IMPORTANT: + * DO NOT INITIALIZE ANY CARVEOUT BEFORE TZ. + * + * Trust Zone needs to be initialized after the DRAM initialization + * because carveout registers are programmed during DRAM init. + * cbmem_initialize() is dependent on the Trust Zone region + * initalization because CBMEM lives right below the Trust Zone which + * needs to be properly identified. + */ + trustzone_region_init(); + + gpu_region_init(); + + /* + * When romstage is running it's always on the reboot path -- never a + * resume path where cbmem recovery is required. Therefore, always + * initialize the cbmem area to be empty. + */ + cbmem_initialize_empty(); + + ccplex_cpu_prepare(); + printk(BIOS_INFO, "T210 romstage: cpu prepare done\n"); + + romstage_mainboard_init(); + + run_ramstage(); +} + +void platform_prog_run(struct prog *prog) +{ + ccplex_cpu_start(prog_entry(prog)); + + clock_halt_avp(); +} diff --git a/src/soc/nvidia/tegra210/romstage_asm.S b/src/soc/nvidia/tegra210/romstage_asm.S new file mode 100644 index 0000000000..350934df32 --- /dev/null +++ b/src/soc/nvidia/tegra210/romstage_asm.S @@ -0,0 +1,27 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/asm.h> +#include "stack.S" + + .section ".text", "ax", %progbits + +ENTRY(main) + stack_init stack_top=_estack stack_bottom=_stack seed=0 func=romstage +ENDPROC(main) diff --git a/src/soc/nvidia/tegra210/sdram.c b/src/soc/nvidia/tegra210/sdram.c new file mode 100644 index 0000000000..786caa4b07 --- /dev/null +++ b/src/soc/nvidia/tegra210/sdram.c @@ -0,0 +1,1082 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + * + */ + +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/emc.h> +#include <soc/mc.h> +#include <soc/pmc.h> +#include <soc/sdram.h> +#include <stdlib.h> +#include <soc/nvidia/tegra/apbmisc.h> + +static void sdram_patch(uintptr_t addr, uint32_t value) +{ + if (addr) + write32((uint32_t *)addr, value); +} + +static void writebits(uint32_t value, uint32_t *addr, uint32_t mask) +{ + clrsetbits_le32(addr, mask, (value & mask)); +} + +static void sdram_trigger_emc_timing_update(struct tegra_emc_regs *regs) +{ + write32(®s->timing_control, EMC_TIMING_CONTROL_TIMING_UPDATE); +} + +/* PMC must be configured before clock-enable and de-reset of MC/EMC. */ +static void sdram_configure_pmc(const struct sdram_params *param, + struct tegra_pmc_regs *regs) +{ + /* VDDP Select */ + write32(®s->vddp_sel, param->PmcVddpSel); + udelay(param->PmcVddpSelWait); + + /* Set DDR pad voltage */ + writebits(param->PmcDdrPwr, ®s->ddr_pwr, PMC_DDR_PWR_VAL_MASK); + + /* Turn on MEM IO Power */ + writebits(param->PmcNoIoPower, ®s->no_iopower, + (PMC_NO_IOPOWER_MEM_MASK | PMC_NO_IOPOWER_MEM_COMP_MASK)); + + write32(®s->reg_short, param->PmcRegShort); + write32(®s->ddr_cntrl, param->PmcDdrCntrl); +} + +static void sdram_set_ddr_control(const struct sdram_params *param, + struct tegra_pmc_regs *regs) +{ + u32 ddrcntrl = read32(®s->ddr_cntrl); + + /* Deassert HOLD_CKE_LOW */ + ddrcntrl &= ~PMC_CMD_HOLD_LOW_BR00_11_MASK; + write32(®s->ddr_cntrl, ddrcntrl); + udelay(param->PmcDdrCntrlWait); +} + +static void sdram_start_clocks(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + struct clk_rst_ctlr *clk_rst = (void *)TEGRA_CLK_RST_BASE; + + u32 is_same_freq = (param->McEmemArbMisc0 & + MC_EMEM_ARB_MISC0_MC_EMC_SAME_FREQ_MASK) ? 1 : 0; + u32 clk_source_emc = param->EmcClockSource; + + /* Enable the clocks for EMC and MC */ + setbits_le32(&clk_rst->clk_enb_h_set, (1 << 25)); // ENB_EMC + setbits_le32(&clk_rst->clk_enb_h_set, (1 << 0)); // ENB_MC + + if ((clk_source_emc >> EMC_2X_CLK_SRC_SHIFT) != PLLM_UD) + setbits_le32(&clk_rst->clk_enb_x_set, CLK_ENB_EMC_DLL); + + /* Remove the EMC and MC controllers from reset */ + clrbits_le32(&clk_rst->rst_dev_h, (1 << 25)); // SWR_EMC + clrbits_le32(&clk_rst->rst_dev_h, (1 << 0)); // SWR_MC + + clk_source_emc |= (is_same_freq << 16); + + write32(&clk_rst->clk_src_emc, clk_source_emc); + write32(&clk_rst->clk_src_emc_dll, param->EmcClockSourceDll); + + clock_sdram(param->PllMInputDivider, param->PllMFeedbackDivider, + param->PllMPostDivider, param->PllMSetupControl, + param->PllMKVCO, param->PllMKCP, param->PllMStableTime, + param->EmcClockSource, is_same_freq); + + if (param->ClkRstControllerPllmMisc2OverrideEnable) + write32(&clk_rst->pllm_misc2, + param->ClkRstControllerPllmMisc2Override); + + /* Wait for enough time for clk switch to take place */ + udelay(5); + + write32(&clk_rst->clk_enb_w_clr, param->ClearClk2Mc1); +} + +static void sdram_set_swizzle(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + write32(®s->swizzle_rank0_byte0, param->EmcSwizzleRank0Byte0); + write32(®s->swizzle_rank0_byte1, param->EmcSwizzleRank0Byte1); + write32(®s->swizzle_rank0_byte2, param->EmcSwizzleRank0Byte2); + write32(®s->swizzle_rank0_byte3, param->EmcSwizzleRank0Byte3); + + write32(®s->swizzle_rank1_byte0, param->EmcSwizzleRank1Byte0); + write32(®s->swizzle_rank1_byte1, param->EmcSwizzleRank1Byte1); + write32(®s->swizzle_rank1_byte2, param->EmcSwizzleRank1Byte2); + write32(®s->swizzle_rank1_byte3, param->EmcSwizzleRank1Byte3); +} + +static void sdram_set_pad_controls(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Program the pad controls */ + write32(®s->xm2comppadctrl, param->EmcXm2CompPadCtrl); + write32(®s->xm2comppadctrl2, param->EmcXm2CompPadCtrl2); + write32(®s->xm2comppadctrl3, param->EmcXm2CompPadCtrl3); +} + +static void sdram_set_pad_macros(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + u32 rfu_reset, rfu_mask1, rfu_mask2, rfu_step1, rfu_step2; + u32 cpm_reset_settings, cpm_mask1, cpm_step1; + + write32(®s->pmacro_vttgen_ctrl0, param->EmcPmacroVttgenCtrl0); + write32(®s->pmacro_vttgen_ctrl1, param->EmcPmacroVttgenCtrl1); + write32(®s->pmacro_vttgen_ctrl2, param->EmcPmacroVttgenCtrl2); + /* Trigger timing update so above writes take place */ + sdram_trigger_emc_timing_update(regs); + /* Add a wait to ensure the regulators settle */ + udelay(10); + + write32(®s->dbg, + param->EmcDbg | (param->EmcDbgWriteMux & WRITE_MUX_ACTIVE)); + + rfu_reset = EMC_PMACRO_BRICK_CTRL_RFU1_RESET_VAL; + rfu_mask1 = 0x01120112; + rfu_mask2 = 0x01BF01BF; + + rfu_step1 = rfu_reset & (param->EmcPmacroBrickCtrlRfu1 | ~rfu_mask1); + rfu_step2 = rfu_reset & (param->EmcPmacroBrickCtrlRfu1 | ~rfu_mask2); + + /* common pad macro (cpm) */ + cpm_reset_settings = 0x0000000F; + cpm_mask1 = 0x00000001; + cpm_step1 = cpm_reset_settings; + cpm_step1 &= (param->EmcPmacroCommonPadTxCtrl | ~cpm_mask1); + + /* Patch 2 using BCT spare variables */ + sdram_patch(param->EmcBctSpare2, param->EmcBctSpare3); + + /* + * Program CMD mapping. Required before brick mapping, else + * we can't gaurantee CK will be differential at all times. + */ + write32(®s->fbio_cfg7, param->EmcFbioCfg7); + + write32(®s->cmd_mapping_cmd0_0, param->EmcCmdMappingCmd0_0); + write32(®s->cmd_mapping_cmd0_1, param->EmcCmdMappingCmd0_1); + write32(®s->cmd_mapping_cmd0_2, param->EmcCmdMappingCmd0_2); + write32(®s->cmd_mapping_cmd1_0, param->EmcCmdMappingCmd1_0); + write32(®s->cmd_mapping_cmd1_1, param->EmcCmdMappingCmd1_1); + write32(®s->cmd_mapping_cmd1_2, param->EmcCmdMappingCmd1_2); + write32(®s->cmd_mapping_cmd2_0, param->EmcCmdMappingCmd2_0); + write32(®s->cmd_mapping_cmd2_1, param->EmcCmdMappingCmd2_1); + write32(®s->cmd_mapping_cmd2_2, param->EmcCmdMappingCmd2_2); + write32(®s->cmd_mapping_cmd3_0, param->EmcCmdMappingCmd3_0); + write32(®s->cmd_mapping_cmd3_1, param->EmcCmdMappingCmd3_1); + write32(®s->cmd_mapping_cmd3_2, param->EmcCmdMappingCmd3_2); + write32(®s->cmd_mapping_byte, param->EmcCmdMappingByte); + + /* Program brick mapping. */ + write32(®s->pmacro_brick_mapping0, param->EmcPmacroBrickMapping0); + write32(®s->pmacro_brick_mapping1, param->EmcPmacroBrickMapping1); + write32(®s->pmacro_brick_mapping2, param->EmcPmacroBrickMapping2); + + write32(®s->pmacro_brick_ctrl_rfu1, rfu_step1); + + /* This is required to do any reads from the pad macros */ + write32(®s->config_sample_delay, param->EmcConfigSampleDelay); + + write32(®s->fbio_cfg8, param->EmcFbioCfg8); + + sdram_set_swizzle(param, regs); + + /* Patch 4 using BCT spare variables */ + sdram_patch(param->EmcBctSpare6, param->EmcBctSpare7); + + sdram_set_pad_controls(param, regs); + + /* Program Autocal controls with shadowed register fields */ + write32(®s->auto_cal_config2, param->EmcAutoCalConfig2); + write32(®s->auto_cal_config3, param->EmcAutoCalConfig3); + write32(®s->auto_cal_config4, param->EmcAutoCalConfig4); + write32(®s->auto_cal_config5, param->EmcAutoCalConfig5); + write32(®s->auto_cal_config6, param->EmcAutoCalConfig6); + write32(®s->auto_cal_config7, param->EmcAutoCalConfig7); + write32(®s->auto_cal_config8, param->EmcAutoCalConfig8); + + write32(®s->pmacro_rx_term, param->EmcPmacroRxTerm); + write32(®s->pmacro_dq_tx_drv, param->EmcPmacroDqTxDrv); + write32(®s->pmacro_ca_tx_drv, param->EmcPmacroCaTxDrv); + write32(®s->pmacro_cmd_tx_drv, param->EmcPmacroCmdTxDrv); + write32(®s->pmacro_autocal_cfg_common, + param->EmcPmacroAutocalCfgCommon); + write32(®s->auto_cal_channel, param->EmcAutoCalChannel); + write32(®s->pmacro_zctrl, param->EmcPmacroZctrl); + + write32(®s->dll_cfg0, param->EmcDllCfg0); + write32(®s->dll_cfg1, param->EmcDllCfg1); + write32(®s->cfg_dig_dll_1, param->EmcCfgDigDll_1); + + write32(®s->data_brlshft_0, param->EmcDataBrlshft0); + write32(®s->data_brlshft_1, param->EmcDataBrlshft1); + write32(®s->dqs_brlshft_0, param->EmcDqsBrlshft0); + write32(®s->dqs_brlshft_1, param->EmcDqsBrlshft1); + write32(®s->cmd_brlshft_0, param->EmcCmdBrlshft0); + write32(®s->cmd_brlshft_1, param->EmcCmdBrlshft1); + write32(®s->cmd_brlshft_2, param->EmcCmdBrlshft2); + write32(®s->cmd_brlshft_3, param->EmcCmdBrlshft3); + write32(®s->quse_brlshft_0, param->EmcQuseBrlshft0); + write32(®s->quse_brlshft_1, param->EmcQuseBrlshft1); + write32(®s->quse_brlshft_2, param->EmcQuseBrlshft2); + write32(®s->quse_brlshft_3, param->EmcQuseBrlshft3); + + write32(®s->pmacro_brick_ctrl_rfu1, rfu_step2); + write32(®s->pmacro_pad_cfg_ctrl, param->EmcPmacroPadCfgCtrl); + + write32(®s->pmacro_pad_cfg_ctrl, param->EmcPmacroPadCfgCtrl); + write32(®s->pmacro_cmd_brick_ctrl_fdpd, + param->EmcPmacroCmdBrickCtrlFdpd); + write32(®s->pmacro_brick_ctrl_rfu2, + param->EmcPmacroBrickCtrlRfu2 & 0xFF7FFF7F); + write32(®s->pmacro_data_brick_ctrl_fdpd, + param->EmcPmacroDataBrickCtrlFdpd); + write32(®s->pmacro_bg_bias_ctrl_0, param->EmcPmacroBgBiasCtrl0); + write32(®s->pmacro_data_pad_rx_ctrl, param->EmcPmacroDataPadRxCtrl); + write32(®s->pmacro_cmd_pad_rx_ctrl, param->EmcPmacroCmdPadRxCtrl); + write32(®s->pmacro_data_pad_tx_ctrl, param->EmcPmacroDataPadTxCtrl); + write32(®s->pmacro_data_rx_term_mode, + param->EmcPmacroDataRxTermMode); + write32(®s->pmacro_cmd_rx_term_mode, param->EmcPmacroCmdRxTermMode); + write32(®s->pmacro_cmd_pad_tx_ctrl, param->EmcPmacroCmdPadTxCtrl); + + write32(®s->cfg_3, param->EmcCfg3); + write32(®s->pmacro_tx_pwrd_0, param->EmcPmacroTxPwrd0); + write32(®s->pmacro_tx_pwrd_1, param->EmcPmacroTxPwrd1); + write32(®s->pmacro_tx_pwrd_2, param->EmcPmacroTxPwrd2); + write32(®s->pmacro_tx_pwrd_3, param->EmcPmacroTxPwrd3); + write32(®s->pmacro_tx_pwrd_4, param->EmcPmacroTxPwrd4); + write32(®s->pmacro_tx_pwrd_5, param->EmcPmacroTxPwrd5); + write32(®s->pmacro_tx_sel_clk_src_0, param->EmcPmacroTxSelClkSrc0); + write32(®s->pmacro_tx_sel_clk_src_1, param->EmcPmacroTxSelClkSrc1); + write32(®s->pmacro_tx_sel_clk_src_2, param->EmcPmacroTxSelClkSrc2); + write32(®s->pmacro_tx_sel_clk_src_3, param->EmcPmacroTxSelClkSrc3); + write32(®s->pmacro_tx_sel_clk_src_4, param->EmcPmacroTxSelClkSrc4); + write32(®s->pmacro_tx_sel_clk_src_5, param->EmcPmacroTxSelClkSrc5); + write32(®s->pmacro_ddll_bypass, param->EmcPmacroDdllBypass); + write32(®s->pmacro_ddll_pwrd_0, param->EmcPmacroDdllPwrd0); + write32(®s->pmacro_ddll_pwrd_1, param->EmcPmacroDdllPwrd1); + write32(®s->pmacro_ddll_pwrd_2, param->EmcPmacroDdllPwrd2); + write32(®s->pmacro_cmd_ctrl_0, param->EmcPmacroCmdCtrl0); + write32(®s->pmacro_cmd_ctrl_1, param->EmcPmacroCmdCtrl1); + write32(®s->pmacro_cmd_ctrl_2, param->EmcPmacroCmdCtrl2); + write32(®s->pmacro_ib_vref_dq_0, param->EmcPmacroIbVrefDq_0); + write32(®s->pmacro_ib_vref_dq_1, param->EmcPmacroIbVrefDq_1); + write32(®s->pmacro_ib_vref_dqs_0, param->EmcPmacroIbVrefDqs_0); + write32(®s->pmacro_ib_vref_dqs_1, param->EmcPmacroIbVrefDqs_1); + write32(®s->pmacro_ib_rxrt, param->EmcPmacroIbRxrt); + write32(®s->pmacro_quse_ddll_rank0_0, + param->EmcPmacroQuseDdllRank0_0); + write32(®s->pmacro_quse_ddll_rank0_1, + param->EmcPmacroQuseDdllRank0_1); + write32(®s->pmacro_quse_ddll_rank0_2, + param->EmcPmacroQuseDdllRank0_2); + write32(®s->pmacro_quse_ddll_rank0_3, + param->EmcPmacroQuseDdllRank0_3); + write32(®s->pmacro_quse_ddll_rank0_4, + param->EmcPmacroQuseDdllRank0_4); + write32(®s->pmacro_quse_ddll_rank0_5, + param->EmcPmacroQuseDdllRank0_5); + write32(®s->pmacro_quse_ddll_rank1_0, + param->EmcPmacroQuseDdllRank1_0); + write32(®s->pmacro_quse_ddll_rank1_1, + param->EmcPmacroQuseDdllRank1_1); + write32(®s->pmacro_quse_ddll_rank1_2, + param->EmcPmacroQuseDdllRank1_2); + write32(®s->pmacro_quse_ddll_rank1_3, + param->EmcPmacroQuseDdllRank1_3); + write32(®s->pmacro_quse_ddll_rank1_4, + param->EmcPmacroQuseDdllRank1_4); + write32(®s->pmacro_quse_ddll_rank1_5, + param->EmcPmacroQuseDdllRank1_5); + write32(®s->pmacro_brick_ctrl_rfu1, param->EmcPmacroBrickCtrlRfu1); + write32(®s->pmacro_ob_ddll_long_dq_rank0_0, + param->EmcPmacroObDdllLongDqRank0_0); + write32(®s->pmacro_ob_ddll_long_dq_rank0_1, + param->EmcPmacroObDdllLongDqRank0_1); + write32(®s->pmacro_ob_ddll_long_dq_rank0_2, + param->EmcPmacroObDdllLongDqRank0_2); + write32(®s->pmacro_ob_ddll_long_dq_rank0_3, + param->EmcPmacroObDdllLongDqRank0_3); + write32(®s->pmacro_ob_ddll_long_dq_rank0_4, + param->EmcPmacroObDdllLongDqRank0_4); + write32(®s->pmacro_ob_ddll_long_dq_rank0_5, + param->EmcPmacroObDdllLongDqRank0_5); + write32(®s->pmacro_ob_ddll_long_dq_rank1_0, + param->EmcPmacroObDdllLongDqRank1_0); + write32(®s->pmacro_ob_ddll_long_dq_rank1_1, + param->EmcPmacroObDdllLongDqRank1_1); + write32(®s->pmacro_ob_ddll_long_dq_rank1_2, + param->EmcPmacroObDdllLongDqRank1_2); + write32(®s->pmacro_ob_ddll_long_dq_rank1_3, + param->EmcPmacroObDdllLongDqRank1_3); + write32(®s->pmacro_ob_ddll_long_dq_rank1_4, + param->EmcPmacroObDdllLongDqRank1_4); + write32(®s->pmacro_ob_ddll_long_dq_rank1_5, + param->EmcPmacroObDdllLongDqRank1_5); + + write32(®s->pmacro_ob_ddll_long_dqs_rank0_0, + param->EmcPmacroObDdllLongDqsRank0_0); + write32(®s->pmacro_ob_ddll_long_dqs_rank0_1, + param->EmcPmacroObDdllLongDqsRank0_1); + write32(®s->pmacro_ob_ddll_long_dqs_rank0_2, + param->EmcPmacroObDdllLongDqsRank0_2); + write32(®s->pmacro_ob_ddll_long_dqs_rank0_3, + param->EmcPmacroObDdllLongDqsRank0_3); + write32(®s->pmacro_ob_ddll_long_dqs_rank0_4, + param->EmcPmacroObDdllLongDqsRank0_4); + write32(®s->pmacro_ob_ddll_long_dqs_rank0_5, + param->EmcPmacroObDdllLongDqsRank0_5); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_0, + param->EmcPmacroObDdllLongDqsRank1_0); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_1, + param->EmcPmacroObDdllLongDqsRank1_1); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_2, + param->EmcPmacroObDdllLongDqsRank1_2); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_3, + param->EmcPmacroObDdllLongDqsRank1_3); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_4, + param->EmcPmacroObDdllLongDqsRank1_4); + write32(®s->pmacro_ob_ddll_long_dqs_rank1_5, + param->EmcPmacroObDdllLongDqsRank1_5); + write32(®s->pmacro_ib_ddll_long_dqs_rank0_0, + param->EmcPmacroIbDdllLongDqsRank0_0); + write32(®s->pmacro_ib_ddll_long_dqs_rank0_1, + param->EmcPmacroIbDdllLongDqsRank0_1); + write32(®s->pmacro_ib_ddll_long_dqs_rank0_2, + param->EmcPmacroIbDdllLongDqsRank0_2); + write32(®s->pmacro_ib_ddll_long_dqs_rank0_3, + param->EmcPmacroIbDdllLongDqsRank0_3); + write32(®s->pmacro_ib_ddll_long_dqs_rank1_0, + param->EmcPmacroIbDdllLongDqsRank1_0); + write32(®s->pmacro_ib_ddll_long_dqs_rank1_1, + param->EmcPmacroIbDdllLongDqsRank1_1); + write32(®s->pmacro_ib_ddll_long_dqs_rank1_2, + param->EmcPmacroIbDdllLongDqsRank1_2); + write32(®s->pmacro_ib_ddll_long_dqs_rank1_3, + param->EmcPmacroIbDdllLongDqsRank1_3); + write32(®s->pmacro_ddll_long_cmd_0, param->EmcPmacroDdllLongCmd_0); + write32(®s->pmacro_ddll_long_cmd_1, param->EmcPmacroDdllLongCmd_1); + write32(®s->pmacro_ddll_long_cmd_2, param->EmcPmacroDdllLongCmd_2); + write32(®s->pmacro_ddll_long_cmd_3, param->EmcPmacroDdllLongCmd_3); + write32(®s->pmacro_ddll_long_cmd_4, param->EmcPmacroDdllLongCmd_4); + write32(®s->pmacro_ddll_short_cmd_0, param->EmcPmacroDdllShortCmd_0); + write32(®s->pmacro_ddll_short_cmd_1, param->EmcPmacroDdllShortCmd_1); + write32(®s->pmacro_ddll_short_cmd_2, param->EmcPmacroDdllShortCmd_2); + write32(®s->pmacro_common_pad_tx_ctrl, cpm_step1); +} + +static void sdram_setup_wpr_carveouts(const struct sdram_params *param, + struct tegra_mc_regs *regs) +{ + /* Program the 5 WPR carveouts with initial BCT settings. */ + write32(®s->security_carveout1_bom, + param->McGeneralizedCarveout1Bom); + write32(®s->security_carveout1_bom_hi, + param->McGeneralizedCarveout1BomHi); + write32(®s->security_carveout1_size_128kb, + param->McGeneralizedCarveout1Size128kb); + write32(®s->security_carveout1_ca0, + param->McGeneralizedCarveout1Access0); + write32(®s->security_carveout1_ca1, + param->McGeneralizedCarveout1Access1); + write32(®s->security_carveout1_ca2, + param->McGeneralizedCarveout1Access2); + write32(®s->security_carveout1_ca3, + param->McGeneralizedCarveout1Access3); + write32(®s->security_carveout1_ca4, + param->McGeneralizedCarveout1Access4); + write32(®s->security_carveout1_cfia0, + param->McGeneralizedCarveout1ForceInternalAccess0); + write32(®s->security_carveout1_cfia1, + param->McGeneralizedCarveout1ForceInternalAccess1); + write32(®s->security_carveout1_cfia2, + param->McGeneralizedCarveout1ForceInternalAccess2); + write32(®s->security_carveout1_cfia3, + param->McGeneralizedCarveout1ForceInternalAccess3); + write32(®s->security_carveout1_cfia4, + param->McGeneralizedCarveout1ForceInternalAccess4); + write32(®s->security_carveout1_cfg0, + param->McGeneralizedCarveout1Cfg0); + + write32(®s->security_carveout2_bom, + param->McGeneralizedCarveout2Bom); + write32(®s->security_carveout2_bom_hi, + param->McGeneralizedCarveout2BomHi); + write32(®s->security_carveout2_size_128kb, + param->McGeneralizedCarveout2Size128kb); + write32(®s->security_carveout2_ca0, + param->McGeneralizedCarveout2Access0); + write32(®s->security_carveout2_ca1, + param->McGeneralizedCarveout2Access1); + write32(®s->security_carveout2_ca2, + param->McGeneralizedCarveout2Access2); + write32(®s->security_carveout2_ca3, + param->McGeneralizedCarveout2Access3); + write32(®s->security_carveout2_ca4, + param->McGeneralizedCarveout2Access4); + write32(®s->security_carveout2_cfia0, + param->McGeneralizedCarveout2ForceInternalAccess0); + write32(®s->security_carveout2_cfia1, + param->McGeneralizedCarveout2ForceInternalAccess1); + write32(®s->security_carveout2_cfia2, + param->McGeneralizedCarveout2ForceInternalAccess2); + write32(®s->security_carveout2_cfia3, + param->McGeneralizedCarveout2ForceInternalAccess3); + write32(®s->security_carveout2_cfia4, + param->McGeneralizedCarveout2ForceInternalAccess4); + write32(®s->security_carveout2_cfg0, + param->McGeneralizedCarveout2Cfg0); + + write32(®s->security_carveout3_bom, + param->McGeneralizedCarveout3Bom); + write32(®s->security_carveout3_bom_hi, + param->McGeneralizedCarveout3BomHi); + write32(®s->security_carveout3_size_128kb, + param->McGeneralizedCarveout3Size128kb); + write32(®s->security_carveout3_ca0, + param->McGeneralizedCarveout3Access0); + write32(®s->security_carveout3_ca1, + param->McGeneralizedCarveout3Access1); + write32(®s->security_carveout3_ca2, + param->McGeneralizedCarveout3Access2); + write32(®s->security_carveout3_ca3, + param->McGeneralizedCarveout3Access3); + write32(®s->security_carveout3_ca4, + param->McGeneralizedCarveout3Access4); + write32(®s->security_carveout3_cfia0, + param->McGeneralizedCarveout3ForceInternalAccess0); + write32(®s->security_carveout3_cfia1, + param->McGeneralizedCarveout3ForceInternalAccess1); + write32(®s->security_carveout3_cfia2, + param->McGeneralizedCarveout3ForceInternalAccess2); + write32(®s->security_carveout3_cfia3, + param->McGeneralizedCarveout3ForceInternalAccess3); + write32(®s->security_carveout3_cfia4, + param->McGeneralizedCarveout3ForceInternalAccess4); + write32(®s->security_carveout3_cfg0, + param->McGeneralizedCarveout3Cfg0); + + write32(®s->security_carveout4_bom, + param->McGeneralizedCarveout4Bom); + write32(®s->security_carveout4_bom_hi, + param->McGeneralizedCarveout4BomHi); + write32(®s->security_carveout4_size_128kb, + param->McGeneralizedCarveout4Size128kb); + write32(®s->security_carveout4_ca0, + param->McGeneralizedCarveout4Access0); + write32(®s->security_carveout4_ca1, + param->McGeneralizedCarveout4Access1); + write32(®s->security_carveout4_ca2, + param->McGeneralizedCarveout4Access2); + write32(®s->security_carveout4_ca3, + param->McGeneralizedCarveout4Access3); + write32(®s->security_carveout4_ca4, + param->McGeneralizedCarveout4Access4); + write32(®s->security_carveout4_cfia0, + param->McGeneralizedCarveout4ForceInternalAccess0); + write32(®s->security_carveout4_cfia1, + param->McGeneralizedCarveout4ForceInternalAccess1); + write32(®s->security_carveout4_cfia2, + param->McGeneralizedCarveout4ForceInternalAccess2); + write32(®s->security_carveout4_cfia3, + param->McGeneralizedCarveout4ForceInternalAccess3); + write32(®s->security_carveout4_cfia4, + param->McGeneralizedCarveout4ForceInternalAccess4); + write32(®s->security_carveout4_cfg0, + param->McGeneralizedCarveout4Cfg0); + + write32(®s->security_carveout5_bom, + param->McGeneralizedCarveout5Bom); + write32(®s->security_carveout5_bom_hi, + param->McGeneralizedCarveout5BomHi); + write32(®s->security_carveout5_size_128kb, + param->McGeneralizedCarveout5Size128kb); + write32(®s->security_carveout5_ca0, + param->McGeneralizedCarveout5Access0); + write32(®s->security_carveout5_ca1, + param->McGeneralizedCarveout5Access1); + write32(®s->security_carveout5_ca2, + param->McGeneralizedCarveout5Access2); + write32(®s->security_carveout5_ca3, + param->McGeneralizedCarveout5Access3); + write32(®s->security_carveout5_ca4, + param->McGeneralizedCarveout5Access4); + write32(®s->security_carveout5_cfia0, + param->McGeneralizedCarveout5ForceInternalAccess0); + write32(®s->security_carveout5_cfia1, + param->McGeneralizedCarveout5ForceInternalAccess1); + write32(®s->security_carveout5_cfia2, + param->McGeneralizedCarveout5ForceInternalAccess2); + write32(®s->security_carveout5_cfia3, + param->McGeneralizedCarveout5ForceInternalAccess3); + write32(®s->security_carveout5_cfia4, + param->McGeneralizedCarveout5ForceInternalAccess4); + write32(®s->security_carveout5_cfg0, + param->McGeneralizedCarveout5Cfg0); +} + +static void sdram_init_mc(const struct sdram_params *param, + struct tegra_mc_regs *regs) +{ + /* Initialize MC VPR settings */ + write32(®s->video_protect_bom, param->McVideoProtectBom); + write32(®s->video_protect_bom_adr_hi, + param->McVideoProtectBomAdrHi); + write32(®s->video_protect_size_mb, param->McVideoProtectSizeMb); + write32(®s->video_protect_vpr_override, + param->McVideoProtectVprOverride); + write32(®s->video_protect_vpr_override1, + param->McVideoProtectVprOverride1); + write32(®s->video_protect_gpu_override_0, + param->McVideoProtectGpuOverride0); + write32(®s->video_protect_gpu_override_1, + param->McVideoProtectGpuOverride1); + + /* Program SDRAM geometry paarameters */ + write32(®s->emem_adr_cfg, param->McEmemAdrCfg); + write32(®s->emem_adr_cfg_dev0, param->McEmemAdrCfgDev0); + write32(®s->emem_adr_cfg_dev1, param->McEmemAdrCfgDev1); + write32(®s->emem_adr_cfg_channel_mask, + param->McEmemAdrCfgChannelMask); + + /* Program bank swizzling */ + write32(®s->emem_adr_cfg_bank_mask_0, param->McEmemAdrCfgBankMask0); + write32(®s->emem_adr_cfg_bank_mask_1, param->McEmemAdrCfgBankMask1); + write32(®s->emem_adr_cfg_bank_mask_2, param->McEmemAdrCfgBankMask2); + + /* Program external memory aperature (base and size) */ + write32(®s->emem_cfg, param->McEmemCfg); + + /* Program SEC carveout (base and size) */ + write32(®s->sec_carveout_bom, param->McSecCarveoutBom); + write32(®s->sec_carveout_adr_hi, param->McSecCarveoutAdrHi); + write32(®s->sec_carveout_size_mb, param->McSecCarveoutSizeMb); + + /* Program MTS carveout (base and size) */ + write32(®s->mts_carveout_bom, param->McMtsCarveoutBom); + write32(®s->mts_carveout_adr_hi, param->McMtsCarveoutAdrHi); + write32(®s->mts_carveout_size_mb, param->McMtsCarveoutSizeMb); + + /* Initialize the WPR carveouts */ + sdram_setup_wpr_carveouts(param, regs); + + /* Program the memory arbiter */ + write32(®s->emem_arb_cfg, param->McEmemArbCfg); + write32(®s->emem_arb_outstanding_req, + param->McEmemArbOutstandingReq); + write32(®s->emem_arb_refpb_hp_ctrl, param->McEmemArbRefpbHpCtrl); + write32(®s->emem_arb_refpb_bank_ctrl, param->McEmemArbRefpbBankCtrl); + write32(®s->emem_arb_timing_rcd, param->McEmemArbTimingRcd); + write32(®s->emem_arb_timing_rp, param->McEmemArbTimingRp); + write32(®s->emem_arb_timing_rc, param->McEmemArbTimingRc); + write32(®s->emem_arb_timing_ras, param->McEmemArbTimingRas); + write32(®s->emem_arb_timing_faw, param->McEmemArbTimingFaw); + write32(®s->emem_arb_timing_rrd, param->McEmemArbTimingRrd); + write32(®s->emem_arb_timing_rap2pre, param->McEmemArbTimingRap2Pre); + write32(®s->emem_arb_timing_wap2pre, param->McEmemArbTimingWap2Pre); + write32(®s->emem_arb_timing_r2r, param->McEmemArbTimingR2R); + write32(®s->emem_arb_timing_w2w, param->McEmemArbTimingW2W); + write32(®s->emem_arb_timing_ccdmw, param->McEmemArbTimingCcdmw); + write32(®s->emem_arb_timing_r2w, param->McEmemArbTimingR2W); + write32(®s->emem_arb_timing_w2r, param->McEmemArbTimingW2R); + write32(®s->emem_arb_timing_rfcpb, param->McEmemArbTimingRFCPB); + write32(®s->emem_arb_da_turns, param->McEmemArbDaTurns); + write32(®s->emem_arb_da_covers, param->McEmemArbDaCovers); + write32(®s->emem_arb_misc0, param->McEmemArbMisc0); + write32(®s->emem_arb_misc1, param->McEmemArbMisc1); + write32(®s->emem_arb_misc2, param->McEmemArbMisc2); + write32(®s->emem_arb_ring1_throttle, param->McEmemArbRing1Throttle); + write32(®s->emem_arb_override, param->McEmemArbOverride); + write32(®s->emem_arb_override_1, param->McEmemArbOverride1); + write32(®s->emem_arb_rsv, param->McEmemArbRsv); + write32(®s->da_config0, param->McDaCfg0); + + /* Trigger MC timing update */ + write32(®s->timing_control, EMC_TIMING_CONTROL_TIMING_UPDATE); + + /* Program second-level clock enable overrides */ + write32(®s->clken_override, param->McClkenOverride); + + /* Program statistics gathering */ + write32(®s->stat_control, param->McStatControl); +} + +static void sdram_init_emc(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Program SDRAM geometry parameters */ + write32(®s->adr_cfg, param->EmcAdrCfg); + + /* Program second-level clock enable overrides */ + write32(®s->clken_override, param->EmcClkenOverride); + + /* Program EMC pad auto calibration */ + write32(®s->pmacro_autocal_cfg0, param->EmcPmacroAutocalCfg0); + write32(®s->pmacro_autocal_cfg1, param->EmcPmacroAutocalCfg1); + write32(®s->pmacro_autocal_cfg2, param->EmcPmacroAutocalCfg2); + + write32(®s->auto_cal_vref_sel0, param->EmcAutoCalVrefSel0); + write32(®s->auto_cal_vref_sel1, param->EmcAutoCalVrefSel1); + + write32(®s->auto_cal_interval, param->EmcAutoCalInterval); + write32(®s->auto_cal_config, param->EmcAutoCalConfig); + udelay(param->EmcAutoCalWait); +} + +static void sdram_set_emc_timing(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Program EMC timing configuration */ + write32(®s->cfg_2, param->EmcCfg2); + write32(®s->cfg_pipe, param->EmcCfgPipe); + write32(®s->cfg_pipe1, param->EmcCfgPipe1); + write32(®s->cfg_pipe2, param->EmcCfgPipe2); + write32(®s->cmdq, param->EmcCmdQ); + write32(®s->mc2emcq, param->EmcMc2EmcQ); + write32(®s->mrs_wait_cnt, param->EmcMrsWaitCnt); + write32(®s->mrs_wait_cnt2, param->EmcMrsWaitCnt2); + write32(®s->fbio_cfg5, param->EmcFbioCfg5); + write32(®s->rc, param->EmcRc); + write32(®s->rfc, param->EmcRfc); + write32(®s->rfcpb, param->EmcRfcPb); + write32(®s->refctrl2, param->EmcRefctrl2); + write32(®s->rfc_slr, param->EmcRfcSlr); + write32(®s->ras, param->EmcRas); + write32(®s->rp, param->EmcRp); + write32(®s->tppd, param->EmcTppd); + write32(®s->r2r, param->EmcR2r); + write32(®s->w2w, param->EmcW2w); + write32(®s->r2w, param->EmcR2w); + write32(®s->w2r, param->EmcW2r); + write32(®s->r2p, param->EmcR2p); + write32(®s->w2p, param->EmcW2p); + write32(®s->ccdmw, param->EmcCcdmw); + write32(®s->rd_rcd, param->EmcRdRcd); + write32(®s->wr_rcd, param->EmcWrRcd); + write32(®s->rrd, param->EmcRrd); + write32(®s->rext, param->EmcRext); + write32(®s->wext, param->EmcWext); + write32(®s->wdv, param->EmcWdv); + write32(®s->wdv_chk, param->EmcWdvChk); + write32(®s->wsv, param->EmcWsv); + write32(®s->wev, param->EmcWev); + write32(®s->wdv_mask, param->EmcWdvMask); + write32(®s->ws_duration, param->EmcWsDuration); + write32(®s->we_duration, param->EmcWeDuration); + write32(®s->quse, param->EmcQUse); + write32(®s->quse_width, param->EmcQuseWidth); + write32(®s->ibdly, param->EmcIbdly); + write32(®s->obdly, param->EmcObdly); + write32(®s->einput, param->EmcEInput); + write32(®s->einput_duration, param->EmcEInputDuration); + write32(®s->puterm_extra, param->EmcPutermExtra); + write32(®s->puterm_width, param->EmcPutermWidth); + + write32(®s->pmacro_common_pad_tx_ctrl, + param->EmcPmacroCommonPadTxCtrl); + write32(®s->dbg, param->EmcDbg); + write32(®s->qrst, param->EmcQRst); + write32(®s->issue_qrst, 1); + write32(®s->issue_qrst, 0); + write32(®s->qsafe, param->EmcQSafe); + write32(®s->rdv, param->EmcRdv); + write32(®s->rdv_mask, param->EmcRdvMask); + write32(®s->rdv_early, param->EmcRdvEarly); + write32(®s->rdv_early_mask, param->EmcRdvEarlyMask); + write32(®s->qpop, param->EmcQpop); + write32(®s->refresh, param->EmcRefresh); + write32(®s->burst_refresh_num, param->EmcBurstRefreshNum); + write32(®s->pre_refresh_req_cnt, param->EmcPreRefreshReqCnt); + write32(®s->pdex2wr, param->EmcPdEx2Wr); + write32(®s->pdex2rd, param->EmcPdEx2Rd); + write32(®s->pchg2pden, param->EmcPChg2Pden); + write32(®s->act2pden, param->EmcAct2Pden); + write32(®s->ar2pden, param->EmcAr2Pden); + write32(®s->rw2pden, param->EmcRw2Pden); + write32(®s->cke2pden, param->EmcCke2Pden); + write32(®s->pdex2cke, param->EmcPdex2Cke); + write32(®s->pdex2mrr, param->EmcPdex2Mrr); + write32(®s->txsr, param->EmcTxsr); + write32(®s->txsrdll, param->EmcTxsrDll); + write32(®s->tcke, param->EmcTcke); + write32(®s->tckesr, param->EmcTckesr); + write32(®s->tpd, param->EmcTpd); + write32(®s->tfaw, param->EmcTfaw); + write32(®s->trpab, param->EmcTrpab); + write32(®s->tclkstable, param->EmcTClkStable); + write32(®s->tclkstop, param->EmcTClkStop); + write32(®s->trefbw, param->EmcTRefBw); + write32(®s->odt_write, param->EmcOdtWrite); + write32(®s->cfg_dig_dll, param->EmcCfgDigDll); + write32(®s->cfg_dig_dll_period, param->EmcCfgDigDllPeriod); + + /* Don't write CFG_ADR_EN (bit 1) here - lock bit written later */ + write32(®s->fbio_spare, param->EmcFbioSpare & ~CFG_ADR_EN_LOCKED); + write32(®s->cfg_rsv, param->EmcCfgRsv); + write32(®s->pmc_scratch1, param->EmcPmcScratch1); + write32(®s->pmc_scratch2, param->EmcPmcScratch2); + write32(®s->pmc_scratch3, param->EmcPmcScratch3); + write32(®s->acpd_control, param->EmcAcpdControl); + write32(®s->txdsrvttgen, param->EmcTxdsrvttgen); + + /* + * Set pipe bypass enable bits before sending any DRAM commands. + * Note other bits in EMC_CFG must be set AFTER REFCTRL is configured. + */ + writebits(param->EmcCfg, ®s->cfg, + (EMC_CFG_EMC2PMACRO_CFG_BYPASS_ADDRPIPE_MASK | + EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE1_MASK | + EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE2_MASK)); +} + +static void sdram_patch_bootrom(const struct sdram_params *param, + struct tegra_mc_regs *regs) +{ + if (param->BootRomPatchControl & BOOT_ROM_PATCH_CONTROL_ENABLE_MASK) { + uintptr_t addr = ((param->BootRomPatchControl & + BOOT_ROM_PATCH_CONTROL_OFFSET_MASK) >> + BOOT_ROM_PATCH_CONTROL_OFFSET_SHIFT); + addr = BOOT_ROM_PATCH_CONTROL_BASE_ADDRESS + (addr << 2); + write32((uint32_t *)addr, param->BootRomPatchData); + write32(®s->timing_control, + EMC_TIMING_CONTROL_TIMING_UPDATE); + } +} + +static void sdram_rel_dpd(const struct sdram_params *param, + struct tegra_pmc_regs *regs) +{ + u32 dpd3_val, dpd3_val_sel_dpd; + + /* Release SEL_DPD_CMD */ + dpd3_val = (param->EmcPmcScratch1 & 0x3FFFFFFF) | DPD_OFF; + dpd3_val_sel_dpd = dpd3_val & 0xCFFF0000; + write32(®s->io_dpd3_req, dpd3_val_sel_dpd); + udelay(param->PmcIoDpd3ReqWait); +} + +/* Program DPD3/DPD4 regs (coldboot path) */ +static void sdram_set_dpd(const struct sdram_params *param, + struct tegra_pmc_regs *regs) +{ + u32 dpd3_val, dpd3_val_sel_dpd; + u32 dpd4_val, dpd4_val_e_dpd, dpd4_val_e_dpd_vttgen; + + /* Enable sel_dpd on unused pins */ + dpd3_val = (param->EmcPmcScratch1 & 0x3FFFFFFF) | DPD_ON; + dpd3_val_sel_dpd = (dpd3_val ^ 0x0000FFFF) & 0xC000FFFF; + write32(®s->io_dpd3_req, dpd3_val_sel_dpd); + udelay(param->PmcIoDpd3ReqWait); + + dpd4_val = dpd3_val; + /* Disable e_dpd_vttgen */ + dpd4_val_e_dpd_vttgen = (dpd4_val ^ 0x3FFF0000) & 0xFFFF0000; + write32(®s->io_dpd4_req, dpd4_val_e_dpd_vttgen); + udelay(param->PmcIoDpd4ReqWait); + + /* Disable e_dpd_bg */ + dpd4_val_e_dpd = (dpd4_val ^ 0x0000FFFF) & 0xC000FFFF; + write32(®s->io_dpd4_req, dpd4_val_e_dpd); + udelay(param->PmcIoDpd4ReqWait); + + write32(®s->weak_bias, 0); + /* Add a wait to make sure clock switch takes place */ + udelay(1); +} + +static void sdram_set_clock_enable_signal(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + volatile uint32_t dummy = 0; + uint32_t val = 0; + + if (param->MemoryType == NvBootMemoryType_LpDdr4) { + + val = (param->EmcPinGpioEn << EMC_PIN_GPIOEN_SHIFT) | + (param->EmcPinGpio << EMC_PIN_GPIO_SHIFT); + write32(®s->pin, val); + + clrbits_le32(®s->pin, + (EMC_PIN_RESET_MASK | EMC_PIN_DQM_MASK | + EMC_PIN_CKE_MASK)); + /* + * Assert dummy read of PIN register to ensure above write goes + * through. Wait an additional 200us here as per NVIDIA. + */ + dummy |= read32(®s->pin); + udelay(param->EmcPinExtraWait + 200); + + /* Deassert reset */ + setbits_le32(®s->pin, EMC_PIN_RESET_INACTIVE); + + /* + * Assert dummy read of PIN register to ensure above write goes + * through. Wait an additional 2000us here as per NVIDIA. + */ + dummy |= read32(®s->pin); + udelay(param->EmcPinExtraWait + 2000); + } + + /* Enable clock enable signal */ + setbits_le32(®s->pin, EMC_PIN_CKE_NORMAL); + + /* Dummy read of PIN register to ensure final write goes through */ + dummy |= read32(®s->pin); + udelay(param->EmcPinProgramWait); + + if (!dummy) + die("Failed to program EMC pin."); + + if (param->MemoryType != NvBootMemoryType_LpDdr4) { + + /* Send NOP (trigger just needs to be non-zero) */ + writebits(((1 << EMC_NOP_CMD_SHIFT) | + (param->EmcDevSelect << EMC_NOP_DEV_SELECTN_SHIFT)), + ®s->nop, + EMC_NOP_CMD_MASK | EMC_NOP_DEV_SELECTN_MASK); + } + + /* On coldboot w/LPDDR2/3, wait 200 uSec after asserting CKE high */ + if (param->MemoryType == NvBootMemoryType_LpDdr2) + udelay(param->EmcPinExtraWait + 200); +} + +static void sdram_init_lpddr3(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Precharge all banks. DEV_SELECTN = 0 => Select all devices */ + write32(®s->pre, + ((param->EmcDevSelect << EMC_REF_DEV_SELECTN_SHIFT) | 1)); + + /* Send Reset MRW command */ + write32(®s->mrw, param->EmcMrwResetCommand); + udelay(param->EmcMrwResetNInitWait); + + write32(®s->mrw, param->EmcZcalInitDev0); + udelay(param->EmcZcalInitWait); + + if ((param->EmcDevSelect & 2) == 0) { + write32(®s->mrw, param->EmcZcalInitDev1); + udelay(param->EmcZcalInitWait); + } + + /* Write mode registers */ + write32(®s->mrw2, param->EmcMrw2); + write32(®s->mrw, param->EmcMrw1); + write32(®s->mrw3, param->EmcMrw3); + write32(®s->mrw4, param->EmcMrw4); + + /* Patch 6 using BCT spare variables */ + sdram_patch(param->EmcBctSpare10, param->EmcBctSpare11); + + if (param->EmcExtraModeRegWriteEnable) + write32(®s->mrw, param->EmcMrwExtra); +} + +static void sdram_init_lpddr4(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Patch 6 using BCT spare variables */ + sdram_patch(param->EmcBctSpare10, param->EmcBctSpare11); + + /* Write mode registers */ + write32(®s->mrw2, param->EmcMrw2); + write32(®s->mrw, param->EmcMrw1); + write32(®s->mrw3, param->EmcMrw3); + write32(®s->mrw4, param->EmcMrw4); + write32(®s->mrw6, param->EmcMrw6); + write32(®s->mrw14, param->EmcMrw14); + + write32(®s->mrw8, param->EmcMrw8); + write32(®s->mrw12, param->EmcMrw12); + write32(®s->mrw9, param->EmcMrw9); + write32(®s->mrw13, param->EmcMrw13); + + /* Issue ZQCAL start, device 0 */ + write32(®s->zq_cal, param->EmcZcalInitDev0); + udelay(param->EmcZcalInitWait); + /* Issue ZQCAL latch */ + write32(®s->zq_cal, (param->EmcZcalInitDev0 ^ 0x3)); + + if ((param->EmcDevSelect & 2) == 0) { + /* Same for device 1 */ + write32(®s->zq_cal, param->EmcZcalInitDev1); + udelay(param->EmcZcalInitWait); + write32(®s->zq_cal, (param->EmcZcalInitDev1 ^ 0x3)); + } +} + +static void sdram_init_zq_calibration(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + if (param->MemoryType == NvBootMemoryType_LpDdr2) + sdram_init_lpddr3(param, regs); + else if (param->MemoryType == NvBootMemoryType_LpDdr4) + sdram_init_lpddr4(param, regs); +} + +static void sdram_set_zq_calibration(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + if (param->EmcAutoCalInterval == 0) + write32(®s->auto_cal_config, + param->EmcAutoCalConfig | AUTOCAL_MEASURE_STALL_ENABLE); + + write32(®s->pmacro_brick_ctrl_rfu2, param->EmcPmacroBrickCtrlRfu2); + + /* ZQ CAL setup (not actually issuing ZQ CAL now) */ + if (param->MemoryType == NvBootMemoryType_LpDdr4) { + write32(®s->zcal_wait_cnt, param->EmcZcalWaitCnt); + write32(®s->zcal_mrw_cmd, param->EmcZcalMrwCmd); + } + + sdram_trigger_emc_timing_update(regs); + udelay(param->EmcTimingControlWait); +} + +static void sdram_set_refresh(const struct sdram_params *param, + struct tegra_emc_regs *regs) +{ + /* Insert burst refresh */ + if (param->EmcExtraRefreshNum > 0) { + uint32_t refresh_num = (1 << param->EmcExtraRefreshNum) - 1; + + writebits((EMC_REF_CMD_REFRESH | EMC_REF_NORMAL_ENABLED | + (refresh_num << EMC_REF_NUM_SHIFT) | + (param->EmcDevSelect << EMC_REF_DEV_SELECTN_SHIFT)), + ®s->ref, (EMC_REF_CMD_MASK | EMC_REF_NORMAL_MASK | + EMC_REF_NUM_MASK | + EMC_REF_DEV_SELECTN_MASK)); + } + + /* Enable refresh */ + write32(®s->refctrl, + (param->EmcDevSelect | EMC_REFCTRL_REF_VALID_ENABLED)); + + /* + * NOTE: Programming CFG must happen after REFCTRL to delay + * active power-down to after init (DDR2 constraint). + */ + write32(®s->dyn_self_ref_control, param->EmcDynSelfRefControl); + write32(®s->cfg_update, param->EmcCfgUpdate); + write32(®s->cfg, param->EmcCfg); + write32(®s->fdpd_ctrl_dq, param->EmcFdpdCtrlDq); + write32(®s->fdpd_ctrl_cmd, param->EmcFdpdCtrlCmd); + write32(®s->sel_dpd_ctrl, param->EmcSelDpdCtrl); + + /* Write addr swizzle lock bit */ + write32(®s->fbio_spare, param->EmcFbioSpare | CFG_ADR_EN_LOCKED); + + /* Re-trigger timing to latch power saving functions */ + sdram_trigger_emc_timing_update(regs); + + /* Enable EMC pipe clock gating */ + write32(®s->cfg_pipe_clk, param->EmcCfgPipeClk); + /* Depending on freqency, enable CMD/CLK fdpd */ + write32(®s->fdpd_ctrl_cmd_no_ramp, param->EmcFdpdCtrlCmdNoRamp); +} + +#define AHB_ARB_XBAR_CTRL 0x6000C0E0 + +static void sdram_enable_arbiter(const struct sdram_params *param) +{ + /* TODO(hungte) Move values here to standalone header file. */ + uint32_t *ahb_arbitration_xbar_ctrl = (uint32_t *)(AHB_ARB_XBAR_CTRL); + + setbits_le32(ahb_arbitration_xbar_ctrl, + param->AhbArbitrationXbarCtrlMemInitDone << 16); +} + +static void sdram_lock_carveouts(const struct sdram_params *param, + struct tegra_mc_regs *regs) +{ + /* Lock carveouts, and emem_cfg registers */ + write32(®s->video_protect_reg_ctrl, + param->McVideoProtectWriteAccess); + write32(®s->sec_carveout_reg_ctrl, + param->McSecCarveoutProtectWriteAccess); + write32(®s->mts_carveout_reg_ctrl, param->McMtsCarveoutRegCtrl); + + /* Write this last, locks access */ + write32(®s->emem_cfg_access_ctrl, + MC_EMEM_CFG_ACCESS_CTRL_WRITE_ACCESS_DISABLED); +} + +void sdram_init(const struct sdram_params *param) +{ + struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs *)TEGRA_PMC_BASE; + struct tegra_mc_regs *mc = (struct tegra_mc_regs *)TEGRA_MC_BASE; + struct tegra_emc_regs *emc = (struct tegra_emc_regs *)TEGRA_EMC_BASE; + + printk(BIOS_DEBUG, "Initializing SDRAM of type %d with %dKHz\n", + param->MemoryType, clock_get_pll_input_khz() * + param->PllMFeedbackDivider / param->PllMInputDivider / + (1 + param->PllMPostDivider)); + if (param->MemoryType != NvBootMemoryType_LpDdr4 && + param->MemoryType != NvBootMemoryType_LpDdr2) + die("Unsupported memory type!\n"); + + sdram_configure_pmc(param, pmc); + sdram_patch(param->EmcBctSpare0, param->EmcBctSpare1); + + sdram_set_dpd(param, pmc); + sdram_start_clocks(param, emc); + sdram_set_pad_macros(param, emc); + sdram_patch(param->EmcBctSpare4, param->EmcBctSpare5); + + sdram_trigger_emc_timing_update(emc); + sdram_init_mc(param, mc); + sdram_init_emc(param, emc); + sdram_patch(param->EmcBctSpare8, param->EmcBctSpare9); + + sdram_set_emc_timing(param, emc); + sdram_patch_bootrom(param, mc); + sdram_rel_dpd(param, pmc); + sdram_set_zq_calibration(param, emc); + sdram_set_ddr_control(param, pmc); + sdram_set_clock_enable_signal(param, emc); + + sdram_init_zq_calibration(param, emc); + + /* Set package and DPD pad control */ + write32(&pmc->ddr_cfg, param->PmcDdrCfg); + + /* Start periodic ZQ calibration (LPDDRx only) */ + if (param->MemoryType == NvBootMemoryType_LpDdr4 || + param->MemoryType == NvBootMemoryType_LpDdr2) { + write32(&emc->zcal_interval, param->EmcZcalInterval); + write32(&emc->zcal_wait_cnt, param->EmcZcalWaitCnt); + write32(&emc->zcal_mrw_cmd, param->EmcZcalMrwCmd); + } + sdram_patch(param->EmcBctSpare12, param->EmcBctSpare13); + + sdram_trigger_emc_timing_update(emc); + sdram_set_refresh(param, emc); + sdram_enable_arbiter(param); + sdram_lock_carveouts(param, mc); +#if defined(CONFIG_LP0) + sdram_lp0_save_params(param); +#endif +} diff --git a/src/soc/nvidia/tegra210/sdram_lp0.c b/src/soc/nvidia/tegra210/sdram_lp0.c new file mode 100644 index 0000000000..c63b823006 --- /dev/null +++ b/src/soc/nvidia/tegra210/sdram_lp0.c @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 <arch/cache.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/clk_rst.h> +#include <soc/pmc.h> +#include <soc/sdram.h> +#include <stdlib.h> + +/* + * This function reads SDRAM parameters (and a few CLK_RST regsiter values) from + * the common BCT format and writes them into PMC scratch registers (where the + * BootROM expects them on LP0 resume). Since those store the same values in a + * different format, we follow a "translation table" taken from Nvidia's U-Boot + * implementation to shift bits into the right position. + * + * Contrary to U-Boot, we transform the same macros directly into hardcoded + * assignments (without any pesky function calls or volatile qualifiers) to give + * the compiler as much room for optimization as possible. For that reason, we + * also intentionally avoid <arch/io.h> read/write macros, under the assumption + * that PMC scratch register accesses should not have side effects and can be + * arbitrarily reordered. For the few accesses that do have side-effects, the + * code must contain explicit memory barriers. + */ +void sdram_lp0_save_params(const struct sdram_params *sdram) +{ + struct tegra_pmc_regs * pmc = (void *)TEGRA_PMC_BASE; + struct clk_rst_ctlr * clk_rst = (void *)TEGRA_CLK_RST_BASE; + +#define pack(src, src_bits, dst, dst_bits) { \ + _Static_assert((1 ? src_bits) >= (0 ? src_bits) && (1 ? dst_bits) >= \ + (0 ? dst_bits), "byte range flipped (must be MSB:LSB)" ); \ + _Static_assert((1 ? src_bits) - (0 ? src_bits) == (1 ? dst_bits) - \ + (0 ? dst_bits), "src and dst byte range lengths differ" ); \ + u32 mask = 0xffffffff >> (31 - ((1 ? src_bits) - (0 ? src_bits))); \ + dst &= ~(mask << (0 ? dst_bits)); \ + dst |= ((src >> (0 ? src_bits)) & mask) << (0 ? dst_bits); \ +} + +#define s(param, src_bits, pmcreg, dst_bits) \ + pack(sdram->param, src_bits, pmc->pmcreg, dst_bits) + +#define m(clkreg, src_bits, pmcreg, dst_bits) \ + pack(clk_rst->clkreg, src_bits, pmc->pmcreg, dst_bits) + +#define c(value, pmcreg, dst_bits) \ + pack(value, (1 ? dst_bits) - (0 ? dst_bits) : 0, pmc->pmcreg, dst_bits) + + s(EmcClockSource, 7:0, scratch6, 15:8); + s(EmcClockSource, 31:29, scratch6, 18:16); + s(EmcClockSource, 26:26, scratch6, 19:19); + s(EmcOdtWrite, 5:0, scratch6, 25:20); + s(EmcOdtWrite, 11:8, scratch6, 29:26); + s(EmcOdtWrite, 30:30, scratch6, 30:30); + s(EmcOdtWrite, 31:31, scratch6, 31:31); + s(EmcXm2DqPadCtrl2, 18:16, scratch7, 22:20); + s(EmcXm2DqPadCtrl2, 22:20, scratch7, 25:23); + s(EmcXm2DqPadCtrl2, 26:24, scratch7, 28:26); + s(EmcXm2DqPadCtrl2, 30:28, scratch7, 31:29); + s(EmcXm2DqPadCtrl3, 18:16, scratch8, 22:20); + s(EmcXm2DqPadCtrl3, 22:20, scratch8, 25:23); + s(EmcXm2DqPadCtrl3, 26:24, scratch8, 28:26); + s(EmcXm2DqPadCtrl3, 30:28, scratch8, 31:29); + s(EmcTxsrDll, 11:0, scratch9, 31:20); + c(0, scratch10, 31:0); + s(EmcDsrVttgenDrv, 5:0, scratch10, 25:20); + s(EmcDsrVttgenDrv, 18:16, scratch10, 28:26); + s(EmcDsrVttgenDrv, 26:24, scratch10, 31:29); + s(EmcFbioSpare, 31:24, scratch11, 7:0); + s(EmcFbioSpare, 23:16, scratch11, 15:8); + s(EmcFbioSpare, 15:8, scratch11, 23:16); + s(EmcFbioSpare, 7:0, scratch11, 31:24); + s(EmcCfgRsv, 31:0, scratch12, 31:0); + s(EmcCdbCntl2, 31:0, scratch13, 31:0); + s(McEmemArbDaTurns, 31:0, scratch14, 31:0); + s(EmcCfgDigDll, 0:0, scratch17, 0:0); + s(EmcCfgDigDll, 25:2, scratch17, 24:1); + s(EmcCfgDigDll, 31:27, scratch17, 29:25); + s(EmcCdbCntl1, 29:0, scratch18, 29:0); + s(McEmemArbMisc0, 14:0, scratch19, 14:0); + s(McEmemArbMisc0, 30:16, scratch19, 29:15); + s(EmcXm2DqsPadCtrl, 4:0, scratch22, 4:0); + s(EmcXm2DqsPadCtrl, 12:8, scratch22, 9:5); + s(EmcXm2DqsPadCtrl, 31:14, scratch22, 27:10); + s(EmcRrd, 3:0, scratch22, 31:28); + s(EmcXm2DqPadCtrl, 31:4, scratch23, 27:0); + s(EmcRext, 3:0, scratch23, 31:28); + s(EmcXm2CompPadCtrl, 16:0, scratch24, 16:0); + s(EmcXm2CompPadCtrl, 24:20, scratch24, 21:17); + s(EmcXm2CompPadCtrl, 27:27, scratch24, 22:22); + s(EmcXm2CompPadCtrl, 31:28, scratch24, 26:23); + s(EmcR2w, 4:0, scratch24, 31:27); + s(EmcCfg, 9:1, scratch25, 8:0); + s(EmcCfg, 26:16, scratch25, 19:9); + s(EmcCfg, 31:28, scratch25, 23:20); + s(EmcXm2VttGenPadCtrl, 0:0, scratch25, 24:24); + s(EmcXm2VttGenPadCtrl, 2:2, scratch25, 25:25); + s(EmcXm2VttGenPadCtrl, 18:16, scratch25, 28:26); + s(EmcXm2VttGenPadCtrl, 26:24, scratch25, 31:29); + s(EmcZcalInterval, 23:10, scratch26, 13:0); + s(EmcZcalInterval, 9:0, scratch26, 23:14); + s(EmcSelDpdCtrl, 5:2, scratch26, 27:24); + s(EmcSelDpdCtrl, 8:8, scratch26, 28:28); + s(EmcSelDpdCtrl, 18:16, scratch26, 31:29); + s(EmcXm2VttGenPadCtrl3, 22:0, scratch27, 22:0); + s(EmcXm2VttGenPadCtrl3, 24:24, scratch27, 23:23); + s(EmcSwizzleRank0ByteCfg, 1:0, scratch27, 25:24); + s(EmcSwizzleRank0ByteCfg, 5:4, scratch27, 27:26); + s(EmcSwizzleRank0ByteCfg, 9:8, scratch27, 29:28); + s(EmcSwizzleRank0ByteCfg, 13:12, scratch27, 31:30); + s(EmcXm2ClkPadCtrl2, 5:0, scratch28, 5:0); + s(EmcXm2ClkPadCtrl2, 13:8, scratch28, 11:6); + s(EmcXm2ClkPadCtrl2, 20:16, scratch28, 16:12); + s(EmcXm2ClkPadCtrl2, 23:23, scratch28, 17:17); + s(EmcXm2ClkPadCtrl2, 28:24, scratch28, 22:18); + s(EmcXm2ClkPadCtrl2, 31:31, scratch28, 23:23); + s(EmcSwizzleRank1ByteCfg, 1:0, scratch28, 25:24); + s(EmcSwizzleRank1ByteCfg, 5:4, scratch28, 27:26); + s(EmcSwizzleRank1ByteCfg, 9:8, scratch28, 29:28); + s(EmcSwizzleRank1ByteCfg, 13:12, scratch28, 31:30); + s(McEmemArbDaCovers, 23:0, scratch29, 23:0); + s(McEmemArbRsv, 7:0, scratch29, 31:24); + s(EmcAutoCalConfig, 4:0, scratch30, 4:0); + s(EmcAutoCalConfig, 12:8, scratch30, 9:5); + s(EmcAutoCalConfig, 18:16, scratch30, 12:10); + s(EmcAutoCalConfig, 25:20, scratch30, 18:13); + s(EmcAutoCalConfig, 31:28, scratch30, 22:19); + s(EmcRfc, 8:0, scratch30, 31:23); + s(EmcXm2DqsPadCtrl2, 21:0, scratch31, 21:0); + s(EmcXm2DqsPadCtrl2, 24:24, scratch31, 22:22); + s(EmcAr2Pden, 8:0, scratch31, 31:23); + s(EmcXm2ClkPadCtrl, 0:0, scratch32, 0:0); + s(EmcXm2ClkPadCtrl, 4:2, scratch32, 3:1); + s(EmcXm2ClkPadCtrl, 7:7, scratch32, 4:4); + s(EmcXm2ClkPadCtrl, 31:14, scratch32, 22:5); + s(EmcRfcSlr, 8:0, scratch32, 31:23); + s(EmcXm2DqsPadCtrl3, 0:0, scratch33, 0:0); + s(EmcXm2DqsPadCtrl3, 5:5, scratch33, 1:1); + s(EmcXm2DqsPadCtrl3, 12:8, scratch33, 6:2); + s(EmcXm2DqsPadCtrl3, 18:14, scratch33, 11:7); + s(EmcXm2DqsPadCtrl3, 24:20, scratch33, 16:12); + s(EmcXm2DqsPadCtrl3, 30:26, scratch33, 21:17); + s(EmcTxsr, 9:0, scratch33, 31:22); + s(McEmemArbCfg, 8:0, scratch40, 8:0); + s(McEmemArbCfg, 20:16, scratch40, 13:9); + s(McEmemArbCfg, 27:24, scratch40, 17:14); + s(McEmemArbCfg, 31:28, scratch40, 21:18); + s(EmcMc2EmcQ, 2:0, scratch40, 24:22); + s(EmcMc2EmcQ, 10:8, scratch40, 27:25); + s(EmcMc2EmcQ, 27:24, scratch40, 31:28); + s(EmcAutoCalInterval, 20:0, scratch42, 20:0); + s(McEmemArbOutstandingReq, 8:0, scratch42, 29:21); + s(McEmemArbOutstandingReq, 31:30, scratch42, 31:30); + s(EmcMrsWaitCnt2, 9:0, scratch44, 9:0); + s(EmcMrsWaitCnt2, 25:16, scratch44, 19:10); + s(EmcTxdsrvttgen, 11:0, scratch44, 31:20); + s(EmcMrsWaitCnt, 9:0, scratch45, 9:0); + s(EmcMrsWaitCnt, 25:16, scratch45, 19:10); + s(EmcCfgPipe, 1:0, scratch45, 21:20); + s(EmcCfgPipe, 9:4, scratch45, 27:22); + s(EmcCfgPipe, 15:12, scratch45, 31:28); + s(EmcXm2DqsPadCtrl4, 22:18, scratch46, 4:0); + s(EmcXm2DqsPadCtrl4, 16:12, scratch46, 9:5); + s(EmcXm2DqsPadCtrl4, 10:6, scratch46, 14:10); + s(EmcXm2DqsPadCtrl4, 4:0, scratch46, 19:15); + s(EmcZcalWaitCnt, 9:0, scratch46, 29:20); + s(EmcXm2DqsPadCtrl5, 22:18, scratch47, 4:0); + s(EmcXm2DqsPadCtrl5, 16:12, scratch47, 9:5); + s(EmcXm2DqsPadCtrl5, 10:6, scratch47, 14:10); + s(EmcXm2DqsPadCtrl5, 4:0, scratch47, 19:15); + s(EmcXm2VttGenPadCtrl2, 5:0, scratch47, 25:20); + s(EmcXm2VttGenPadCtrl2, 31:28, scratch47, 29:26); + s(EmcXm2DqsPadCtrl6, 12:8, scratch48, 4:0); + s(EmcXm2DqsPadCtrl6, 18:14, scratch48, 9:5); + s(EmcXm2DqsPadCtrl6, 24:20, scratch48, 14:10); + s(EmcXm2DqsPadCtrl6, 30:26, scratch48, 19:15); + s(EmcAutoCalConfig3, 4:0, scratch48, 24:20); + s(EmcAutoCalConfig3, 12:8, scratch48, 29:25); + s(EmcFbioCfg5, 1:0, scratch48, 31:30); + s(EmcDllXformQUse8, 4:0, scratch50, 4:0); + s(EmcDllXformQUse8, 22:8, scratch50, 19:5); + s(McEmemArbRing1Throttle, 4:0, scratch50, 24:20); + s(McEmemArbRing1Throttle, 20:16, scratch50, 29:25); + s(EmcFbioCfg5, 3:2, scratch50, 31:30); + s(EmcDllXformQUse9, 4:0, scratch51, 4:0); + s(EmcDllXformQUse9, 22:8, scratch51, 19:5); + s(EmcCttTermCtrl, 2:0, scratch51, 22:20); + s(EmcCttTermCtrl, 12:8, scratch51, 27:23); + s(EmcCttTermCtrl, 31:31, scratch51, 28:28); + s(EmcFbioCfg6, 2:0, scratch51, 31:29); + s(EmcDllXformQUse10, 4:0, scratch56, 4:0); + s(EmcDllXformQUse10, 22:8, scratch56, 19:5); + s(EmcXm2CmdPadCtrl, 10:3, scratch56, 27:20); + s(EmcXm2CmdPadCtrl, 28:28, scratch56, 28:28); + s(EmcPutermAdj, 1:0, scratch56, 30:29); + s(EmcPutermAdj, 7:7, scratch56, 31:31); + s(EmcDllXformQUse11, 4:0, scratch57, 4:0); + s(EmcDllXformQUse11, 22:8, scratch57, 19:5); + s(EmcWdv, 3:0, scratch57, 31:28); + s(EmcDllXformQUse12, 4:0, scratch58, 4:0); + s(EmcDllXformQUse12, 22:8, scratch58, 19:5); + s(EmcBurstRefreshNum, 3:0, scratch58, 31:28); + s(EmcDllXformQUse13, 4:0, scratch59, 4:0); + s(EmcDllXformQUse13, 22:8, scratch59, 19:5); + s(EmcWext, 3:0, scratch59, 31:28); + s(EmcDllXformQUse14, 4:0, scratch60, 4:0); + s(EmcDllXformQUse14, 22:8, scratch60, 19:5); + s(EmcClkenOverride, 3:1, scratch60, 30:28); + s(EmcClkenOverride, 6:6, scratch60, 31:31); + s(EmcDllXformQUse15, 4:0, scratch61, 4:0); + s(EmcDllXformQUse15, 22:8, scratch61, 19:5); + s(EmcR2r, 3:0, scratch61, 31:28); + s(EmcDllXformDq4, 4:0, scratch62, 4:0); + s(EmcDllXformDq4, 22:8, scratch62, 19:5); + s(EmcRc, 6:0, scratch62, 26:20); + s(EmcW2r, 4:0, scratch62, 31:27); + s(EmcDllXformDq5, 4:0, scratch63, 4:0); + s(EmcDllXformDq5, 22:8, scratch63, 19:5); + s(EmcTfaw, 6:0, scratch63, 26:20); + s(EmcR2p, 4:0, scratch63, 31:27); + s(EmcDllXformDq6, 4:0, scratch64, 4:0); + s(EmcDllXformDq6, 22:8, scratch64, 19:5); + s(EmcDliTrimTxDqs0, 6:0, scratch64, 26:20); + s(EmcQSafe, 4:0, scratch64, 31:27); + s(EmcDllXformDq7, 4:0, scratch65, 4:0); + s(EmcDllXformDq7, 22:8, scratch65, 19:5); + s(EmcDliTrimTxDqs1, 6:0, scratch65, 26:20); + s(EmcTClkStable, 4:0, scratch65, 31:27); + s(EmcAutoCalConfig2, 4:0, scratch66, 4:0); + s(EmcAutoCalConfig2, 12:8, scratch66, 9:5); + s(EmcAutoCalConfig2, 20:16, scratch66, 14:10); + s(EmcAutoCalConfig2, 28:24, scratch66, 19:15); + s(EmcDliTrimTxDqs2, 6:0, scratch66, 26:20); + s(EmcTClkStop, 4:0, scratch66, 31:27); + s(McEmemArbMisc1, 1:0, scratch67, 1:0); + s(McEmemArbMisc1, 12:4, scratch67, 10:2); + s(McEmemArbMisc1, 25:21, scratch67, 15:11); + s(McEmemArbMisc1, 31:28, scratch67, 19:16); + s(EmcDliTrimTxDqs3, 6:0, scratch67, 26:20); + s(EmcEInputDuration, 4:0, scratch67, 31:27); + s(EmcZcalMrwCmd, 7:0, scratch68, 7:0); + s(EmcZcalMrwCmd, 23:16, scratch68, 15:8); + s(EmcZcalMrwCmd, 31:30, scratch68, 17:16); + s(EmcTRefBw, 13:0, scratch68, 31:18); + s(EmcXm2CmdPadCtrl2, 31:14, scratch69, 17:0); + s(EmcDliTrimTxDqs4, 6:0, scratch69, 24:18); + s(EmcDliTrimTxDqs5, 6:0, scratch69, 31:25); + s(EmcXm2CmdPadCtrl3, 31:14, scratch70, 17:0); + s(EmcDliTrimTxDqs6, 6:0, scratch70, 24:18); + s(EmcDliTrimTxDqs7, 6:0, scratch70, 31:25); + s(EmcXm2CmdPadCtrl5, 2:0, scratch71, 2:0); + s(EmcXm2CmdPadCtrl5, 6:4, scratch71, 5:3); + s(EmcXm2CmdPadCtrl5, 10:8, scratch71, 8:6); + s(EmcXm2CmdPadCtrl5, 14:12, scratch71, 11:9); + s(EmcXm2CmdPadCtrl5, 18:16, scratch71, 14:12); + s(EmcXm2CmdPadCtrl5, 22:20, scratch71, 17:15); + s(EmcDliTrimTxDqs8, 6:0, scratch71, 24:18); + s(EmcDliTrimTxDqs9, 6:0, scratch71, 31:25); + s(EmcCdbCntl3, 17:0, scratch72, 17:0); + s(EmcDliTrimTxDqs10, 6:0, scratch72, 24:18); + s(EmcDliTrimTxDqs11, 6:0, scratch72, 31:25); + s(EmcSwizzleRank0Byte0, 2:0, scratch73, 2:0); + s(EmcSwizzleRank0Byte0, 6:4, scratch73, 5:3); + s(EmcSwizzleRank0Byte0, 10:8, scratch73, 8:6); + s(EmcSwizzleRank0Byte0, 14:12, scratch73, 11:9); + s(EmcSwizzleRank0Byte0, 18:16, scratch73, 14:12); + s(EmcSwizzleRank0Byte0, 22:20, scratch73, 17:15); + s(EmcDliTrimTxDqs12, 6:0, scratch73, 24:18); + s(EmcDliTrimTxDqs13, 6:0, scratch73, 31:25); + s(EmcSwizzleRank0Byte1, 2:0, scratch74, 2:0); + s(EmcSwizzleRank0Byte1, 6:4, scratch74, 5:3); + s(EmcSwizzleRank0Byte1, 10:8, scratch74, 8:6); + s(EmcSwizzleRank0Byte1, 14:12, scratch74, 11:9); + s(EmcSwizzleRank0Byte1, 18:16, scratch74, 14:12); + s(EmcSwizzleRank0Byte1, 22:20, scratch74, 17:15); + s(EmcDliTrimTxDqs14, 6:0, scratch74, 24:18); + s(EmcDliTrimTxDqs15, 6:0, scratch74, 31:25); + s(EmcSwizzleRank0Byte2, 2:0, scratch75, 2:0); + s(EmcSwizzleRank0Byte2, 6:4, scratch75, 5:3); + s(EmcSwizzleRank0Byte2, 10:8, scratch75, 8:6); + s(EmcSwizzleRank0Byte2, 14:12, scratch75, 11:9); + s(EmcSwizzleRank0Byte2, 18:16, scratch75, 14:12); + s(EmcSwizzleRank0Byte2, 22:20, scratch75, 17:15); + s(McEmemArbTimingRp, 6:0, scratch75, 24:18); + s(McEmemArbTimingRc, 6:0, scratch75, 31:25); + s(EmcSwizzleRank0Byte3, 2:0, scratch76, 2:0); + s(EmcSwizzleRank0Byte3, 6:4, scratch76, 5:3); + s(EmcSwizzleRank0Byte3, 10:8, scratch76, 8:6); + s(EmcSwizzleRank0Byte3, 14:12, scratch76, 11:9); + s(EmcSwizzleRank0Byte3, 18:16, scratch76, 14:12); + s(EmcSwizzleRank0Byte3, 22:20, scratch76, 17:15); + s(McEmemArbTimingFaw, 6:0, scratch76, 24:18); + s(McEmemArbTimingWap2Pre, 6:0, scratch76, 31:25); + s(EmcSwizzleRank1Byte0, 2:0, scratch77, 2:0); + s(EmcSwizzleRank1Byte0, 6:4, scratch77, 5:3); + s(EmcSwizzleRank1Byte0, 10:8, scratch77, 8:6); + s(EmcSwizzleRank1Byte0, 14:12, scratch77, 11:9); + s(EmcSwizzleRank1Byte0, 18:16, scratch77, 14:12); + s(EmcSwizzleRank1Byte0, 22:20, scratch77, 17:15); + s(EmcRas, 5:0, scratch77, 23:18); + s(EmcRp, 5:0, scratch77, 29:24); + s(EmcCfg2, 9:8, scratch77, 31:30); + s(EmcSwizzleRank1Byte1, 2:0, scratch78, 2:0); + s(EmcSwizzleRank1Byte1, 6:4, scratch78, 5:3); + s(EmcSwizzleRank1Byte1, 10:8, scratch78, 8:6); + s(EmcSwizzleRank1Byte1, 14:12, scratch78, 11:9); + s(EmcSwizzleRank1Byte1, 18:16, scratch78, 14:12); + s(EmcSwizzleRank1Byte1, 22:20, scratch78, 17:15); + s(EmcW2p, 5:0, scratch78, 23:18); + s(EmcRdRcd, 5:0, scratch78, 29:24); + s(EmcCfg2, 27:26, scratch78, 31:30); + s(EmcSwizzleRank1Byte2, 2:0, scratch79, 2:0); + s(EmcSwizzleRank1Byte2, 6:4, scratch79, 5:3); + s(EmcSwizzleRank1Byte2, 10:8, scratch79, 8:6); + s(EmcSwizzleRank1Byte2, 14:12, scratch79, 11:9); + s(EmcSwizzleRank1Byte2, 18:16, scratch79, 14:12); + s(EmcSwizzleRank1Byte2, 22:20, scratch79, 17:15); + s(EmcWrRcd, 5:0, scratch79, 23:18); + s(EmcQUse, 5:0, scratch79, 29:24); + s(EmcFbioCfg5, 4:4, scratch79, 31:31); + s(EmcSwizzleRank1Byte3, 2:0, scratch80, 2:0); + s(EmcSwizzleRank1Byte3, 6:4, scratch80, 5:3); + s(EmcSwizzleRank1Byte3, 10:8, scratch80, 8:6); + s(EmcSwizzleRank1Byte3, 14:12, scratch80, 11:9); + s(EmcSwizzleRank1Byte3, 18:16, scratch80, 14:12); + s(EmcSwizzleRank1Byte3, 22:20, scratch80, 17:15); + s(EmcQRst, 5:0, scratch80, 23:18); + s(EmcRdv, 5:0, scratch80, 29:24); + s(EmcFbioCfg5, 6:5, scratch80, 31:30); + s(EmcDynSelfRefControl, 15:0, scratch81, 15:0); + s(EmcDynSelfRefControl, 31:31, scratch81, 16:16); + s(EmcPdEx2Wr, 5:0, scratch81, 22:17); + s(EmcPdEx2Rd, 5:0, scratch81, 28:23); + s(EmcRefresh, 5:0, scratch82, 5:0); + s(EmcRefresh, 15:6, scratch82, 15:6); + s(EmcCmdQ, 4:0, scratch82, 20:16); + s(EmcCmdQ, 10:8, scratch82, 23:21); + s(EmcCmdQ, 14:12, scratch82, 26:24); + s(EmcCmdQ, 28:24, scratch82, 31:27); + s(EmcAcpdControl, 15:0, scratch83, 15:0); + s(EmcCfgDigDllPeriod, 15:0, scratch83, 31:16); + s(EmcDllXformDqs0, 4:0, scratch84, 4:0); + s(EmcDllXformDqs0, 22:12, scratch84, 15:5); + s(EmcDllXformDqs1, 4:0, scratch84, 20:16); + s(EmcDllXformDqs1, 22:12, scratch84, 31:21); + s(EmcDllXformDqs2, 4:0, scratch85, 4:0); + s(EmcDllXformDqs2, 22:12, scratch85, 15:5); + s(EmcDllXformDqs3, 4:0, scratch85, 20:16); + s(EmcDllXformDqs3, 22:12, scratch85, 31:21); + s(EmcDllXformDqs4, 4:0, scratch86, 4:0); + s(EmcDllXformDqs4, 22:12, scratch86, 15:5); + s(EmcDllXformDqs5, 4:0, scratch86, 20:16); + s(EmcDllXformDqs5, 22:12, scratch86, 31:21); + s(EmcDllXformDqs6, 4:0, scratch87, 4:0); + s(EmcDllXformDqs6, 22:12, scratch87, 15:5); + s(EmcDllXformDqs7, 4:0, scratch87, 20:16); + s(EmcDllXformDqs7, 22:12, scratch87, 31:21); + s(EmcDllXformDqs8, 4:0, scratch88, 4:0); + s(EmcDllXformDqs8, 22:12, scratch88, 15:5); + s(EmcDllXformDqs9, 4:0, scratch88, 20:16); + s(EmcDllXformDqs9, 22:12, scratch88, 31:21); + s(EmcDllXformDqs10, 4:0, scratch89, 4:0); + s(EmcDllXformDqs10, 22:12, scratch89, 15:5); + s(EmcDllXformDqs11, 4:0, scratch89, 20:16); + s(EmcDllXformDqs11, 22:12, scratch89, 31:21); + s(EmcDllXformDqs12, 4:0, scratch90, 4:0); + s(EmcDllXformDqs12, 22:12, scratch90, 15:5); + s(EmcDllXformDqs13, 4:0, scratch90, 20:16); + s(EmcDllXformDqs13, 22:12, scratch90, 31:21); + s(EmcDllXformDqs14, 4:0, scratch91, 4:0); + s(EmcDllXformDqs14, 22:12, scratch91, 15:5); + s(EmcDllXformDqs15, 4:0, scratch91, 20:16); + s(EmcDllXformDqs15, 22:12, scratch91, 31:21); + s(EmcDllXformQUse0, 4:0, scratch92, 4:0); + s(EmcDllXformQUse0, 22:12, scratch92, 15:5); + s(EmcDllXformQUse1, 4:0, scratch92, 20:16); + s(EmcDllXformQUse1, 22:12, scratch92, 31:21); + s(EmcDllXformQUse2, 4:0, scratch93, 4:0); + s(EmcDllXformQUse2, 22:12, scratch93, 15:5); + s(EmcDllXformQUse3, 4:0, scratch93, 20:16); + s(EmcDllXformQUse3, 22:12, scratch93, 31:21); + s(EmcDllXformQUse4, 4:0, scratch94, 4:0); + s(EmcDllXformQUse4, 22:12, scratch94, 15:5); + s(EmcDllXformQUse5, 4:0, scratch94, 20:16); + s(EmcDllXformQUse5, 22:12, scratch94, 31:21); + s(EmcDllXformQUse6, 4:0, scratch95, 4:0); + s(EmcDllXformQUse6, 22:12, scratch95, 15:5); + s(EmcDllXformQUse7, 4:0, scratch95, 20:16); + s(EmcDllXformQUse7, 22:12, scratch95, 31:21); + s(EmcDllXformDq0, 4:0, scratch96, 4:0); + s(EmcDllXformDq0, 22:12, scratch96, 15:5); + s(EmcDllXformDq1, 4:0, scratch96, 20:16); + s(EmcDllXformDq1, 22:12, scratch96, 31:21); + s(EmcDllXformDq2, 4:0, scratch97, 4:0); + s(EmcDllXformDq2, 22:12, scratch97, 15:5); + s(EmcDllXformDq3, 4:0, scratch97, 20:16); + s(EmcDllXformDq3, 22:12, scratch97, 31:21); + s(EmcPreRefreshReqCnt, 15:0, scratch98, 15:0); + s(EmcDllXformAddr0, 4:0, scratch98, 20:16); + s(EmcDllXformAddr0, 22:12, scratch98, 31:21); + s(EmcDllXformAddr1, 4:0, scratch99, 4:0); + s(EmcDllXformAddr1, 22:12, scratch99, 15:5); + s(EmcDllXformAddr2, 4:0, scratch99, 20:16); + s(EmcDllXformAddr2, 22:12, scratch99, 31:21); + s(EmcDllXformAddr3, 4:0, scratch100, 4:0); + s(EmcDllXformAddr3, 22:12, scratch100, 15:5); + s(EmcDllXformAddr4, 4:0, scratch100, 20:16); + s(EmcDllXformAddr4, 22:12, scratch100, 31:21); + s(EmcDllXformAddr5, 4:0, scratch101, 4:0); + s(EmcDllXformAddr5, 22:12, scratch101, 15:5); + s(EmcPChg2Pden, 5:0, scratch102, 5:0); + s(EmcAct2Pden, 5:0, scratch102, 11:6); + s(EmcRw2Pden, 5:0, scratch102, 17:12); + s(EmcTcke, 5:0, scratch102, 23:18); + s(EmcTrpab, 5:0, scratch102, 29:24); + s(EmcFbioCfg5, 8:7, scratch102, 31:30); + s(EmcCtt, 5:0, scratch103, 5:0); + s(EmcEInput, 5:0, scratch103, 11:6); + s(EmcPutermExtra, 21:16, scratch103, 17:12); + s(EmcTckesr, 5:0, scratch103, 23:18); + s(EmcTpd, 5:0, scratch103, 29:24); + s(EmcFbioCfg5, 10:9, scratch103, 31:30); + s(EmcRdvMask, 5:0, scratch104, 5:0); + s(EmcXm2CmdPadCtrl4, 0:0, scratch104, 6:6); + s(EmcXm2CmdPadCtrl4, 2:2, scratch104, 7:7); + s(EmcXm2CmdPadCtrl4, 4:4, scratch104, 8:8); + s(EmcXm2CmdPadCtrl4, 6:6, scratch104, 9:9); + s(EmcXm2CmdPadCtrl4, 8:8, scratch104, 10:10); + s(EmcXm2CmdPadCtrl4, 10:10, scratch104, 11:11); + s(EmcQpop, 5:0, scratch104, 17:12); + s(McEmemArbTimingRcd, 5:0, scratch104, 23:18); + s(McEmemArbTimingRas, 5:0, scratch104, 29:24); + s(EmcFbioCfg5, 12:11, scratch104, 31:30); + s(McEmemArbTimingRap2Pre, 5:0, scratch105, 5:0); + s(McEmemArbTimingR2W, 5:0, scratch105, 11:6); + s(McEmemArbTimingW2R, 5:0, scratch105, 17:12); + s(EmcIbdly, 4:0, scratch105, 22:18); + s(McEmemArbTimingR2R, 4:0, scratch105, 27:23); + s(EmcW2w, 3:0, scratch105, 31:28); + s(McEmemArbTimingW2W, 4:0, scratch106, 4:0); + s(McEmemArbOverride, 27:27, scratch106, 5:5); + s(McEmemArbOverride, 26:26, scratch106, 6:6); + s(McEmemArbOverride, 16:16, scratch106, 7:7); + s(McEmemArbOverride, 10:10, scratch106, 8:8); + s(McEmemArbOverride, 4:4, scratch106, 9:9); + s(EmcWdvMask, 3:0, scratch106, 13:10); + s(EmcCttDuration, 3:0, scratch106, 17:14); + s(EmcQuseWidth, 3:0, scratch106, 21:18); + s(EmcPutermWidth, 3:0, scratch106, 25:22); + s(EmcBgbiasCtl0, 3:0, scratch106, 29:26); + s(EmcFbioCfg5, 25:24, scratch106, 31:30); + s(McEmemArbTimingRrd, 3:0, scratch107, 3:0); + s(EmcFbioCfg5, 23:20, scratch107, 10:7); + s(EmcFbioCfg5, 15:13, scratch107, 13:11); + s(EmcCfg2, 5:3, scratch107, 16:14); + s(EmcFbioCfg5, 26:26, scratch107, 17:17); + s(EmcFbioCfg5, 28:28, scratch107, 18:18); + s(EmcCfg2, 2:0, scratch107, 21:19); + s(EmcCfg2, 7:6, scratch107, 23:22); + s(EmcCfg2, 15:10, scratch107, 29:24); + s(EmcCfg2, 23:22, scratch107, 31:30); + s(EmcCfg2, 25:24, scratch108, 1:0); + s(EmcCfg2, 31:28, scratch108, 5:2); + s(BootRomPatchData, 31:0, scratch15, 31:0); + s(BootRomPatchControl, 31:0, scratch16, 31:0); + s(EmcDevSelect, 1:0, scratch17, 31:30); + s(EmcZcalWarmColdBootEnables, 1:0, scratch18, 31:30); + s(EmcCfgDigDllPeriodWarmBoot, 1:0, scratch19, 31:30); + s(EmcWarmBootExtraModeRegWriteEnable, 0:0, scratch46, 30:30); + s(McClkenOverrideAllWarmBoot, 0:0, scratch46, 31:31); + s(EmcClkenOverrideAllWarmBoot, 0:0, scratch47, 30:30); + s(EmcMrsWarmBootEnable, 0:0, scratch47, 31:31); + s(EmcTimingControlWait, 7:0, scratch57, 27:20); + s(EmcZcalWarmBootWait, 7:0, scratch58, 27:20); + s(EmcAutoCalWait, 7:0, scratch59, 27:20); + s(WarmBootWait, 7:0, scratch60, 27:20); + s(EmcPinProgramWait, 7:0, scratch61, 27:20); + s(AhbArbitrationXbarCtrlMemInitDone, 0:0, scratch79, 30:30); + s(EmcExtraRefreshNum, 2:0, scratch81, 31:29); + s(SwizzleRankByteEncode, 15:0, scratch101, 31:16); + s(MemoryType, 2:0, scratch107, 6:4); + + switch (sdram->MemoryType) { + case NvBootMemoryType_LpDdr2: + s(EmcMrwLpddr2ZcalWarmBoot, 23:16, scratch5, 7:0); + s(EmcMrwLpddr2ZcalWarmBoot, 7:0, scratch5, 15:8); + s(EmcWarmBootMrwExtra, 23:16, scratch5, 23:16); + s(EmcWarmBootMrwExtra, 7:0, scratch5, 31:24); + s(EmcMrwLpddr2ZcalWarmBoot, 31:30, scratch6, 1:0); + s(EmcWarmBootMrwExtra, 31:30, scratch6, 3:2); + s(EmcMrwLpddr2ZcalWarmBoot, 27:26, scratch6, 5:4); + s(EmcWarmBootMrwExtra, 27:26, scratch6, 7:6); + s(EmcMrw1, 7:0, scratch7, 7:0); + s(EmcMrw1, 23:16, scratch7, 15:8); + s(EmcMrw1, 27:26, scratch7, 17:16); + s(EmcMrw1, 31:30, scratch7, 19:18); + s(EmcMrw2, 7:0, scratch8, 7:0); + s(EmcMrw2, 23:16, scratch8, 15:8); + s(EmcMrw2, 27:26, scratch8, 17:16); + s(EmcMrw2, 31:30, scratch8, 19:18); + s(EmcMrw3, 7:0, scratch9, 7:0); + s(EmcMrw3, 23:16, scratch9, 15:8); + s(EmcMrw3, 27:26, scratch9, 17:16); + s(EmcMrw3, 31:30, scratch9, 19:18); + s(EmcMrw4, 7:0, scratch10, 7:0); + s(EmcMrw4, 23:16, scratch10, 15:8); + s(EmcMrw4, 27:26, scratch10, 17:16); + s(EmcMrw4, 31:30, scratch10, 19:18); + break; + case NvBootMemoryType_Ddr3: + s(EmcMrs, 13:0, scratch5, 13:0); + s(EmcEmrs, 13:0, scratch5, 27:14); + s(EmcMrs, 21:20, scratch5, 29:28); + s(EmcMrs, 31:30, scratch5, 31:30); + s(EmcEmrs2, 13:0, scratch7, 13:0); + s(EmcEmrs, 21:20, scratch7, 15:14); + s(EmcEmrs, 31:30, scratch7, 17:16); + s(EmcEmrs2, 21:20, scratch7, 19:18); + s(EmcEmrs3, 13:0, scratch8, 13:0); + s(EmcEmrs2, 31:30, scratch8, 15:14); + s(EmcEmrs3, 21:20, scratch8, 17:16); + s(EmcEmrs3, 31:30, scratch8, 19:18); + s(EmcWarmBootMrsExtra, 13:0, scratch9, 13:0); + s(EmcWarmBootMrsExtra, 31:30, scratch9, 15:14); + s(EmcWarmBootMrsExtra, 21:20, scratch9, 17:16); + s(EmcZqCalDdr3WarmBoot, 31:30, scratch9, 19:18); + s(EmcMrs, 27:26, scratch10, 1:0); + s(EmcEmrs, 27:26, scratch10, 3:2); + s(EmcEmrs2, 27:26, scratch10, 5:4); + s(EmcEmrs3, 27:26, scratch10, 7:6); + s(EmcWarmBootMrsExtra, 27:27, scratch10, 8:8); + s(EmcWarmBootMrsExtra, 26:26, scratch10, 9:9); + s(EmcZqCalDdr3WarmBoot, 0:0, scratch10, 10:10); + s(EmcZqCalDdr3WarmBoot, 4:4, scratch10, 11:11); + c(0, scratch116, 31:0); + c(0, scratch117, 31:0); + break; + default: + printk(BIOS_CRIT, "ERROR: %s() unrecognized MemoryType %d!\n", + __func__, sdram->MemoryType); + } + + s(McVideoProtectGpuOverride0, 31:0, secure_scratch8, 31:0); + s(McVideoProtectVprOverride, 3:0, secure_scratch9, 3:0); + s(McVideoProtectVprOverride, 11:6, secure_scratch9, 9:4); + s(McVideoProtectVprOverride, 23:14, secure_scratch9, 19:10); + s(McVideoProtectVprOverride, 26:26, secure_scratch9, 20:20); + s(McVideoProtectVprOverride, 31:29, secure_scratch9, 23:21); + s(EmcFbioCfg5, 19:16, secure_scratch9, 27:24); + s(McDisplaySnapRing, 1:0, secure_scratch9, 29:28); + s(McDisplaySnapRing, 31:31, secure_scratch9, 30:30); + s(EmcAdrCfg, 0:0, secure_scratch9, 31:31); + s(McVideoProtectGpuOverride1, 15:0, secure_scratch10, 15:0); + s(McEmemAdrCfgBankMask0, 15:0, secure_scratch10, 31:16); + s(McEmemAdrCfgBankMask1, 15:0, secure_scratch11, 15:0); + s(McEmemAdrCfgBankMask2, 15:0, secure_scratch11, 31:16); + s(McEmemCfg, 13:0, secure_scratch12, 13:0); + s(McEmemCfg, 31:31, secure_scratch12, 14:14); + s(McVideoProtectBom, 31:20, secure_scratch12, 26:15); + s(McVideoProtectVprOverride1, 1:0, secure_scratch12, 28:27); + s(McVideoProtectVprOverride1, 4:4, secure_scratch12, 29:29); + s(McVideoProtectBomAdrHi, 1:0, secure_scratch12, 31:30); + s(McVideoProtectSizeMb, 11:0, secure_scratch13, 11:0); + s(McSecCarveoutBom, 31:20, secure_scratch13, 23:12); + s(McEmemAdrCfgBankSwizzle3, 2:0, secure_scratch13, 26:24); + s(McVideoProtectWriteAccess, 1:0, secure_scratch13, 28:27); + s(McSecCarveoutAdrHi, 1:0, secure_scratch13, 30:29); + s(McEmemAdrCfg, 0:0, secure_scratch13, 31:31); + s(McSecCarveoutSizeMb, 11:0, secure_scratch14, 11:0); + s(McMtsCarveoutBom, 31:20, secure_scratch14, 23:12); + s(McMtsCarveoutAdrHi, 1:0, secure_scratch14, 25:24); + s(McSecCarveoutProtectWriteAccess, 0:0, secure_scratch14, 26:26); + s(McMtsCarveoutRegCtrl, 0:0, secure_scratch14, 27:27); + s(McMtsCarveoutSizeMb, 11:0, secure_scratch15, 11:0); + s(McEmemAdrCfgDev0, 2:0, secure_scratch15, 14:12); + s(McEmemAdrCfgDev0, 9:8, secure_scratch15, 16:15); + s(McEmemAdrCfgDev0, 19:16, secure_scratch15, 20:17); + s(McEmemAdrCfgDev1, 2:0, secure_scratch15, 23:21); + s(McEmemAdrCfgDev1, 9:8, secure_scratch15, 25:24); + s(McEmemAdrCfgDev1, 19:16, secure_scratch15, 29:26); + + c(0x1555555, sec_disable2, 25:0); + c(0xff, sec_disable, 19:12); + + c(0, scratch2, 31:0); + m(pllm_base, 15:0, scratch2, 15:0); + m(pllm_base, 20:20, scratch2, 16:16); + m(pllm_misc2, 2:0, scratch2, 19:17); + c(0, scratch35, 31:0); + m(pllm_misc1, 23:0, scratch35, 23:0); + m(pllm_misc1, 30:28, scratch35, 30:28); + c(0, scratch3, 31:0); + s(PllMInputDivider, 7:0, scratch3, 7:0); + c(0x3e, scratch3, 15:8); + c(0, scratch3, 19:16); + s(PllMKVCO, 0:0, scratch3, 20:20); + s(PllMKCP, 1:0, scratch3, 22:21); + c(0, scratch36, 31:0); + s(PllMSetupControl, 23:0, scratch36, 23:0); + c(0, scratch4, 31:0); + s(PllMStableTime, 9:0, scratch4, 9:0); + s(PllMStableTime, 9:0, scratch4, 19:10); + + s(PllMSelectDiv2, 0:0, pllm_wb0_override2, 27:27); + s(PllMKVCO, 0:0, pllm_wb0_override2, 26:26); + s(PllMKCP, 1:0, pllm_wb0_override2, 25:24); + s(PllMSetupControl, 23:0, pllm_wb0_override2, 23:0); + s(PllMFeedbackDivider, 7:0, pllm_wb0_override_freq, 15:8); + s(PllMInputDivider, 7:0, pllm_wb0_override_freq, 7:0); + + c(3, pllp_wb0_override, 12:11); +} diff --git a/src/soc/nvidia/tegra210/secmon.c b/src/soc/nvidia/tegra210/secmon.c new file mode 100644 index 0000000000..66ebed2ea7 --- /dev/null +++ b/src/soc/nvidia/tegra210/secmon.c @@ -0,0 +1,50 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/secmon.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <soc/mmu_operations.h> + +static void soc_get_secure_mem(uint64_t *base, size_t *size) +{ + uintptr_t tz_base_mib; + size_t tz_size_mib; + + carveout_range(CARVEOUT_TZ, &tz_base_mib, &tz_size_mib); + + tz_base_mib *= MiB; + tz_size_mib *= MiB; + + *base = tz_base_mib; + *size = tz_size_mib; +} + +void soc_get_secmon_base_size(uint64_t *base, size_t *size) +{ + uintptr_t tz_base; + size_t ttb_size, tz_size; + + soc_get_secure_mem(&tz_base, &tz_size); + + ttb_size = TTB_SIZE * MiB; + + *base = tz_base + ttb_size; + *size = tz_size - ttb_size; +} diff --git a/src/soc/nvidia/tegra210/soc.c b/src/soc/nvidia/tegra210/soc.c new file mode 100644 index 0000000000..f3290c3ac6 --- /dev/null +++ b/src/soc/nvidia/tegra210/soc.c @@ -0,0 +1,191 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * Copyright 2014 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. + */ + +#include <arch/io.h> +#include <arch/cache.h> +#include <arch/spintable.h> +#include <cpu/cpu.h> +#include <bootstate.h> +#include <cbmem.h> +#include <console/console.h> +#include <device/device.h> +#include <soc/nvidia/tegra/dc.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <soc/cpu.h> +#include <soc/mc.h> +#include <soc/mtc.h> +#include <soc/nvidia/tegra/apbmisc.h> +#include <string.h> +#include <timer.h> +#include <vendorcode/google/chromeos/chromeos.h> + +#include "chip.h" + +static void soc_read_resources(device_t dev) +{ + unsigned long index = 0; + int i; uintptr_t begin, end; + size_t size; + + for (i = 0; i < CARVEOUT_NUM; i++) { + carveout_range(i, &begin, &size); + if (size == 0) + continue; + reserved_ram_resource(dev, index++, begin * KiB, size * KiB); + } + + memory_in_range_below_4gb(&begin, &end); + size = end - begin; + ram_resource(dev, index++, begin * KiB, size * KiB); + + memory_in_range_above_4gb(&begin, &end); + size = end - begin; + ram_resource(dev, index++, begin * KiB, size * KiB); +} + +static size_t cntrl_total_cpus(void) +{ + return CONFIG_MAX_CPUS; +} + +static int cntrl_start_cpu(unsigned int id, void (*entry)(void)) +{ + if (id >= CONFIG_MAX_CPUS) + return -1; + start_cpu(id, entry); + return 0; +} + +static struct cpu_control_ops cntrl_ops = { + .total_cpus = cntrl_total_cpus, + .start_cpu = cntrl_start_cpu, +}; + + +static void lock_down_vpr(void) +{ + struct tegra_mc_regs *regs = (void *)(uintptr_t)TEGRA_MC_BASE; + + write32(®s->video_protect_bom, 0); + write32(®s->video_protect_size_mb, 0); + + write32(®s->video_protect_gpu_override_0, 1); + /* + * Set both _ACCESS bits so that kernel/secure code + * can reconfig VPR careveout as needed from the TrustZone. + */ + + write32(®s->video_protect_reg_ctrl, + (MC_VPR_WR_ACCESS_DISABLE | MC_VPR_ALLOW_TZ_WR_ACCESS_ENABLE)); +} + +static void soc_init(device_t dev) +{ + struct soc_nvidia_tegra210_config *cfg; + + clock_init_arm_generic_timer(); + + cfg = dev->chip_info; + spintable_init((void *)cfg->spintable_addr); + arch_initialize_cpus(dev, &cntrl_ops); + + /* Lock down VPR */ + lock_down_vpr(); + +#if IS_ENABLED(CONFIG_MAINBOARD_DO_NATIVE_VGA_INIT) + if (vboot_skip_display_init()) + printk(BIOS_INFO, "Skipping display init.\n"); + else + display_startup(dev); +#endif +} + +static void soc_noop(device_t dev) +{ +} + +static struct device_operations soc_ops = { + .read_resources = soc_read_resources, + .set_resources = soc_noop, + .enable_resources = soc_noop, + .init = soc_init, + .scan_bus = NULL, +}; + +static void enable_tegra210_dev(device_t dev) +{ + if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) + dev->ops = &soc_ops; +} + +static void tegra210_init(void *chip_info) +{ + struct tegra_revision rev; + + tegra_revision_info(&rev); + + printk(BIOS_INFO, "chip %x rev %02x.%x\n", + rev.chip_id, rev.major, rev.minor); +} + +struct chip_operations soc_nvidia_tegra210_ops = { + CHIP_NAME("SOC Nvidia Tegra210") + .init = tegra210_init, + .enable_dev = enable_tegra210_dev, +}; + +static void tegra210_cpu_init(device_t cpu) +{ + if (cpu_is_bsp()) + if (tegra210_run_mtc() != 0) + printk(BIOS_ERR, "MTC: Training failed\n"); +} + +static const struct cpu_device_id ids[] = { + { 0x411fd071 }, + { CPU_ID_END }, +}; + +static struct device_operations cpu_dev_ops = { + .init = tegra210_cpu_init, +}; + +static const struct cpu_driver driver __cpu_driver = { + .ops = &cpu_dev_ops, + .id_table = ids, +}; + +static void enable_plld(void *unused) +{ + /* + * Configure a conservative 300MHz clock for PLLD. The kernel cannot + * handle PLLD not being configured so enable PLLD unconditionally + * with a default clock rate. + */ + clock_configure_plld(300 * MHz); +} + +/* + * The PLLD being enabled is done at BS_DEV_INIT time because mainboard_init() + * is the first thing called. This ensures PLLD is up and functional before + * anything that mainboard can do that implicitly relies on PLLD. + */ +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, enable_plld, NULL); diff --git a/src/soc/nvidia/tegra210/sor.c b/src/soc/nvidia/tegra210/sor.c new file mode 100644 index 0000000000..116656e782 --- /dev/null +++ b/src/soc/nvidia/tegra210/sor.c @@ -0,0 +1,1100 @@ +/* + * drivers/video/tegra/dc/sor.c + * + * Copyright (c) 2011-2015, NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <console/console.h> +#include <arch/io.h> +#include <stdint.h> +#include <lib.h> +#include <stdlib.h> +#include <delay.h> +#include <soc/addressmap.h> +#include <device/device.h> +#include <stdlib.h> +#include <string.h> +#include <cpu/cpu.h> +#include <boot/tables.h> +#include <cbmem.h> +#include <soc/nvidia/tegra/dc.h> +#include <soc/nvidia/tegra/types.h> +#include <soc/sor.h> +#include <soc/nvidia/tegra/displayport.h> +#include <soc/clk_rst.h> +#include <soc/clock.h> +#include "chip.h" +#include <soc/display.h> + +#define DEBUG_SOR 0 + +#define APBDEV_PMC_DPD_SAMPLE (0x20) +#define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE (0) +#define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE (1) +#define APBDEV_PMC_SEL_DPD_TIM (0x1c8) +#define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT (0x7f) +#define APBDEV_PMC_IO_DPD2_REQ (0x1c0) +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT (25) +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF (0 << 25) +#define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON (1 << 25) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT (30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK (0x3 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE (0 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF (1 << 30) +#define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON (2 << 30) +#define APBDEV_PMC_IO_DPD2_STATUS (0x1c4) +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT (25) +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25) +#define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25) + +#define DC_N_WINDOWS 5 + +static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg) +{ + void *addr = sor->base + (u32) (reg << 2); + u32 reg_val = READL(addr); + return reg_val; +} + +static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, + u32 reg, u32 val) +{ + void *addr = sor->base + (u32) (reg << 2); + WRITEL(val, addr); +} + +static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor, + u32 reg, u32 mask, u32 val) +{ + u32 reg_val = tegra_sor_readl(sor, reg); + reg_val &= ~mask; + reg_val |= val; + tegra_sor_writel(sor, reg, reg_val); +} + +void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor) +{ + tegra_sor_write_field(sor, + NV_SOR_DP_PADCTL(sor->portnum), + NV_SOR_DP_PADCTL_TX_PU_MASK, + NV_SOR_DP_PADCTL_TX_PU_DISABLE); +} + +void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, + u32 pe_reg, u32 vs_reg, u32 pc_reg, u8 pc_supported) +{ + tegra_sor_write_field(sor, NV_SOR_PR(sor->portnum), + mask, pe_reg); + tegra_sor_write_field(sor, NV_SOR_DC(sor->portnum), + mask, vs_reg); + if (pc_supported) { + tegra_sor_write_field( + sor, NV_SOR_POSTCURSOR(sor->portnum), + mask, pc_reg); + } +} + +static u32 tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, + u32 reg, u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us) +{ + u32 temp = timeout_us; + u32 reg_val = 0; + + do { + udelay(poll_interval_us); + reg_val = tegra_sor_readl(sor, reg); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + } while ((reg_val & mask) != exp_val); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + printk(BIOS_ERR, + "sor_poll_register 0x%x: timeout, " + "(reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", + reg, reg_val, mask, exp_val); + + return temp; +} + +int tegra_dc_sor_set_power_state(struct tegra_dc_sor_data *sor, int pu_pd) +{ + u32 reg_val; + u32 orig_val; + + orig_val = tegra_sor_readl(sor, NV_SOR_PWR); + + reg_val = pu_pd ? NV_SOR_PWR_NORMAL_STATE_PU : + NV_SOR_PWR_NORMAL_STATE_PD; /* normal state only */ + + if (reg_val == orig_val) + return 0; /* No update needed */ + + reg_val |= NV_SOR_PWR_SETTING_NEW_TRIGGER; + tegra_sor_writel(sor, NV_SOR_PWR, reg_val); + + /* Poll to confirm it is done */ + if (tegra_dc_sor_poll_register(sor, NV_SOR_PWR, + NV_SOR_PWR_SETTING_NEW_DEFAULT_MASK, + NV_SOR_PWR_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS * 1000)) { + printk(BIOS_ERR, + "dc timeout waiting for SOR_PWR = NEW_DONE\n"); + return -EFAULT; + } + return 0; +} + +void tegra_dc_sor_set_dp_linkctl(struct tegra_dc_sor_data *sor, int ena, + u8 training_pattern, const struct tegra_dc_dp_link_config *link_cfg) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_DP_LINKCTL(sor->portnum)); + + if (ena) + reg_val |= NV_SOR_DP_LINKCTL_ENABLE_YES; + else + reg_val &= NV_SOR_DP_LINKCTL_ENABLE_NO; + + reg_val &= ~NV_SOR_DP_LINKCTL_TUSIZE_MASK; + reg_val |= (link_cfg->tu_size << NV_SOR_DP_LINKCTL_TUSIZE_SHIFT); + + if (link_cfg->enhanced_framing) + reg_val |= NV_SOR_DP_LINKCTL_ENHANCEDFRAME_ENABLE; + + tegra_sor_writel(sor, NV_SOR_DP_LINKCTL(sor->portnum), reg_val); + + switch (training_pattern) { + case training_pattern_1: + tegra_sor_writel(sor, NV_SOR_DP_TPG, 0x41414141); + break; + case training_pattern_2: + case training_pattern_3: + reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ? + 0x43434343 : 0x42424242; + tegra_sor_writel(sor, NV_SOR_DP_TPG, reg_val); + break; + default: + tegra_sor_writel(sor, NV_SOR_DP_TPG, 0x50505050); + break; + } +} + +static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor, + int pu, int is_lvds) +{ + u32 reg_val; + + /* SOR lane sequencer */ + if (pu) + reg_val = NV_SOR_LANE_SEQ_CTL_SETTING_NEW_TRIGGER | + NV_SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | + NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PU; + else + reg_val = NV_SOR_LANE_SEQ_CTL_SETTING_NEW_TRIGGER | + NV_SOR_LANE_SEQ_CTL_SEQUENCE_UP | + NV_SOR_LANE_SEQ_CTL_NEW_POWER_STATE_PD; + + if (is_lvds) + reg_val |= 15 << NV_SOR_LANE_SEQ_CTL_DELAY_SHIFT; + else + reg_val |= 1 << NV_SOR_LANE_SEQ_CTL_DELAY_SHIFT; + + tegra_sor_writel(sor, NV_SOR_LANE_SEQ_CTL, reg_val); + + if (tegra_dc_sor_poll_register(sor, NV_SOR_LANE_SEQ_CTL, + NV_SOR_LANE_SEQ_CTL_SETTING_MASK, + NV_SOR_LANE_SEQ_CTL_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS*1000)) { + printk(BIOS_ERR, + "dp: timeout while waiting for SOR lane sequencer " + "to power down langes\n"); + return -1; + } + return 0; +} + +static int tegra_dc_sor_power_dplanes(struct tegra_dc_sor_data *sor, + u32 lane_count, int pu) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_DP_PADCTL(sor->portnum)); + + if (pu) { + switch (lane_count) { + case 4: + reg_val |= (NV_SOR_DP_PADCTL_PD_TXD_3_NO | + NV_SOR_DP_PADCTL_PD_TXD_2_NO); + /* fall through */ + case 2: + reg_val |= NV_SOR_DP_PADCTL_PD_TXD_1_NO; + case 1: + reg_val |= NV_SOR_DP_PADCTL_PD_TXD_0_NO; + break; + default: + printk(BIOS_ERR, + "dp: invalid lane number %d\n", lane_count); + return -1; + } + + tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), reg_val); + tegra_dc_sor_set_lane_count(sor, lane_count); + } + return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0); +} + +void tegra_dc_sor_set_panel_power(struct tegra_dc_sor_data *sor, + int power_up) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_DP_PADCTL(sor->portnum)); + + if (power_up) + reg_val &= ~NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN; + else + reg_val |= NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN; + + tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), reg_val); +} + +static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div, + u32 pwm_dutycycle) +{ + tegra_sor_writel(sor, NV_SOR_PWM_DIV, pwm_div); + tegra_sor_writel(sor, NV_SOR_PWM_CTL, + (pwm_dutycycle & NV_SOR_PWM_CTL_DUTY_CYCLE_MASK) | + NV_SOR_PWM_CTL_SETTING_NEW_TRIGGER); + + if (tegra_dc_sor_poll_register(sor, NV_SOR_PWM_CTL, + NV_SOR_PWM_CTL_SETTING_NEW_SHIFT, + NV_SOR_PWM_CTL_SETTING_NEW_DONE, + 100, TEGRA_SOR_TIMEOUT_MS * 1000)) { + printk(BIOS_ERR, + "dp: timeout while waiting for SOR PWM setting\n"); + } +} + +static void tegra_dc_sor_set_dp_mode(struct tegra_dc_sor_data *sor, + const struct tegra_dc_dp_link_config *link_cfg) +{ + u32 reg_val; + + tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw); + + tegra_dc_sor_set_dp_linkctl(sor, 1, training_pattern_none, link_cfg); + reg_val = tegra_sor_readl(sor, NV_SOR_DP_CONFIG(sor->portnum)); + reg_val &= ~NV_SOR_DP_CONFIG_WATERMARK_MASK; + reg_val |= link_cfg->watermark; + reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_MASK; + reg_val |= (link_cfg->active_count << + NV_SOR_DP_CONFIG_ACTIVESYM_COUNT_SHIFT); + reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_MASK; + reg_val |= (link_cfg->active_frac << + NV_SOR_DP_CONFIG_ACTIVESYM_FRAC_SHIFT); + if (link_cfg->activepolarity) + reg_val |= NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; + else + reg_val &= ~NV_SOR_DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; + reg_val |= (NV_SOR_DP_CONFIG_ACTIVESYM_CNTL_ENABLE | + NV_SOR_DP_CONFIG_RD_RESET_VAL_NEGATIVE); + + tegra_sor_writel(sor, NV_SOR_DP_CONFIG(sor->portnum), reg_val); + + /* program h/vblank sym */ + tegra_sor_write_field(sor, NV_SOR_DP_AUDIO_HBLANK_SYMBOLS, + NV_SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK, link_cfg->hblank_sym); + + tegra_sor_write_field(sor, NV_SOR_DP_AUDIO_VBLANK_SYMBOLS, + NV_SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK, link_cfg->vblank_sym); +} + +static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor) +{ + tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 0); + tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 1); + tegra_sor_writel(sor, NV_SOR_SUPER_STATE0, 0); +} + +static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor) +{ + tegra_sor_writel(sor, NV_SOR_STATE0, 0); + tegra_sor_writel(sor, NV_SOR_STATE0, 1); + tegra_sor_writel(sor, NV_SOR_STATE0, 0); +} + +static void tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up) +{ + u32 reg_val; + void *pmc_base = sor->pmc_base; + + if (up) { + WRITEL(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE, + pmc_base + APBDEV_PMC_DPD_SAMPLE); + WRITEL(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM); + } + + reg_val = READL(pmc_base + APBDEV_PMC_IO_DPD2_REQ); + reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON || + APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK); + + reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON | + APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF : + APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF | + APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON; + + WRITEL(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ); + + /* Polling */ + u32 temp = 10*1000; + do { + udelay(20); + reg_val = READL(pmc_base + APBDEV_PMC_IO_DPD2_STATUS); + if (temp > 20) + temp -= 20; + else + break; + } while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0); + + if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) + printk(BIOS_ERR, + "PMC_IO_DPD2 polling failed (0x%x)\n", reg_val); + + if (up) + WRITEL(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE, + pmc_base + APBDEV_PMC_DPD_SAMPLE); +} + +void tegra_dc_sor_set_internal_panel(struct tegra_dc_sor_data *sor, int is_int) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_DP_SPARE(sor->portnum)); + if (is_int) + reg_val |= NV_SOR_DP_SPARE_PANEL_INTERNAL; + else + reg_val &= ~NV_SOR_DP_SPARE_PANEL_INTERNAL; + + reg_val |= NV_SOR_DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK | + NV_SOR_DP_SPARE_SEQ_ENABLE_YES; + tegra_sor_writel(sor, NV_SOR_DP_SPARE(sor->portnum), reg_val); +} + +void tegra_dc_sor_read_link_config(struct tegra_dc_sor_data *sor, u8 *link_bw, + u8 *lane_count) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_CLK_CNTRL); + *link_bw = (reg_val & NV_SOR_CLK_CNTRL_DP_LINK_SPEED_MASK) + >> NV_SOR_CLK_CNTRL_DP_LINK_SPEED_SHIFT; + reg_val = tegra_sor_readl(sor, + NV_SOR_DP_LINKCTL(sor->portnum)); + + switch (reg_val & NV_SOR_DP_LINKCTL_LANECOUNT_MASK) { + case NV_SOR_DP_LINKCTL_LANECOUNT_ZERO: + *lane_count = 0; + break; + case NV_SOR_DP_LINKCTL_LANECOUNT_ONE: + *lane_count = 1; + break; + case NV_SOR_DP_LINKCTL_LANECOUNT_TWO: + *lane_count = 2; + break; + case NV_SOR_DP_LINKCTL_LANECOUNT_FOUR: + *lane_count = 4; + break; + default: + printk(BIOS_ERR, "Unknown lane count\n"); + } +} + +void tegra_dc_sor_set_link_bandwidth(struct tegra_dc_sor_data *sor, u8 link_bw) +{ + tegra_sor_write_field(sor, NV_SOR_CLK_CNTRL, + NV_SOR_CLK_CNTRL_DP_LINK_SPEED_MASK, + link_bw << NV_SOR_CLK_CNTRL_DP_LINK_SPEED_SHIFT); +} + +void tegra_dc_sor_set_lane_count(struct tegra_dc_sor_data *sor, u8 lane_count) +{ + u32 reg_val; + + reg_val = tegra_sor_readl(sor, NV_SOR_DP_LINKCTL(sor->portnum)); + reg_val &= ~NV_SOR_DP_LINKCTL_LANECOUNT_MASK; + switch (lane_count) { + case 0: + break; + case 1: + reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_ONE; + break; + case 2: + reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_TWO; + break; + case 4: + reg_val |= NV_SOR_DP_LINKCTL_LANECOUNT_FOUR; + break; + default: + /* 0 should be handled earlier. */ + printk(BIOS_ERR, "dp: Invalid lane count %d\n", + lane_count); + return; + } + tegra_sor_writel(sor, NV_SOR_DP_LINKCTL(sor->portnum), reg_val); +} + +static void tegra_sor_enable_edp_clock(struct tegra_dc_sor_data *sor) +{ + sor_clock_start(); +} + +/* The SOR power sequencer does not work for t1xx so SW has to + go through the power sequence manually */ +/* Power up steps from spec: */ +/* STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL */ +/* 1 1 1 1 1 1 1 1 */ +/* 2 1 1 1 1 1 0 1 */ +/* 3 1 1 0 1 1 0 1 */ +/* 4 1 0 0 0 0 0 1 */ +/* 5 0 0 0 0 0 0 1 */ +static void tegra_dc_sor_power_up(struct tegra_dc_sor_data *sor, + int is_lvds) +{ + if (sor->power_is_up) + return; + + /* Set link bw */ + tegra_dc_sor_set_link_bandwidth(sor, + is_lvds ? NV_SOR_CLK_CNTRL_DP_LINK_SPEED_LVDS : + NV_SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62); + + /* step 1 */ + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */ + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */ + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */ + NV_SOR_PLL2_AUX7_PORT_POWERDOWN_ENABLE | + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE | + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE); + tegra_sor_write_field(sor, NV_SOR_PLL0, + NV_SOR_PLL0_PWR_MASK | /* PDPLL */ + NV_SOR_PLL0_VCOPD_MASK, /* PLLVCOPD */ + NV_SOR_PLL0_PWR_OFF | + NV_SOR_PLL0_VCOPD_ASSERT); + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */ + NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERUP); + + /* step 2 */ + tegra_dc_sor_io_set_dpd(sor, 1); + udelay(15); + + /* step 3 */ + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK, + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); + udelay(25); + + /* step 4 */ + tegra_sor_write_field(sor, NV_SOR_PLL0, + NV_SOR_PLL0_PWR_MASK | /* PDPLL */ + NV_SOR_PLL0_VCOPD_MASK, /* PLLVCOPD */ + NV_SOR_PLL0_PWR_ON | NV_SOR_PLL0_VCOPD_RESCIND); + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */ + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); + udelay(225); + + /* step 5 */ + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK, /* PDPORT */ + NV_SOR_PLL2_AUX7_PORT_POWERDOWN_DISABLE); + + sor->power_is_up = 1; +} + +#if DEBUG_SOR +static void dump_sor_reg(struct tegra_dc_sor_data *sor) +{ +#define DUMP_REG(a) printk(BIOS_INFO, "%-32s %03x %08x\n", \ + #a, a, tegra_sor_readl(sor, a)); + + DUMP_REG(NV_SOR_SUPER_STATE0); + DUMP_REG(NV_SOR_SUPER_STATE1); + DUMP_REG(NV_SOR_STATE0); + DUMP_REG(NV_SOR_STATE1); + DUMP_REG(NV_HEAD_STATE0(0)); + DUMP_REG(NV_HEAD_STATE0(1)); + DUMP_REG(NV_HEAD_STATE1(0)); + DUMP_REG(NV_HEAD_STATE1(1)); + DUMP_REG(NV_HEAD_STATE2(0)); + DUMP_REG(NV_HEAD_STATE2(1)); + DUMP_REG(NV_HEAD_STATE3(0)); + DUMP_REG(NV_HEAD_STATE3(1)); + DUMP_REG(NV_HEAD_STATE4(0)); + DUMP_REG(NV_HEAD_STATE4(1)); + DUMP_REG(NV_HEAD_STATE5(0)); + DUMP_REG(NV_HEAD_STATE5(1)); + DUMP_REG(NV_SOR_CRC_CNTRL); + DUMP_REG(NV_SOR_CLK_CNTRL); + DUMP_REG(NV_SOR_CAP); + DUMP_REG(NV_SOR_PWR); + DUMP_REG(NV_SOR_TEST); + DUMP_REG(NV_SOR_PLL0); + DUMP_REG(NV_SOR_PLL1); + DUMP_REG(NV_SOR_PLL2); + DUMP_REG(NV_SOR_PLL3); + DUMP_REG(NV_SOR_CSTM); + DUMP_REG(NV_SOR_LVDS); + DUMP_REG(NV_SOR_CRCA); + DUMP_REG(NV_SOR_CRCB); + DUMP_REG(NV_SOR_SEQ_CTL); + DUMP_REG(NV_SOR_LANE_SEQ_CTL); + DUMP_REG(NV_SOR_SEQ_INST(0)); + DUMP_REG(NV_SOR_SEQ_INST(1)); + DUMP_REG(NV_SOR_SEQ_INST(2)); + DUMP_REG(NV_SOR_SEQ_INST(3)); + DUMP_REG(NV_SOR_SEQ_INST(4)); + DUMP_REG(NV_SOR_SEQ_INST(5)); + DUMP_REG(NV_SOR_SEQ_INST(6)); + DUMP_REG(NV_SOR_SEQ_INST(7)); + DUMP_REG(NV_SOR_SEQ_INST(8)); + DUMP_REG(NV_SOR_PWM_DIV); + DUMP_REG(NV_SOR_PWM_CTL); + DUMP_REG(NV_SOR_MSCHECK); + DUMP_REG(NV_SOR_XBAR_CTRL); + DUMP_REG(NV_SOR_DP_LINKCTL(0)); + DUMP_REG(NV_SOR_DP_LINKCTL(1)); + DUMP_REG(NV_SOR_DC(0)); + DUMP_REG(NV_SOR_DC(1)); + DUMP_REG(NV_SOR_LANE_DRIVE_CURRENT(0)); + DUMP_REG(NV_SOR_PR(0)); + DUMP_REG(NV_SOR_LANE4_PREEMPHASIS(0)); + DUMP_REG(NV_SOR_POSTCURSOR(0)); + DUMP_REG(NV_SOR_DP_CONFIG(0)); + DUMP_REG(NV_SOR_DP_CONFIG(1)); + DUMP_REG(NV_SOR_DP_MN(0)); + DUMP_REG(NV_SOR_DP_MN(1)); + DUMP_REG(NV_SOR_DP_PADCTL(0)); + DUMP_REG(NV_SOR_DP_PADCTL(1)); + DUMP_REG(NV_SOR_DP_DEBUG(0)); + DUMP_REG(NV_SOR_DP_DEBUG(1)); + DUMP_REG(NV_SOR_DP_SPARE(0)); + DUMP_REG(NV_SOR_DP_SPARE(1)); + DUMP_REG(NV_SOR_DP_TPG); + + return; +} +#endif + +static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor, + int is_lvds) +{ + const struct tegra_dc *dc = sor->dc; + const struct tegra_dc_dp_data *dp = dc->out; + const struct tegra_dc_dp_link_config *link_cfg = &dp->link_cfg; + const struct soc_nvidia_tegra210_config *config = dc->config; + + const int head_num = 0; + u32 reg_val = NV_SOR_STATE1_ASY_OWNER_HEAD0 << head_num; + u32 vsync_end, hsync_end; + u32 vblank_end, hblank_end; + u32 vblank_start, hblank_start; + + reg_val |= is_lvds ? NV_SOR_STATE1_ASY_PROTOCOL_LVDS_CUSTOM : + NV_SOR_STATE1_ASY_PROTOCOL_DP_A; + reg_val |= NV_SOR_STATE1_ASY_SUBOWNER_NONE | + NV_SOR_STATE1_ASY_CRCMODE_COMPLETE_RASTER; + + reg_val |= NV_SOR_STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE; + reg_val |= NV_SOR_STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE; + reg_val |= (link_cfg->bits_per_pixel > 18) ? + NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_24_444 : + NV_SOR_STATE1_ASY_PIXELDEPTH_BPP_18_444; + + tegra_sor_writel(sor, NV_SOR_STATE1, reg_val); + + /* Skipping programming NV_HEAD_STATE0, assuming: + interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB */ + + tegra_sor_writel(sor, NV_HEAD_STATE1(head_num), + vtotal(config) << NV_HEAD_STATE1_VTOTAL_SHIFT | + htotal(config) << NV_HEAD_STATE1_HTOTAL_SHIFT); + + vsync_end = config->vsync_width - 1; + hsync_end = config->hsync_width - 1; + tegra_sor_writel(sor, NV_HEAD_STATE2(head_num), + vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT | + hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT); + + vblank_end = vsync_end + config->vback_porch; + hblank_end = hsync_end + config->hback_porch; + tegra_sor_writel(sor, NV_HEAD_STATE3(head_num), + vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT | + hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT); + + vblank_start = vblank_end + config->yres; + hblank_start = hblank_end + config->xres; + tegra_sor_writel(sor, NV_HEAD_STATE4(head_num), + vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT | + hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT); + + /* TODO: adding interlace mode support */ + tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1); + + tegra_sor_write_field(sor, NV_SOR_CSTM, + NV_SOR_CSTM_ROTCLK_DEFAULT_MASK | + NV_SOR_CSTM_LVDS_EN_ENABLE, + 2 << NV_SOR_CSTM_ROTCLK_SHIFT | + is_lvds ? NV_SOR_CSTM_LVDS_EN_ENABLE : + NV_SOR_CSTM_LVDS_EN_DISABLE); + + tegra_dc_sor_config_pwm(sor, 1024, 1024); +} + +static void tegra_dc_sor_enable_dc(struct tegra_dc_sor_data *sor) +{ + struct tegra_dc *dc = sor->dc; + struct display_controller *disp_ctrl = (void *)dc->base; + + u32 reg_val = READL(&disp_ctrl->cmd.state_access); + + WRITEL(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); + WRITEL(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt); + + /* Enable DC now - otherwise pure text console may not show. */ + WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd); + WRITEL(reg_val, &disp_ctrl->cmd.state_access); +} + +void tegra_dc_sor_enable_dp(struct tegra_dc_sor_data *sor) +{ + const struct tegra_dc_dp_link_config *link_cfg = sor->link_cfg; + + tegra_sor_write_field(sor, NV_SOR_CLK_CNTRL, + NV_SOR_CLK_CNTRL_DP_CLK_SEL_MASK, + NV_SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK); + + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_MASK, + NV_SOR_PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); + udelay(25); + + tegra_sor_write_field(sor, NV_SOR_PLL3, + NV_SOR_PLL3_PLLVDD_MODE_MASK, + NV_SOR_PLL3_PLLVDD_MODE_V3_3); + tegra_sor_writel(sor, NV_SOR_PLL0, + 0xf << NV_SOR_PLL0_ICHPMP_SHFIT | + 0x3 << NV_SOR_PLL0_VCOCAP_SHIFT | + NV_SOR_PLL0_PLLREG_LEVEL_V45 | + NV_SOR_PLL0_RESISTORSEL_EXT | + NV_SOR_PLL0_PWR_ON | NV_SOR_PLL0_VCOPD_RESCIND); + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX1_SEQ_MASK | NV_SOR_PLL2_AUX9_LVDSEN_OVERRIDE | + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, + NV_SOR_PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE | + NV_SOR_PLL2_AUX9_LVDSEN_OVERRIDE | + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); + tegra_sor_writel(sor, NV_SOR_PLL1, + NV_SOR_PLL1_TERM_COMPOUT_HIGH | NV_SOR_PLL1_TMDS_TERM_ENABLE); + + if (tegra_dc_sor_poll_register(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, + NV_SOR_PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE, + 100, TEGRA_SOR_TIMEOUT_MS * 1000)) { + printk(BIOS_ERR, "DP failed to lock PLL\n"); + return; + } + + tegra_sor_write_field(sor, NV_SOR_PLL2, + NV_SOR_PLL2_AUX2_MASK | NV_SOR_PLL2_AUX7_PORT_POWERDOWN_MASK, + NV_SOR_PLL2_AUX2_OVERRIDE_POWERDOWN | + NV_SOR_PLL2_AUX7_PORT_POWERDOWN_DISABLE); + + tegra_dc_sor_power_up(sor, 0); + + /* re-enable SOR clock */ + tegra_sor_enable_edp_clock(sor); /* select pll_dp as clock source */ + + /* Power up lanes */ + tegra_dc_sor_power_dplanes(sor, link_cfg->lane_count, 1); + + tegra_dc_sor_set_dp_mode(sor, link_cfg); + +} + +void tegra_dc_sor_attach(struct tegra_dc_sor_data *sor) +{ + u32 reg_val; + struct display_controller *disp_ctrl = (void *)sor->dc->base; + + tegra_dc_sor_enable_dc(sor); + tegra_dc_sor_config_panel(sor, 0); + + WRITEL(0x9f00, &disp_ctrl->cmd.state_ctrl); + WRITEL(0x9f, &disp_ctrl->cmd.state_ctrl); + + WRITEL(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | + PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, + &disp_ctrl->cmd.disp_pow_ctrl); + + reg_val = tegra_sor_readl(sor, NV_SOR_TEST); + if (reg_val & NV_SOR_TEST_ATTACHED_TRUE) + return; + + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ATTACHED_NO); + + /* + * Enable display2sor clock at least 2 cycles before DC start, + * to clear sor internal valid signal. + */ + + /* Stop dc for 3 cycles */ + WRITEL(0, &disp_ctrl->disp.disp_win_opt); + WRITEL(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + udelay(FRAME_IN_MS * 1000 * 3); + + /* Attach head */ + tegra_dc_sor_update(sor); + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ATTACHED_YES); + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ATTACHED_YES | + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_AWAKE | + NV_SOR_SUPER_STATE1_ASY_ORMODE_NORMAL); + tegra_dc_sor_super_update(sor); + + /* wait for another 5 cycles */ + udelay(FRAME_IN_MS * 1000 * 5); + + /* Re-enable dc */ + WRITEL(READ_MUX_ACTIVE | WRITE_MUX_ACTIVE, + &disp_ctrl->cmd.state_access); + WRITEL(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt); + + WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd); + WRITEL(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + + if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST, + NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + NV_SOR_TEST_ACT_HEAD_OPMODE_AWAKE, + 100, TEGRA_SOR_ATTACH_TIMEOUT_MS * 1000)) + printk(BIOS_ERR, "dc timeout waiting for OPMOD = AWAKE\n"); + else + printk(BIOS_INFO, "%s: sor is attached\n", __func__); + +#if DEBUG_SOR + dump_sor_reg(sor); +#endif +} + +void tegra_dc_sor_set_lane_parm(struct tegra_dc_sor_data *sor, + const struct tegra_dc_dp_link_config *link_cfg) +{ + tegra_sor_writel(sor, NV_SOR_LANE_DRIVE_CURRENT(sor->portnum), + link_cfg->drive_current); + tegra_sor_writel(sor, NV_SOR_PR(sor->portnum), + link_cfg->preemphasis); + tegra_sor_writel(sor, NV_SOR_POSTCURSOR(sor->portnum), + link_cfg->postcursor); + tegra_sor_writel(sor, NV_SOR_LVDS, 0); + + tegra_dc_sor_set_link_bandwidth(sor, link_cfg->link_bw); + tegra_dc_sor_set_lane_count(sor, link_cfg->lane_count); + + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + NV_SOR_DP_PADCTL_TX_PU_ENABLE | + NV_SOR_DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK, + NV_SOR_DP_PADCTL_TX_PU_ENABLE | + 2 << NV_SOR_DP_PADCTL_TX_PU_VALUE_SHIFT); + + /* Precharge */ + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + 0xf0, 0xf0); + udelay(20); + + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + 0xf0, 0x0); +} + +void tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor) +{ + u32 drive_current = 0; + u32 pre_emphasis = 0; + + /* Set to a known-good pre-calibrated setting */ + switch (sor->link_cfg->link_bw) { + case SOR_LINK_SPEED_G1_62: + case SOR_LINK_SPEED_G2_7: + drive_current = 0x13131313; + pre_emphasis = 0; + break; + case SOR_LINK_SPEED_G5_4: + printk(BIOS_WARNING, "T1xx does not support 5.4G link" + " clock.\n"); + default: + printk(BIOS_WARNING, "Invalid sor link bandwidth: %d\n", + sor->link_cfg->link_bw); + return; + } + + tegra_sor_writel(sor, NV_SOR_LANE_DRIVE_CURRENT(sor->portnum), + drive_current); + tegra_sor_writel(sor, NV_SOR_PR(sor->portnum), pre_emphasis); +} + +void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor) +{ + u32 pad_ctrl = 0; + int err = 0; + + switch (sor->link_cfg->lane_count) { + case 4: + pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO | + NV_SOR_DP_PADCTL_PD_TXD_1_NO | + NV_SOR_DP_PADCTL_PD_TXD_2_NO | + NV_SOR_DP_PADCTL_PD_TXD_3_NO); + break; + case 2: + pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO | + NV_SOR_DP_PADCTL_PD_TXD_1_NO | + NV_SOR_DP_PADCTL_PD_TXD_2_YES | + NV_SOR_DP_PADCTL_PD_TXD_3_YES); + break; + case 1: + pad_ctrl = (NV_SOR_DP_PADCTL_PD_TXD_0_NO | + NV_SOR_DP_PADCTL_PD_TXD_1_YES | + NV_SOR_DP_PADCTL_PD_TXD_2_YES | + NV_SOR_DP_PADCTL_PD_TXD_3_YES); + break; + default: + printk(BIOS_ERR, "Invalid sor lane count: %u\n", + sor->link_cfg->lane_count); + return; + } + + pad_ctrl |= NV_SOR_DP_PADCTL_PAD_CAL_PD_POWERDOWN; + tegra_sor_writel(sor, NV_SOR_DP_PADCTL(sor->portnum), pad_ctrl); + + err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0); + if (err) { + printk(BIOS_ERR, + "Wait for lane power down failed: %d\n", err); + return; + } +} + +void tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor) +{ + const struct tegra_dc_dp_link_config *cfg = sor->link_cfg; + u32 val = 0; + + switch (cfg->lane_count) { + case 4: + val |= (NV_SOR_DP_PADCTL_PD_TXD_3_NO | + NV_SOR_DP_PADCTL_PD_TXD_2_NO); + /* fall through */ + case 2: + val |= NV_SOR_DP_PADCTL_PD_TXD_1_NO; + /* fall through */ + case 1: + val |= NV_SOR_DP_PADCTL_PD_TXD_0_NO; + break; + default: + printk(BIOS_ERR, + "dp: invalid lane number %d\n", cfg->lane_count); + return; + } + + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + (0xf << NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), + (val << NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT)); + udelay(100); + tegra_sor_write_field(sor, NV_SOR_DP_PADCTL(sor->portnum), + (0xf << NV_SOR_DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), 0); +} + +static u32 tegra_dc_poll_register(void *reg, + u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us) +{ + u32 temp = timeout_us; + u32 reg_val = 0; + + do { + udelay(poll_interval_us); + reg_val = READL(reg); + if (timeout_us > poll_interval_us) + timeout_us -= poll_interval_us; + else + break; + } while ((reg_val & mask) != exp_val); + + if ((reg_val & mask) == exp_val) + return 0; /* success */ + + return temp; +} + +static void tegra_dc_sor_general_act(struct display_controller *disp_ctrl) +{ + WRITEL(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); + + if (tegra_dc_poll_register(&disp_ctrl->cmd.state_ctrl, + GENERAL_ACT_REQ, 0, 100, + TEGRA_DC_POLL_TIMEOUT_MS*1000)) + printk(BIOS_ERR, + "dc timeout waiting for DC to stop\n"); +} + +static struct tegra_dc_mode min_mode = { + .h_ref_to_sync = 0, + .v_ref_to_sync = 1, + .h_sync_width = 1, + .v_sync_width = 1, + .h_back_porch = 20, + .v_back_porch = 0, + .h_active = 16, + .v_active = 16, + .h_front_porch = 1, + .v_front_porch = 2, +}; + +/* Disable windows and set minimum raster timings */ +static void +tegra_dc_sor_disable_win_short_raster(struct display_controller *disp_ctrl, + int *dc_reg_ctx) +{ + int selected_windows, i; + + selected_windows = READL(&disp_ctrl->cmd.disp_win_header); + + /* Store and clear window options */ + for (i = 0; i < DC_N_WINDOWS; ++i) { + WRITEL(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header); + dc_reg_ctx[i] = READL(&disp_ctrl->win.win_opt); + WRITEL(0, &disp_ctrl->win.win_opt); + WRITEL(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl); + } + + WRITEL(selected_windows, &disp_ctrl->cmd.disp_win_header); + + /* Store current raster timings and set minimum timings */ + dc_reg_ctx[i++] = READL(&disp_ctrl->disp.ref_to_sync); + WRITEL(min_mode.h_ref_to_sync | (min_mode.v_ref_to_sync << 16), + &disp_ctrl->disp.ref_to_sync); + + dc_reg_ctx[i++] = READL(&disp_ctrl->disp.sync_width); + WRITEL(min_mode.h_sync_width | (min_mode.v_sync_width << 16), + &disp_ctrl->disp.sync_width); + + dc_reg_ctx[i++] = READL(&disp_ctrl->disp.back_porch); + WRITEL(min_mode.h_back_porch | + min_mode.v_back_porch << 16, + &disp_ctrl->disp.back_porch); + + dc_reg_ctx[i++] = READL(&disp_ctrl->disp.front_porch); + WRITEL(min_mode.h_front_porch | + min_mode.v_front_porch << 16, + &disp_ctrl->disp.front_porch); + + dc_reg_ctx[i++] = READL(&disp_ctrl->disp.disp_active); + WRITEL(min_mode.h_active | (min_mode.v_active << 16), + &disp_ctrl->disp.disp_active); + + WRITEL(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); +} + +/* Restore previous windows status and raster timings */ +static void +tegra_dc_sor_restore_win_and_raster(struct display_controller *disp_ctrl, + int *dc_reg_ctx) +{ + int selected_windows, i; + + selected_windows = READL(&disp_ctrl->cmd.disp_win_header); + + for (i = 0; i < DC_N_WINDOWS; ++i) { + WRITEL(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header); + WRITEL(dc_reg_ctx[i], &disp_ctrl->win.win_opt); + WRITEL(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl); + } + + WRITEL(selected_windows, &disp_ctrl->cmd.disp_win_header); + + WRITEL(dc_reg_ctx[i++], &disp_ctrl->disp.ref_to_sync); + WRITEL(dc_reg_ctx[i++], &disp_ctrl->disp.sync_width); + WRITEL(dc_reg_ctx[i++], &disp_ctrl->disp.back_porch); + WRITEL(dc_reg_ctx[i++], &disp_ctrl->disp.front_porch); + WRITEL(dc_reg_ctx[i++], &disp_ctrl->disp.disp_active); + + WRITEL(GENERAL_UPDATE, &disp_ctrl->cmd.state_ctrl); +} + +static void tegra_dc_sor_enable_sor(struct tegra_dc_sor_data *sor, int enable) +{ + struct display_controller *disp_ctrl = (void *)sor->dc->base; + u32 reg_val = READL(&disp_ctrl->disp.disp_win_opt); + + reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE; + WRITEL(reg_val, &disp_ctrl->disp.disp_win_opt); +} + +void tegra_dc_detach(struct tegra_dc_sor_data *sor) +{ + struct display_controller *disp_ctrl = (void *)sor->dc->base; + int dc_reg_ctx[DC_N_WINDOWS + 5]; + unsigned long dc_int_mask; + + /* Sleep mode */ + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SLEEP | + NV_SOR_SUPER_STATE1_ASY_ORMODE_SAFE | + NV_SOR_SUPER_STATE1_ATTACHED_YES); + tegra_dc_sor_super_update(sor); + + tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx); + + if (tegra_dc_sor_poll_register(sor, NV_SOR_TEST, + NV_SOR_TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, + NV_SOR_TEST_ACT_HEAD_OPMODE_SLEEP, + 100, TEGRA_SOR_ATTACH_TIMEOUT_MS*1000)) { + printk(BIOS_ERR, + "dc timeout waiting for OPMOD = SLEEP\n"); + } + + tegra_sor_writel(sor, NV_SOR_SUPER_STATE1, + NV_SOR_SUPER_STATE1_ASY_HEAD_OP_SLEEP | + NV_SOR_SUPER_STATE1_ASY_ORMODE_SAFE | + NV_SOR_SUPER_STATE1_ATTACHED_NO); + + /* Mask DC interrupts during the 2 dummy frames required for detach */ + dc_int_mask = READL(&disp_ctrl->cmd.int_mask); + WRITEL(0, &disp_ctrl->cmd.int_mask); + + /* Stop DC->SOR path */ + tegra_dc_sor_enable_sor(sor, 0); + tegra_dc_sor_general_act(disp_ctrl); + + /* Stop DC */ + WRITEL(DISP_CTRL_MODE_STOP, &disp_ctrl->cmd.disp_cmd); + tegra_dc_sor_general_act(disp_ctrl); + + tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx); + + WRITEL(dc_int_mask, &disp_ctrl->cmd.int_mask); +} diff --git a/src/soc/nvidia/tegra210/spi.c b/src/soc/nvidia/tegra210/spi.c new file mode 100644 index 0000000000..ffb4d47c70 --- /dev/null +++ b/src/soc/nvidia/tegra210/spi.c @@ -0,0 +1,960 @@ +/* + * NVIDIA Tegra SPI controller (T114 and later) + * + * Copyright (c) 2010-2013 NVIDIA Corporation + * Copyright (C) 2013 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. + */ + +#include <arch/cache.h> +#include <arch/io.h> +#include <assert.h> +#include <boot_device.h> +#include <cbfs.h> +#include <console/console.h> +#include <delay.h> +#include <inttypes.h> +#include <spi-generic.h> +#include <spi_flash.h> +#include <soc/addressmap.h> +#include <soc/dma.h> +#include <soc/spi.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <symbols.h> +#include <timer.h> + + +#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI +# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "TEGRA_SPI: " x) +#else +# define DEBUG_SPI(x,...) +#endif + +/* + * 64 packets in FIFO mode, BLOCK_SIZE packets in DMA mode. Packets can vary + * in size from 4 to 32 bits. To keep things simple we'll use 8-bit packets. + */ +#define SPI_PACKET_SIZE_BYTES 1 +#define SPI_MAX_TRANSFER_BYTES_FIFO (64 * SPI_PACKET_SIZE_BYTES) +#define SPI_MAX_TRANSFER_BYTES_DMA (65535 * SPI_PACKET_SIZE_BYTES) + +/* + * This is used to workaround an issue seen where it may take some time for + * packets to show up in the FIFO after they have been received and the + * BLOCK_COUNT has been incremented. + */ +#define SPI_FIFO_XFER_TIMEOUT_US 1000 + +/* COMMAND1 */ +#define SPI_CMD1_GO (1 << 31) +#define SPI_CMD1_M_S (1 << 30) +#define SPI_CMD1_MODE_MASK 0x3 +#define SPI_CMD1_MODE_SHIFT 28 +#define SPI_CMD1_CS_SEL_MASK 0x3 +#define SPI_CMD1_CS_SEL_SHIFT 26 +#define SPI_CMD1_CS_POL_INACTIVE3 (1 << 25) +#define SPI_CMD1_CS_POL_INACTIVE2 (1 << 24) +#define SPI_CMD1_CS_POL_INACTIVE1 (1 << 23) +#define SPI_CMD1_CS_POL_INACTIVE0 (1 << 22) +#define SPI_CMD1_CS_SW_HW (1 << 21) +#define SPI_CMD1_CS_SW_VAL (1 << 20) +#define SPI_CMD1_IDLE_SDA_MASK 0x3 +#define SPI_CMD1_IDLE_SDA_SHIFT 18 +#define SPI_CMD1_BIDIR (1 << 17) +#define SPI_CMD1_LSBI_FE (1 << 16) +#define SPI_CMD1_LSBY_FE (1 << 15) +#define SPI_CMD1_BOTH_EN_BIT (1 << 14) +#define SPI_CMD1_BOTH_EN_BYTE (1 << 13) +#define SPI_CMD1_RX_EN (1 << 12) +#define SPI_CMD1_TX_EN (1 << 11) +#define SPI_CMD1_PACKED (1 << 5) +#define SPI_CMD1_BIT_LEN_MASK 0x1f +#define SPI_CMD1_BIT_LEN_SHIFT 0 + +/* COMMAND2 */ +#define SPI_CMD2_TX_CLK_TAP_DELAY (1 << 6) +#define SPI_CMD2_TX_CLK_TAP_DELAY_MASK (0x3F << 6) +#define SPI_CMD2_RX_CLK_TAP_DELAY (1 << 0) +#define SPI_CMD2_RX_CLK_TAP_DELAY_MASK (0x3F << 0) + +/* SPI_TRANS_STATUS */ +#define SPI_STATUS_RDY (1 << 30) +#define SPI_STATUS_SLV_IDLE_COUNT_MASK 0xff +#define SPI_STATUS_SLV_IDLE_COUNT_SHIFT 16 +#define SPI_STATUS_BLOCK_COUNT 0xffff +#define SPI_STATUS_BLOCK_COUNT_SHIFT 0 + +/* SPI_FIFO_STATUS */ +#define SPI_FIFO_STATUS_CS_INACTIVE (1 << 31) +#define SPI_FIFO_STATUS_FRAME_END (1 << 30) +#define SPI_FIFO_STATUS_RX_FIFO_FULL_COUNT_MASK 0x7f +#define SPI_FIFO_STATUS_RX_FIFO_FULL_COUNT_SHIFT 23 +#define SPI_FIFO_STATUS_TX_FIFO_EMPTY_COUNT_MASK 0x7f +#define SPI_FIFO_STATUS_TX_FIFO_EMPTY_COUNT_SHIFT 16 +#define SPI_FIFO_STATUS_RX_FIFO_FLUSH (1 << 15) +#define SPI_FIFO_STATUS_TX_FIFO_FLUSH (1 << 14) +#define SPI_FIFO_STATUS_ERR (1 << 8) +#define SPI_FIFO_STATUS_TX_FIFO_OVF (1 << 7) +#define SPI_FIFO_STATUS_TX_FIFO_UNR (1 << 6) +#define SPI_FIFO_STATUS_RX_FIFO_OVF (1 << 5) +#define SPI_FIFO_STATUS_RX_FIFO_UNR (1 << 4) +#define SPI_FIFO_STATUS_TX_FIFO_FULL (1 << 3) +#define SPI_FIFO_STATUS_TX_FIFO_EMPTY (1 << 2) +#define SPI_FIFO_STATUS_RX_FIFO_FULL (1 << 1) +#define SPI_FIFO_STATUS_RX_FIFO_EMPTY (1 << 0) + +/* SPI_DMA_CTL */ +#define SPI_DMA_CTL_DMA (1 << 31) +#define SPI_DMA_CTL_CONT (1 << 30) +#define SPI_DMA_CTL_IE_RX (1 << 29) +#define SPI_DMA_CTL_IE_TX (1 << 28) +#define SPI_DMA_CTL_RX_TRIG_MASK 0x3 +#define SPI_DMA_CTL_RX_TRIG_SHIFT 19 +#define SPI_DMA_CTL_TX_TRIG_MASK 0x3 +#define SPI_DMA_CTL_TX_TRIG_SHIFT 15 + +/* SPI_DMA_BLK */ +#define SPI_DMA_CTL_BLOCK_SIZE_MASK 0xffff +#define SPI_DMA_CTL_BLOCK_SIZE_SHIFT 0 + +static struct tegra_spi_channel tegra_spi_channels[] = { + /* + * Note: Tegra pinmux must be setup for corresponding SPI channel in + * order for its registers to be accessible. If pinmux has not been + * set up, access to the channel's registers will simply hang. + * + * TODO(dhendrix): Clarify or remove this comment (is clock setup + * necessary first, or just pinmux, or both?) + */ + { + .slave = { .bus = 1, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI1_BASE, + .req_sel = APBDMA_SLAVE_SL2B1, + }, + { + .slave = { .bus = 2, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI2_BASE, + .req_sel = APBDMA_SLAVE_SL2B2, + }, + { + .slave = { .bus = 3, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI3_BASE, + .req_sel = APBDMA_SLAVE_SL2B3, + }, + { + .slave = { .bus = 4, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI4_BASE, + .req_sel = APBDMA_SLAVE_SL2B4, + }, + { + .slave = { .bus = 5, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI5_BASE, + .req_sel = APBDMA_SLAVE_SL2B5, + }, + { + .slave = { .bus = 6, }, + .regs = (struct tegra_spi_regs *)TEGRA_SPI6_BASE, + .req_sel = APBDMA_SLAVE_SL2B6, + }, + { + .slave = { .bus = 7, }, + .regs = (struct tegra_spi_regs *)TEGRA_QSPI_BASE, + .req_sel = APBDMA_SLAVE_QSPI, + }, +}; + +enum spi_direction { + SPI_SEND, + SPI_RECEIVE, +}; + +struct tegra_spi_channel *tegra_spi_init(unsigned int bus) +{ + int i; + struct tegra_spi_channel *spi = NULL; + + for (i = 0; i < ARRAY_SIZE(tegra_spi_channels); i++) { + if (tegra_spi_channels[i].slave.bus == bus) { + spi = &tegra_spi_channels[i]; + break; + } + } + if (!spi) + return NULL; + + /* software drives chip-select, set value to high */ + setbits_le32(&spi->regs->command1, + SPI_CMD1_CS_SW_HW | SPI_CMD1_CS_SW_VAL); + + /* 8-bit transfers, unpacked mode, most significant bit first */ + clrbits_le32(&spi->regs->command1, + SPI_CMD1_BIT_LEN_MASK | SPI_CMD1_PACKED); + setbits_le32(&spi->regs->command1, 7 << SPI_CMD1_BIT_LEN_SHIFT); + + return spi; +} + +static struct tegra_spi_channel * const to_tegra_spi(int bus) { + return &tegra_spi_channels[bus - 1]; +} + +static unsigned int tegra_spi_speed(unsigned int bus) +{ + /* FIXME: implement this properly, for now use max value (50MHz) */ + return 50000000; +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_regs *regs = to_tegra_spi(slave->bus)->regs; + u32 val; + + tegra_spi_init(slave->bus); + + val = read32(®s->command1); + + /* select appropriate chip-select line */ + val &= ~(SPI_CMD1_CS_SEL_MASK << SPI_CMD1_CS_SEL_SHIFT); + val |= (slave->cs << SPI_CMD1_CS_SEL_SHIFT); + + /* drive chip-select with the inverse of the "inactive" value */ + if (val & (SPI_CMD1_CS_POL_INACTIVE0 << slave->cs)) + val &= ~SPI_CMD1_CS_SW_VAL; + else + val |= SPI_CMD1_CS_SW_VAL; + + write32(®s->command1, val); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct tegra_spi_regs *regs = to_tegra_spi(slave->bus)->regs; + u32 val; + + val = read32(®s->command1); + + if (val & (SPI_CMD1_CS_POL_INACTIVE0 << slave->cs)) + val |= SPI_CMD1_CS_SW_VAL; + else + val &= ~SPI_CMD1_CS_SW_VAL; + + write32(®s->command1, val); +} + +static void dump_fifo_status(struct tegra_spi_channel *spi) +{ + u32 status = read32(&spi->regs->fifo_status); + + printk(BIOS_INFO, "Raw FIFO status: 0x%08x\n", status); + if (status & SPI_FIFO_STATUS_TX_FIFO_OVF) + printk(BIOS_INFO, "\tTx overflow detected\n"); + if (status & SPI_FIFO_STATUS_TX_FIFO_UNR) + printk(BIOS_INFO, "\tTx underrun detected\n"); + if (status & SPI_FIFO_STATUS_RX_FIFO_OVF) + printk(BIOS_INFO, "\tRx overflow detected\n"); + if (status & SPI_FIFO_STATUS_RX_FIFO_UNR) + printk(BIOS_INFO, "\tRx underrun detected\n"); + + printk(BIOS_INFO, "TX_FIFO: 0x%08x, TX_DATA: 0x%08x\n", + read32(&spi->regs->tx_fifo), read32(&spi->regs->tx_data)); + printk(BIOS_INFO, "RX_FIFO: 0x%08x, RX_DATA: 0x%08x\n", + read32(&spi->regs->rx_fifo), read32(&spi->regs->rx_data)); +} + +static void clear_fifo_status(struct tegra_spi_channel *spi) +{ + clrbits_le32(&spi->regs->fifo_status, + SPI_FIFO_STATUS_ERR | + SPI_FIFO_STATUS_TX_FIFO_OVF | + SPI_FIFO_STATUS_TX_FIFO_UNR | + SPI_FIFO_STATUS_RX_FIFO_OVF | + SPI_FIFO_STATUS_RX_FIFO_UNR); +} + +static void dump_spi_regs(struct tegra_spi_channel *spi) +{ + printk(BIOS_INFO, "SPI regs:\n" + "\tdma_blk: 0x%08x\n" + "\tcommand1: 0x%08x\n" + "\tdma_ctl: 0x%08x\n" + "\ttrans_status: 0x%08x\n", + read32(&spi->regs->dma_blk), + read32(&spi->regs->command1), + read32(&spi->regs->dma_ctl), + read32(&spi->regs->trans_status)); +} + +static void dump_dma_regs(struct apb_dma_channel *dma) +{ + printk(BIOS_INFO, "DMA regs:\n" + "\tahb_ptr: 0x%08x\n" + "\tapb_ptr: 0x%08x\n" + "\tahb_seq: 0x%08x\n" + "\tapb_seq: 0x%08x\n" + "\tcsr: 0x%08x\n" + "\tcsre: 0x%08x\n" + "\twcount: 0x%08x\n" + "\tdma_byte_sta: 0x%08x\n" + "\tword_transfer: 0x%08x\n", + read32(&dma->regs->ahb_ptr), + read32(&dma->regs->apb_ptr), + read32(&dma->regs->ahb_seq), + read32(&dma->regs->apb_seq), + read32(&dma->regs->csr), + read32(&dma->regs->csre), + read32(&dma->regs->wcount), + read32(&dma->regs->dma_byte_sta), + read32(&dma->regs->word_transfer)); +} + +static inline unsigned int spi_byte_count(struct tegra_spi_channel *spi) +{ + /* FIXME: Make this take total packet size into account */ + return read32(&spi->regs->trans_status) & + (SPI_STATUS_BLOCK_COUNT << SPI_STATUS_BLOCK_COUNT_SHIFT); +} + +/* + * This calls udelay() with a calculated value based on the SPI speed and + * number of bytes remaining to be transferred. It assumes that if the + * calculated delay period is less than MIN_DELAY_US then it is probably + * not worth the overhead of yielding. + */ +#define MIN_DELAY_US 250 +static void spi_delay(struct tegra_spi_channel *spi, + unsigned int bytes_remaining) +{ + unsigned int ns_per_byte, delay_us; + + ns_per_byte = 1000000000 / (tegra_spi_speed(spi->slave.bus) / 8); + delay_us = (ns_per_byte * bytes_remaining) / 1000; + + if (delay_us < MIN_DELAY_US) + return; + + udelay(delay_us); +} + +static void tegra_spi_wait(struct tegra_spi_channel *spi) +{ + unsigned int count, dma_blk; + + dma_blk = 1 + (read32(&spi->regs->dma_blk) & + (SPI_DMA_CTL_BLOCK_SIZE_MASK << SPI_DMA_CTL_BLOCK_SIZE_SHIFT)); + + while ((count = spi_byte_count(spi)) != dma_blk) + spi_delay(spi, dma_blk - count); +} + + +static int fifo_error(struct tegra_spi_channel *spi) +{ + return read32(&spi->regs->fifo_status) & SPI_FIFO_STATUS_ERR ? 1 : 0; +} + +static int tegra_spi_pio_prepare(struct tegra_spi_channel *spi, + unsigned int bytes, enum spi_direction dir) +{ + u8 *p = spi->out_buf; + unsigned int todo = MIN(bytes, SPI_MAX_TRANSFER_BYTES_FIFO); + u32 flush_mask, enable_mask; + + if (dir == SPI_SEND) { + flush_mask = SPI_FIFO_STATUS_TX_FIFO_FLUSH; + enable_mask = SPI_CMD1_TX_EN; + } else { + flush_mask = SPI_FIFO_STATUS_RX_FIFO_FLUSH; + enable_mask = SPI_CMD1_RX_EN; + } + + setbits_le32(&spi->regs->fifo_status, flush_mask); + while (read32(&spi->regs->fifo_status) & flush_mask) + ; + + /* + * BLOCK_SIZE in SPI_DMA_BLK register applies to both DMA and + * PIO transfers. And, it should be programmed before RX_EN or + * TX_EN is set. + */ + write32(&spi->regs->dma_blk, todo - 1); + + setbits_le32(&spi->regs->command1, enable_mask); + + if (dir == SPI_SEND) { + unsigned int to_fifo = bytes; + while (to_fifo) { + write32(&spi->regs->tx_fifo, *p); + p++; + to_fifo--; + } + } + + return todo; +} + +static void tegra_spi_pio_start(struct tegra_spi_channel *spi) +{ + setbits_le32(&spi->regs->trans_status, SPI_STATUS_RDY); + /* + * Need to stabilize other reg bit before GO bit set. + * + * From IAS: + * For successful operation at various freq combinations, min of 4-5 + * spi_clk cycle delay might be required before enabling PIO or DMA bit. + * This is needed to overcome the MCP between core and pad_macro. + * The worst case delay calculation can be done considering slowest + * qspi_clk as 1 MHz. based on that 1 us delay should be enough before + * enabling pio or dma. + */ + udelay(2); + setbits_le32(&spi->regs->command1, SPI_CMD1_GO); + /* Need to wait a few cycles before command1 register is read */ + udelay(1); + /* Make sure the write to command1 completes. */ + read32(&spi->regs->command1); +} + +static inline u32 rx_fifo_count(struct tegra_spi_channel *spi) +{ + return (read32(&spi->regs->fifo_status) >> + SPI_FIFO_STATUS_RX_FIFO_FULL_COUNT_SHIFT) & + SPI_FIFO_STATUS_RX_FIFO_FULL_COUNT_MASK; +} + +static int tegra_spi_pio_finish(struct tegra_spi_channel *spi) +{ + u8 *p = spi->in_buf; + struct stopwatch sw; + + clrbits_le32(&spi->regs->command1, SPI_CMD1_RX_EN | SPI_CMD1_TX_EN); + + /* + * Allow some time in case the Rx FIFO does not yet have + * all packets pushed into it. See chrome-os-partner:24215. + */ + stopwatch_init_usecs_expire(&sw, SPI_FIFO_XFER_TIMEOUT_US); + do { + if (rx_fifo_count(spi) == spi_byte_count(spi)) + break; + } while (!stopwatch_expired(&sw)); + + while (!(read32(&spi->regs->fifo_status) & + SPI_FIFO_STATUS_RX_FIFO_EMPTY)) { + *p = read8(&spi->regs->rx_fifo); + p++; + } + + if (fifo_error(spi)) { + printk(BIOS_ERR, "%s: ERROR:\n", __func__); + dump_spi_regs(spi); + dump_fifo_status(spi); + return -1; + } + + return 0; +} + +static void setup_dma_params(struct tegra_spi_channel *spi, + struct apb_dma_channel *dma) +{ + /* APB bus width = 8-bits, address wrap for each word */ + clrbits_le32(&dma->regs->apb_seq, + APB_BUS_WIDTH_MASK << APB_BUS_WIDTH_SHIFT); + /* AHB 1 word burst, bus width = 32 bits (fixed in hardware), + * no address wrapping */ + clrsetbits_le32(&dma->regs->ahb_seq, + (AHB_BURST_MASK << AHB_BURST_SHIFT), + 4 << AHB_BURST_SHIFT); + + /* Set ONCE mode to transfer one "block" at a time (64KB) and enable + * flow control. */ + clrbits_le32(&dma->regs->csr, + APB_CSR_REQ_SEL_MASK << APB_CSR_REQ_SEL_SHIFT); + setbits_le32(&dma->regs->csr, APB_CSR_ONCE | APB_CSR_FLOW | + (spi->req_sel << APB_CSR_REQ_SEL_SHIFT)); +} + +static int tegra_spi_dma_prepare(struct tegra_spi_channel *spi, + unsigned int bytes, enum spi_direction dir) +{ + unsigned int todo, wcount; + + /* + * For DMA we need to think of things in terms of word count. + * AHB width is fixed at 32-bits. To avoid overrunning + * the in/out buffers we must align down. (Note: lowest 2-bits + * in WCOUNT register are ignored, and WCOUNT seems to count + * words starting at n-1) + * + * Example: If "bytes" is 7 and we are transferring 1-byte at a time, + * WCOUNT should be 4. The remaining 3 bytes must be transferred + * using PIO. + */ + todo = MIN(bytes, SPI_MAX_TRANSFER_BYTES_DMA - TEGRA_DMA_ALIGN_BYTES); + todo = ALIGN_DOWN(todo, TEGRA_DMA_ALIGN_BYTES); + wcount = ALIGN_DOWN(todo - TEGRA_DMA_ALIGN_BYTES, TEGRA_DMA_ALIGN_BYTES); + + if (dir == SPI_SEND) { + spi->dma_out = dma_claim(); + if (!spi->dma_out) + return -1; + + /* ensure bytes to send will be visible to DMA controller */ + dcache_clean_by_mva(spi->out_buf, bytes); + + write32(&spi->dma_out->regs->apb_ptr, + (uintptr_t) & spi->regs->tx_fifo); + write32(&spi->dma_out->regs->ahb_ptr, (uintptr_t)spi->out_buf); + setbits_le32(&spi->dma_out->regs->csr, APB_CSR_DIR); + setup_dma_params(spi, spi->dma_out); + write32(&spi->dma_out->regs->wcount, wcount); + } else { + spi->dma_in = dma_claim(); + if (!spi->dma_in) + return -1; + + /* avoid data collisions */ + dcache_clean_invalidate_by_mva(spi->in_buf, bytes); + + write32(&spi->dma_in->regs->apb_ptr, + (uintptr_t)&spi->regs->rx_fifo); + write32(&spi->dma_in->regs->ahb_ptr, (uintptr_t)spi->in_buf); + clrbits_le32(&spi->dma_in->regs->csr, APB_CSR_DIR); + setup_dma_params(spi, spi->dma_in); + write32(&spi->dma_in->regs->wcount, wcount); + } + + /* BLOCK_SIZE starts at n-1 */ + write32(&spi->regs->dma_blk, todo - 1); + return todo; +} + +static void tegra_spi_dma_start(struct tegra_spi_channel *spi) +{ + /* + * The RDY bit in SPI_TRANS_STATUS needs to be cleared manually + * (set bit to clear) between each transaction. Otherwise the next + * transaction does not start. + */ + setbits_le32(&spi->regs->trans_status, SPI_STATUS_RDY); + + struct apb_dma * const apb_dma = (struct apb_dma *)TEGRA_APB_DMA_BASE; + + /* + * The DMA triggers have units of packets. As each packet is currently + * 1 byte the triggers need to be set to 4 packets (0b01) to match + * the AHB 32-bit (4 byte) tranfser. Otherwise the FIFO errors can + * occur. + */ + if (spi->dma_out) { + /* Enable secure access for the channel. */ + setbits_le32(&apb_dma->security_reg, + SECURITY_EN_BIT(spi->dma_out->num)); + clrsetbits_le32(&spi->regs->dma_ctl, + SPI_DMA_CTL_TX_TRIG_MASK << SPI_DMA_CTL_TX_TRIG_SHIFT, + 1 << SPI_DMA_CTL_TX_TRIG_SHIFT); + setbits_le32(&spi->regs->command1, SPI_CMD1_TX_EN); + } + if (spi->dma_in) { + /* Enable secure access for the channel. */ + setbits_le32(&apb_dma->security_reg, + SECURITY_EN_BIT(spi->dma_in->num)); + clrsetbits_le32(&spi->regs->dma_ctl, + SPI_DMA_CTL_RX_TRIG_MASK << SPI_DMA_CTL_RX_TRIG_SHIFT, + 1 << SPI_DMA_CTL_RX_TRIG_SHIFT); + setbits_le32(&spi->regs->command1, SPI_CMD1_RX_EN); + } + + /* + * To avoid underrun conditions, enable APB DMA before SPI DMA for + * Tx and enable SPI DMA before APB DMA before Rx. + */ + if (spi->dma_out) + dma_start(spi->dma_out); + setbits_le32(&spi->regs->dma_ctl, SPI_DMA_CTL_DMA); + if (spi->dma_in) + dma_start(spi->dma_in); + + +} + +static int tegra_spi_dma_finish(struct tegra_spi_channel *spi) +{ + int ret; + unsigned int todo; + + struct apb_dma * const apb_dma = (struct apb_dma *)TEGRA_APB_DMA_BASE; + + todo = read32(&spi->dma_in->regs->wcount); + + if (spi->dma_in) { + while ((read32(&spi->dma_in->regs->dma_byte_sta) < todo) || + dma_busy(spi->dma_in)) + ; /* this shouldn't take long, no udelay */ + dma_stop(spi->dma_in); + clrbits_le32(&spi->regs->command1, SPI_CMD1_RX_EN); + /* Disable secure access for the channel. */ + clrbits_le32(&apb_dma->security_reg, + SECURITY_EN_BIT(spi->dma_in->num)); + dma_release(spi->dma_in); + } + + if (spi->dma_out) { + while ((read32(&spi->dma_out->regs->dma_byte_sta) < todo) || + dma_busy(spi->dma_out)) + spi_delay(spi, todo - spi_byte_count(spi)); + clrbits_le32(&spi->regs->command1, SPI_CMD1_TX_EN); + dma_stop(spi->dma_out); + /* Disable secure access for the channel. */ + clrbits_le32(&apb_dma->security_reg, + SECURITY_EN_BIT(spi->dma_out->num)); + dma_release(spi->dma_out); + } + + if (fifo_error(spi)) { + printk(BIOS_ERR, "%s: ERROR:\n", __func__); + dump_dma_regs(spi->dma_out); + dump_dma_regs(spi->dma_in); + dump_spi_regs(spi); + dump_fifo_status(spi); + ret = -1; + goto done; + } + + ret = 0; +done: + spi->dma_in = NULL; + spi->dma_out = NULL; + return ret; +} + +/* + * xfer_setup() prepares a transfer. It does sanity checking, alignment, and + * sets transfer mode used by this channel (if not set already). + * + * A few caveats to watch out for: + * - The number of bytes which can be transferred may be smaller than the + * number of bytes the caller specifies. The number of bytes ready for + * a transfer will be returned (unless an error occurs). + * + * - Only one mode can be used for both RX and TX. The transfer mode of the + * SPI channel (spi->xfer_mode) is checked each time this function is called. + * If conflicting modes are detected, spi->xfer_mode will be set to + * XFER_MODE_NONE and an error will be returned. + * + * Returns bytes ready for transfer if successful, <0 to indicate error. + */ +static int xfer_setup(struct tegra_spi_channel *spi, void *buf, + unsigned int bytes, enum spi_direction dir) +{ + unsigned int line_size = dcache_line_bytes(); + unsigned int align; + int ret = -1; + + if (!bytes) + return 0; + + if (dir == SPI_SEND) + spi->out_buf = buf; + else if (dir == SPI_RECEIVE) + spi->in_buf = buf; + + /* + * Alignment consideratons: + * When we enable caching we'll need to clean/invalidate portions of + * memory. So we need to be careful about memory alignment. Also, DMA + * likes to operate on 4-bytes at a time on the AHB side. So for + * example, if we only want to receive 1 byte, 4 bytes will be be + * written in memory even if those extra 3 bytes are beyond the length + * we want. + * + * For now we'll use PIO to send/receive unaligned bytes. We may + * consider setting aside some space for a kind of bounce buffer to + * stay in DMA mode once we have a chance to benchmark the two + * approaches. + */ + + if (bytes < line_size) { + if (spi->xfer_mode == XFER_MODE_DMA) { + spi->xfer_mode = XFER_MODE_NONE; + ret = -1; + } else { + spi->xfer_mode = XFER_MODE_PIO; + ret = tegra_spi_pio_prepare(spi, bytes, dir); + } + goto done; + } + + /* transfer bytes before the aligned boundary */ + align = line_size - ((uintptr_t)buf % line_size); + if ((align != 0) && (align != line_size)) { + if (spi->xfer_mode == XFER_MODE_DMA) { + spi->xfer_mode = XFER_MODE_NONE; + ret = -1; + } else { + spi->xfer_mode = XFER_MODE_PIO; + ret = tegra_spi_pio_prepare(spi, align, dir); + } + goto done; + } + + /* do aligned DMA transfer */ + align = (((uintptr_t)buf + bytes) % line_size); + if (bytes - align > 0) { + unsigned int dma_bytes = bytes - align; + + if (spi->xfer_mode == XFER_MODE_PIO) { + spi->xfer_mode = XFER_MODE_NONE; + ret = -1; + } else { + spi->xfer_mode = XFER_MODE_DMA; + ret = tegra_spi_dma_prepare(spi, dma_bytes, dir); + } + + goto done; + } + + /* transfer any remaining unaligned bytes */ + if (align) { + if (spi->xfer_mode == XFER_MODE_DMA) { + spi->xfer_mode = XFER_MODE_NONE; + ret = -1; + } else { + spi->xfer_mode = XFER_MODE_PIO; + ret = tegra_spi_pio_prepare(spi, align, dir); + } + goto done; + } + +done: + return ret; +} + +static void xfer_start(struct tegra_spi_channel *spi) +{ + if (spi->xfer_mode == XFER_MODE_DMA) + tegra_spi_dma_start(spi); + else + tegra_spi_pio_start(spi); +} + +static void xfer_wait(struct tegra_spi_channel *spi) +{ + tegra_spi_wait(spi); +} + +static int xfer_finish(struct tegra_spi_channel *spi) +{ + int ret; + + if (spi->xfer_mode == XFER_MODE_DMA) + ret = tegra_spi_dma_finish(spi); + else + ret = tegra_spi_pio_finish(spi); + + spi->xfer_mode = XFER_MODE_NONE; + return ret; +} + +unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len) +{ + return buf_len; +} + +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned int out_bytes, void *din, unsigned int in_bytes) +{ + struct tegra_spi_channel *spi = to_tegra_spi(slave->bus); + u8 *out_buf = (u8 *)dout; + u8 *in_buf = (u8 *)din; + unsigned int todo; + int ret = 0; + + /* tegra bus numbers start at 1 */ + ASSERT(slave->bus >= 1 && slave->bus <= ARRAY_SIZE(tegra_spi_channels)); + + while (out_bytes || in_bytes) { + int x = 0; + + if (out_bytes == 0) + todo = in_bytes; + else if (in_bytes == 0) + todo = out_bytes; + else + todo = MIN(out_bytes, in_bytes); + + if (out_bytes) { + x = xfer_setup(spi, out_buf, todo, SPI_SEND); + if (x < 0) { + if (spi->xfer_mode == XFER_MODE_NONE) { + spi->xfer_mode = XFER_MODE_PIO; + continue; + } else { + ret = -1; + break; + } + } + } + if (in_bytes) { + x = xfer_setup(spi, in_buf, todo, SPI_RECEIVE); + if (x < 0) { + if (spi->xfer_mode == XFER_MODE_NONE) { + spi->xfer_mode = XFER_MODE_PIO; + continue; + } else { + ret = -1; + break; + } + } + } + + /* + * Note: Some devices (such as Chrome EC) are sensitive to + * delays, so be careful when adding debug prints not to + * cause timeouts between transfers. + */ + xfer_start(spi); + xfer_wait(spi); + if (xfer_finish(spi)) { + ret = -1; + break; + } + + /* Post-processing. */ + if (out_bytes) { + out_bytes -= x; + out_buf += x; + } + if (in_bytes) { + in_bytes -= x; + in_buf += x; + } + } + + if (ret < 0) { + printk(BIOS_ERR, "%s: Error detected\n", __func__); + printk(BIOS_ERR, "Transaction size: %u, bytes remaining: " + "%u out / %u in\n", todo, out_bytes, in_bytes); + clear_fifo_status(spi); + } + return ret; +} + +#define JEDEC_READ 0x03 +#define JEDEC_READ_OUTSIZE 0x04 +#define JEDEC_FAST_READ_DUAL 0x3b +#define JEDEC_FAST_READ_DUAL_OUTSIZE 0x05 + +static struct spi_slave *boot_slave; + +static ssize_t tegra_spi_readat(const struct region_device *rdev, void *dest, + size_t offset, size_t count) +{ + u8 spi_read_cmd[JEDEC_FAST_READ_DUAL_OUTSIZE]; + unsigned int read_cmd_bytes; + int ret = count; + struct tegra_spi_channel *channel; + + channel = to_tegra_spi(boot_slave->bus); + + if (channel->dual_mode) { + /* + * Command 0x3b will interleave data only, command 0xbb will + * interleave the address as well. It's nice to see the address + * plainly when debugging, and we're mostly concerned with + * large transfers so the optimization of using 0xbb isn't + * really worthwhile. + */ + spi_read_cmd[0] = JEDEC_FAST_READ_DUAL; + spi_read_cmd[4] = 0x00; /* dummy byte */ + read_cmd_bytes = JEDEC_FAST_READ_DUAL_OUTSIZE; + } else { + spi_read_cmd[0] = JEDEC_READ; + read_cmd_bytes = JEDEC_READ_OUTSIZE; + } + spi_read_cmd[1] = (offset >> 16) & 0xff; + spi_read_cmd[2] = (offset >> 8) & 0xff; + spi_read_cmd[3] = offset & 0xff; + + spi_claim_bus(boot_slave); + + if (spi_xfer(boot_slave, spi_read_cmd, + read_cmd_bytes, NULL, 0) < 0) { + ret = -1; + printk(BIOS_ERR, "%s: Failed to transfer %zu bytes\n", + __func__, sizeof(spi_read_cmd)); + goto tegra_spi_cbfs_read_exit; + } + + if (channel->dual_mode) { + setbits_le32(&channel->regs->command1, SPI_CMD1_BOTH_EN_BIT); + } + if (spi_xfer(boot_slave, NULL, 0, dest, count)) { + ret = -1; + printk(BIOS_ERR, "%s: Failed to transfer %zu bytes\n", + __func__, count); + } + if (channel->dual_mode) + clrbits_le32(&channel->regs->command1, SPI_CMD1_BOTH_EN_BIT); + +tegra_spi_cbfs_read_exit: + /* de-assert /CS */ + spi_release_bus(boot_slave); + return ret; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs) +{ + struct tegra_spi_channel *channel = to_tegra_spi(bus); + if (!channel) + return NULL; + + return &channel->slave; +} + +static const struct region_device_ops tegra_spi_ops = { + .mmap = mmap_helper_rdev_mmap, + .munmap = mmap_helper_rdev_munmap, + .readat = tegra_spi_readat, +}; + +__attribute__((unused)) +static struct mmap_helper_region_device mdev = + MMAP_HELPER_REGION_INIT(&tegra_spi_ops, 0, CONFIG_ROM_SIZE); + +#if !IS_ENABLED(CONFIG_COMMON_CBFS_SPI_WRAPPER) +const struct region_device *boot_device_ro(void) +{ + return &mdev.rdev; +} + +void boot_device_init(void) +{ + struct tegra_spi_channel *boot_chan; + + boot_chan = &tegra_spi_channels[CONFIG_BOOT_MEDIA_SPI_BUS - 1]; + boot_chan->slave.cs = CONFIG_BOOT_MEDIA_SPI_CHIP_SELECT; + +#if CONFIG_SPI_FLASH_FAST_READ_DUAL_OUTPUT_3B == 1 + boot_chan->dual_mode = 1; +#endif + boot_slave = &boot_chan->slave; + + mmap_helper_device_init(&mdev, _cbfs_cache, _cbfs_cache_size); +} +#endif diff --git a/src/soc/nvidia/tegra210/stack.S b/src/soc/nvidia/tegra210/stack.S new file mode 100644 index 0000000000..5a48d0e46c --- /dev/null +++ b/src/soc/nvidia/tegra210/stack.S @@ -0,0 +1,45 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +/* Macro to initialize stack, perform seeding if required and finally call the + * function provided + * @stack_top : First address above the stack + * @stack_bottom : Lowest address on the stack + * @seed : Stack seeding required (1=yes/otherwise=no) + * @func : Function to call after initializing stack + */ +.macro stack_init stack_top, stack_bottom, seed, func + /* Check if stack seeding is required */ + mov r0, #\seed + cmp r0, #1 + bne call_func + /* Stack seeding */ + ldr r0, =\stack_bottom + ldr r1, =\stack_top + ldr r2, =0xdeadbeef +init_stack_loop: + str r2, [r0] + add r0, #4 + cmp r0, r1 + bne init_stack_loop + +call_func: + ldr sp, =\stack_top /* Set up stack pointer */ + bl \func +.endm diff --git a/src/soc/nvidia/tegra210/uart.c b/src/soc/nvidia/tegra210/uart.c new file mode 100644 index 0000000000..773bc89bd1 --- /dev/null +++ b/src/soc/nvidia/tegra210/uart.c @@ -0,0 +1,135 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2009 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; 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. + */ + +#include <arch/io.h> +#include <boot/coreboot_tables.h> +#include <console/console.h> /* for __console definition */ +#include <console/uart.h> +#include <drivers/uart/uart8250reg.h> +#include <stdint.h> + +struct tegra210_uart { + union { + uint32_t thr; // Transmit holding register. + uint32_t rbr; // Receive buffer register. + uint32_t dll; // Divisor latch lsb. + }; + union { + uint32_t ier; // Interrupt enable register. + uint32_t dlm; // Divisor latch msb. + }; + union { + uint32_t iir; // Interrupt identification register. + uint32_t fcr; // FIFO control register. + }; + uint32_t lcr; // Line control register. + uint32_t mcr; // Modem control register. + uint32_t lsr; // Line status register. + uint32_t msr; // Modem status register. +} __attribute__ ((packed)); + + +static struct tegra210_uart * const uart_ptr = + (void *)CONFIG_CONSOLE_SERIAL_TEGRA210_UART_ADDRESS; + +static void tegra210_uart_tx_flush(void); +static int tegra210_uart_tst_byte(void); + +static void tegra210_uart_init(void) +{ + // Use a hardcoded divisor for now. + const unsigned divisor = 221; + const uint8_t line_config = UART8250_LCR_WLS_8; // 8n1 + + tegra210_uart_tx_flush(); + + // Disable interrupts. + write8(&uart_ptr->ier, 0); + // Force DTR and RTS to high. + write8(&uart_ptr->mcr, UART8250_MCR_DTR | UART8250_MCR_RTS); + // Set line configuration, access divisor latches. + write8(&uart_ptr->lcr, UART8250_LCR_DLAB | line_config); + // Set the divisor. + write8(&uart_ptr->dll, divisor & 0xff); + write8(&uart_ptr->dlm, (divisor >> 8) & 0xff); + // Hide the divisor latches. + write8(&uart_ptr->lcr, line_config); + // Enable FIFOs, and clear receive and transmit. + write8(&uart_ptr->fcr, + UART8250_FCR_FIFO_EN | + UART8250_FCR_CLEAR_RCVR | + UART8250_FCR_CLEAR_XMIT); +} + +static void tegra210_uart_tx_byte(unsigned char data) +{ + while (!(read8(&uart_ptr->lsr) & UART8250_LSR_THRE)); + write8(&uart_ptr->thr, data); +} + +static void tegra210_uart_tx_flush(void) +{ + while (!(read8(&uart_ptr->lsr) & UART8250_LSR_TEMT)); +} + +static unsigned char tegra210_uart_rx_byte(void) +{ + if (!tegra210_uart_tst_byte()) + return 0; + return read8(&uart_ptr->rbr); +} + +static int tegra210_uart_tst_byte(void) +{ + return (read8(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR; +} + +void uart_init(int idx) +{ + tegra210_uart_init(); +} + +void uart_tx_byte(int idx, unsigned char data) +{ + tegra210_uart_tx_byte(data); +} + +void uart_tx_flush(int idx) +{ + tegra210_uart_tx_flush(); +} + +unsigned char uart_rx_byte(int idx) +{ + return tegra210_uart_rx_byte(); +} + +#ifndef __PRE_RAM__ +void uart_fill_lb(void *data) +{ + struct lb_serial serial; + serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED; + serial.baseaddr = CONFIG_CONSOLE_SERIAL_TEGRA210_UART_ADDRESS; + serial.baud = default_baudrate(); + serial.regwidth = 1; + lb_add_serial(&serial, data); + + lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data); +} +#endif diff --git a/src/soc/nvidia/tegra210/verstage.c b/src/soc/nvidia/tegra210/verstage.c new file mode 100644 index 0000000000..1241f248ce --- /dev/null +++ b/src/soc/nvidia/tegra210/verstage.c @@ -0,0 +1,47 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 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. + */ + +#include <arch/cache.h> +#include <arch/exception.h> +#include <arch/hlt.h> +#include <arch/stages.h> +#include <console/console.h> +#include <soc/verstage.h> +#include <timestamp.h> +#include <vendorcode/google/chromeos/chromeos.h> + +void __attribute__((weak)) verstage_mainboard_init(void) +{ + /* Default empty implementation. */ +} + +static void verstage(void) +{ + console_init(); + exception_init(); + verstage_mainboard_init(); + + run_romstage(); +} + +void main(void) +{ + verstage(); + hlt(); +} |