diff options
251 files changed, 30102 insertions, 746 deletions
@@ -887,7 +887,7 @@ MKIMAGEFLAGS_u-boot.kwb = -n $(srctree)/$(CONFIG_SYS_KWD_CONFIG:"%"=%) \ -T kwbimage -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) MKIMAGEFLAGS_u-boot-spl.kwb = -n $(srctree)/$(CONFIG_SYS_KWD_CONFIG:"%"=%) \ - -T kwbimage -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) + -T kwbimage -a $(CONFIG_SPL_TEXT_BASE) -e $(CONFIG_SPL_TEXT_BASE) MKIMAGEFLAGS_u-boot.pbl = -n $(srctree)/$(CONFIG_SYS_FSL_PBL_RCW:"%"=%) \ -R $(srctree)/$(CONFIG_SYS_FSL_PBL_PBI:"%"=%) -T pblimage @@ -1259,7 +1259,7 @@ u-boot.lds: $(LDSCRIPT) prepare FORCE spl/u-boot-spl.bin: spl/u-boot-spl @: -spl/u-boot-spl: tools prepare +spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb) $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all spl/sunxi-spl.bin: spl/u-boot-spl diff --git a/arch/arm/cpu/u-boot-spl.lds b/arch/arm/cpu/u-boot-spl.lds index a8be204..c5b4f7c 100644 --- a/arch/arm/cpu/u-boot-spl.lds +++ b/arch/arm/cpu/u-boot-spl.lds @@ -32,17 +32,17 @@ SECTIONS } . = ALIGN(4); - .u_boot_list : { - KEEP(*(SORT(.u_boot_list*_i2c_*))); - } - - . = .; #ifdef CONFIG_SPL_DM .u_boot_list : { KEEP(*(SORT(.u_boot_list_*_driver_*))); KEEP(*(SORT(.u_boot_list_*_uclass_*))); } #endif + . = .; + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*_i2c_*))); + } + . = ALIGN(4); __image_copy_end = .; @@ -66,7 +66,7 @@ SECTIONS . = ALIGN(4); __bss_end = .; } - + __bss_size = __bss_end - __bss_start; .dynsym _image_binary_end : { *(.dynsym) } .dynbss : { *(.dynbss) } .dynstr : { *(.dynstr*) } diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 4f477cd..446ce04 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -20,5 +20,7 @@ obj-y += timer.o obj-$(CONFIG_SPL_BUILD) += spl.o obj-$(CONFIG_SPL_BUILD) += lowlevel_spl.o -obj-y += serdes/ +obj-$(CONFIG_SYS_MVEBU_DDR_A38X) += serdes/a38x/ +obj-$(CONFIG_SYS_MVEBU_DDR_AXP) += serdes/axp/ + endif diff --git a/arch/arm/mach-mvebu/cpu.c b/arch/arm/mach-mvebu/cpu.c index 9bc9f00..9496d5f 100644 --- a/arch/arm/mach-mvebu/cpu.c +++ b/arch/arm/mach-mvebu/cpu.c @@ -163,6 +163,14 @@ static void update_sdram_window_sizes(void) } } +void mmu_disable(void) +{ + asm volatile( + "mrc p15, 0, r0, c1, c0, 0\n" + "bic r0, #1\n" + "mcr p15, 0, r0, c1, c0, 0\n"); +} + #ifdef CONFIG_ARCH_CPU_INIT static void set_cbar(u32 addr) { @@ -172,6 +180,16 @@ static void set_cbar(u32 addr) int arch_cpu_init(void) { +#ifndef CONFIG_SPL_BUILD + /* + * Only with disabled MMU its possible to switch the base + * register address on Armada 38x. Without this the SDRAM + * located at >= 0x4000.0000 is also not accessible, as its + * still locked to cache. + */ + mmu_disable(); +#endif + /* Linux expects the internal registers to be at 0xf1000000 */ writel(SOC_REGS_PHY_BASE, INTREG_BASE_ADDR_REG); set_cbar(SOC_REGS_PHY_BASE + 0xC000); diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h index 4bdb633..8bcdef6 100644 --- a/arch/arm/mach-mvebu/include/mach/cpu.h +++ b/arch/arm/mach-mvebu/include/mach/cpu.h @@ -125,7 +125,7 @@ int serdes_phy_config(void); /* * DDR3 init / training code ported from Marvell bin_hdr. Now * available in mainline U-Boot in: - * drivers/ddr/mvebu/ + * drivers/ddr/marvell */ int ddr3_init(void); #endif /* __ASSEMBLY__ */ diff --git a/arch/arm/mach-mvebu/include/mach/soc.h b/arch/arm/mach-mvebu/include/mach/soc.h index 1aaea67..125b5f2 100644 --- a/arch/arm/mach-mvebu/include/mach/soc.h +++ b/arch/arm/mach-mvebu/include/mach/soc.h @@ -28,7 +28,17 @@ /* SOC specific definations */ #define INTREG_BASE 0xd0000000 #define INTREG_BASE_ADDR_REG (INTREG_BASE + 0x20080) +#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SYS_MVEBU_DDR_A38X) +/* + * On A38x switching the regs base address without running from + * SDRAM doesn't seem to work. So let the SPL still use the + * default base address and switch to the new address in the + * main u-boot later. + */ +#define SOC_REGS_PHY_BASE 0xd0000000 +#else #define SOC_REGS_PHY_BASE 0xf1000000 +#endif #define MVEBU_REGISTER(x) (SOC_REGS_PHY_BASE + x) #define MVEBU_SDRAM_SCRATCH (MVEBU_REGISTER(0x01504)) @@ -52,6 +62,7 @@ #define MVEBU_USB20_BASE (MVEBU_REGISTER(0x58000)) #define MVEBU_EGIGA0_BASE (MVEBU_REGISTER(0x70000)) #define MVEBU_EGIGA1_BASE (MVEBU_REGISTER(0x74000)) +#define MVEBU_AXP_SATA_BASE (MVEBU_REGISTER(0xa0000)) #define MVEBU_SATA0_BASE (MVEBU_REGISTER(0xa8000)) #define MVEBU_SDIO_BASE (MVEBU_REGISTER(0xd8000)) diff --git a/arch/arm/mach-mvebu/serdes/a38x/Makefile b/arch/arm/mach-mvebu/serdes/a38x/Makefile new file mode 100644 index 0000000..1503da8 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/Makefile @@ -0,0 +1,10 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SPL_BUILD) = ctrl_pex.o +obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec.o +obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec-38x.o +obj-$(CONFIG_SPL_BUILD) += high_speed_topology_spec-38x.o +obj-$(CONFIG_SPL_BUILD) += seq_exec.o +obj-$(CONFIG_SPL_BUILD) += sys_env_lib.o diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c new file mode 100644 index 0000000..5f223f9 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ctrl_pex.h" +#include "sys_env_lib.h" + +int hws_pex_config(struct serdes_map *serdes_map) +{ + u32 pex_idx, tmp, next_busno, first_busno, temp_pex_reg, + temp_reg, addr, dev_id, ctrl_mode; + enum serdes_type serdes_type; + u32 idx, max_lane_num; + + DEBUG_INIT_FULL_S("\n### hws_pex_config ###\n"); + + max_lane_num = hws_serdes_get_max_lane(); + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + /* configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + tmp = reg_read(PEX_CAPABILITIES_REG(pex_idx)); + tmp &= ~(0xf << 20); + tmp |= (0x4 << 20); + reg_write(PEX_CAPABILITIES_REG(pex_idx), tmp); + } + + tmp = reg_read(SOC_CTRL_REG); + tmp &= ~0x03; + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + switch (serdes_type) { + case PEX0: + tmp |= 0x1 << PCIE0_ENABLE_OFFS; + break; + case PEX1: + tmp |= 0x1 << PCIE1_ENABLE_OFFS; + break; + case PEX2: + tmp |= 0x1 << PCIE2_ENABLE_OFFS; + break; + case PEX3: + tmp |= 0x1 << PCIE3_ENABLE_OFFS; + break; + default: + break; + } + } + + reg_write(SOC_CTRL_REG, tmp); + + /* Support gen1/gen2 */ + DEBUG_INIT_FULL_S("Support gen1/gen2\n"); + next_busno = 0; + mdelay(150); + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + DEBUG_INIT_FULL_S(" serdes_type=0x"); + DEBUG_INIT_FULL_D(serdes_type, 8); + DEBUG_INIT_FULL_S("\n"); + DEBUG_INIT_FULL_S(" idx=0x"); + DEBUG_INIT_FULL_D(idx, 8); + DEBUG_INIT_FULL_S("\n"); + + /* Configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + tmp = reg_read(PEX_DBG_STATUS_REG(pex_idx)); + + first_busno = next_busno; + if ((tmp & 0x7f) != 0x7e) { + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S(": detected no link\n"); + continue; + } + + next_busno++; + temp_pex_reg = reg_read((PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_LINK_CAPABILITY_REG))); + temp_pex_reg &= 0xf; + if (temp_pex_reg != 0x2) + continue; + + temp_reg = (reg_read(PEX_CFG_DIRECT_ACCESS( + pex_idx, + PEX_LINK_CTRL_STAT_REG)) & + 0xf0000) >> 16; + + /* Check if the link established is GEN1 */ + DEBUG_INIT_FULL_S + ("Checking if the link established is gen1\n"); + if (temp_reg != 0x1) + continue; + + pex_local_bus_num_set(pex_idx, first_busno); + pex_local_dev_num_set(pex_idx, 1); + DEBUG_INIT_FULL_S("PCIe, Idx "); + DEBUG_INIT_FULL_D(pex_idx, 1); + + DEBUG_INIT_S(":** Link is Gen1, check the EP capability\n"); + /* link is Gen1, check the EP capability */ + addr = pex_config_read(pex_idx, first_busno, 0, 0, 0x34) & 0xff; + DEBUG_INIT_FULL_C("pex_config_read: return addr=0x%x", addr, 4); + if (addr == 0xff) { + DEBUG_INIT_FULL_C + ("pex_config_read: return 0xff -->PCIe (%d): Detected No Link.", + pex_idx, 1); + continue; + } + + while ((pex_config_read(pex_idx, first_busno, 0, 0, addr) + & 0xff) != 0x10) { + addr = (pex_config_read(pex_idx, first_busno, 0, + 0, addr) & 0xff00) >> 8; + } + + /* Check for Gen2 and above */ + if ((pex_config_read(pex_idx, first_busno, 0, 0, + addr + 0xc) & 0xf) < 0x2) { + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S(": remains Gen1\n"); + continue; + } + + tmp = reg_read(PEX_LINK_CTRL_STATUS2_REG(pex_idx)); + DEBUG_RD_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + tmp &= ~(BIT(0) | BIT(1)); + tmp |= BIT(1); + tmp |= BIT(6); /* Select Deemphasize (-3.5d_b) */ + reg_write(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + DEBUG_WR_REG(PEX_LINK_CTRL_STATUS2_REG(pex_idx), tmp); + + tmp = reg_read(PEX_CTRL_REG(pex_idx)); + DEBUG_RD_REG(PEX_CTRL_REG(pex_idx), tmp); + tmp |= BIT(10); + reg_write(PEX_CTRL_REG(pex_idx), tmp); + DEBUG_WR_REG(PEX_CTRL_REG(pex_idx), tmp); + + /* + * We need to wait 10ms before reading the PEX_DBG_STATUS_REG + * in order not to read the status of the former state + */ + mdelay(10); + + DEBUG_INIT_S("PCIe, Idx "); + DEBUG_INIT_D(pex_idx, 1); + DEBUG_INIT_S + (": Link upgraded to Gen2 based on client cpabilities\n"); + } + + /* Update pex DEVICE ID */ + ctrl_mode = sys_env_model_get(); + + for (idx = 0; idx < max_lane_num; idx++) { + serdes_type = serdes_map[idx].serdes_type; + /* configuration for PEX only */ + if ((serdes_type != PEX0) && (serdes_type != PEX1) && + (serdes_type != PEX2) && (serdes_type != PEX3)) + continue; + + if ((serdes_type != PEX0) && + ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { + /* for PEX by4 - relevant for the first port only */ + continue; + } + + pex_idx = serdes_type - PEX0; + dev_id = reg_read(PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_DEVICE_AND_VENDOR_ID)); + dev_id &= 0xffff; + dev_id |= ((ctrl_mode << 16) & 0xffff0000); + reg_write(PEX_CFG_DIRECT_ACCESS + (pex_idx, PEX_DEVICE_AND_VENDOR_ID), dev_id); + } + DEBUG_INIT_FULL_C("Update PEX Device ID ", ctrl_mode, 4); + + return MV_OK; +} + +int pex_local_bus_num_set(u32 pex_if, u32 bus_num) +{ + u32 pex_status; + + DEBUG_INIT_FULL_S("\n### pex_local_bus_num_set ###\n"); + + if (bus_num >= MAX_PEX_BUSSES) { + DEBUG_INIT_C("pex_local_bus_num_set: Illegal bus number %d\n", + bus_num, 4); + return MV_BAD_PARAM; + } + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + pex_status &= ~PXSR_PEX_BUS_NUM_MASK; + pex_status |= + (bus_num << PXSR_PEX_BUS_NUM_OFFS) & PXSR_PEX_BUS_NUM_MASK; + reg_write(PEX_STATUS_REG(pex_if), pex_status); + + return MV_OK; +} + +int pex_local_dev_num_set(u32 pex_if, u32 dev_num) +{ + u32 pex_status; + + DEBUG_INIT_FULL_S("\n### pex_local_dev_num_set ###\n"); + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + pex_status &= ~PXSR_PEX_DEV_NUM_MASK; + pex_status |= + (dev_num << PXSR_PEX_DEV_NUM_OFFS) & PXSR_PEX_DEV_NUM_MASK; + reg_write(PEX_STATUS_REG(pex_if), pex_status); + + return MV_OK; +} + +/* + * pex_config_read - Read from configuration space + * + * DESCRIPTION: + * This function performs a 32 bit read from PEX configuration space. + * It supports both type 0 and type 1 of Configuration Transactions + * (local and over bridge). In order to read from local bus segment, use + * bus number retrieved from pex_local_bus_num_get(). Other bus numbers + * will result configuration transaction of type 1 (over bridge). + * + * INPUT: + * pex_if - PEX interface number. + * bus - PEX segment bus number. + * dev - PEX device number. + * func - Function number. + * reg_offs - Register offset. + * + * OUTPUT: + * None. + * + * RETURN: + * 32bit register data, 0xffffffff on error + */ +u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off) +{ + u32 pex_data = 0; + u32 local_dev, local_bus; + u32 pex_status; + + pex_status = reg_read(PEX_STATUS_REG(pex_if)); + local_dev = + ((pex_status & PXSR_PEX_DEV_NUM_MASK) >> PXSR_PEX_DEV_NUM_OFFS); + local_bus = + ((pex_status & PXSR_PEX_BUS_NUM_MASK) >> PXSR_PEX_BUS_NUM_OFFS); + + /* + * In PCI Express we have only one device number + * and this number is the first number we encounter + * else that the local_dev + * spec pex define return on config read/write on any device + */ + if (bus == local_bus) { + if (local_dev == 0) { + /* + * if local dev is 0 then the first number we encounter + * after 0 is 1 + */ + if ((dev != 1) && (dev != local_dev)) + return MV_ERROR; + } else { + /* + * if local dev is not 0 then the first number we + * encounter is 0 + */ + if ((dev != 0) && (dev != local_dev)) + return MV_ERROR; + } + } + + /* Creating PEX address to be passed */ + pex_data = (bus << PXCAR_BUS_NUM_OFFS); + pex_data |= (dev << PXCAR_DEVICE_NUM_OFFS); + pex_data |= (func << PXCAR_FUNC_NUM_OFFS); + /* Legacy register space */ + pex_data |= (reg_off & PXCAR_REG_NUM_MASK); + /* Extended register space */ + pex_data |= (((reg_off & PXCAR_REAL_EXT_REG_NUM_MASK) >> + PXCAR_REAL_EXT_REG_NUM_OFFS) << PXCAR_EXT_REG_NUM_OFFS); + pex_data |= PXCAR_CONFIG_EN; + + /* Write the address to the PEX configuration address register */ + reg_write(PEX_CFG_ADDR_REG(pex_if), pex_data); + + /* + * In order to let the PEX controller absorbed the address + * of the read transaction we perform a validity check that + * the address was written + */ + if (pex_data != reg_read(PEX_CFG_ADDR_REG(pex_if))) + return MV_ERROR; + + /* Cleaning Master Abort */ + reg_bit_set(PEX_CFG_DIRECT_ACCESS(pex_if, PEX_STATUS_AND_COMMAND), + PXSAC_MABORT); + /* Read the Data returned in the PEX Data register */ + pex_data = reg_read(PEX_CFG_DATA_REG(pex_if)); + + DEBUG_INIT_FULL_C(" --> ", pex_data, 4); + + return pex_data; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h new file mode 100644 index 0000000..df395bf --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _CTRL_PEX_H +#define _CTRL_PEX_H + +#include "high_speed_env_spec.h" + +/* Sample at Reset */ +#define MPP_SAMPLE_AT_RESET(id) (0xe4200 + (id * 4)) + +/* PCI Express Control and Status Registers */ +#define MAX_PEX_BUSSES 256 + +#define MISC_REGS_OFFSET 0x18200 +#define MV_MISC_REGS_BASE MISC_REGS_OFFSET +#define SOC_CTRL_REG (MV_MISC_REGS_BASE + 0x4) + +#define PEX_IF_REGS_OFFSET(if) ((if) > 0 ? \ + (0x40000 + ((if) - 1) * 0x4000) : \ + 0x80000) +#define PEX_IF_REGS_BASE(if) (PEX_IF_REGS_OFFSET(if)) +#define PEX_CAPABILITIES_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x60) +#define PEX_LINK_CTRL_STATUS2_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x90) +#define PEX_CTRL_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a00) +#define PEX_STATUS_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a04) +#define PEX_DBG_STATUS_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x1a64) +#define PEX_LINK_CAPABILITY_REG 0x6c +#define PEX_LINK_CTRL_STAT_REG 0x70 +#define PXSR_PEX_DEV_NUM_OFFS 16 /* Device Number Indication */ +#define PXSR_PEX_DEV_NUM_MASK (0x1f << PXSR_PEX_DEV_NUM_OFFS) +#define PXSR_PEX_BUS_NUM_OFFS 8 /* Bus Number Indication */ +#define PXSR_PEX_BUS_NUM_MASK (0xff << PXSR_PEX_BUS_NUM_OFFS) + +/* PEX_CAPABILITIES_REG fields */ +#define PCIE0_ENABLE_OFFS 0 +#define PCIE0_ENABLE_MASK (0x1 << PCIE0_ENABLE_OFFS) +#define PCIE1_ENABLE_OFFS 1 +#define PCIE1_ENABLE_MASK (0x1 << PCIE1_ENABLE_OFFS) +#define PCIE2_ENABLE_OFFS 2 +#define PCIE2_ENABLE_MASK (0x1 << PCIE2_ENABLE_OFFS) +#define PCIE3_ENABLE_OFFS 3 +#define PCIE4_ENABLE_MASK (0x1 << PCIE3_ENABLE_OFFS) + +/* Controller revision info */ +#define PEX_DEVICE_AND_VENDOR_ID 0x000 + +/* PCI Express Configuration Address Register */ +#define PXCAR_REG_NUM_OFFS 2 +#define PXCAR_REG_NUM_MAX 0x3f +#define PXCAR_REG_NUM_MASK (PXCAR_REG_NUM_MAX << \ + PXCAR_REG_NUM_OFFS) +#define PXCAR_FUNC_NUM_OFFS 8 +#define PXCAR_FUNC_NUM_MAX 0x7 +#define PXCAR_FUNC_NUM_MASK (PXCAR_FUNC_NUM_MAX << \ + PXCAR_FUNC_NUM_OFFS) +#define PXCAR_DEVICE_NUM_OFFS 11 +#define PXCAR_DEVICE_NUM_MAX 0x1f +#define PXCAR_DEVICE_NUM_MASK (PXCAR_DEVICE_NUM_MAX << \ + PXCAR_DEVICE_NUM_OFFS) +#define PXCAR_BUS_NUM_OFFS 16 +#define PXCAR_BUS_NUM_MAX 0xff +#define PXCAR_BUS_NUM_MASK (PXCAR_BUS_NUM_MAX << \ + PXCAR_BUS_NUM_OFFS) +#define PXCAR_EXT_REG_NUM_OFFS 24 +#define PXCAR_EXT_REG_NUM_MAX 0xf + +#define PEX_CFG_ADDR_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x18f8) +#define PEX_CFG_DATA_REG(if) ((PEX_IF_REGS_BASE(if)) + 0x18fc) + +#define PXCAR_REAL_EXT_REG_NUM_OFFS 8 +#define PXCAR_REAL_EXT_REG_NUM_MASK (0xf << PXCAR_REAL_EXT_REG_NUM_OFFS) + +#define PXCAR_CONFIG_EN BIT(31) +#define PEX_STATUS_AND_COMMAND 0x004 +#define PXSAC_MABORT BIT(29) /* Recieved Master Abort */ + +int hws_pex_config(struct serdes_map *serdes_map); +int pex_local_bus_num_set(u32 pex_if, u32 bus_num); +int pex_local_dev_num_set(u32 pex_if, u32 dev_num); +u32 pex_config_read(u32 pex_if, u32 bus, u32 dev, u32 func, u32 reg_off); + +#endif diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c new file mode 100644 index 0000000..5ff8567 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec-38x.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_env_spec.h" +#include "sys_env_lib.h" + +#define SERDES_VERION "2.0" + +u8 selectors_serdes_rev1_map[LAST_SERDES_TYPE][MAX_SERDES_LANES] = { + /* 0 1 2 3 4 5 */ + {0x1, 0x1, NA, NA, NA, NA}, /* PEX0 */ + {NA, 0x2, 0x1, NA, 0x1, NA}, /* PEX1 */ + {NA, NA, 0x2, NA, NA, 0x1}, /* PEX2 */ + {NA, NA, NA, 0x1, NA, NA}, /* PEX3 */ + {0x2, 0x3, NA, NA, NA, NA}, /* SATA0 */ + {NA, NA, 0x3, NA, 0x2, NA}, /* SATA1 */ + {NA, NA, NA, NA, 0x6, 0x2}, /* SATA2 */ + {NA, NA, NA, 0x3, NA, NA}, /* SATA3 */ + {0x3, 0x4, NA, NA, NA, NA}, /* SGMII0 */ + {NA, 0x5, 0x4, NA, 0x3, NA}, /* SGMII1 */ + {NA, NA, NA, 0x4, NA, 0x3}, /* SGMII2 */ + {NA, 0x7, NA, NA, NA, NA}, /* QSGMII */ + {NA, 0x6, NA, NA, 0x4, NA}, /* USB3_HOST0 */ + {NA, NA, NA, 0x5, NA, 0x4}, /* USB3_HOST1 */ + {NA, NA, NA, 0x6, 0x5, 0x5}, /* USB3_DEVICE */ + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} /* DEFAULT_SERDES */ +}; + +int hws_serdes_seq_init(void) +{ + DEBUG_INIT_FULL_S("\n### serdes_seq_init ###\n"); + + if (hws_serdes_seq_db_init() != MV_OK) { + printf("hws_serdes_seq_init: Error: Serdes initialization fail\n"); + return MV_FAIL; + } + + return MV_OK; +} + +int serdes_power_up_ctrl_ext(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock) +{ + return MV_NOT_SUPPORTED; +} + +u32 hws_serdes_silicon_ref_clock_get(void) +{ + DEBUG_INIT_FULL_S("\n### hws_serdes_silicon_ref_clock_get ###\n"); + + return REF_CLOCK_25MHZ; +} + +u32 hws_serdes_get_max_lane(void) +{ + switch (sys_env_device_id_get()) { + case MV_6811: /* A381/A3282: 6811/6821: single/dual cpu */ + return 4; + case MV_6810: + return 5; + case MV_6820: + case MV_6828: + return 6; + default: /* not the right module */ + printf("%s: Device ID Error, using 4 SerDes lanes\n", + __func__); + return 4; + } + return 6; +} + +int hws_is_serdes_active(u8 lane_num) +{ + int ret = 1; + + /* Maximum lane count for A388 (6828) is 6 */ + if (lane_num > 6) + ret = 0; + + /* 4th Lane (#4 on Device 6810 is not Active */ + if (sys_env_device_id_get() == MV_6810 && lane_num == 4) { + printf("%s: Error: Lane#4 on Device 6810 is not Active.\n", + __func__); + return 0; + } + + /* + * 6th Lane (#5) on Device 6810 is Active, even though 6810 + * has only 5 lanes + */ + if (sys_env_device_id_get() == MV_6810 && lane_num == 5) + return 1; + + if (lane_num >= hws_serdes_get_max_lane()) + ret = 0; + + return ret; +} + +int hws_get_ext_base_addr(u32 serdes_num, u32 base_addr, u32 unit_base_offset, + u32 *unit_base_reg, u32 *unit_offset) +{ + *unit_base_reg = base_addr; + *unit_offset = unit_base_offset; + + return MV_OK; +} + +/* + * hws_serdes_get_phy_selector_val + * + * DESCRIPTION: Get the mapping of Serdes Selector values according to the + * Serdes revision number + * INPUT: serdes_num - Serdes number + * serdes_type - Serdes type + * OUTPUT: None + * RETURN: + * Mapping of Serdes Selector values + */ +u32 hws_serdes_get_phy_selector_val(int serdes_num, + enum serdes_type serdes_type) +{ + if (serdes_type >= LAST_SERDES_TYPE) + return 0xff; + + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + return selectors_serdes_rev1_map + [serdes_type][serdes_num]; + } else + return selectors_serdes_rev2_map + [serdes_type][serdes_num]; +} + +u32 hws_get_physical_serdes_num(u32 serdes_num) +{ + if ((serdes_num == 4) && (sys_env_device_id_get() == MV_6810)) { + /* + * For 6810, there are 5 Serdes and Serdes Num 4 doesn't + * exist. Instead Serdes Num 5 is connected. + */ + return 5; + } else { + return serdes_num; + } +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c new file mode 100644 index 0000000..23af769 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c @@ -0,0 +1,2228 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_env_spec.h" +#include "high_speed_topology_spec.h" +#include "sys_env_lib.h" +#include "ctrl_pex.h" + +#if defined(CONFIG_ARMADA_38X) +#elif defined(CONFIG_ARMADA_39X) +#else +#error "No device is defined" +#endif + +/* + * The board topology map, initialized in the beginning of + * ctrl_high_speed_serdes_phy_config + */ +struct serdes_map serdes_configuration_map[MAX_SERDES_LANES]; + +/* + * serdes_seq_db - holds all serdes sequences, their size and the + * relevant index in the data array initialized in serdes_seq_init + */ +struct cfg_seq serdes_seq_db[SERDES_LAST_SEQ]; + +#define SERDES_VERION "2.0" +#define ENDED_OK "High speed PHY - Ended Successfully\n" + +#define LINK_WAIT_CNTR 100 +#define LINK_WAIT_SLEEP 100 + +#define MAX_UNIT_NUMB 4 +#define TOPOLOGY_TEST_OK 0 +#define WRONG_NUMBER_OF_UNITS 1 +#define SERDES_ALREADY_IN_USE 2 +#define UNIT_NUMBER_VIOLATION 3 + +/* + * serdes_lane_in_use_count contains the exact amount of serdes lanes + * needed per type + */ +u8 serdes_lane_in_use_count[MAX_UNITS_ID][MAX_UNIT_NUMB] = { + /* 0 1 2 3 */ + { 1, 1, 1, 1 }, /* PEX */ + { 1, 1, 1, 1 }, /* ETH_GIG */ + { 1, 1, 0, 0 }, /* USB3H */ + { 1, 1, 1, 0 }, /* USB3D */ + { 1, 1, 1, 1 }, /* SATA */ + { 1, 0, 0, 0 }, /* QSGMII */ + { 4, 0, 0, 0 }, /* XAUI */ + { 2, 0, 0, 0 } /* RXAUI */ +}; + +/* + * serdes_unit_count count unit number. + * (i.e a single XAUI is counted as 1 unit) + */ +u8 serdes_unit_count[MAX_UNITS_ID] = { 0 }; + +/* Selector mapping for A380-A0 and A390-Z1 */ +u8 selectors_serdes_rev2_map[LAST_SERDES_TYPE][MAX_SERDES_LANES] = { + /* 0 1 2 3 4 5 6 */ + { 0x1, 0x1, NA, NA, NA, NA, NA }, /* PEX0 */ + { NA, NA, 0x1, NA, 0x1, NA, 0x1 }, /* PEX1 */ + { NA, NA, NA, NA, 0x7, 0x1, NA }, /* PEX2 */ + { NA, NA, NA, 0x1, NA, NA, NA }, /* PEX3 */ + { 0x2, 0x3, NA, NA, NA, NA, NA }, /* SATA0 */ + { NA, NA, 0x3, NA, NA, NA, NA }, /* SATA1 */ + { NA, NA, NA, NA, 0x6, 0x2, NA }, /* SATA2 */ + { NA, NA, NA, 0x3, NA, NA, NA }, /* SATA3 */ + { 0x3, 0x4, NA, NA, NA, NA, NA }, /* SGMII0 */ + { NA, 0x5, 0x4, NA, 0x3, NA, NA }, /* SGMII1 */ + { NA, NA, NA, 0x4, NA, 0x3, NA }, /* SGMII2 */ + { NA, 0x7, NA, NA, NA, NA, NA }, /* QSGMII */ + { NA, 0x6, NA, NA, 0x4, NA, NA }, /* USB3_HOST0 */ + { NA, NA, NA, 0x5, NA, 0x4, NA }, /* USB3_HOST1 */ + { NA, NA, NA, 0x6, 0x5, 0x5, NA }, /* USB3_DEVICE */ +#ifdef CONFIG_ARMADA_39X + { NA, NA, 0x5, NA, 0x8, NA, 0x2 }, /* SGMII3 */ + { NA, NA, NA, 0x8, 0x9, 0x8, 0x4 }, /* XAUI */ + { NA, NA, NA, NA, NA, 0x8, 0x4 }, /* RXAUI */ +#endif + { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, NA } /* DEFAULT_SERDES */ +}; + +/* Selector mapping for PEX by 4 confiuration */ +u8 common_phys_selectors_pex_by4_lanes[] = { 0x1, 0x2, 0x2, 0x2 }; + +static const char *const serdes_type_to_string[] = { + "PCIe0", + "PCIe1", + "PCIe2", + "PCIe3", + "SATA0", + "SATA1", + "SATA2", + "SATA3", + "SGMII0", + "SGMII1", + "SGMII2", + "QSGMII", + "USB3 HOST0", + "USB3 HOST1", + "USB3 DEVICE", + "SGMII3", + "XAUI", + "RXAUI", + "DEFAULT SERDES", + "LAST_SERDES_TYPE" +}; + +struct serdes_unit_data { + u8 serdes_unit_id; + u8 serdes_unit_num; +}; + +static struct serdes_unit_data serdes_type_to_unit_info[] = { + {PEX_UNIT_ID, 0,}, + {PEX_UNIT_ID, 1,}, + {PEX_UNIT_ID, 2,}, + {PEX_UNIT_ID, 3,}, + {SATA_UNIT_ID, 0,}, + {SATA_UNIT_ID, 1,}, + {SATA_UNIT_ID, 2,}, + {SATA_UNIT_ID, 3,}, + {ETH_GIG_UNIT_ID, 0,}, + {ETH_GIG_UNIT_ID, 1,}, + {ETH_GIG_UNIT_ID, 2,}, + {QSGMII_UNIT_ID, 0,}, + {USB3H_UNIT_ID, 0,}, + {USB3H_UNIT_ID, 1,}, + {USB3D_UNIT_ID, 0,}, + {ETH_GIG_UNIT_ID, 3,}, + {XAUI_UNIT_ID, 0,}, + {RXAUI_UNIT_ID, 0,}, +}; + +/* Sequences DB */ + +/* + * SATA and SGMII + */ + +struct op_params sata_port0_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Access to reg 0x48(OOB param 1) */ + {SATA_VENDOR_PORT_0_REG_ADDR, 0x38000, 0xffffffff, {0x48,}, 0, 0}, + /* OOB Com_wake and Com_reset spacing upper limit data */ + {SATA_VENDOR_PORT_0_REG_DATA, 0x38000, 0xf03f, {0x6018,}, 0, 0}, + /* Access to reg 0xa(PHY Control) */ + {SATA_VENDOR_PORT_0_REG_ADDR, 0x38000, 0xffffffff, {0xa,}, 0, 0}, + /* Rx clk and Tx clk select non-inverted mode */ + {SATA_VENDOR_PORT_0_REG_DATA, 0x38000, 0x3000, {0x0,}, 0, 0}, + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0,}, 0, 0}, + /* Power Down Sata Port 0 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffff00ff, {0xc40040,}, 0, 0}, +}; + +struct op_params sata_port1_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Access to reg 0x48(OOB param 1) */ + {SATA_VENDOR_PORT_1_REG_ADDR, 0x38000, 0xffffffff, {0x48,}, 0, 0}, + /* OOB Com_wake and Com_reset spacing upper limit data */ + {SATA_VENDOR_PORT_1_REG_DATA, 0x38000, 0xf03f, {0x6018,}, 0, 0}, + /* Access to reg 0xa(PHY Control) */ + {SATA_VENDOR_PORT_1_REG_ADDR, 0x38000, 0xffffffff, {0xa,}, 0, 0}, + /* Rx clk and Tx clk select non-inverted mode */ + {SATA_VENDOR_PORT_1_REG_DATA, 0x38000, 0x3000, {0x0,}, 0, 0}, + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0,}, 0, 0}, + /* Power Down Sata Port 1 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffff00, {0xc44000,}, 0, 0}, +}; + +/* SATA and SGMII - power up seq */ +struct op_params sata_and_sgmii_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Power Up */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x90006, {0x80002, 0x80002}, + 0, 0}, + /* Unreset */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0x6000}, 0, 0}, + /* Phy Selector */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x0e0, {0x0, 0x80}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x440, {0x440, 0x400}, 0, 0} +}; + +/* SATA and SGMII - speed config seq */ +struct op_params sata_and_sgmii_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, + * SGMII (1.25G), SGMII (3.125G), wait_time, num_of_loops + */ + /* Baud Rate */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc00000, + {0x8800000, 0x19800000, 0x22000000}, 0, 0}, + /* Select Baud Rate for SATA only */ + {INTERFACE_REG, 0x800, 0xc00, {0x800, NO_DATA, NO_DATA}, 0, 0}, + /* Phy Gen RX and TX */ + {ISOLATE_REG, 0x800, 0xff, {NO_DATA, 0x66, 0x66}, 0, 0}, + /* Bus Width */ + {LOOPBACK_REG, 0x800, 0xe, {0x4, 0x2, 0x2}, 0, 0} +}; + +/* SATA and SGMII - TX config seq */ +struct op_params sata_and_sgmii_tx_config_params1[] = { + /* + * unitunit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + {GLUE_REG, 0x800, 0x1800, {NO_DATA, 0x800}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401, 0x401}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0, 0x0}, 0, 0}, + /* Power up PLL, RX and TX */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0xf0000, {0x70000, 0x70000}, + 0, 0} +}; + +struct op_params sata_port0_tx_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0}, 0, 0}, + /* Power Down Sata Port 0 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffff00ff, {0xc40000}, 0, 0}, + /* Regret bit addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x4}, 0, 0}, + /* Regret bit data */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffffff, {0x80}, 0, 0} +}; + +struct op_params sata_port1_tx_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* Power Down Sata addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x0}, 0, 0}, + /* Power Down Sata Port 1 */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffff00, {0xc40000}, 0, 0}, + /* Regret bit addr */ + {SATA_CTRL_REG_IND_ADDR, 0x38000, 0xffffffff, {0x4}, 0, 0}, + /* Regret bit data */ + {SATA_CTRL_REG_IND_DATA, 0x38000, 0xffffffff, {0x80}, 0, 0} +}; + +struct op_params sata_and_sgmii_tx_config_serdes_rev1_params2[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc, 0xc}, 10, 1000}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1, 0x1}, 1, 1000} +}; + +struct op_params sata_and_sgmii_tx_config_serdes_rev2_params2[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, SGMII data, + * wait_time, num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc, 0xc}, 10, 1000}, + /* Assert Rx Init for SGMII */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {NA, 0x40000000}, + 0, 0}, + /* Assert Rx Init for SATA */ + {ISOLATE_REG, 0x800, 0x400, {0x400, NA}, 0, 0}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1, 0x1}, 1, 1000}, + /* De-assert Rx Init for SGMII */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {NA, 0x0}, 0, 0}, + /* De-assert Rx Init for SATA */ + {ISOLATE_REG, 0x800, 0x400, {0x0, NA}, 0, 0}, + /* os_ph_offset_force (align 90) */ + {RX_REG3, 0x800, 0xff, {0xde, NO_DATA}, 0, 0}, + /* Set os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x100, NO_DATA}, 0, 0}, + /* Unset os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x0, NO_DATA}, 0, 0}, +}; + +struct op_params sata_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* enable SSC and DFE update enable */ + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x400008, {0x400000,}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000,}, 0, 0}, + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xfff, {0x6cf,}, 0, 0}, + /* G1_TX SLEW, EMPH1 and AMP */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8a32,}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9,}, 0, 0}, + /* G2_TX SLEW, EMPH1 and AMP */ + {G2_SETTINGS_0_REG, 0x800, 0xffff, {0x8b5c,}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2,}, 0, 0}, + /* G3_TX SLEW, EMPH1 and AMP */ + {G3_SETTINGS_0_REG, 0x800, 0xffff, {0xe6e,}, 0, 0}, + /* G3_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G3_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2,}, 0, 0}, + /* Cal rxclkalign90 ext enable and Cal os ph ext */ + {CAL_REG6, 0x800, 0xff00, {0xdd00,}, 0, 0}, + /* Dtl Clamping disable and Dtl clamping Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70,}, 0, 0}, +}; + +struct op_params sata_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, SATA data, wait_time, + * num_of_loops + */ + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xf00, {0x600}, 0, 0}, + /* enable SSC and DFE update enable */ + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x400008, {0x400000}, 0, 0}, + /* G1_TX SLEW, EMPH1 and AMP */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8a32}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2_TX SLEW, EMPH1 and AMP */ + {G2_SETTINGS_0_REG, 0x800, 0xffff, {0x8b5c}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* G3_TX SLEW, EMPH1 and AMP */ + {G3_SETTINGS_0_REG, 0x800, 0xffff, {0xe6e}, 0, 0}, + /* + * G3_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI & DFE_En Gen3, + * DC wander calibration dis + */ + {G3_SETTINGS_1_REG, 0x800, 0x47ff, {0x7d2}, 0, 0}, + /* Bit[12]=0x0 idle_sync_en */ + {PCIE_REG0, 0x800, 0x1000, {0x0}, 0, 0}, + /* Dtl Clamping disable and Dtl clamping Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70,}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, + /* DFE_STEP_FINE_FX[3:0] =0xa */ + {DFE_REG0, 0x800, 0xa00f, {0x800a}, 0, 0}, + /* DFE_EN and Dis Update control from pin disable */ + {DFE_REG3, 0x800, 0xc000, {0x0}, 0, 0}, + /* FFE Force FFE_REs and cap settings for Gen1 */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* FFE Force FFE_REs and cap settings for Gen2 */ + {G2_SETTINGS_3_REG, 0x800, 0xff, {0xbf}, 0, 0}, + /* FE Force FFE_REs=4 and cap settings for Gen3n */ + {G3_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* Set DFE Gen 3 Resolution to 3 */ + {G3_SETTINGS_4_REG, 0x800, 0x300, {0x300}, 0, 0}, +}; + +struct op_params sgmii_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, SGMII (1.25G), SGMII (3.125G), + * wait_time, num_of_loops + */ + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9, 0x3c9}, 0, 0}, + /* SQ_THRESH and FFE Setting */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xfff, {0x8f, 0xbf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000, 0x4000}, 0, 0}, +}; + +struct op_params sgmii_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, SGMII (1.25G), SGMII (3.125G), + * wait_time, num_of_loops + */ + /* Set Slew_rate, Emph and Amp */ + {G1_SETTINGS_0_REG, 0x800, 0xffff, {0x8fa, 0x8fa}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9, 0x3c9}, 0, 0}, + /* DTL_FLOOP_EN */ + {RX_REG2, 0x800, 0x4, {0x0, 0x0}, 0, 0}, + /* G1 FFE Setting Force, RES and CAP */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0x8f, 0xbf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000, 0x3000}, 0, 0}, +}; + +/* + * PEX and USB3 + */ + +/* PEX and USB3 - power up seq for Serdes Rev 1.2 */ +struct op_params pex_and_usb3_power_up_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc7f806, + {0x4471804, 0x4479804}, 0, 0}, + {COMMON_PHY_CONFIGURATION2_REG, 0x28, 0x5c, {0x58, 0x58}, 0, 0}, + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x3, {0x1, 0x1}, 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0xe000}, 0, 0}, + {GLOBAL_CLK_CTRL, 0x800, 0xd, {0x5, 0x1}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x80, 0x4c0}, 0, 0} +}; + +/* PEX and USB3 - power up seq for Serdes Rev 2.1 */ +struct op_params pex_and_usb3_power_up_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc7f806, + {0x4471804, 0x4479804}, 0, 0}, + {COMMON_PHY_CONFIGURATION2_REG, 0x28, 0x5c, {0x58, 0x58}, 0, 0}, + {COMMON_PHY_CONFIGURATION4_REG, 0x28, 0x3, {0x1, 0x1}, 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000, 0xe000}, 0, 0}, + {GLOBAL_CLK_CTRL, 0x800, 0xd, {0x5, 0x1}, 0, 0}, + {GLOBAL_MISC_CTRL, 0x800, 0xc0, {0x0, NO_DATA}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x80, 0x4c0}, 0, 0} +}; + +/* PEX and USB3 - speed config seq */ +struct op_params pex_and_usb3_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Maximal PHY Generation Setting */ + {INTERFACE_REG, 0x800, 0xc00, {0x400, 0x400, 0x400, 0x400, 0x400}, + 0, 0}, +}; + +struct op_params usb3_electrical_config_serdes_rev1_params[] = { + /* Spread Spectrum Clock Enable */ + {LANE_CFG4_REG, 0x800, 0x80, {0x80}, 0, 0}, + /* G2_TX_SSC_AMP[6:0]=4.5k_p_pM and TX emphasis mode=m_v */ + {G2_SETTINGS_2_REG, 0x800, 0xfe40, {0x4440}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x4000}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* FFE Setting Force, RES and CAP */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xff, {0xef}, 0, 0}, + /* Dtl Clamping disable and Dtl-clamping-Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70}, 0, 0}, + /* cal_rxclkalign90_ext_en and cal_os_ph_ext */ + {CAL_REG6, 0x800, 0xff00, {0xd500}, 0, 0}, + /* vco_cal_vth_sel */ + {REF_REG0, 0x800, 0x38, {0x20}, 0, 0}, +}; + +struct op_params usb3_electrical_config_serdes_rev2_params[] = { + /* Spread Spectrum Clock Enable */ + {LANE_CFG4_REG, 0x800, 0x80, {0x80}, 0, 0}, + /* G2_TX_SSC_AMP[6:0]=4.5k_p_pM and TX emphasis mode=m_v */ + {G2_SETTINGS_2_REG, 0x800, 0xfe40, {0x4440}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3d2}, 0, 0}, + /* Dtl Clamping disable and Dtl-clamping-Sel(6000ppm) */ + {RX_REG2, 0x800, 0xf0, {0x70}, 0, 0}, + /* vco_cal_vth_sel */ + {REF_REG0, 0x800, 0x38, {0x20}, 0, 0}, + /* Spread Spectrum Clock Enable */ + {LANE_CFG5_REG, 0x800, 0x4, {0x4}, 0, 0}, +}; + +/* PEX and USB3 - TX config seq */ + +/* + * For PEXx1: the pex_and_usb3_tx_config_params1/2/3 configurations should run + * one by one on the lane. + * For PEXx4: the pex_and_usb3_tx_config_params1/2/3 configurations should run + * by setting each sequence for all 4 lanes. + */ +struct op_params pex_and_usb3_tx_config_params1[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + {GLOBAL_CLK_CTRL, 0x800, 0x1, {0x0, 0x0}, 0, 0}, + /* 10ms delay */ + {0x0, 0x0, 0x0, {0x0, 0x0}, 10, 0}, + /* os_ph_offset_force (align 90) */ + {RX_REG3, 0x800, 0xff, {0xdc, NO_DATA}, 0, 0}, + /* Set os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x100, NO_DATA}, 0, 0}, + /* Unset os_ph_valid */ + {RX_REG3, 0x800, 0x100, {0x0, NO_DATA}, 0, 0}, +}; + +struct op_params pex_and_usb3_tx_config_params2[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401, 0x401}, 0, 0}, +}; + +struct op_params pex_and_usb3_tx_config_params3[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, USB3 data, + * wait_time, num_of_loops + */ + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0, 0x0}, 0, 0}, + /* 10ms delay */ + {0x0, 0x0, 0x0, {0x0, 0x0}, 10, 0} +}; + +/* PEX by 4 config seq */ +struct op_params pex_by4_config_params[] = { + /* unit_base_reg, unit_offset, mask, data, wait_time, num_of_loops */ + {GLOBAL_CLK_SRC_HI, 0x800, 0x7, {0x5, 0x0, 0x0, 0x2}, 0, 0}, + /* Lane Alignement enable */ + {LANE_ALIGN_REG0, 0x800, 0x1000, {0x0, 0x0, 0x0, 0x0}, 0, 0}, + /* Max PLL phy config */ + {CALIBRATION_CTRL_REG, 0x800, 0x1000, {0x1000, 0x1000, 0x1000, 0x1000}, + 0, 0}, + /* Max PLL pipe config */ + {LANE_CFG1_REG, 0x800, 0x600, {0x600, 0x600, 0x600, 0x600}, 0, 0}, +}; + +/* USB3 device donfig seq */ +struct op_params usb3_device_config_params[] = { + /* unit_base_reg, unit_offset, mask, data, wait_time, num_of_loops */ + {LANE_CFG4_REG, 0x800, 0x200, {0x200}, 0, 0} +}; + +/* PEX - electrical configuration seq Rev 1.2 */ +struct op_params pex_electrical_config_serdes_rev1_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* G1_TX_SLEW_CTRL_EN and G1_TX_SLEW_RATE */ + {G1_SETTINGS_0_REG, 0x800, 0xf000, {0xb000}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* CFG_DFE_EN_SEL */ + {LANE_CFG4_REG, 0x800, 0x8, {0x8}, 0, 0}, + /* FFE Setting Force, RES and CAP */ + {SQUELCH_FFE_SETTING_REG, 0x800, 0xff, {0xaf}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, + /* cal_rxclkalign90_ext_en and cal_os_ph_ext */ + {CAL_REG6, 0x800, 0xff00, {0xdc00}, 0, 0}, +}; + +/* PEX - electrical configuration seq Rev 2.1 */ +struct op_params pex_electrical_config_serdes_rev2_params[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* G1_TX_SLEW_CTRL_EN and G1_TX_SLEW_RATE */ + {G1_SETTINGS_0_REG, 0x800, 0xf000, {0xb000}, 0, 0}, + /* G1_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G1_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G1 FFE Setting Force, RES and CAP */ + {G1_SETTINGS_3_REG, 0x800, 0xff, {0xcf}, 0, 0}, + /* G2_RX SELMUFF, SELMUFI, SELMUPF and SELMUPI */ + {G2_SETTINGS_1_REG, 0x800, 0x3ff, {0x3c9}, 0, 0}, + /* G2 FFE Setting Force, RES and CAP */ + {G2_SETTINGS_3_REG, 0x800, 0xff, {0xaf}, 0, 0}, + /* G2 DFE resolution value */ + {G2_SETTINGS_4_REG, 0x800, 0x300, {0x300}, 0, 0}, + /* DFE resolution force */ + {DFE_REG0, 0x800, 0x8000, {0x8000}, 0, 0}, + /* Tx amplitude for Tx Margin 0 */ + {PCIE_REG1, 0x800, 0xf80, {0xd00}, 0, 0}, + /* Tx_Emph value for -3.5d_b and -6d_b */ + {PCIE_REG3, 0x800, 0xff00, {0xaf00}, 0, 0}, + /* CFG_DFE_EN_SEL */ + {LANE_CFG4_REG, 0x800, 0x8, {0x8}, 0, 0}, + /* tximpcal_th and rximpcal_th */ + {VTHIMPCAL_CTRL_REG, 0x800, 0xff00, {0x3000}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_25MHz */ +struct op_params pex_config_ref_clock25_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x2 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x2}, 0, 0}, + /* Bit[10]=0x1 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x400}, 0, 0}, + /* Bits[7:0]=0x7 - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0x7}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_40MHz */ +struct op_params pex_config_ref_clock40_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x3 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x3}, 0, 0}, + /* Bits[10]=0x1 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x400}, 0, 0}, + /* Bits[7:0]=0xc - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0xc}, 0, 0}, +}; + +/* PEX - configuration seq for REF_CLOCK_100MHz */ +struct op_params pex_config_ref_clock100_m_hz[] = { + /* + * unit_base_reg, unit_offset, mask, PEX data, wait_time, + * num_of_loops + */ + /* Bits[4:0]=0x0 - REF_FREF_SEL */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0x1f, {0x0}, 0, 0}, + /* Bit[10]=0x0 - REFCLK_SEL */ + {MISC_REG, 0x800, 0x400, {0x0}, 0, 0}, + /* Bits[7:0]=0x1e - CFG_PM_RXDLOZ_WAIT */ + {GLOBAL_PM_CTRL, 0x800, 0xff, {0x1e}, 0, 0}, +}; + +/* + * USB2 + */ + +struct op_params usb2_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, USB2 data, wait_time, + * num_of_loops + */ + /* Init phy 0 */ + {0x18440, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Init phy 1 */ + {0x18444, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Init phy 2 */ + {0x18448, 0x0 /*NA*/, 0xffffffff, {0x62}, 0, 0}, + /* Phy offset 0x0 - PLL_CONTROL0 */ + {0xc0000, 0x0 /*NA*/, 0xffffffff, {0x40605205}, 0, 0}, + {0xc001c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + {0xc201c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + {0xc401c, 0x0 /*NA*/, 0xffffffff, {0x39f16ce}, 0, 0}, + /* Phy offset 0x1 - PLL_CONTROL1 */ + {0xc0004, 0x0 /*NA*/, 0x1, {0x1}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc000c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc200c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* Phy0 register 3 - TX Channel control 0 */ + {0xc400c, 0x0 /*NA*/, 0x1000000, {0x1000000}, 0, 0}, + /* check PLLCAL_DONE is set and IMPCAL_DONE is set */ + {0xc0008, 0x0 /*NA*/, 0x80800000, {0x80800000}, 1, 1000}, + /* check REG_SQCAL_DONE is set */ + {0xc0018, 0x0 /*NA*/, 0x80000000, {0x80000000}, 1, 1000}, + /* check PLL_READY is set */ + {0xc0000, 0x0 /*NA*/, 0x80000000, {0x80000000}, 1, 1000} +}; + +/* + * QSGMII + */ + +/* QSGMII - power up seq */ +struct op_params qsgmii_port_power_up_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Connect the QSGMII to Gigabit Ethernet units */ + {QSGMII_CONTROL_REG1, 0x0, 0x40000000, {0x40000000}, 0, 0}, + /* Power Up */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0xf0006, {0x80002}, 0, 0}, + /* Unreset */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x7800, {0x6000}, 0, 0}, + /* Phy Selector */ + {POWER_AND_PLL_CTRL_REG, 0x800, 0xff, {0xfc81}, 0, 0}, + /* Ref clock source select */ + {MISC_REG, 0x800, 0x4c0, {0x480}, 0, 0} +}; + +/* QSGMII - speed config seq */ +struct op_params qsgmii_port_speed_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Baud Rate */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x3fc00000, {0xcc00000}, 0, 0}, + /* Phy Gen RX and TX */ + {ISOLATE_REG, 0x800, 0xff, {0x33}, 0, 0}, + /* Bus Width */ + {LOOPBACK_REG, 0x800, 0xe, {0x2}, 0, 0} +}; + +/* QSGMII - Select electrical param seq */ +struct op_params qsgmii_port_electrical_config_params[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Slew rate and emphasis */ + {G1_SETTINGS_0_REG, 0x800, 0x8000, {0x0}, 0, 0} +}; + +/* QSGMII - TX config seq */ +struct op_params qsgmii_port_tx_config_params1[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + {GLUE_REG, 0x800, 0x1800, {0x800}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x401}, 0, 0}, + /* Sft Reset pulse */ + {RESET_DFE_REG, 0x800, 0x401, {0x0}, 0, 0}, + /* Lane align */ + {LANE_ALIGN_REG0, 0x800, 0x1000, {0x1000}, 0, 0}, + /* Power up PLL, RX and TX */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x70000, {0x70000}, 0, 0}, + /* Tx driver output idle */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x80000, {0x80000}, 0, 0} +}; + +struct op_params qsgmii_port_tx_config_params2[] = { + /* + * unit_base_reg, unit_offset, mask, QSGMII data, wait_time, + * num_of_loops + */ + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0xc, {0xc}, 10, 1000}, + /* Assert Rx Init and Tx driver output valid */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40080000, {0x40000000}, 0, 0}, + /* Wait for PHY power up sequence to finish */ + {COMMON_PHY_STATUS1_REG, 0x28, 0x1, {0x1}, 1, 1000}, + /* De-assert Rx Init */ + {COMMON_PHY_CONFIGURATION1_REG, 0x28, 0x40000000, {0x0}, 0, 0} +}; + +/* SERDES_POWER_DOWN */ +struct op_params serdes_power_down_params[] = { + {COMMON_PHY_CONFIGURATION1_REG, 0x28, (0xf << 11), {(0x3 << 11)}, + 0, 0}, + {COMMON_PHY_CONFIGURATION1_REG, 0x28, (0x7 << 16), {0}, 0, 0} +}; + +/* + * hws_ctrl_serdes_rev_get + * + * DESCRIPTION: Get the Serdes revision number + * + * INPUT: config_field - Field description enum + * + * OUTPUT: None + * + * RETURN: + * 8bit Serdes revision number + */ +u8 hws_ctrl_serdes_rev_get(void) +{ +#ifdef CONFIG_ARMADA_38X + /* for A38x-Z1 */ + if (sys_env_device_rev_get() == MV_88F68XX_Z1_ID) + return MV_SERDES_REV_1_2; +#endif + + /* for A39x-Z1, A38x-A0 */ + return MV_SERDES_REV_2_1; +} + +u32 hws_serdes_topology_verify(enum serdes_type serdes_type, u32 serdes_id, + enum serdes_mode serdes_mode) +{ + u32 test_result = 0; + u8 serd_max_num, unit_numb; + enum unit_id unit_id; + + if (serdes_type > RXAUI) { + printf("%s: Warning: Wrong serdes type %s serdes#%d\n", + __func__, serdes_type_to_string[serdes_type], serdes_id); + return MV_FAIL; + } + + unit_id = serdes_type_to_unit_info[serdes_type].serdes_unit_id; + unit_numb = serdes_type_to_unit_info[serdes_type].serdes_unit_num; + serd_max_num = sys_env_unit_max_num_get(unit_id); + + /* if didn't exceed amount of required Serdes lanes for current type */ + if (serdes_lane_in_use_count[unit_id][unit_numb] != 0) { + /* update amount of required Serdes lanes for current type */ + serdes_lane_in_use_count[unit_id][unit_numb]--; + + /* + * If reached the exact amount of required Serdes lanes for + * current type + */ + if (serdes_lane_in_use_count[unit_id][unit_numb] == 0) { + if (((serdes_type <= PEX3)) && + ((serdes_mode == PEX_END_POINT_X4) || + (serdes_mode == PEX_ROOT_COMPLEX_X4))) { + /* PCiex4 uses 2 SerDes */ + serdes_unit_count[PEX_UNIT_ID] += 2; + } else { + serdes_unit_count[unit_id]++; + } + + /* test SoC unit count limitation */ + if (serdes_unit_count[unit_id] > serd_max_num) { + test_result = WRONG_NUMBER_OF_UNITS; + } else if (unit_numb >= serd_max_num) { + /* test SoC unit number limitation */ + test_result = UNIT_NUMBER_VIOLATION; + } + } + } else { + test_result = SERDES_ALREADY_IN_USE; + if (test_result == SERDES_ALREADY_IN_USE) { + printf("%s: Error: serdes lane %d is configured to type %s: type already in use\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type]); + return MV_FAIL; + } else if (test_result == WRONG_NUMBER_OF_UNITS) { + printf("%s: Warning: serdes lane %d is set to type %s.\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type]); + printf("%s: Maximum supported lanes are already set to this type (limit = %d)\n", + __func__, serd_max_num); + return MV_FAIL; + } else if (test_result == UNIT_NUMBER_VIOLATION) { + printf("%s: Warning: serdes lane %d type is %s: current device support only %d units of this type.\n", + __func__, serdes_id, + serdes_type_to_string[serdes_type], + serd_max_num); + return MV_FAIL; + } + } + + return MV_OK; +} + +void hws_serdes_xaui_topology_verify(void) +{ + /* + * If XAUI is in use - serdes_lane_in_use_count has to be = 0; + * if it is not in use hast be = 4 + */ + if ((serdes_lane_in_use_count[XAUI_UNIT_ID][0] != 0) && + (serdes_lane_in_use_count[XAUI_UNIT_ID][0] != 4)) { + printf("%s: Warning: wrong number of lanes is set to XAUI - %d\n", + __func__, serdes_lane_in_use_count[XAUI_UNIT_ID][0]); + printf("%s: XAUI has to be defined on 4 lanes\n", __func__); + } + + /* + * If RXAUI is in use - serdes_lane_in_use_count has to be = 0; + * if it is not in use hast be = 2 + */ + if ((serdes_lane_in_use_count[RXAUI_UNIT_ID][0] != 0) && + (serdes_lane_in_use_count[RXAUI_UNIT_ID][0] != 2)) { + printf("%s: Warning: wrong number of lanes is set to RXAUI - %d\n", + __func__, serdes_lane_in_use_count[RXAUI_UNIT_ID][0]); + printf("%s: RXAUI has to be defined on 2 lanes\n", __func__); + } +} + +int hws_serdes_seq_db_init(void) +{ + u8 serdes_rev = hws_ctrl_serdes_rev_get(); + + DEBUG_INIT_FULL_S("\n### serdes_seq38x_init ###\n"); + + if (serdes_rev == MV_SERDES_REV_NA) { + printf("hws_serdes_seq_db_init: serdes revision number is not supported\n"); + return MV_NOT_SUPPORTED; + } + + /* SATA_PORT_0_ONLY_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].op_params_ptr = + sata_port0_power_up_params; + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_port0_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_0_ONLY_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_PORT_1_ONLY_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].op_params_ptr = + sata_port1_power_up_params; + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_port1_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_1_ONLY_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_POWER_UP_SEQ sequence init */ + serdes_seq_db[SATA_POWER_UP_SEQ].op_params_ptr = + sata_and_sgmii_power_up_params; + serdes_seq_db[SATA_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SATA_POWER_UP_SEQ].data_arr_idx = SATA; + + /* SATA_1_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_1_5_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_3_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_3_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_6_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SATA_6_SPEED_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sata_electrical_config_serdes_rev1_params; + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sata_electrical_config_serdes_rev2_params; + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[SATA_ELECTRICAL_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_TX_CONFIG_SEQ1].op_params_ptr = + sata_and_sgmii_tx_config_params1; + serdes_seq_db[SATA_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[SATA_TX_CONFIG_SEQ1].data_arr_idx = SATA; + + /* SATA_PORT_0_ONLY_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].op_params_ptr = + sata_port0_tx_config_params; + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_port0_tx_config_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_0_ONLY_TX_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_PORT_1_ONLY_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].op_params_ptr = + sata_port1_tx_config_params; + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_port1_tx_config_params) / sizeof(struct op_params); + serdes_seq_db[SATA_PORT_1_ONLY_TX_CONFIG_SEQ].data_arr_idx = SATA; + + /* SATA_TX_CONFIG_SEQ2 sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SATA_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev1_params2; + serdes_seq_db[SATA_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev1_params2) / + sizeof(struct op_params); + } else { + serdes_seq_db[SATA_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev2_params2; + serdes_seq_db[SATA_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev2_params2) / + sizeof(struct op_params); + } + serdes_seq_db[SATA_TX_CONFIG_SEQ2].data_arr_idx = SATA; + + /* SGMII_POWER_UP_SEQ sequence init */ + serdes_seq_db[SGMII_POWER_UP_SEQ].op_params_ptr = + sata_and_sgmii_power_up_params; + serdes_seq_db[SGMII_POWER_UP_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_power_up_params) / sizeof(struct op_params); + serdes_seq_db[SGMII_POWER_UP_SEQ].data_arr_idx = SGMII; + + /* SGMII_1_25_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SGMII_1_25_SPEED_CONFIG_SEQ].data_arr_idx = SGMII; + + /* SGMII_3_125_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].op_params_ptr = + sata_and_sgmii_speed_config_params; + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(sata_and_sgmii_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[SGMII_3_125_SPEED_CONFIG_SEQ].data_arr_idx = SGMII_3_125; + + /* SGMII_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sgmii_electrical_config_serdes_rev1_params; + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sgmii_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + sgmii_electrical_config_serdes_rev2_params; + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(sgmii_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[SGMII_ELECTRICAL_CONFIG_SEQ].data_arr_idx = SGMII; + + /* SGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].op_params_ptr = + sata_and_sgmii_tx_config_params1; + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[SGMII_TX_CONFIG_SEQ1].data_arr_idx = SGMII; + + /* SGMII_TX_CONFIG_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev1_params2; + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev1_params2) / + sizeof(struct op_params); + } else { + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].op_params_ptr = + sata_and_sgmii_tx_config_serdes_rev2_params2; + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(sata_and_sgmii_tx_config_serdes_rev2_params2) / + sizeof(struct op_params); + } + serdes_seq_db[SGMII_TX_CONFIG_SEQ2].data_arr_idx = SGMII; + + /* PEX_POWER_UP_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[PEX_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev1_params; + serdes_seq_db[PEX_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[PEX_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev2_params; + serdes_seq_db[PEX_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[PEX_POWER_UP_SEQ].data_arr_idx = PEX; + + /* PEX_2_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_2_5_SPEED_CONFIG_SEQ].data_arr_idx = + PEXSERDES_SPEED_2_5_GBPS; + + /* PEX_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_5_SPEED_CONFIG_SEQ].data_arr_idx = + PEXSERDES_SPEED_5_GBPS; + + /* PEX_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + pex_electrical_config_serdes_rev1_params; + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + pex_electrical_config_serdes_rev2_params; + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[PEX_ELECTRICAL_CONFIG_SEQ].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ1 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ1].op_params_ptr = + pex_and_usb3_tx_config_params1; + serdes_seq_db[PEX_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ1].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ2 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ2].op_params_ptr = + pex_and_usb3_tx_config_params2; + serdes_seq_db[PEX_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params2) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ2].data_arr_idx = PEX; + + /* PEX_TX_CONFIG_SEQ3 sequence init */ + serdes_seq_db[PEX_TX_CONFIG_SEQ3].op_params_ptr = + pex_and_usb3_tx_config_params3; + serdes_seq_db[PEX_TX_CONFIG_SEQ3].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params3) / sizeof(struct op_params); + serdes_seq_db[PEX_TX_CONFIG_SEQ3].data_arr_idx = PEX; + + /* PEX_BY_4_CONFIG_SEQ sequence init */ + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].op_params_ptr = + pex_by4_config_params; + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_by4_config_params) / sizeof(struct op_params); + serdes_seq_db[PEX_BY_4_CONFIG_SEQ].data_arr_idx = PEX; + + /* PEX_CONFIG_REF_CLOCK_25MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].op_params_ptr = + pex_config_ref_clock25_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock25_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_25MHZ_SEQ].data_arr_idx = PEX; + + /* PEX_ELECTRICAL_CONFIG_REF_CLOCK_40MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].op_params_ptr = + pex_config_ref_clock40_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock40_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_40MHZ_SEQ].data_arr_idx = PEX; + + /* PEX_CONFIG_REF_CLOCK_100MHZ_SEQ sequence init */ + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].op_params_ptr = + pex_config_ref_clock100_m_hz; + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].cfg_seq_size = + sizeof(pex_config_ref_clock100_m_hz) / sizeof(struct op_params); + serdes_seq_db[PEX_CONFIG_REF_CLOCK_100MHZ_SEQ].data_arr_idx = PEX; + + /* USB3_POWER_UP_SEQ sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[USB3_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev1_params; + serdes_seq_db[USB3_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[USB3_POWER_UP_SEQ].op_params_ptr = + pex_and_usb3_power_up_serdes_rev2_params; + serdes_seq_db[USB3_POWER_UP_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_power_up_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[USB3_POWER_UP_SEQ].data_arr_idx = USB3; + + /* USB3_HOST_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_HOST_SPEED_CONFIG_SEQ].data_arr_idx = + USB3SERDES_SPEED_5_GBPS_HOST; + + /* USB3_DEVICE_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].op_params_ptr = + pex_and_usb3_speed_config_params; + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(pex_and_usb3_speed_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_DEVICE_SPEED_CONFIG_SEQ].data_arr_idx = + USB3SERDES_SPEED_5_GBPS_DEVICE; + + /* USB3_ELECTRICAL_CONFIG_SEQ seq sequence init */ + if (serdes_rev == MV_SERDES_REV_1_2) { + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + usb3_electrical_config_serdes_rev1_params; + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_electrical_config_serdes_rev1_params) / + sizeof(struct op_params); + } else { + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + usb3_electrical_config_serdes_rev2_params; + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_electrical_config_serdes_rev2_params) / + sizeof(struct op_params); + } + serdes_seq_db[USB3_ELECTRICAL_CONFIG_SEQ].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ1].op_params_ptr = + pex_and_usb3_tx_config_params1; + serdes_seq_db[USB3_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params1) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ1].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ2].op_params_ptr = + pex_and_usb3_tx_config_params2; + serdes_seq_db[USB3_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params2) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ2].data_arr_idx = USB3; + + /* USB3_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_TX_CONFIG_SEQ3].op_params_ptr = + pex_and_usb3_tx_config_params3; + serdes_seq_db[USB3_TX_CONFIG_SEQ3].cfg_seq_size = + sizeof(pex_and_usb3_tx_config_params3) / sizeof(struct op_params); + serdes_seq_db[USB3_TX_CONFIG_SEQ3].data_arr_idx = USB3; + + /* USB2_POWER_UP_SEQ sequence init */ + serdes_seq_db[USB2_POWER_UP_SEQ].op_params_ptr = usb2_power_up_params; + serdes_seq_db[USB2_POWER_UP_SEQ].cfg_seq_size = + sizeof(usb2_power_up_params) / sizeof(struct op_params); + serdes_seq_db[USB2_POWER_UP_SEQ].data_arr_idx = 0; + + /* USB3_DEVICE_CONFIG_SEQ sequence init */ + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].op_params_ptr = + usb3_device_config_params; + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].cfg_seq_size = + sizeof(usb3_device_config_params) / sizeof(struct op_params); + serdes_seq_db[USB3_DEVICE_CONFIG_SEQ].data_arr_idx = 0; /* Not relevant */ + + /* SERDES_POWER_DOWN_SEQ sequence init */ + serdes_seq_db[SERDES_POWER_DOWN_SEQ].op_params_ptr = + serdes_power_down_params; + serdes_seq_db[SERDES_POWER_DOWN_SEQ].cfg_seq_size = + sizeof(serdes_power_down_params) / + sizeof(struct op_params); + serdes_seq_db[SERDES_POWER_DOWN_SEQ].data_arr_idx = FIRST_CELL; + + if (serdes_rev == MV_SERDES_REV_2_1) { + /* QSGMII_POWER_UP_SEQ sequence init */ + serdes_seq_db[QSGMII_POWER_UP_SEQ].op_params_ptr = + qsgmii_port_power_up_params; + serdes_seq_db[QSGMII_POWER_UP_SEQ].cfg_seq_size = + sizeof(qsgmii_port_power_up_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_POWER_UP_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_5_SPEED_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].op_params_ptr = + qsgmii_port_speed_config_params; + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].cfg_seq_size = + sizeof(qsgmii_port_speed_config_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_5_SPEED_CONFIG_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_ELECTRICAL_CONFIG_SEQ seq sequence init */ + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].op_params_ptr = + qsgmii_port_electrical_config_params; + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].cfg_seq_size = + sizeof(qsgmii_port_electrical_config_params) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_ELECTRICAL_CONFIG_SEQ].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].op_params_ptr = + qsgmii_port_tx_config_params1; + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].cfg_seq_size = + sizeof(qsgmii_port_tx_config_params1) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_TX_CONFIG_SEQ1].data_arr_idx = + QSGMII_SEQ_IDX; + + /* QSGMII_TX_CONFIG_SEQ sequence init */ + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].op_params_ptr = + qsgmii_port_tx_config_params2; + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].cfg_seq_size = + sizeof(qsgmii_port_tx_config_params2) / + sizeof(struct op_params); + serdes_seq_db[QSGMII_TX_CONFIG_SEQ2].data_arr_idx = + QSGMII_SEQ_IDX; + } + + return MV_OK; +} + +enum serdes_seq serdes_type_and_speed_to_speed_seq(enum serdes_type serdes_type, + enum serdes_speed baud_rate) +{ + enum serdes_seq seq_id = SERDES_LAST_SEQ; + + DEBUG_INIT_FULL_S("\n### serdes_type_and_speed_to_speed_seq ###\n"); + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + if (baud_rate == SERDES_SPEED_2_5_GBPS) + seq_id = PEX_2_5_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = PEX_5_SPEED_CONFIG_SEQ; + break; + case USB3_HOST0: + case USB3_HOST1: + if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = USB3_HOST_SPEED_CONFIG_SEQ; + break; + case USB3_DEVICE: + if (baud_rate == SERDES_SPEED_5_GBPS) + seq_id = USB3_DEVICE_SPEED_CONFIG_SEQ; + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + if (baud_rate == SERDES_SPEED_1_5_GBPS) + seq_id = SATA_1_5_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_3_GBPS) + seq_id = SATA_3_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_6_GBPS) + seq_id = SATA_6_SPEED_CONFIG_SEQ; + break; + case SGMII0: + case SGMII1: + case SGMII2: +#ifdef CONFIG_ARMADA_39X + case SGMII3: +#endif + if (baud_rate == SERDES_SPEED_1_25_GBPS) + seq_id = SGMII_1_25_SPEED_CONFIG_SEQ; + else if (baud_rate == SERDES_SPEED_3_125_GBPS) + seq_id = SGMII_3_125_SPEED_CONFIG_SEQ; + break; + case QSGMII: + seq_id = QSGMII_5_SPEED_CONFIG_SEQ; + break; +#ifdef CONFIG_ARMADA_39X + case XAUI: + seq_id = XAUI_3_125_SPEED_CONFIG_SEQ; + break; + case RXAUI: + seq_id = RXAUI_6_25_SPEED_CONFIG_SEQ; + break; +#endif + default: + return SERDES_LAST_SEQ; + } + + return seq_id; +} + +/* + * This is the weak default function for the Marvell evaluation or + * development boarrds. Like the DB-88F6820-GP and others. + * Custom boards should define this function in their board + * code (board directory). And overwrite this default function + * with this custom specific code. + */ +__weak int hws_board_topology_load(struct serdes_map *serdes_map_array) +{ + u32 board_id = mv_board_id_get(); + u32 board_id_index = mv_board_id_index_get(board_id); + + DEBUG_INIT_FULL_S("\n### hws_board_topology_load ###\n"); + /* getting board topology according to the board id */ + DEBUG_INIT_FULL_S("Getting board topology according to the board id\n"); + + CHECK_STATUS(load_topology_func_arr[board_id_index] (serdes_map_array)); + + return MV_OK; +} + +void print_topology_details(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + + DEBUG_INIT_S("board SerDes lanes topology details:\n"); + + DEBUG_INIT_S(" | Lane # | Speed | Type |\n"); + DEBUG_INIT_S(" --------------------------------\n"); + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + if (serdes_map_array[lane_num].serdes_type == DEFAULT_SERDES) + continue; + DEBUG_INIT_S(" | "); + DEBUG_INIT_D(hws_get_physical_serdes_num(lane_num), 1); + DEBUG_INIT_S(" | "); + DEBUG_INIT_D(serdes_map_array[lane_num].serdes_speed, 2); + DEBUG_INIT_S(" | "); + DEBUG_INIT_S((char *) + serdes_type_to_string[serdes_map_array[lane_num]. + serdes_type]); + DEBUG_INIT_S("\t|\n"); + } + DEBUG_INIT_S(" --------------------------------\n"); +} + +int hws_pre_serdes_init_config(void) +{ + u32 data; + + /* + * Configure Core PLL + */ + /* + * set PLL parameters + * bits[2:0] =0x3 (Core-PLL Kdiv) + * bits[20:12]=0x9f (Core-PLL Ndiv) + * bits[24:21]=0x7(Core-PLL VCO Band) + * bits[28:25]=0x1(Core-PLL Rlf) + * bits[31:29]=0x2(Core-PLL charge-pump adjust) + */ + reg_write(CORE_PLL_PARAMETERS_REG, 0x42e9f003); + + /* Enable PLL Configuration */ + data = reg_read(CORE_PLL_CONFIG_REG); + data = SET_BIT(data, 9); + reg_write(CORE_PLL_CONFIG_REG, data); + + return MV_OK; +} + +int serdes_phy_config(void) +{ + DEBUG_INIT_FULL_S("\n### ctrl_high_speed_serdes_phy_config ###\n"); + + DEBUG_INIT_S("High speed PHY - Version: "); + DEBUG_INIT_S(SERDES_VERION); + DEBUG_INIT_S("\n"); + + /* Init serdes sequences DB */ + if (hws_serdes_seq_init() != MV_OK) { + printf("hws_ctrl_high_speed_serdes_phy_config: Error: Serdes initialization fail\n"); + return MV_FAIL; + } + + /* I2C init */ + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + /* Board topology load */ + DEBUG_INIT_FULL_S + ("ctrl_high_speed_serdes_phy_config: Loading board topology..\n"); + CHECK_STATUS(hws_board_topology_load(serdes_configuration_map)); + + /* print topology */ + print_topology_details(serdes_configuration_map); + CHECK_STATUS(hws_pre_serdes_init_config()); + + /* Power-Up sequence */ + DEBUG_INIT_FULL_S + ("ctrl_high_speed_serdes_phy_config: Starting serdes power up sequence\n"); + + CHECK_STATUS(hws_power_up_serdes_lanes(serdes_configuration_map)); + + DEBUG_INIT_FULL_S + ("\n### ctrl_high_speed_serdes_phy_config ended successfully ###\n"); + + DEBUG_INIT_S(ENDED_OK); + + return MV_OK; +} + +int serdes_polarity_config(u32 serdes_num, int is_rx) +{ + u32 data; + u32 reg_addr; + u8 bit_off = (is_rx) ? 11 : 10; + + reg_addr = SERDES_REGS_LANE_BASE_OFFSET(serdes_num) + SYNC_PATTERN_REG; + data = reg_read(reg_addr); + data = SET_BIT(data, bit_off); + reg_write(reg_addr, data); + + return MV_OK; +} + +int hws_power_up_serdes_lanes(struct serdes_map *serdes_config_map) +{ + u32 serdes_id, serdes_lane_num; + enum ref_clock ref_clock; + enum serdes_type serdes_type; + enum serdes_speed serdes_speed; + enum serdes_mode serdes_mode; + int serdes_rx_polarity_swap; + int serdes_tx_polarity_swap; + int is_pex_enabled = 0; + + /* + * is_pex_enabled: + * Flag which indicates that one of the Serdes is of PEX. + * In this case, PEX unit will be initialized after Serdes power-up + */ + + DEBUG_INIT_FULL_S("\n### hws_power_up_serdes_lanes ###\n"); + + /* COMMON PHYS SELECTORS register configuration */ + DEBUG_INIT_FULL_S + ("hws_power_up_serdes_lanes: Updating COMMON PHYS SELECTORS reg\n"); + CHECK_STATUS(hws_update_serdes_phy_selectors(serdes_configuration_map)); + + /* per Serdes Power Up */ + for (serdes_id = 0; serdes_id < hws_serdes_get_max_lane(); + serdes_id++) { + DEBUG_INIT_FULL_S + ("calling serdes_power_up_ctrl: serdes lane number "); + DEBUG_INIT_FULL_D_10(serdes_lane_num, 1); + DEBUG_INIT_FULL_S("\n"); + + serdes_lane_num = hws_get_physical_serdes_num(serdes_id); + serdes_type = serdes_config_map[serdes_id].serdes_type; + serdes_speed = serdes_config_map[serdes_id].serdes_speed; + serdes_mode = serdes_config_map[serdes_id].serdes_mode; + serdes_rx_polarity_swap = serdes_config_map[serdes_id].swap_rx; + serdes_tx_polarity_swap = serdes_config_map[serdes_id].swap_tx; + + /* serdes lane is not in use */ + if (serdes_type == DEFAULT_SERDES) + continue; + else if (serdes_type <= PEX3) /* PEX type */ + is_pex_enabled = 1; + + ref_clock = hws_serdes_get_ref_clock_val(serdes_type); + if (ref_clock == REF_CLOCK_UNSUPPORTED) { + DEBUG_INIT_S + ("hws_power_up_serdes_lanes: unsupported ref clock\n"); + return MV_NOT_SUPPORTED; + } + CHECK_STATUS(serdes_power_up_ctrl(serdes_lane_num, + 1, + serdes_type, + serdes_speed, + serdes_mode, ref_clock)); + + /* RX Polarity config */ + if (serdes_rx_polarity_swap) + CHECK_STATUS(serdes_polarity_config + (serdes_lane_num, 1)); + + /* TX Polarity config */ + if (serdes_tx_polarity_swap) + CHECK_STATUS(serdes_polarity_config + (serdes_lane_num, 0)); + } + + if (is_pex_enabled) { + /* Set PEX_TX_CONFIG_SEQ sequence for PEXx4 mode. + After finish the Power_up sequence for all lanes, + the lanes should be released from reset state. */ + CHECK_STATUS(hws_pex_tx_config_seq(serdes_config_map)); + + /* PEX configuration */ + CHECK_STATUS(hws_pex_config(serdes_config_map)); + } + + /* USB2 configuration */ + DEBUG_INIT_FULL_S("hws_power_up_serdes_lanes: init USB2 Phys\n"); + CHECK_STATUS(mv_seq_exec(0 /* not relevant */ , USB2_POWER_UP_SEQ)); + + DEBUG_INIT_FULL_S + ("### hws_power_up_serdes_lanes ended successfully ###\n"); + + return MV_OK; +} + +int ctrl_high_speed_serdes_phy_config(void) +{ + return hws_ctrl_high_speed_serdes_phy_config(); +} + +static int serdes_pex_usb3_pipe_delay_w_a(u32 serdes_num, u8 serdes_type) +{ + u32 reg_data; + + /* WA for A380 Z1 relevant for lanes 3,4,5 only */ + if (serdes_num >= 3) { + reg_data = reg_read(GENERAL_PURPOSE_RESERVED0_REG); + /* set delay on pipe - + * When lane 3 is connected to a MAC of Pex -> set bit 7 to 1. + * When lane 3 is connected to a MAC of USB3 -> set bit 7 to 0. + * When lane 4 is connected to a MAC of Pex -> set bit 8 to 1. + * When lane 4 is connected to a MAC of USB3 -> set bit 8 to 0. + * When lane 5 is connected to a MAC of Pex -> set bit 8 to 1. + * When lane 5 is connected to a MAC of USB3 -> set bit 8 to 0. + */ + if (serdes_type == PEX) + reg_data |= 1 << (7 + (serdes_num - 3)); + if (serdes_type == USB3) { + /* USB3 */ + reg_data &= ~(1 << (7 + (serdes_num - 3))); + } + reg_write(GENERAL_PURPOSE_RESERVED0_REG, reg_data); + } + + return MV_OK; +} + +/* + * hws_serdes_pex_ref_clock_satr_get - + * + * DESCRIPTION: Get the reference clock value from DEVICE_SAMPLE_AT_RESET1_REG + * and check: + * bit[2] for PEX#0, bit[3] for PEX#1, bit[30] for PEX#2, bit[31] + * for PEX#3. + * If bit=0 --> REF_CLOCK_100MHz + * If bit=1 && DEVICE_SAMPLE_AT_RESET2_REG bit[0]=0 + * --> REF_CLOCK_25MHz + * If bit=1 && DEVICE_SAMPLE_AT_RESET2_REG bit[0]=1 + * --> REF_CLOCK_40MHz + * + * INPUT: serdes_type - Type of Serdes + * + * OUTPUT: pex_satr - Return the REF_CLOCK value: + * REF_CLOCK_25MHz, REF_CLOCK_40MHz or REF_CLOCK_100MHz + * + * RETURNS: MV_OK - for success + * MV_BAD_PARAM - for fail + */ +int hws_serdes_pex_ref_clock_satr_get(enum serdes_type serdes_type, u32 *pex_satr) +{ + u32 data, reg_satr1; + + reg_satr1 = reg_read(DEVICE_SAMPLE_AT_RESET1_REG); + + switch (serdes_type) { + case PEX0: + data = REF_CLK_SELECTOR_VAL_PEX0(reg_satr1); + break; + case PEX1: + data = REF_CLK_SELECTOR_VAL_PEX1(reg_satr1); + break; + case PEX2: + data = REF_CLK_SELECTOR_VAL_PEX2(reg_satr1); + break; + case PEX3: + data = REF_CLK_SELECTOR_VAL_PEX3(reg_satr1); + break; + default: + printf("%s: Error: SerDes type %d is not supported\n", + __func__, serdes_type); + return MV_BAD_PARAM; + } + + *pex_satr = data; + + return MV_OK; +} + +u32 hws_serdes_get_ref_clock_val(enum serdes_type serdes_type) +{ + u32 pex_satr; + enum ref_clock ref_clock; + + DEBUG_INIT_FULL_S("\n### hws_serdes_get_ref_clock_val ###\n"); + + if (serdes_type >= LAST_SERDES_TYPE) + return REF_CLOCK_UNSUPPORTED; + + /* read ref clock from S@R */ + ref_clock = hws_serdes_silicon_ref_clock_get(); + + if (serdes_type > PEX3) { + /* for all Serdes types but PCIe */ + return ref_clock; + } + + /* for PCIe, need also to check PCIe S@R */ + CHECK_STATUS(hws_serdes_pex_ref_clock_satr_get + (serdes_type, &pex_satr)); + + if (pex_satr == 0) { + return REF_CLOCK_100MHZ; + } else if (pex_satr == 1) { + /* value of 1 means we can use ref clock from SoC (as other Serdes types) */ + return ref_clock; + } else { + printf + ("%s: Error: REF_CLK_SELECTOR_VAL for SerDes type %d is wrong\n", + __func__, serdes_type); + return REF_CLOCK_UNSUPPORTED; + } +} + +int serdes_power_up_ctrl(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, enum ref_clock ref_clock) +{ + u32 sata_idx, pex_idx, sata_port; + enum serdes_seq speed_seq_id; + u32 reg_data; + int is_pex_by1; + + DEBUG_INIT_FULL_S("\n### serdes_power_up_ctrl ###\n"); + + if (serdes_power_up == 1) { /* Serdes power up */ + DEBUG_INIT_FULL_S + ("serdes_power_up_ctrl: executing power up.. "); + DEBUG_INIT_FULL_C("serdes num = ", serdes_num, 2); + DEBUG_INIT_FULL_C("serdes type = ", serdes_type, 2); + + DEBUG_INIT_FULL_S("Going access 1"); + + /* Getting the Speed Select sequence id */ + speed_seq_id = + serdes_type_and_speed_to_speed_seq(serdes_type, + baud_rate); + if (speed_seq_id == SERDES_LAST_SEQ) { + printf + ("serdes_power_up_ctrl: serdes type %d and speed %d are not supported together\n", + serdes_type, baud_rate); + + return MV_BAD_PARAM; + } + + /* Executing power up, ref clock set, speed config and TX config */ + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + CHECK_STATUS(serdes_pex_usb3_pipe_delay_w_a + (serdes_num, PEX)); + } + + is_pex_by1 = (serdes_mode == PEX_ROOT_COMPLEX_X1) || + (serdes_mode == PEX_END_POINT_X1); + pex_idx = serdes_type - PEX0; + + if ((is_pex_by1 == 1) || (serdes_type == PEX0)) { + /* For PEX by 4, init only the PEX 0 */ + reg_data = reg_read(SOC_CONTROL_REG1); + if (is_pex_by1 == 1) + reg_data |= 0x4000; + else + reg_data &= ~0x4000; + reg_write(SOC_CONTROL_REG1, reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x6c)); + reg_data &= ~0x3f0; + if (is_pex_by1 == 1) + reg_data |= 0x10; + else + reg_data |= 0x40; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x6c), + reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x6c)); + reg_data &= ~0xf; + reg_data |= 0x2; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x6c), + reg_data); + + reg_data = + reg_read(((PEX_IF_REGS_BASE(pex_idx)) + + 0x70)); + reg_data &= ~0x40; + reg_data |= 0x40; + reg_write(((PEX_IF_REGS_BASE(pex_idx)) + 0x70), + reg_data); + } + + CHECK_STATUS(mv_seq_exec(serdes_num, PEX_POWER_UP_SEQ)); + if (is_pex_by1 == 0) { + /* + * for PEX by 4 - use the PEX index as the + * seq array index + */ + serdes_seq_db[PEX_BY_4_CONFIG_SEQ]. + data_arr_idx = pex_idx; + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_BY_4_CONFIG_SEQ)); + } + + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_ELECTRICAL_CONFIG_SEQ)); + + if (is_pex_by1 == 1) { + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ2)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ3)); + CHECK_STATUS(mv_seq_exec + (serdes_num, PEX_TX_CONFIG_SEQ1)); + } + udelay(20); + + break; + case USB3_HOST0: + case USB3_HOST1: + case USB3_DEVICE: + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) { + CHECK_STATUS(serdes_pex_usb3_pipe_delay_w_a + (serdes_num, USB3)); + } + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + if (serdes_type == USB3_DEVICE) { + CHECK_STATUS(mv_seq_exec + (serdes_num, + USB3_DEVICE_CONFIG_SEQ)); + } + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ2)); + CHECK_STATUS(mv_seq_exec + (serdes_num, USB3_TX_CONFIG_SEQ3)); + + udelay(10000); + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + sata_idx = ((serdes_type == SATA0) || + (serdes_type == SATA1)) ? 0 : 1; + sata_port = ((serdes_type == SATA0) || + (serdes_type == SATA2)) ? 0 : 1; + + CHECK_STATUS(mv_seq_exec + (sata_idx, (sata_port == 0) ? + SATA_PORT_0_ONLY_POWER_UP_SEQ : + SATA_PORT_1_ONLY_POWER_UP_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (sata_idx, (sata_port == 0) ? + SATA_PORT_0_ONLY_TX_CONFIG_SEQ : + SATA_PORT_1_ONLY_TX_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SATA_TX_CONFIG_SEQ2)); + + udelay(10000); + break; + case SGMII0: + case SGMII1: + case SGMII2: + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, SGMII_TX_CONFIG_SEQ2)); + + /* GBE configuration */ + reg_data = reg_read(GBE_CONFIGURATION_REG); + /* write the SGMII index */ + reg_data |= 0x1 << (serdes_type - SGMII0); + reg_write(GBE_CONFIGURATION_REG, reg_data); + + break; + case QSGMII: + if (hws_ctrl_serdes_rev_get() < MV_SERDES_REV_2_1) + return MV_NOT_SUPPORTED; + + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_POWER_UP_SEQ)); + CHECK_STATUS(hws_ref_clock_set + (serdes_num, serdes_type, ref_clock)); + CHECK_STATUS(mv_seq_exec(serdes_num, speed_seq_id)); + CHECK_STATUS(mv_seq_exec + (serdes_num, + QSGMII_ELECTRICAL_CONFIG_SEQ)); + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_TX_CONFIG_SEQ1)); + CHECK_STATUS(mv_seq_exec + (serdes_num, QSGMII_TX_CONFIG_SEQ2)); + break; + case SGMII3: + case XAUI: + case RXAUI: + CHECK_STATUS(serdes_power_up_ctrl_ext + (serdes_num, serdes_power_up, serdes_type, + baud_rate, serdes_mode, ref_clock)); + break; + default: + DEBUG_INIT_S + ("serdes_power_up_ctrl: bad serdes_type parameter\n"); + return MV_BAD_PARAM; + } + } else { /* Serdes power down */ + DEBUG_INIT_FULL_S("serdes_power_up: executing power down.. "); + DEBUG_INIT_FULL_C("serdes num = ", serdes_num, 1); + + CHECK_STATUS(mv_seq_exec(serdes_num, SERDES_POWER_DOWN_SEQ)); + } + + DEBUG_INIT_FULL_C( + "serdes_power_up_ctrl ended successfully for serdes ", + serdes_num, 2); + + return MV_OK; +} + +int hws_update_serdes_phy_selectors(struct serdes_map *serdes_config_map) +{ + u32 lane_data, idx, serdes_lane_hw_num, reg_data = 0; + enum serdes_type serdes_type; + enum serdes_mode serdes_mode; + u8 select_bit_off; + int is_pex_x4 = 0; + int updated_topology_print = 0; + + DEBUG_INIT_FULL_S("\n### hws_update_serdes_phy_selectors ###\n"); + DEBUG_INIT_FULL_S + ("Updating the COMMON PHYS SELECTORS register with the serdes types\n"); + + if (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2) + select_bit_off = 3; + else + select_bit_off = 4; + + /* + * Updating bits 0-17 in the COMMON PHYS SELECTORS register + * according to the serdes types + */ + for (idx = 0; idx < hws_serdes_get_max_lane(); + idx++) { + serdes_type = serdes_config_map[idx].serdes_type; + serdes_mode = serdes_config_map[idx].serdes_mode; + serdes_lane_hw_num = hws_get_physical_serdes_num(idx); + + lane_data = + hws_serdes_get_phy_selector_val(serdes_lane_hw_num, + serdes_type); + + if (serdes_type == DEFAULT_SERDES) + continue; + + if (hws_serdes_topology_verify + (serdes_type, idx, serdes_mode) != MV_OK) { + serdes_config_map[idx].serdes_type = + DEFAULT_SERDES; + printf("%s: SerDes lane #%d is disabled\n", __func__, + serdes_lane_hw_num); + updated_topology_print = 1; + continue; + } + + /* + * Checking if the board topology configuration includes + * PEXx4 - for the next step + */ + if ((serdes_mode == PEX_END_POINT_X4) || + (serdes_mode == PEX_ROOT_COMPLEX_X4)) { + /* update lane data to the 3 next SERDES lanes */ + lane_data = + common_phys_selectors_pex_by4_lanes + [serdes_lane_hw_num]; + if (serdes_type == PEX0) + is_pex_x4 = 1; + } + + if (lane_data == NA) { + printf + ("%s: Warning: SerDes lane #%d and type %d are not supported together\n", + __func__, serdes_lane_hw_num, serdes_mode); + serdes_config_map[idx].serdes_type = + DEFAULT_SERDES; + printf("%s: SerDes lane #%d is disabled\n", __func__, + serdes_lane_hw_num); + continue; + } + + /* + * Updating the data that will be written to + * COMMON_PHYS_SELECTORS_REG + */ + reg_data |= (lane_data << + (select_bit_off * serdes_lane_hw_num)); + } + + /* + * Check that number of used lanes for XAUI and RXAUI + * (if used) is right + */ + hws_serdes_xaui_topology_verify(); + + /* Print topology */ + if (updated_topology_print) + print_topology_details(serdes_config_map); + + /* + * Updating the PEXx4 Enable bit in the COMMON PHYS SELECTORS + * register for PEXx4 mode + */ + reg_data |= (is_pex_x4 == 1) ? (0x1 << PEX_X4_ENABLE_OFFS) : 0; + + /* Updating the COMMON PHYS SELECTORS register */ + reg_write(COMMON_PHYS_SELECTORS_REG, reg_data); + + return MV_OK; +} + +int hws_ref_clock_set(u32 serdes_num, enum serdes_type serdes_type, + enum ref_clock ref_clock) +{ + u32 data1 = 0, data2 = 0, data3 = 0, reg_data; + + DEBUG_INIT_FULL_S("\n### hws_ref_clock_set ###\n"); + + if (hws_is_serdes_active(serdes_num) != 1) { + printf("%s: SerDes lane #%d is not Active\n", __func__, + serdes_num); + return MV_BAD_PARAM; + } + + switch (serdes_type) { + case PEX0: + case PEX1: + case PEX2: + case PEX3: + switch (ref_clock) { + case REF_CLOCK_25MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_25MHZ_SEQ)); + return MV_OK; + case REF_CLOCK_100MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_100MHZ_SEQ)); + return MV_OK; +#ifdef CONFIG_ARMADA_39X + case REF_CLOCK_40MHZ: + CHECK_STATUS(mv_seq_exec + (serdes_num, + PEX_CONFIG_REF_CLOCK_40MHZ_SEQ)); + return MV_OK; +#endif + default: + printf + ("%s: Error: ref_clock %d for SerDes lane #%d, type %d is not supported\n", + __func__, ref_clock, serdes_num, serdes_type); + return MV_BAD_PARAM; + } + case USB3_HOST0: + case USB3_HOST1: + case USB3_DEVICE: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_2; + data2 = GLOBAL_PM_CTRL_REG_25MHZ_VAL; + data3 = LANE_CFG4_REG_25MHZ_VAL; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + data2 = GLOBAL_PM_CTRL_REG_40MHZ_VAL; + data3 = LANE_CFG4_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; + case SATA0: + case SATA1: + case SATA2: + case SATA3: + case SGMII0: + case SGMII1: + case SGMII2: + case QSGMII: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; +#ifdef CONFIG_ARMADA_39X + case SGMII3: + case XAUI: + case RXAUI: + if (ref_clock == REF_CLOCK_25MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1; + } else if (ref_clock == REF_CLOCK_40MHZ) { + data1 = POWER_AND_PLL_CTRL_REG_40MHZ_VAL; + } else { + printf + ("hws_ref_clock_set: ref clock is not valid for serdes type %d\n", + serdes_type); + return MV_BAD_PARAM; + } + break; +#endif + default: + DEBUG_INIT_S("hws_ref_clock_set: not supported serdes type\n"); + return MV_BAD_PARAM; + } + + /* + * Write the ref_clock to relevant SELECT_REF_CLOCK_REG bits and + * offset + */ + reg_data = reg_read(POWER_AND_PLL_CTRL_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= POWER_AND_PLL_CTRL_REG_MASK; + reg_data |= data1; + reg_write(POWER_AND_PLL_CTRL_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + + if ((serdes_type == USB3_HOST0) || (serdes_type == USB3_HOST1) || + (serdes_type == USB3_DEVICE)) { + reg_data = reg_read(GLOBAL_PM_CTRL + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= GLOBAL_PM_CTRL_REG_MASK; + reg_data |= data2; + reg_write(GLOBAL_PM_CTRL + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + + reg_data = reg_read(LANE_CFG4_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num)); + reg_data &= LANE_CFG4_REG_MASK; + reg_data |= data3; + reg_write(LANE_CFG4_REG + + SERDES_REGS_LANE_BASE_OFFSET(serdes_num), reg_data); + } + + return MV_OK; +} + +/* + * hws_pex_tx_config_seq - + * + * DESCRIPTION: Set PEX_TX_CONFIG_SEQ sequence init for PEXx4 mode + * INPUT: serdes_map - The board topology map + * OUTPUT: None + * RETURNS: MV_OK - for success + * MV_BAD_PARAM - for fail + */ +int hws_pex_tx_config_seq(struct serdes_map *serdes_map) +{ + enum serdes_mode serdes_mode; + u32 serdes_lane_id, serdes_lane_hw_num; + + DEBUG_INIT_FULL_S("\n### hws_pex_tx_config_seq ###\n"); + + /* + * For PEXx4: the pex_and_usb3_tx_config_params1/2/3 + * configurations should run by setting each sequence for + * all 4 lanes. + */ + + /* relese pipe soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ1)); + } + } + + /* set phy soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ2)); + } + } + + /* set phy soft reset for all lanes */ + for (serdes_lane_id = 0; serdes_lane_id < hws_serdes_get_max_lane(); + serdes_lane_id++) { + serdes_mode = serdes_map[serdes_lane_id].serdes_mode; + serdes_lane_hw_num = + hws_get_physical_serdes_num(serdes_lane_id); + if ((serdes_mode == PEX_ROOT_COMPLEX_X4) || + (serdes_mode == PEX_END_POINT_X4)) { + CHECK_STATUS(mv_seq_exec + (serdes_lane_hw_num, PEX_TX_CONFIG_SEQ3)); + } + } + + return MV_OK; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h new file mode 100644 index 0000000..2508721 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _HIGH_SPEED_ENV_SPEC_H +#define _HIGH_SPEED_ENV_SPEC_H + +#include "seq_exec.h" + +/* + * For setting or clearing a certain bit (bit is a number between 0 and 31) + * in the data + */ +#define SET_BIT(data, bit) ((data) | (0x1 << (bit))) +#define CLEAR_BIT(data, bit) ((data) & (~(0x1 << (bit)))) + +#define MAX_SERDES_LANES 7 /* as in a39x */ + +/* Serdes revision */ +/* Serdes revision 1.2 (for A38x-Z1) */ +#define MV_SERDES_REV_1_2 0x0 +/* Serdes revision 2.1 (for A39x-Z1, A38x-A0) */ +#define MV_SERDES_REV_2_1 0x1 +#define MV_SERDES_REV_NA 0xff + +#define SERDES_REGS_LANE_BASE_OFFSET(lane) (0x800 * (lane)) + +#define PEX_X4_ENABLE_OFFS \ + (hws_ctrl_serdes_rev_get() == MV_SERDES_REV_1_2 ? 18 : 31) + +/* Serdes lane types */ +enum serdes_type { + PEX0, + PEX1, + PEX2, + PEX3, + SATA0, + SATA1, + SATA2, + SATA3, + SGMII0, + SGMII1, + SGMII2, + QSGMII, + USB3_HOST0, + USB3_HOST1, + USB3_DEVICE, + SGMII3, + XAUI, + RXAUI, + DEFAULT_SERDES, + LAST_SERDES_TYPE +}; + +/* Serdes baud rates */ +enum serdes_speed { + SERDES_SPEED_1_25_GBPS, + SERDES_SPEED_1_5_GBPS, + SERDES_SPEED_2_5_GBPS, + SERDES_SPEED_3_GBPS, + SERDES_SPEED_3_125_GBPS, + SERDES_SPEED_5_GBPS, + SERDES_SPEED_6_GBPS, + SERDES_SPEED_6_25_GBPS, + LAST_SERDES_SPEED +}; + +/* Serdes modes */ +enum serdes_mode { + PEX_ROOT_COMPLEX_X1, + PEX_ROOT_COMPLEX_X4, + PEX_END_POINT_X1, + PEX_END_POINT_X4, + + SERDES_DEFAULT_MODE, /* not pex */ + + SERDES_LAST_MODE +}; + +struct serdes_map { + enum serdes_type serdes_type; + enum serdes_speed serdes_speed; + enum serdes_mode serdes_mode; + int swap_rx; + int swap_tx; +}; + +/* Serdes ref clock options */ +enum ref_clock { + REF_CLOCK_25MHZ, + REF_CLOCK_100MHZ, + REF_CLOCK_40MHZ, + REF_CLOCK_UNSUPPORTED +}; + +/* Serdes sequences */ +enum serdes_seq { + SATA_PORT_0_ONLY_POWER_UP_SEQ, + SATA_PORT_1_ONLY_POWER_UP_SEQ, + SATA_POWER_UP_SEQ, + SATA_1_5_SPEED_CONFIG_SEQ, + SATA_3_SPEED_CONFIG_SEQ, + SATA_6_SPEED_CONFIG_SEQ, + SATA_ELECTRICAL_CONFIG_SEQ, + SATA_TX_CONFIG_SEQ1, + SATA_PORT_0_ONLY_TX_CONFIG_SEQ, + SATA_PORT_1_ONLY_TX_CONFIG_SEQ, + SATA_TX_CONFIG_SEQ2, + + SGMII_POWER_UP_SEQ, + SGMII_1_25_SPEED_CONFIG_SEQ, + SGMII_3_125_SPEED_CONFIG_SEQ, + SGMII_ELECTRICAL_CONFIG_SEQ, + SGMII_TX_CONFIG_SEQ1, + SGMII_TX_CONFIG_SEQ2, + + PEX_POWER_UP_SEQ, + PEX_2_5_SPEED_CONFIG_SEQ, + PEX_5_SPEED_CONFIG_SEQ, + PEX_ELECTRICAL_CONFIG_SEQ, + PEX_TX_CONFIG_SEQ1, + PEX_TX_CONFIG_SEQ2, + PEX_TX_CONFIG_SEQ3, + PEX_BY_4_CONFIG_SEQ, + PEX_CONFIG_REF_CLOCK_25MHZ_SEQ, + PEX_CONFIG_REF_CLOCK_100MHZ_SEQ, + PEX_CONFIG_REF_CLOCK_40MHZ_SEQ, + + USB3_POWER_UP_SEQ, + USB3_HOST_SPEED_CONFIG_SEQ, + USB3_DEVICE_SPEED_CONFIG_SEQ, + USB3_ELECTRICAL_CONFIG_SEQ, + USB3_TX_CONFIG_SEQ1, + USB3_TX_CONFIG_SEQ2, + USB3_TX_CONFIG_SEQ3, + USB3_DEVICE_CONFIG_SEQ, + + USB2_POWER_UP_SEQ, + + SERDES_POWER_DOWN_SEQ, + + SGMII3_POWER_UP_SEQ, + SGMII3_1_25_SPEED_CONFIG_SEQ, + SGMII3_TX_CONFIG_SEQ1, + SGMII3_TX_CONFIG_SEQ2, + + QSGMII_POWER_UP_SEQ, + QSGMII_5_SPEED_CONFIG_SEQ, + QSGMII_ELECTRICAL_CONFIG_SEQ, + QSGMII_TX_CONFIG_SEQ1, + QSGMII_TX_CONFIG_SEQ2, + + XAUI_POWER_UP_SEQ, + XAUI_3_125_SPEED_CONFIG_SEQ, + XAUI_ELECTRICAL_CONFIG_SEQ, + XAUI_TX_CONFIG_SEQ1, + XAUI_TX_CONFIG_SEQ2, + + RXAUI_POWER_UP_SEQ, + RXAUI_6_25_SPEED_CONFIG_SEQ, + RXAUI_ELECTRICAL_CONFIG_SEQ, + RXAUI_TX_CONFIG_SEQ1, + RXAUI_TX_CONFIG_SEQ2, + + SERDES_LAST_SEQ +}; + +/* The different sequence types for PEX and USB3 */ +enum { + PEX, + USB3, + LAST_PEX_USB_SEQ_TYPE +}; + +enum { + PEXSERDES_SPEED_2_5_GBPS, + PEXSERDES_SPEED_5_GBPS, + USB3SERDES_SPEED_5_GBPS_HOST, + USB3SERDES_SPEED_5_GBPS_DEVICE, + LAST_PEX_USB_SPEED_SEQ_TYPE +}; + +/* The different sequence types for SATA and SGMII */ +enum { + SATA, + SGMII, + SGMII_3_125, + LAST_SATA_SGMII_SEQ_TYPE +}; + +enum { + QSGMII_SEQ_IDX, + LAST_QSGMII_SEQ_TYPE +}; + +enum { + XAUI_SEQ_IDX, + RXAUI_SEQ_IDX, + LAST_XAUI_RXAUI_SEQ_TYPE +}; + +enum { + SATASERDES_SPEED_1_5_GBPS, + SATASERDES_SPEED_3_GBPS, + SATASERDES_SPEED_6_GBPS, + SGMIISERDES_SPEED_1_25_GBPS, + SGMIISERDES_SPEED_3_125_GBPS, + LAST_SATA_SGMII_SPEED_SEQ_TYPE +}; + +extern u8 selectors_serdes_rev1_map[LAST_SERDES_TYPE][MAX_SERDES_LANES]; +extern u8 selectors_serdes_rev2_map[LAST_SERDES_TYPE][MAX_SERDES_LANES]; + +u8 hws_ctrl_serdes_rev_get(void); +int mv_update_serdes_select_phy_mode_seq(void); +int hws_board_topology_load(struct serdes_map *serdes_map_array); +enum serdes_seq serdes_type_and_speed_to_speed_seq(enum serdes_type serdes_type, + enum serdes_speed baud_rate); +int hws_serdes_seq_init(void); +int hws_serdes_seq_db_init(void); +int hws_power_up_serdes_lanes(struct serdes_map *serdes_config_map); +int hws_ctrl_high_speed_serdes_phy_config(void); +int serdes_power_up_ctrl(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock); +int serdes_power_up_ctrl_ext(u32 serdes_num, int serdes_power_up, + enum serdes_type serdes_type, + enum serdes_speed baud_rate, + enum serdes_mode serdes_mode, + enum ref_clock ref_clock); +u32 hws_serdes_silicon_ref_clock_get(void); +int hws_serdes_pex_ref_clock_get(enum serdes_type serdes_type, + enum ref_clock *ref_clock); +int hws_ref_clock_set(u32 serdes_num, enum serdes_type serdes_type, + enum ref_clock ref_clock); +int hws_update_serdes_phy_selectors(struct serdes_map *serdes_config_map); +u32 hws_serdes_get_phy_selector_val(int serdes_num, + enum serdes_type serdes_type); +u32 hws_serdes_get_ref_clock_val(enum serdes_type serdes_type); +u32 hws_serdes_get_max_lane(void); +int hws_get_ext_base_addr(u32 serdes_num, u32 base_addr, u32 unit_base_offset, + u32 *unit_base_reg, u32 *unit_offset); +int hws_pex_tx_config_seq(struct serdes_map *serdes_map); +u32 hws_get_physical_serdes_num(u32 serdes_num); +int hws_is_serdes_active(u8 lane_num); + +#endif /* _HIGH_SPEED_ENV_SPEC_H */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c new file mode 100644 index 0000000..5f2c3eb --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec-38x.c @@ -0,0 +1,1009 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "high_speed_topology_spec.h" +#include "sys_env_lib.h" + +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT +/* + * This is an example implementation for this custom board + * specific function + */ +static struct serdes_map custom_board_topology_config[] = { + /* Customer Board Topology - reference from Marvell DB-GP board */ + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +int hws_board_topology_load(struct serdes_map *serdes_map_array) +{ + serdes_map_array = custom_board_topology_config; +} +#endif + +load_topology_func_ptr load_topology_func_arr[] = { + load_topology_rd, /* RD NAS */ + load_topology_db, /* 6820 DB-BP (A38x) */ + load_topology_rd, /* RD AP */ + load_topology_db_ap, /* DB AP */ + load_topology_db_gp, /* DB GP */ + load_topology_db_381, /* 6821 DB-BP (A381) */ + load_topology_db_amc, /* DB-AMC */ +}; + +/*****************************************/ +/** Load topology - Marvell 380 DB - BP **/ +/*****************************************/ +/* Configuration options */ +struct serdes_map db_config_default[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_c[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, +}; + +struct serdes_map db_config_slm1363_d[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_e[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1363_f[MAX_SERDES_LANES] = { + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1364_d[MAX_SERDES_LANES] = { + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1364_e[MAX_SERDES_LANES] = { + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +struct serdes_map db_config_slm1364_f[MAX_SERDES_LANES] = { + {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, 0}, + {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +/*************************************************************************/ +/** The following structs are mapping for DB board 'SatR' configuration **/ +/*************************************************************************/ +struct serdes_map db_satr_config_lane1[SATR_DB_LANE1_MAX_OPTIONS] = { + /* 0 */ {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, + 0}, + /* 1 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 2 */ {SATA0, SERDES_SPEED_3_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII0, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 4 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 6 */ {QSGMII, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_satr_config_lane2[SATR_DB_LANE2_MAX_OPTIONS] = { + /* 0 */ {DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE, 0, + 0}, + /* 1 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 2 */ {SATA1, SERDES_SPEED_3_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0} +}; + +/*******************************************************/ +/* Configuration options DB ****************************/ +/* mapping from TWSI address data to configuration map */ +/*******************************************************/ +struct serdes_map *topology_config_db[] = { + db_config_slm1363_c, + db_config_slm1363_d, + db_config_slm1363_e, + db_config_slm1363_f, + db_config_slm1364_d, + db_config_slm1364_e, + db_config_slm1364_f, + db_config_default +}; + +/*************************************/ +/** Load topology - Marvell DB - AP **/ +/*************************************/ +struct serdes_map db_ap_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 1 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 2 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 3 */ {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 4 */ {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0} +}; + +/*************************************/ +/** Load topology - Marvell DB - GP **/ +/*************************************/ +struct serdes_map db_gp_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + /* 1 */ {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 2 */ {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 3 */ {SATA3, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 4 */ {SATA2, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 0, 0}, + /* 5 */ {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, + 0} +}; + +struct serdes_map db_amc_config_default[MAX_SERDES_LANES] = { + /* 0 */ {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 1 */ {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 2 */ {PEX2, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 3 */ {PEX3, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X4, 0, 0}, + /* 4 */ {SGMII1, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, + /* 5 */ {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 0, + 0}, +}; + +/*****************************************/ +/** Load topology - Marvell 381 DB - BP **/ +/*****************************************/ +/* Configuration options */ +struct serdes_map db381_config_default[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {PEX1, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 0, 0}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 0, 0} +}; + +struct serdes_map db_config_slm1427[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {PEX0, SERDES_SPEED_5_GBPS, PEX_ROOT_COMPLEX_X1, 1, 1}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {USB3_HOST1, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 1, 1} +}; + +struct serdes_map db_config_slm1426[MAX_SERDES_LANES] = { + {SATA0, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {USB3_HOST0, SERDES_SPEED_5_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {SATA1, SERDES_SPEED_3_GBPS, SERDES_DEFAULT_MODE, 1, 1}, + {SGMII2, SERDES_SPEED_3_125_GBPS, SERDES_DEFAULT_MODE, 1, 1} +}; + +/* + * this array must be aligned with enum topology_config_db381 enum, + * every update to this array requires update to enum topology_config_db381 + * enum + */ +struct serdes_map *topology_config_db_381[] = { + db_config_slm1427, + db_config_slm1426, + db381_config_default, +}; + +u8 topology_config_db_mode_get(void) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### topology_config_db_mode_get ###\n"); + + /* Default - return DB_CONFIG_DEFAULT */ + + if (!i2c_read(DB_GET_MODE_SLM1363_ADDR, 0, 1, &mode, 1)) { + switch (mode & 0xf) { + case 0xc: + DEBUG_INIT_S("\nInit DB board SLM 1363 C topology\n"); + return DB_CONFIG_SLM1363_C; + case 0xd: + DEBUG_INIT_S("\nInit DB board SLM 1363 D topology\n"); + return DB_CONFIG_SLM1363_D; + case 0xe: + DEBUG_INIT_S("\nInit DB board SLM 1363 E topology\n"); + return DB_CONFIG_SLM1363_E; + case 0xf: + DEBUG_INIT_S("\nInit DB board SLM 1363 F topology\n"); + return DB_CONFIG_SLM1363_F; + default: /* not the right module */ + break; + } + } + + /* SLM1364 Module */ + if (i2c_read(DB_GET_MODE_SLM1364_ADDR, 0, 1, &mode, 1)) { + DEBUG_INIT_S("\nInit DB board default topology\n"); + return DB_CONFIG_DEFAULT; + } + + switch (mode & 0xf) { + case 0xd: + DEBUG_INIT_S("\nInit DB board SLM 1364 D topology\n"); + return DB_CONFIG_SLM1364_D; + case 0xe: + DEBUG_INIT_S("\nInit DB board SLM 1364 E topology\n"); + return DB_CONFIG_SLM1364_E; + case 0xf: + DEBUG_INIT_S("\nInit DB board SLM 1364 F topology\n"); + return DB_CONFIG_SLM1364_F; + default: /* Default configuration */ + DEBUG_INIT_S("\nInit DB board default topology\n"); + return DB_CONFIG_DEFAULT; + } +} + +u8 topology_config_db_381_mode_get(void) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### topology_config_db_381_mode_get ###\n"); + + if (!i2c_read(DB381_GET_MODE_SLM1426_1427_ADDR, 0, 2, &mode, 1)) { + switch (mode & 0xf) { + case 0x1: + DEBUG_INIT_S("\nInit DB-381 board SLM 1427 topology\n"); + return DB_CONFIG_SLM1427; + case 0x2: + DEBUG_INIT_S("\nInit DB-381 board SLM 1426 topology\n"); + return DB_CONFIG_SLM1426; + default: /* not the right module */ + break; + } + } + + /* in case not detected any supported module, use default topology */ + DEBUG_INIT_S("\nInit DB-381 board default topology\n"); + return DB_381_CONFIG_DEFAULT; +} + +/* + * Read SatR field 'sgmiispeed' and update lane topology SGMII entries + * speed setup + */ +int update_topology_sgmii_speed(struct serdes_map *serdes_map_array) +{ + u32 serdes_type, lane_num; + u8 config_val; + + /* Update SGMII speed settings by 'sgmiispeed' SatR value */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_type = serdes_map_array[lane_num].serdes_type; + /*Read SatR configuration for SGMII speed */ + if ((serdes_type == SGMII0) || (serdes_type == SGMII1) || + (serdes_type == SGMII2)) { + /* Read SatR 'sgmiispeed' value */ + if (i2c_read(EEPROM_I2C_ADDR, 0, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'sgmiispeed' failed\n", + __func__); + return MV_FAIL; + } + + if (0 == (config_val & 0x40)) { + serdes_map_array[lane_num].serdes_speed = + SERDES_SPEED_1_25_GBPS; + } else { + serdes_map_array[lane_num].serdes_speed = + SERDES_SPEED_3_125_GBPS; + } + } + } + return MV_OK; +} + +struct serdes_map default_lane = { + DEFAULT_SERDES, LAST_SERDES_SPEED, SERDES_DEFAULT_MODE +}; +int is_custom_topology = 0; /* indicate user of non-default topology */ + +/* + * Read SatR fields (dbserdes1/2 , gpserdes1/2/5) and update lane + * topology accordingly + */ +int update_topology_satr(struct serdes_map *serdes_map_array) +{ + u8 config_val, lane_select, i; + u32 board_id = mv_board_id_get(); + + switch (board_id) { + case DB_68XX_ID: /* read 'dbserdes1' & 'dbserdes2' */ + case DB_BP_6821_ID: + if (i2c_read(EEPROM_I2C_ADDR, 1, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'dbserdes1/2' failed\n", + __func__); + return MV_FAIL; + } + + /* Lane #1 */ + lane_select = (config_val & SATR_DB_LANE1_CFG_MASK) >> + SATR_DB_LANE1_CFG_OFFSET; + if (lane_select >= SATR_DB_LANE1_MAX_OPTIONS) { + printf("\n\%s: Error: invalid value for SatR field 'dbserdes1' (%x)\n", + __func__, lane_select); + printf("\t_skipping Topology update (run 'SatR write default')\n"); + return MV_FAIL; + } + + /* + * If modified default serdes_type for lane#1, update + * topology and mark it as custom + */ + if (serdes_map_array[1].serdes_type != + db_satr_config_lane1[lane_select].serdes_type) { + serdes_map_array[1] = db_satr_config_lane1[lane_select]; + is_custom_topology = 1; + /* DB 381/2 board has inverted SerDes polarity */ + if (board_id == DB_BP_6821_ID) + serdes_map_array[1].swap_rx = + serdes_map_array[1].swap_tx = 1; + } + + /* Lane #2 */ + lane_select = (config_val & SATR_DB_LANE2_CFG_MASK) >> + SATR_DB_LANE2_CFG_OFFSET; + if (lane_select >= SATR_DB_LANE2_MAX_OPTIONS) { + printf("\n\%s: Error: invalid value for SatR field 'dbserdes2' (%x)\n", + __func__, lane_select); + printf("\t_skipping Topology update (run 'SatR write default')\n"); + return MV_FAIL; + } + + /* + * If modified default serdes_type for lane@2, update + * topology and mark it as custom + */ + if (serdes_map_array[2].serdes_type != + db_satr_config_lane2[lane_select].serdes_type) { + serdes_map_array[2] = db_satr_config_lane2[lane_select]; + is_custom_topology = 1; + /* DB 381/2 board has inverted SerDes polarity */ + if (board_id == DB_BP_6821_ID) + serdes_map_array[2].swap_rx = + serdes_map_array[2].swap_tx = 1; + } + + if (is_custom_topology == 1) { + /* + * Check for conflicts with detected lane #1 and + * lane #2 (Disable conflicted lanes) + */ + for (i = 0; i < hws_serdes_get_max_lane(); i++) { + if (i != 1 && serdes_map_array[1].serdes_type == + serdes_map_array[i].serdes_type) { + printf("\t_lane #%d Type conflicts with Lane #1 (Lane #%d disabled)\n", + i, i); + serdes_map_array[i] = + db_satr_config_lane1[0]; + } + + if (i != 2 && + serdes_map_array[2].serdes_type == + serdes_map_array[i].serdes_type) { + printf("\t_lane #%d Type conflicts with Lane #2 (Lane #%d disabled)\n", + i, i); + serdes_map_array[i] = + db_satr_config_lane1[0]; + } + } + } + + break; /* case DB_68XX_ID */ + case DB_GP_68XX_ID: /* read 'gpserdes1' & 'gpserdes2' */ + if (i2c_read(EEPROM_I2C_ADDR, 2, 2, &config_val, 1)) { + printf("%s: TWSI Read of 'gpserdes1/2' failed\n", + __func__); + return MV_FAIL; + } + + /* + * Lane #1: + * lane_select = 0 --> SATA0, + * lane_select = 1 --> PCIe0 (mini PCIe) + */ + lane_select = (config_val & SATR_GP_LANE1_CFG_MASK) >> + SATR_GP_LANE1_CFG_OFFSET; + if (lane_select == 1) { + serdes_map_array[1].serdes_mode = PEX0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[1].serdes_type = PEX_ROOT_COMPLEX_X1; + /* + * If lane 1 is set to PCIe0 --> disable PCIe0 + * on lane 0 + */ + serdes_map_array[0] = default_lane; + /* indicate user of non-default topology */ + is_custom_topology = 1; + } + printf("Lane 1 detection: %s\n", + lane_select ? "PCIe0 (mini PCIe)" : "SATA0"); + + /* + * Lane #2: + * lane_select = 0 --> SATA1, + * lane_select = 1 --> PCIe1 (mini PCIe) + */ + lane_select = (config_val & SATR_GP_LANE2_CFG_MASK) >> + SATR_GP_LANE2_CFG_OFFSET; + if (lane_select == 1) { + serdes_map_array[2].serdes_type = PEX1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[2].serdes_mode = PEX_ROOT_COMPLEX_X1; + /* indicate user of non-default topology */ + is_custom_topology = 1; + } + printf("Lane 2 detection: %s\n", + lane_select ? "PCIe1 (mini PCIe)" : "SATA1"); + break; /* case DB_GP_68XX_ID */ + } + + if (is_custom_topology) + printf("\nDetected custom SerDes topology (to restore default run 'SatR write default')\n\n"); + + return MV_OK; +} + +/* + * hws_update_device_toplogy + * DESCRIPTION: Update the default board topology for specific device Id + * INPUT: + * topology_config_ptr - pointer to the Serdes mapping + * topology_mode - topology mode (index) + * OUTPUT: None + * RRETURNS: + * MV_OK - if updating the board topology success + * MV_BAD_PARAM - if the input parameter is wrong + */ +int hws_update_device_toplogy(struct serdes_map *topology_config_ptr, + enum topology_config_db topology_mode) +{ + u32 dev_id = sys_env_device_id_get(); + u32 board_id = mv_board_id_get(); + + switch (topology_mode) { + case DB_CONFIG_DEFAULT: + switch (dev_id) { + case MV_6810: + /* + * DB-AP : default for Lane3=SGMII2 --> + * 6810 supports only 2 SGMII interfaces: + * lane 3 disabled + */ + if (board_id == DB_AP_68XX_ID) { + printf("Device 6810 supports only 2 SGMII interfaces: SGMII-2 @ lane3 disabled\n"); + topology_config_ptr[3] = default_lane; + } + + /* + * 6810 has only 4 SerDes and the forth one is + * Serdes number 5 (i.e. Serdes 4 is not connected), + * therefore we need to copy SerDes 5 configuration + * to SerDes 4 + */ + printf("Device 6810 does not supports SerDes Lane #4: replaced topology entry with lane #5\n"); + topology_config_ptr[4] = topology_config_ptr[5]; + + /* + * No break between cases since the 1st + * 6820 limitation apply on 6810 + */ + case MV_6820: + /* + * DB-GP & DB-BP: default for Lane3=SATA3 --> + * 6810/20 supports only 2 SATA interfaces: + * lane 3 disabled + */ + if ((board_id == DB_68XX_ID) || + (board_id == DB_GP_68XX_ID)) { + printf("Device 6810/20 supports only 2 SATA interfaces: SATA Port 3 @ lane3 disabled\n"); + topology_config_ptr[3] = default_lane; + } + /* + * DB-GP on 6820 only: default for Lane4=SATA2 + * --> 6820 supports only 2 SATA interfaces: + * lane 3 disabled + */ + if (board_id == DB_GP_68XX_ID && dev_id == MV_6820) { + printf("Device 6820 supports only 2 SATA interfaces: SATA Port 2 @ lane4 disabled\n"); + topology_config_ptr[4] = default_lane; + } + break; + default: + break; + } + break; + + default: + printf("sys_env_update_device_toplogy: selected topology is not supported by this routine\n"); + break; + } + + return MV_OK; +} + +int load_topology_db_381(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + u8 topology_mode; + struct serdes_map *topology_config_ptr; + u8 twsi_data; + u8 usb3_host0_or_device = 0, usb3_host1_or_device = 0; + + printf("\nInitialize DB-88F6821-BP board topology\n"); + + /* Getting the relevant topology mode (index) */ + topology_mode = topology_config_db_381_mode_get(); + topology_config_ptr = topology_config_db_381[topology_mode]; + + /* Read USB3.0 mode: HOST/DEVICE */ + if (load_topology_usb_mode_get(&twsi_data) == MV_OK) { + usb3_host0_or_device = (twsi_data & 0x1); + /* Only one USB3 device is enabled */ + if (usb3_host0_or_device == 0) + usb3_host1_or_device = ((twsi_data >> 1) & 0x1); + } + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + + /* Update USB3 device if needed */ + if (usb3_host0_or_device == 1 && + serdes_map_array[lane_num].serdes_type == USB3_HOST0) + serdes_map_array[lane_num].serdes_type = USB3_DEVICE; + + if (usb3_host1_or_device == 1 && + serdes_map_array[lane_num].serdes_type == USB3_HOST1) + serdes_map_array[lane_num].serdes_type = USB3_DEVICE; + } + + /* If not detected any SerDes Site module, read 'SatR' lane setup */ + if (topology_mode == DB_381_CONFIG_DEFAULT) + update_topology_satr(serdes_map_array); + + /* update 'sgmiispeed' settings */ + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + u8 topology_mode; + struct serdes_map *topology_config_ptr; + u8 twsi_data; + u8 usb3_host0_or_device = 0, usb3_host1_or_device = 0; + + printf("\nInitialize DB-88F6820-BP board topology\n"); + + /* Getting the relevant topology mode (index) */ + topology_mode = topology_config_db_mode_get(); + + if (topology_mode == DB_NO_TOPOLOGY) + topology_mode = DB_CONFIG_DEFAULT; + + topology_config_ptr = topology_config_db[topology_mode]; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, topology_mode)); + + /* Read USB3.0 mode: HOST/DEVICE */ + if (load_topology_usb_mode_get(&twsi_data) == MV_OK) { + usb3_host0_or_device = (twsi_data & 0x1); + /* Only one USB3 device is enabled */ + if (usb3_host0_or_device == 0) + usb3_host1_or_device = ((twsi_data >> 1) & 0x1); + } + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + + /* + * Update USB3 device if needed - relevant for + * lane 3,4,5 only + */ + if (lane_num >= 3) { + if ((serdes_map_array[lane_num].serdes_type == + USB3_HOST0) && (usb3_host0_or_device == 1)) + serdes_map_array[lane_num].serdes_type = + USB3_DEVICE; + + if ((serdes_map_array[lane_num].serdes_type == + USB3_HOST1) && (usb3_host1_or_device == 1)) + serdes_map_array[lane_num].serdes_type = + USB3_DEVICE; + } + } + + /* If not detected any SerDes Site module, read 'SatR' lane setup */ + if (topology_mode == DB_CONFIG_DEFAULT) + update_topology_satr(serdes_map_array); + + /* update 'sgmiispeed' settings */ + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_ap(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + + DEBUG_INIT_FULL_S("\n### load_topology_db_ap ###\n"); + + printf("\nInitialize DB-AP board topology\n"); + topology_config_ptr = db_ap_config_default; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_gp(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + int is_sgmii = 0; + + DEBUG_INIT_FULL_S("\n### load_topology_db_gp ###\n"); + + topology_config_ptr = db_gp_config_default; + + printf("\nInitialize DB-GP board topology\n"); + + /* check S@R: if lane 5 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) + printf("%s: TWSI Read failed - Loading Default Topology\n", + __func__); + else { + topology_config_ptr[5].serdes_type = + is_sgmii ? SGMII2 : USB3_HOST1; + topology_config_ptr[5].serdes_speed = is_sgmii ? + SERDES_SPEED_3_125_GBPS : SERDES_SPEED_5_GBPS; + topology_config_ptr[5].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + /* + * Update 'gpserdes1/2/3' lane configuration , and 'sgmiispeed' + * for SGMII lanes + */ + update_topology_satr(serdes_map_array); + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_db_amc(struct serdes_map *serdes_map_array) +{ + u32 lane_num; + struct serdes_map *topology_config_ptr; + + DEBUG_INIT_FULL_S("\n### load_topology_db_amc ###\n"); + + printf("\nInitialize DB-AMC board topology\n"); + topology_config_ptr = db_amc_config_default; + + /* Update the default board topology device flavours */ + CHECK_STATUS(hws_update_device_toplogy + (topology_config_ptr, DB_CONFIG_DEFAULT)); + + /* Updating the topology map */ + for (lane_num = 0; lane_num < hws_serdes_get_max_lane(); lane_num++) { + serdes_map_array[lane_num].serdes_mode = + topology_config_ptr[lane_num].serdes_mode; + serdes_map_array[lane_num].serdes_speed = + topology_config_ptr[lane_num].serdes_speed; + serdes_map_array[lane_num].serdes_type = + topology_config_ptr[lane_num].serdes_type; + serdes_map_array[lane_num].swap_rx = + topology_config_ptr[lane_num].swap_rx; + serdes_map_array[lane_num].swap_tx = + topology_config_ptr[lane_num].swap_tx; + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_rd(struct serdes_map *serdes_map_array) +{ + u8 mode; + + DEBUG_INIT_FULL_S("\n### load_topology_rd ###\n"); + + DEBUG_INIT_S("\nInit RD board "); + + /* Reading mode */ + DEBUG_INIT_FULL_S("load_topology_rd: getting mode\n"); + if (i2c_read(EEPROM_I2C_ADDR, 0, 2, &mode, 1)) { + DEBUG_INIT_S("load_topology_rd: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Updating the topology map */ + DEBUG_INIT_FULL_S("load_topology_rd: Loading board topology details\n"); + + /* RD mode: 0 = NAS, 1 = AP */ + if (((mode >> 1) & 0x1) == 0) { + CHECK_STATUS(load_topology_rd_nas(serdes_map_array)); + } else { + CHECK_STATUS(load_topology_rd_ap(serdes_map_array)); + } + + update_topology_sgmii_speed(serdes_map_array); + + return MV_OK; +} + +int load_topology_rd_nas(struct serdes_map *serdes_map_array) +{ + int is_sgmii = 0; + u32 i; + + DEBUG_INIT_S("\nInit RD NAS topology "); + + /* check if lane 4 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) { + DEBUG_INIT_S("load_topology_rd NAS: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Lane 0 */ + serdes_map_array[0].serdes_type = PEX0; + serdes_map_array[0].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[0].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 1 */ + serdes_map_array[1].serdes_type = SATA0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[1].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 2 */ + serdes_map_array[2].serdes_type = SATA1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[2].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 3 */ + serdes_map_array[3].serdes_type = SATA3; + serdes_map_array[3].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[3].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 4 */ + if (is_sgmii == 1) { + DEBUG_INIT_S("Serdes Lane 4 is SGMII\n"); + serdes_map_array[4].serdes_type = SGMII1; + serdes_map_array[4].serdes_speed = SERDES_SPEED_3_125_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } else { + DEBUG_INIT_S("Serdes Lane 4 is USB3\n"); + serdes_map_array[4].serdes_type = USB3_HOST0; + serdes_map_array[4].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Lane 5 */ + serdes_map_array[5].serdes_type = SATA2; + serdes_map_array[5].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[5].serdes_mode = SERDES_DEFAULT_MODE; + + /* init swap configuration */ + for (i = 0; i <= 5; i++) { + serdes_map_array[i].swap_rx = 0; + serdes_map_array[i].swap_tx = 0; + } + + return MV_OK; +} + +int load_topology_rd_ap(struct serdes_map *serdes_map_array) +{ + int is_sgmii = 0; + u32 i; + + DEBUG_INIT_S("\nInit RD AP topology "); + + /* check if lane 4 is USB3 or SGMII */ + if (load_topology_rd_sgmii_usb(&is_sgmii) != MV_OK) { + DEBUG_INIT_S("load_topology_rd AP: TWSI Read failed\n"); + return MV_FAIL; + } + + /* Lane 0 */ + serdes_map_array[0].serdes_type = DEFAULT_SERDES; + serdes_map_array[0].serdes_speed = LAST_SERDES_SPEED; + serdes_map_array[0].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 1 */ + serdes_map_array[1].serdes_type = PEX0; + serdes_map_array[1].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[1].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 2 */ + serdes_map_array[2].serdes_type = PEX1; + serdes_map_array[2].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[2].serdes_mode = PEX_ROOT_COMPLEX_X1; + + /* Lane 3 */ + serdes_map_array[3].serdes_type = SATA3; + serdes_map_array[3].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[3].serdes_mode = SERDES_DEFAULT_MODE; + + /* Lane 4 */ + if (is_sgmii == 1) { + DEBUG_INIT_S("Serdes Lane 4 is SGMII\n"); + serdes_map_array[4].serdes_type = SGMII1; + serdes_map_array[4].serdes_speed = SERDES_SPEED_3_125_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } else { + DEBUG_INIT_S("Serdes Lane 4 is USB3\n"); + serdes_map_array[4].serdes_type = USB3_HOST0; + serdes_map_array[4].serdes_speed = SERDES_SPEED_5_GBPS; + serdes_map_array[4].serdes_mode = SERDES_DEFAULT_MODE; + } + + /* Lane 5 */ + serdes_map_array[5].serdes_type = SATA2; + serdes_map_array[5].serdes_speed = SERDES_SPEED_3_GBPS; + serdes_map_array[5].serdes_mode = SERDES_DEFAULT_MODE; + + /* init swap configuration */ + for (i = 0; i <= 5; i++) { + serdes_map_array[i].swap_rx = 0; + serdes_map_array[i].swap_tx = 0; + } + + return MV_OK; +} + +int load_topology_rd_sgmii_usb(int *is_sgmii) +{ + u8 mode; + + /* + * DB-GP board: Device 6810 supports only 2 GbE ports: + * SGMII2 not supported (USE USB3 Host instead) + */ + if (sys_env_device_id_get() == MV_6810) { + printf("Device 6810 supports only 2 GbE ports: SGMII-2 @ lane5 disabled (setting USB3.0 H1 instead)\n"); + *is_sgmii = 0; + return MV_OK; + } + + if (!i2c_read(RD_GET_MODE_ADDR, 1, 2, &mode, 1)) { + *is_sgmii = ((mode >> 2) & 0x1); + } else { + /* else use the default - USB3 */ + *is_sgmii = 0; + } + + if (*is_sgmii) + is_custom_topology = 1; + + printf("Lane 5 detection: %s\n", + *is_sgmii ? "SGMII2" : "USB3.0 Host Port 1"); + + return MV_OK; +} + +/* + * 'usb3port0'/'usb3port1' fields are located in EEPROM, + * at 3rd byte(offset=2), bit 0:1 (respectively) + */ +int load_topology_usb_mode_get(u8 *twsi_data) +{ + if (!i2c_read(EEPROM_I2C_ADDR, 2, 2, twsi_data, 1)) + return MV_OK; + + return MV_ERROR; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h new file mode 100644 index 0000000..3cfb1c7 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_topology_spec.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _HIGHSPEED_TOPOLOGY_SPEC_H +#define _HIGHSPEED_TOPOLOGY_SPEC_H + +#include "high_speed_env_spec.h" + +/* Topology map options for the DB_A38X_BP board */ +enum topology_config_db { + DB_CONFIG_SLM1363_C, + DB_CONFIG_SLM1363_D, + DB_CONFIG_SLM1363_E, + DB_CONFIG_SLM1363_F, + DB_CONFIG_SLM1364_D, + DB_CONFIG_SLM1364_E, + DB_CONFIG_SLM1364_F, + DB_CONFIG_DEFAULT, + DB_NO_TOPOLOGY +}; + +/* + * this enum must be aligned with topology_config_db_381 array, + * every update to this enum requires update to topology_config_db_381 + * array + */ +enum topology_config_db381 { + DB_CONFIG_SLM1427, /* enum for db_config_slm1427 */ + DB_CONFIG_SLM1426, /* enum for db_config_slm1426 */ + DB_381_CONFIG_DEFAULT, + DB_381_NO_TOPOLOGY +}; + +/* A generic function pointer for loading the board topology map */ +typedef int (*load_topology_func_ptr)(struct serdes_map *serdes_map_array); + +extern load_topology_func_ptr load_topology_func_arr[]; + +/* + * topology_config_db_mode_get - + * + * DESCRIPTION: Gets the relevant topology mode (index). + * for load_topology_db use only. + * INPUT: None. + * OUTPUT: None. + * RETURNS: the topology mode + */ +u8 topology_config_db_mode_get(void); + +/* + * load_topology_xxx - + * + * DESCRIPTION: Loads the board topology for the XXX board + * INPUT: serdes_map_array - The struct that will contain + * the board topology map + * OUTPUT: The board topology map. + * RETURNS: MV_OK for success + * MV_FAIL for failure (a wrong topology mode was read + * from the board) + */ + +/* load_topology_db - Loads the board topology for DB Board */ +int load_topology_db(struct serdes_map *serdes_map_array); + +/* load_topology_rd - Loads the board topology for RD Board */ +int load_topology_rd(struct serdes_map *serdes_map_array); + +/* load_topology_rd_nas - Loads the board topology for RD NAS Board */ +int load_topology_rd_nas(struct serdes_map *serdes_map_array); + +/* load_topology_rd_ap - Loads the board topology for RD Ap Board */ +int load_topology_rd_ap(struct serdes_map *serdes_map_array); + +/* load_topology_db_ap - Loads the board topology for DB-AP Board */ +int load_topology_db_ap(struct serdes_map *serdes_map_array); + +/* load_topology_db_gp - Loads the board topology for DB GP Board */ +int load_topology_db_gp(struct serdes_map *serdes_map_array); + +/* load_topology_db_381 - Loads the board topology for 381 DB-BP Board */ +int load_topology_db_381(struct serdes_map *serdes_map_array); + +/* load_topology_db_amc - Loads the board topology for DB-AMC Board */ +int load_topology_db_amc(struct serdes_map *serdes_map_array); + +/* + * hws_update_device_toplogy + * DESCRIPTION: Update the default board topology for specific device Id + * INPUT: + * topology_config_ptr - pointer to the Serdes mapping + * topology_mode - topology mode (index) + * OUTPUT: None + * RRETURNS: + * MV_OK - if updating the board topology success + * MV_BAD_PARAM - if the input parameter is wrong + */ +int hws_update_device_toplogy(struct serdes_map *topology_config_ptr, + enum topology_config_db topology_mode); + +/* + * load_topology_rd_sgmii_usb - + * + * DESCRIPTION: For RD board check if lane 4 is USB3 or SGMII + * INPUT: None + * OUTPUT: is_sgmii - return 1 if lane 4 is SGMII + * return 0 if lane 4 is USB. + * RETURNS: MV_OK for success + */ +int load_topology_rd_sgmii_usb(int *is_sgmii); + +/* + * load_topology_usb_mode_get - + * + * DESCRIPTION: For DB board check if USB3.0 mode + * INPUT: None + * OUTPUT: twsi_data - return data read from S@R via I2C + * RETURNS: MV_OK for success + */ +int load_topology_usb_mode_get(u8 *twsi_data); + +#endif /* _HIGHSPEED_TOPOLOGY_SPEC_H */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c new file mode 100644 index 0000000..ee2305b --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "seq_exec.h" +#include "high_speed_env_spec.h" + +#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" + +#if defined(MV_DEBUG_INIT_FULL) || defined(MV_DEBUG) +#define DB(x) x +#else +#define DB(x) +#endif + +/* Array for mapping the operation (write, poll or delay) functions */ +op_execute_func_ptr op_execute_func_arr[] = { + write_op_execute, + delay_op_execute, + poll_op_execute +}; + +int write_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 unit_base_reg, unit_offset, data, mask, reg_data, reg_addr; + + /* Getting write op params from the input parameter */ + data = params->data[data_arr_idx]; + mask = params->mask; + + /* an empty operation */ + if (data == NO_DATA) + return MV_OK; + + /* get updated base address since it can be different between Serdes */ + CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg, + params->unit_offset, + &unit_base_reg, &unit_offset)); + + /* Address calculation */ + reg_addr = unit_base_reg + unit_offset * serdes_num; + +#ifdef SEQ_DEBUG + printf("Write: 0x%x: 0x%x (mask 0x%x) - ", reg_addr, data, mask); +#endif + /* Reading old value */ + reg_data = reg_read(reg_addr); + reg_data &= (~mask); + + /* Writing new data */ + data &= mask; + reg_data |= data; + reg_write(reg_addr, reg_data); + +#ifdef SEQ_DEBUG + printf(" - 0x%x\n", reg_data); +#endif + + return MV_OK; +} + +int delay_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 delay; + + /* Getting delay op params from the input parameter */ + delay = params->wait_time; +#ifdef SEQ_DEBUG + printf("Delay: %d\n", delay); +#endif + mdelay(delay); + + return MV_OK; +} + +int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx) +{ + u32 unit_base_reg, unit_offset, data, mask, num_of_loops, wait_time; + u32 poll_counter = 0; + u32 reg_addr, reg_data; + + /* Getting poll op params from the input parameter */ + data = params->data[data_arr_idx]; + mask = params->mask; + num_of_loops = params->num_of_loops; + wait_time = params->wait_time; + + /* an empty operation */ + if (data == NO_DATA) + return MV_OK; + + /* get updated base address since it can be different between Serdes */ + CHECK_STATUS(hws_get_ext_base_addr(serdes_num, params->unit_base_reg, + params->unit_offset, + &unit_base_reg, &unit_offset)); + + /* Address calculation */ + reg_addr = unit_base_reg + unit_offset * serdes_num; + + /* Polling */ +#ifdef SEQ_DEBUG + printf("Poll: 0x%x: 0x%x (mask 0x%x)\n", reg_addr, data, mask); +#endif + + do { + reg_data = reg_read(reg_addr) & mask; + poll_counter++; + udelay(wait_time); + } while ((reg_data != data) && (poll_counter < num_of_loops)); + + if ((poll_counter >= num_of_loops) && (reg_data != data)) { + DEBUG_INIT_S("poll_op_execute: TIMEOUT\n"); + return MV_TIMEOUT; + } + + return MV_OK; +} + +enum mv_op get_cfg_seq_op(struct op_params *params) +{ + if (params->wait_time == 0) + return WRITE_OP; + else if (params->num_of_loops == 0) + return DELAY_OP; + + return POLL_OP; +} + +int mv_seq_exec(u32 serdes_num, u32 seq_id) +{ + u32 seq_idx; + struct op_params *seq_arr; + u32 seq_size; + u32 data_arr_idx; + enum mv_op curr_op; + + DB(printf("\n### mv_seq_exec ###\n")); + DB(printf("seq id: %d\n", seq_id)); + + if (hws_is_serdes_active(serdes_num) != 1) { + printf("mv_seq_exec_ext:Error: SerDes lane %d is not valid\n", + serdes_num); + return MV_BAD_PARAM; + } + + seq_arr = serdes_seq_db[seq_id].op_params_ptr; + seq_size = serdes_seq_db[seq_id].cfg_seq_size; + data_arr_idx = serdes_seq_db[seq_id].data_arr_idx; + + DB(printf("seq_size: %d\n", seq_size)); + DB(printf("data_arr_idx: %d\n", data_arr_idx)); + + /* Executing the sequence operations */ + for (seq_idx = 0; seq_idx < seq_size; seq_idx++) { + curr_op = get_cfg_seq_op(&seq_arr[seq_idx]); + op_execute_func_arr[curr_op](serdes_num, &seq_arr[seq_idx], + data_arr_idx); + } + + return MV_OK; +} diff --git a/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h new file mode 100644 index 0000000..14f406a --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/seq_exec.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _SEQ_EXEC_H +#define _SEQ_EXEC_H + +#define NA 0xff +#define DEFAULT_PARAM 0 +#define MV_BOARD_TCLK_ERROR 0xffffffff + +#define NO_DATA 0xffffffff +#define MAX_DATA_ARRAY 5 +#define FIRST_CELL 0 + +/* Operation types */ +enum mv_op { + WRITE_OP, + DELAY_OP, + POLL_OP, +}; + +/* Operation parameters */ +struct op_params { + u32 unit_base_reg; + u32 unit_offset; + u32 mask; + u32 data[MAX_DATA_ARRAY]; /* data array */ + u8 wait_time; /* msec */ + u16 num_of_loops; /* for polling only */ +}; + +/* + * Sequence parameters. Each sequence contains: + * 1. Sequence id. + * 2. Sequence size (total amount of operations during the sequence) + * 3. a series of operations. operations can be write, poll or delay + * 4. index in the data array (the entry where the relevant data sits) + */ +struct cfg_seq { + struct op_params *op_params_ptr; + u8 cfg_seq_size; + u8 data_arr_idx; +}; + +extern struct cfg_seq serdes_seq_db[]; + +/* + * A generic function type for executing an operation (write, poll or delay) + */ +typedef int (*op_execute_func_ptr)(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); + +/* Specific functions for executing each operation */ +int write_op_execute(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); +int delay_op_execute(u32 serdes_num, struct op_params *params, + u32 data_arr_idx); +int poll_op_execute(u32 serdes_num, struct op_params *params, u32 data_arr_idx); +enum mv_op get_cfg_seq_op(struct op_params *params); +int mv_seq_exec(u32 serdes_num, u32 seq_id); + +#endif /*_SEQ_EXEC_H*/ diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c new file mode 100644 index 0000000..efd3873 --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "seq_exec.h" +#include "sys_env_lib.h" + +#include "../../../drivers/ddr/marvell/a38x/ddr3_a38x.h" + +#ifdef CONFIG_ARMADA_38X +enum unit_id sys_env_soc_unit_nums[MAX_UNITS_ID][MAX_DEV_ID_NUM] = { +/* 6820 6810 6811 6828 */ +/* PEX_UNIT_ID */ { 4, 3, 3, 4}, +/* ETH_GIG_UNIT_ID */ { 3, 2, 3, 3}, +/* USB3H_UNIT_ID */ { 2, 2, 2, 2}, +/* USB3D_UNIT_ID */ { 1, 1, 1, 1}, +/* SATA_UNIT_ID */ { 2, 2, 2, 4}, +/* QSGMII_UNIT_ID */ { 1, 0, 0, 1}, +/* XAUI_UNIT_ID */ { 0, 0, 0, 0}, +/* RXAUI_UNIT_ID */ { 0, 0, 0, 0} +}; +#else /* if (CONFIG_ARMADA_39X) */ +enum unit_id sys_env_soc_unit_nums[MAX_UNITS_ID][MAX_DEV_ID_NUM] = { +/* 6920 6928 */ +/* PEX_UNIT_ID */ { 4, 4}, +/* ETH_GIG_UNIT_ID */ { 3, 4}, +/* USB3H_UNIT_ID */ { 1, 2}, +/* USB3D_UNIT_ID */ { 0, 1}, +/* SATA_UNIT_ID */ { 0, 4}, +/* QSGMII_UNIT_ID */ { 0, 1}, +/* XAUI_UNIT_ID */ { 1, 1}, +/* RXAUI_UNIT_ID */ { 1, 1} +}; +#endif + +u32 g_dev_id = -1; + +u32 mv_board_id_get(void) +{ +#if defined(CONFIG_DB_88F6820_GP) + return DB_GP_68XX_ID; +#else + /* + * Return 0 here for custom board as this should not be used + * for custom boards. + */ + return 0; +#endif +} + +u32 mv_board_tclk_get(void) +{ + u32 value; + + value = (reg_read(DEVICE_SAMPLE_AT_RESET1_REG) >> 15) & 0x1; + + switch (value) { + case (0x0): + return 250000000; + case (0x1): + return 200000000; + default: + return 0xffffffff; + } +} + +u32 mv_board_id_index_get(u32 board_id) +{ + /* + * Marvell Boards use 0x10 as base for Board ID: + * mask MSB to receive index for board ID + */ + return board_id & (MARVELL_BOARD_ID_MASK - 1); +} + +/* + * sys_env_suspend_wakeup_check + * DESCRIPTION: Reads GPIO input for suspend-wakeup indication. + * INPUT: None. + * OUTPUT: + * RETURNS: u32 indicating suspend wakeup status: + * 0 - Not supported, + * 1 - supported: read magic word detect wakeup, + * 2 - detected wakeup from GPIO. + */ +enum suspend_wakeup_status sys_env_suspend_wakeup_check(void) +{ + u32 reg, board_id_index, gpio; + struct board_wakeup_gpio board_gpio[] = MV_BOARD_WAKEUP_GPIO_INFO; + + board_id_index = mv_board_id_index_get(mv_board_id_get()); + if (!(sizeof(board_gpio) / sizeof(struct board_wakeup_gpio) > + board_id_index)) { + printf("\n_failed loading Suspend-Wakeup information (invalid board ID)\n"); + return SUSPEND_WAKEUP_DISABLED; + } + + /* + * - Detect if Suspend-Wakeup is supported on current board + * - Fetch the GPIO number for wakeup status input indication + */ + if (board_gpio[board_id_index].gpio_num == -1) { + /* Suspend to RAM is not supported */ + return SUSPEND_WAKEUP_DISABLED; + } else if (board_gpio[board_id_index].gpio_num == -2) { + /* + * Suspend to RAM is supported but GPIO indication is + * not implemented - Skip + */ + return SUSPEND_WAKEUP_ENABLED; + } else { + gpio = board_gpio[board_id_index].gpio_num; + } + + /* Initialize MPP for GPIO (set MPP = 0x0) */ + reg = reg_read(MPP_CONTROL_REG(MPP_REG_NUM(gpio))); + /* reset MPP21 to 0x0, keep rest of MPP settings*/ + reg &= ~MPP_MASK(gpio); + reg_write(MPP_CONTROL_REG(MPP_REG_NUM(gpio)), reg); + + /* Initialize GPIO as input */ + reg = reg_read(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio))); + reg |= GPP_MASK(gpio); + reg_write(GPP_DATA_OUT_EN_REG(GPP_REG_NUM(gpio)), reg); + + /* + * Check GPP for input status from PIC: 0 - regular init, + * 1 - suspend wakeup + */ + reg = reg_read(GPP_DATA_IN_REG(GPP_REG_NUM(gpio))); + + /* if GPIO is ON: wakeup from S2RAM indication detected */ + return (reg & GPP_MASK(gpio)) ? SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED : + SUSPEND_WAKEUP_DISABLED; +} + +/* + * mv_ctrl_dev_id_index_get + * + * DESCRIPTION: return SOC device index + * INPUT: None + * OUTPUT: None + * RETURN: + * return SOC device index + */ +u32 sys_env_id_index_get(u32 ctrl_model) +{ + switch (ctrl_model) { + case MV_6820_DEV_ID: + return MV_6820_INDEX; + case MV_6810_DEV_ID: + return MV_6810_INDEX; + case MV_6811_DEV_ID: + return MV_6811_INDEX; + case MV_6828_DEV_ID: + return MV_6828_INDEX; + case MV_6920_DEV_ID: + return MV_6920_INDEX; + case MV_6928_DEV_ID: + return MV_6928_INDEX; + default: + return MV_6820_INDEX; + } +} + +u32 sys_env_unit_max_num_get(enum unit_id unit) +{ + u32 dev_id_index; + + if (unit >= MAX_UNITS_ID) { + printf("%s: Error: Wrong unit type (%u)\n", __func__, unit); + return 0; + } + + dev_id_index = sys_env_id_index_get(sys_env_model_get()); + return sys_env_soc_unit_nums[unit][dev_id_index]; +} + +/* + * sys_env_model_get + * DESCRIPTION: Returns 16bit describing the device model (ID) as defined + * in Vendor ID configuration register + */ +u16 sys_env_model_get(void) +{ + u32 default_ctrl_id, ctrl_id = reg_read(DEV_ID_REG); + ctrl_id = (ctrl_id & (DEV_ID_REG_DEVICE_ID_MASK)) >> + DEV_ID_REG_DEVICE_ID_OFFS; + + switch (ctrl_id) { + case MV_6820_DEV_ID: + case MV_6810_DEV_ID: + case MV_6811_DEV_ID: + case MV_6828_DEV_ID: + case MV_6920_DEV_ID: + case MV_6928_DEV_ID: + return ctrl_id; + default: + /* Device ID Default for A38x: 6820 , for A39x: 6920 */ + #ifdef CONFIG_ARMADA_38X + default_ctrl_id = MV_6820_DEV_ID; + #else + default_ctrl_id = MV_6920_DEV_ID; + #endif + printf("%s: Error retrieving device ID (%x), using default ID = %x\n", + __func__, ctrl_id, default_ctrl_id); + return default_ctrl_id; + } +} + +/* + * sys_env_device_id_get + * DESCRIPTION: Returns enum (0..7) index of the device model (ID) + */ +u32 sys_env_device_id_get(void) +{ + char *device_id_str[7] = { + "6810", "6820", "6811", "6828", "NONE", "6920", "6928" + }; + + if (g_dev_id != -1) + return g_dev_id; + + g_dev_id = reg_read(DEVICE_SAMPLE_AT_RESET1_REG); + g_dev_id = g_dev_id >> SAR_DEV_ID_OFFS & SAR_DEV_ID_MASK; + printf("Detected Device ID %s\n", device_id_str[g_dev_id]); + + return g_dev_id; +} + +#ifdef MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI +/* +* sys_env_get_topology_update_info +* DESCRIPTION: Read TWSI fields to update DDR topology structure +* INPUT: None +* OUTPUT: None, 0 means no topology update +* RETURN: +* Bit mask of changes topology features +*/ +#ifdef CONFIG_ARMADA_39X +u32 sys_env_get_topology_update_info( + struct topology_update_info *tui) +{ + /* Set 16/32 bit configuration*/ + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + +#ifdef CONFIG_DDR3 + if (1 == sys_env_config_get(MV_CONFIG_DDR_BUSWIDTH)) { + /* 16bit */ + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + } else { + /* 32bit */ + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + } +#endif + + /* Set ECC/no ECC bit configuration */ + tui->update_ecc = 1; + if (0 == sys_env_config_get(MV_CONFIG_DDR_ECC_EN)) { + /* NO ECC */ + tui->ecc = TOPOLOGY_UPDATE_ECC_OFF; + } else { + /* ECC */ + tui->ecc = TOPOLOGY_UPDATE_ECC_ON; + } + + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + + return MV_OK; +} +#else /*CONFIG_ARMADA_38X*/ +u32 sys_env_get_topology_update_info( + struct topology_update_info *tui) +{ + u8 config_val; + u8 ecc_mode[A38X_MV_MAX_MARVELL_BOARD_ID - + A38X_MARVELL_BOARD_ID_BASE][5] = TOPOLOGY_UPDATE; + u8 board_id = mv_board_id_get(); + int ret; + + board_id = mv_board_id_index_get(board_id); + ret = i2c_read(EEPROM_I2C_ADDR, 0, 2, &config_val, 1); + if (ret) { + DEBUG_INIT_S("sys_env_get_topology_update_info: TWSI Read failed\n"); + return 0; + } + + /* Set 16/32 bit configuration */ + if ((0 == (config_val & DDR_SATR_CONFIG_MASK_WIDTH)) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT] == 0)) { + /* 16bit by SatR of 32bit mode not supported for the board */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT] != 0)) { + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + } + } else { + /* 32bit */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT] != 0)) { + tui->update_width = 1; + tui->width = TOPOLOGY_UPDATE_WIDTH_32BIT; + } + } + + /* Set ECC/no ECC bit configuration */ + if (0 == (config_val & DDR_SATR_CONFIG_MASK_ECC)) { + /* NO ECC */ + tui->update_ecc = 1; + tui->ecc = TOPOLOGY_UPDATE_ECC_OFF; + } else { + /* ECC */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC_PUP3] != 0)) { + tui->update_ecc = 1; + tui->ecc = TOPOLOGY_UPDATE_ECC_ON; + } + } + + /* Set ECC pup bit configuration */ + if (0 == (config_val & DDR_SATR_CONFIG_MASK_ECC_PUP)) { + /* PUP3 */ + /* + * Check if PUP3 configuration allowed, if not - + * force Pup4 with warning message + */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC_PUP3] != 0)) { + if (tui->width == TOPOLOGY_UPDATE_WIDTH_16BIT) { + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP3; + } else { + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0)) { + printf("DDR Topology Update: ECC PUP3 not valid for 32bit mode, force ECC in PUP4\n"); + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + } else { + if (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != + 0) { + printf("DDR Topology Update: ECC on PUP3 not supported, force ECC on PUP4\n"); + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + } else { + /* PUP4 */ + if ((ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] != 0) || + (ecc_mode[board_id][TOPOLOGY_UPDATE_16BIT_ECC] != 0)) { + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP4; + } + } + + /* + * Check for forbidden ECC mode, + * if by default width and pup selection set 32bit ECC mode and this + * mode not supported for the board - config 16bit with ECC on PUP3 + */ + if ((tui->ecc == TOPOLOGY_UPDATE_ECC_ON) && + (tui->width == TOPOLOGY_UPDATE_WIDTH_32BIT)) { + if (ecc_mode[board_id][TOPOLOGY_UPDATE_32BIT_ECC] == 0) { + printf("DDR Topology Update: 32bit mode with ECC not allowed on this board, forced 16bit with ECC on PUP3\n"); + tui->width = TOPOLOGY_UPDATE_WIDTH_16BIT; + tui->update_ecc_pup3_mode = 1; + tui->ecc_pup_mode_offset = + TOPOLOGY_UPDATE_ECC_OFFSET_PUP3; + } + } + + return MV_OK; +} +#endif /* CONFIG_ARMADA_38X */ +#endif /* MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI */ diff --git a/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h new file mode 100644 index 0000000..3e5373c --- /dev/null +++ b/arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h @@ -0,0 +1,371 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _SYS_ENV_LIB_H +#define _SYS_ENV_LIB_H + +#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" +#include "../../../drivers/ddr/marvell/a38x/ddr3_hws_hw_training.h" + +/* Serdes definitions */ +#define COMMON_PHY_BASE_ADDR 0x18300 + +#define DEVICE_CONFIGURATION_REG0 0x18284 +#define DEVICE_CONFIGURATION_REG1 0x18288 +#define COMMON_PHY_CONFIGURATION1_REG 0x18300 +#define COMMON_PHY_CONFIGURATION2_REG 0x18304 +#define COMMON_PHY_CONFIGURATION4_REG 0x1830c +#define COMMON_PHY_STATUS1_REG 0x18318 +#define COMMON_PHYS_SELECTORS_REG 0x183fc +#define SOC_CONTROL_REG1 0x18204 +#define GENERAL_PURPOSE_RESERVED0_REG 0x182e0 +#define GBE_CONFIGURATION_REG 0x18460 +#define DEVICE_SAMPLE_AT_RESET1_REG 0x18600 +#define DEVICE_SAMPLE_AT_RESET2_REG 0x18604 +#define DEV_ID_REG 0x18238 + +#define CORE_PLL_PARAMETERS_REG 0xe42e0 +#define CORE_PLL_CONFIG_REG 0xe42e4 + +#define QSGMII_CONTROL_REG1 0x18494 + +#define DEV_ID_REG_DEVICE_ID_OFFS 16 +#define DEV_ID_REG_DEVICE_ID_MASK 0xffff0000 + +#define SAR_DEV_ID_OFFS 27 +#define SAR_DEV_ID_MASK 0x7 + +#define POWER_AND_PLL_CTRL_REG 0xa0004 +#define CALIBRATION_CTRL_REG 0xa0008 +#define DFE_REG0 0xa001c +#define DFE_REG3 0xa0028 +#define RESET_DFE_REG 0xa0148 +#define LOOPBACK_REG 0xa008c +#define SYNC_PATTERN_REG 0xa0090 +#define INTERFACE_REG 0xa0094 +#define ISOLATE_REG 0xa0098 +#define MISC_REG 0xa013c +#define GLUE_REG 0xa0140 +#define GENERATION_DIVIDER_FORCE_REG 0xa0144 +#define PCIE_REG0 0xa0120 +#define LANE_ALIGN_REG0 0xa0124 +#define SQUELCH_FFE_SETTING_REG 0xa0018 +#define G1_SETTINGS_0_REG 0xa0034 +#define G1_SETTINGS_1_REG 0xa0038 +#define G1_SETTINGS_3_REG 0xa0440 +#define G1_SETTINGS_4_REG 0xa0444 +#define G2_SETTINGS_0_REG 0xa003c +#define G2_SETTINGS_1_REG 0xa0040 +#define G2_SETTINGS_2_REG 0xa00f8 +#define G2_SETTINGS_3_REG 0xa0448 +#define G2_SETTINGS_4_REG 0xa044c +#define G3_SETTINGS_0_REG 0xa0044 +#define G3_SETTINGS_1_REG 0xa0048 +#define G3_SETTINGS_3_REG 0xa0450 +#define G3_SETTINGS_4_REG 0xa0454 +#define VTHIMPCAL_CTRL_REG 0xa0104 +#define REF_REG0 0xa0134 +#define CAL_REG6 0xa0168 +#define RX_REG2 0xa0184 +#define RX_REG3 0xa0188 +#define PCIE_REG1 0xa0288 +#define PCIE_REG3 0xa0290 +#define LANE_CFG1_REG 0xa0604 +#define LANE_CFG4_REG 0xa0620 +#define LANE_CFG5_REG 0xa0624 +#define GLOBAL_CLK_CTRL 0xa0704 +#define GLOBAL_MISC_CTRL 0xa0718 +#define GLOBAL_CLK_SRC_HI 0xa0710 + +#define GLOBAL_CLK_CTRL 0xa0704 +#define GLOBAL_MISC_CTRL 0xa0718 +#define GLOBAL_PM_CTRL 0xa0740 + +/* SATA registers */ +#define SATA_CTRL_REG_IND_ADDR 0xa80a0 +#define SATA_CTRL_REG_IND_DATA 0xa80a4 + +#define SATA_VENDOR_PORT_0_REG_ADDR 0xa8178 +#define SATA_VENDOR_PORT_1_REG_ADDR 0xa81f8 +#define SATA_VENDOR_PORT_0_REG_DATA 0xa817c +#define SATA_VENDOR_PORT_1_REG_DATA 0xa81fc + +/* Reference clock values and mask */ +#define POWER_AND_PLL_CTRL_REG_100MHZ_VAL 0x0 +#define POWER_AND_PLL_CTRL_REG_25MHZ_VAL_1 0x1 +#define POWER_AND_PLL_CTRL_REG_25MHZ_VAL_2 0x2 +#define POWER_AND_PLL_CTRL_REG_40MHZ_VAL 0x3 +#define GLOBAL_PM_CTRL_REG_25MHZ_VAL 0x7 +#define GLOBAL_PM_CTRL_REG_40MHZ_VAL 0xc +#define LANE_CFG4_REG_25MHZ_VAL 0x200 +#define LANE_CFG4_REG_40MHZ_VAL 0x300 + +#define POWER_AND_PLL_CTRL_REG_MASK (~(0x1f)) +#define GLOBAL_PM_CTRL_REG_MASK (~(0xff)) +#define LANE_CFG4_REG_MASK (~(0x1f00)) + +#define REF_CLK_SELECTOR_VAL_PEX0(reg_val) (reg_val >> 2) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX1(reg_val) (reg_val >> 3) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX2(reg_val) (reg_val >> 30) & 0x1 +#define REF_CLK_SELECTOR_VAL_PEX3(reg_val) (reg_val >> 31) & 0x1 +#define REF_CLK_SELECTOR_VAL(reg_val) (reg_val & 0x1) + +#define MAX_SELECTOR_VAL 10 + +/* TWSI addresses */ +/* starting from A38x A0, i2c address of EEPROM is 0x57 */ +#ifdef CONFIG_ARMADA_39X +#define EEPROM_I2C_ADDR 0x50 +#else +#define EEPROM_I2C_ADDR (sys_env_device_rev_get() == \ + MV_88F68XX_Z1_ID ? 0x50 : 0x57) +#endif +#define RD_GET_MODE_ADDR 0x4c +#define DB_GET_MODE_SLM1363_ADDR 0x25 +#define DB_GET_MODE_SLM1364_ADDR 0x24 +#define DB381_GET_MODE_SLM1426_1427_ADDR 0x56 + +/* DB-BP Board 'SatR' mapping */ +#define SATR_DB_LANE1_MAX_OPTIONS 7 +#define SATR_DB_LANE1_CFG_MASK 0x7 +#define SATR_DB_LANE1_CFG_OFFSET 0 +#define SATR_DB_LANE2_MAX_OPTIONS 4 +#define SATR_DB_LANE2_CFG_MASK 0x38 +#define SATR_DB_LANE2_CFG_OFFSET 3 + +/* GP Board 'SatR' mapping */ +#define SATR_GP_LANE1_CFG_MASK 0x4 +#define SATR_GP_LANE1_CFG_OFFSET 2 +#define SATR_GP_LANE2_CFG_MASK 0x8 +#define SATR_GP_LANE2_CFG_OFFSET 3 + +/* For setting MPP2 and MPP3 to be TWSI mode and MPP 0,1 to UART mode */ +#define MPP_CTRL_REG 0x18000 +#define MPP_SET_MASK (~(0xffff)) +#define MPP_SET_DATA (0x1111) +#define MPP_UART1_SET_MASK (~(0xff000)) +#define MPP_UART1_SET_DATA (0x66000) + +#define AVS_DEBUG_CNTR_REG 0xe4124 +#define AVS_DEBUG_CNTR_DEFAULT_VALUE 0x08008073 + +#define AVS_ENABLED_CONTROL 0xe4130 +#define AVS_LOW_VDD_LIMIT_OFFS 4 +#define AVS_LOW_VDD_LIMIT_MASK (0xff << AVS_LOW_VDD_LIMIT_OFFS) +#define AVS_LOW_VDD_LIMIT_VAL (0x27 << AVS_LOW_VDD_LIMIT_OFFS) + +#define AVS_HIGH_VDD_LIMIT_OFFS 12 +#define AVS_HIGH_VDD_LIMIT_MASK (0xff << AVS_HIGH_VDD_LIMIT_OFFS) +#define AVS_HIGH_VDD_LIMIT_VAL (0x27 << AVS_HIGH_VDD_LIMIT_OFFS) + +/* Board ID numbers */ +#define MARVELL_BOARD_ID_MASK 0x10 +/* Customer boards for A38x */ +#define A38X_CUSTOMER_BOARD_ID_BASE 0x0 +#define A38X_CUSTOMER_BOARD_ID0 (A38X_CUSTOMER_BOARD_ID_BASE + 0) +#define A38X_CUSTOMER_BOARD_ID1 (A38X_CUSTOMER_BOARD_ID_BASE + 1) +#define A38X_MV_MAX_CUSTOMER_BOARD_ID (A38X_CUSTOMER_BOARD_ID_BASE + 2) +#define A38X_MV_CUSTOMER_BOARD_NUM (A38X_MV_MAX_CUSTOMER_BOARD_ID - \ + A38X_CUSTOMER_BOARD_ID_BASE) + +/* Marvell boards for A38x */ +#define A38X_MARVELL_BOARD_ID_BASE 0x10 +#define RD_NAS_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 0) +#define DB_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 1) +#define RD_AP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 2) +#define DB_AP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 3) +#define DB_GP_68XX_ID (A38X_MARVELL_BOARD_ID_BASE + 4) +#define DB_BP_6821_ID (A38X_MARVELL_BOARD_ID_BASE + 5) +#define DB_AMC_6820_ID (A38X_MARVELL_BOARD_ID_BASE + 6) +#define A38X_MV_MAX_MARVELL_BOARD_ID (A38X_MARVELL_BOARD_ID_BASE + 7) +#define A38X_MV_MARVELL_BOARD_NUM (A38X_MV_MAX_MARVELL_BOARD_ID - \ + A38X_MARVELL_BOARD_ID_BASE) + +/* Customer boards for A39x */ +#define A39X_CUSTOMER_BOARD_ID_BASE 0x20 +#define A39X_CUSTOMER_BOARD_ID0 (A39X_CUSTOMER_BOARD_ID_BASE + 0) +#define A39X_CUSTOMER_BOARD_ID1 (A39X_CUSTOMER_BOARD_ID_BASE + 1) +#define A39X_MV_MAX_CUSTOMER_BOARD_ID (A39X_CUSTOMER_BOARD_ID_BASE + 2) +#define A39X_MV_CUSTOMER_BOARD_NUM (A39X_MV_MAX_CUSTOMER_BOARD_ID - \ + A39X_CUSTOMER_BOARD_ID_BASE) + +/* Marvell boards for A39x */ +#define A39X_MARVELL_BOARD_ID_BASE 0x30 +#define A39X_DB_69XX_ID (A39X_MARVELL_BOARD_ID_BASE + 0) +#define A39X_RD_69XX_ID (A39X_MARVELL_BOARD_ID_BASE + 1) +#define A39X_MV_MAX_MARVELL_BOARD_ID (A39X_MARVELL_BOARD_ID_BASE + 2) +#define A39X_MV_MARVELL_BOARD_NUM (A39X_MV_MAX_MARVELL_BOARD_ID - \ + A39X_MARVELL_BOARD_ID_BASE) + +#ifdef CONFIG_ARMADA_38X +#define CUTOMER_BOARD_ID_BASE A38X_CUSTOMER_BOARD_ID_BASE +#define CUSTOMER_BOARD_ID0 A38X_CUSTOMER_BOARD_ID0 +#define CUSTOMER_BOARD_ID1 A38X_CUSTOMER_BOARD_ID1 +#define MV_MAX_CUSTOMER_BOARD_ID A38X_MV_MAX_CUSTOMER_BOARD_ID +#define MV_CUSTOMER_BOARD_NUM A38X_MV_CUSTOMER_BOARD_NUM +#define MARVELL_BOARD_ID_BASE A38X_MARVELL_BOARD_ID_BASE +#define MV_MAX_MARVELL_BOARD_ID A38X_MV_MAX_MARVELL_BOARD_ID +#define MV_MARVELL_BOARD_NUM A38X_MV_MARVELL_BOARD_NUM +#define MV_DEFAULT_BOARD_ID DB_68XX_ID +#define MV_DEFAULT_DEVICE_ID MV_6811 +#elif defined(CONFIG_ARMADA_39X) +#define CUTOMER_BOARD_ID_BASE A39X_CUSTOMER_BOARD_ID_BASE +#define CUSTOMER_BOARD_ID0 A39X_CUSTOMER_BOARD_ID0 +#define CUSTOMER_BOARD_ID1 A39X_CUSTOMER_BOARD_ID1 +#define MV_MAX_CUSTOMER_BOARD_ID A39X_MV_MAX_CUSTOMER_BOARD_ID +#define MV_CUSTOMER_BOARD_NUM A39X_MV_CUSTOMER_BOARD_NUM +#define MARVELL_BOARD_ID_BASE A39X_MARVELL_BOARD_ID_BASE +#define MV_MAX_MARVELL_BOARD_ID A39X_MV_MAX_MARVELL_BOARD_ID +#define MV_MARVELL_BOARD_NUM A39X_MV_MARVELL_BOARD_NUM +#define MV_DEFAULT_BOARD_ID A39X_DB_69XX_ID +#define MV_DEFAULT_DEVICE_ID MV_6920 +#endif + +#define MV_INVALID_BOARD_ID 0xffffffff + +/* device revesion */ +#define DEV_VERSION_ID_REG 0x1823c +#define REVISON_ID_OFFS 8 +#define REVISON_ID_MASK 0xf00 + +/* A38x revisions */ +#define MV_88F68XX_Z1_ID 0x0 +#define MV_88F68XX_A0_ID 0x4 +/* A39x revisions */ +#define MV_88F69XX_Z1_ID 0x2 + +#define MPP_CONTROL_REG(id) (0x18000 + (id * 4)) +#define GPP_DATA_OUT_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x00) +#define GPP_DATA_OUT_EN_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x04) +#define GPP_DATA_IN_REG(grp) (MV_GPP_REGS_BASE(grp) + 0x10) +#define MV_GPP_REGS_BASE(unit) (0x18100 + ((unit) * 0x40)) + +#define MPP_REG_NUM(GPIO_NUM) (GPIO_NUM / 8) +#define MPP_MASK(GPIO_NUM) (0xf << 4 * (GPIO_NUM - \ + (MPP_REG_NUM(GPIO_NUM) * 8))); +#define GPP_REG_NUM(GPIO_NUM) (GPIO_NUM / 32) +#define GPP_MASK(GPIO_NUM) (1 << GPIO_NUM % 32) + +/* device ID */ +/* Armada 38x Family */ +#define MV_6810_DEV_ID 0x6810 +#define MV_6811_DEV_ID 0x6811 +#define MV_6820_DEV_ID 0x6820 +#define MV_6828_DEV_ID 0x6828 +/* Armada 39x Family */ +#define MV_6920_DEV_ID 0x6920 +#define MV_6928_DEV_ID 0x6928 + +enum { + MV_6810, + MV_6820, + MV_6811, + MV_6828, + MV_NONE, + MV_6920, + MV_6928, + MV_MAX_DEV_ID, +}; + +#define MV_6820_INDEX 0 +#define MV_6810_INDEX 1 +#define MV_6811_INDEX 2 +#define MV_6828_INDEX 3 + +#define MV_6920_INDEX 0 +#define MV_6928_INDEX 1 + +#ifdef CONFIG_ARMADA_38X +#define MAX_DEV_ID_NUM 4 +#else +#define MAX_DEV_ID_NUM 2 +#endif + +#define MV_6820_INDEX 0 +#define MV_6810_INDEX 1 +#define MV_6811_INDEX 2 +#define MV_6828_INDEX 3 +#define MV_6920_INDEX 0 +#define MV_6928_INDEX 1 + +enum unit_id { + PEX_UNIT_ID, + ETH_GIG_UNIT_ID, + USB3H_UNIT_ID, + USB3D_UNIT_ID, + SATA_UNIT_ID, + QSGMII_UNIT_ID, + XAUI_UNIT_ID, + RXAUI_UNIT_ID, + MAX_UNITS_ID +}; + +struct board_wakeup_gpio { + u32 board_id; + int gpio_num; +}; + +enum suspend_wakeup_status { + SUSPEND_WAKEUP_DISABLED, + SUSPEND_WAKEUP_ENABLED, + SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED, +}; + +/* + * GPIO status indication for Suspend Wakeup: + * If suspend to RAM is supported and GPIO inidcation is implemented, + * set the gpio number + * If suspend to RAM is supported but GPIO indication is not implemented + * set '-2' + * If suspend to RAM is not supported set '-1' + */ +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT +#ifdef CONFIG_ARMADA_38X +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A38X_CUSTOMER_BOARD_ID0, -1 }, \ + {A38X_CUSTOMER_BOARD_ID0, -1 }, \ +}; +#else +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A39X_CUSTOMER_BOARD_ID0, -1 }, \ + {A39X_CUSTOMER_BOARD_ID0, -1 }, \ +}; +#endif /* CONFIG_ARMADA_38X */ + +#else + +#ifdef CONFIG_ARMADA_38X +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {RD_NAS_68XX_ID, -2 }, \ + {DB_68XX_ID, -1 }, \ + {RD_AP_68XX_ID, -2 }, \ + {DB_AP_68XX_ID, -2 }, \ + {DB_GP_68XX_ID, -2 }, \ + {DB_BP_6821_ID, -2 }, \ + {DB_AMC_6820_ID, -2 }, \ +}; +#else +#define MV_BOARD_WAKEUP_GPIO_INFO { \ + {A39X_RD_69XX_ID, -1 }, \ + {A39X_DB_69XX_ID, -1 }, \ +}; +#endif /* CONFIG_ARMADA_38X */ +#endif /* CONFIG_CUSTOMER_BOARD_SUPPORT */ + +u32 mv_board_tclk_get(void); +u32 mv_board_id_get(void); +u32 mv_board_id_index_get(u32 board_id); +u32 sys_env_unit_max_num_get(enum unit_id unit); +enum suspend_wakeup_status sys_env_suspend_wakeup_check(void); +u8 sys_env_device_rev_get(void); +u32 sys_env_device_id_get(void); +u16 sys_env_model_get(void); +struct dlb_config *sys_env_dlb_config_ptr_get(void); +u32 sys_env_get_topology_update_info( + struct topology_update_info *topology_update_info); +u32 sys_env_get_cs_ena_from_reg(void); + +#endif /* _SYS_ENV_LIB_H */ diff --git a/arch/arm/mach-mvebu/serdes/Makefile b/arch/arm/mach-mvebu/serdes/axp/Makefile index a380fee..a380fee 100644 --- a/arch/arm/mach-mvebu/serdes/Makefile +++ b/arch/arm/mach-mvebu/serdes/axp/Makefile diff --git a/arch/arm/mach-mvebu/serdes/board_env_spec.h b/arch/arm/mach-mvebu/serdes/axp/board_env_spec.h index 36e0ed8..36e0ed8 100644 --- a/arch/arm/mach-mvebu/serdes/board_env_spec.h +++ b/arch/arm/mach-mvebu/serdes/axp/board_env_spec.h diff --git a/arch/arm/mach-mvebu/serdes/high_speed_env_lib.c b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_lib.c index 702273a..702273a 100644 --- a/arch/arm/mach-mvebu/serdes/high_speed_env_lib.c +++ b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_lib.c diff --git a/arch/arm/mach-mvebu/serdes/high_speed_env_spec.c b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_spec.c index 115ec2c..115ec2c 100644 --- a/arch/arm/mach-mvebu/serdes/high_speed_env_spec.c +++ b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_spec.c diff --git a/arch/arm/mach-mvebu/serdes/high_speed_env_spec.h b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_spec.h index e5aa1b0..e10574e 100644 --- a/arch/arm/mach-mvebu/serdes/high_speed_env_spec.h +++ b/arch/arm/mach-mvebu/serdes/axp/high_speed_env_spec.h @@ -7,7 +7,7 @@ #ifndef __HIGHSPEED_ENV_SPEC_H #define __HIGHSPEED_ENV_SPEC_H -#include "../../../drivers/ddr/mvebu/ddr3_hw_training.h" +#include "../../../drivers/ddr/marvell/axp/ddr3_hw_training.h" typedef enum { SERDES_UNIT_UNCONNECTED = 0x0, diff --git a/arch/arm/mach-mvebu/spl.c b/arch/arm/mach-mvebu/spl.c index 402e520..e65f6ca 100644 --- a/arch/arm/mach-mvebu/spl.c +++ b/arch/arm/mach-mvebu/spl.c @@ -26,8 +26,17 @@ void board_init_f(ulong dummy) /* Linux expects the internal registers to be at 0xf1000000 */ arch_cpu_init(); + /* + * Pin muxing needs to be done before UART output, since + * on A38x the UART pins need some re-muxing for output + * to work. + */ + board_early_init_f(); + preloader_console_init(); + timer_init(); + /* First init the serdes PHY's */ serdes_phy_config(); diff --git a/arch/arm/mach-mvebu/timer.c b/arch/arm/mach-mvebu/timer.c index 40c4bc2..c516c41 100644 --- a/arch/arm/mach-mvebu/timer.c +++ b/arch/arm/mach-mvebu/timer.c @@ -41,6 +41,8 @@ #define timestamp gd->arch.tbl #define lastdec gd->arch.lastinc +static int init_done; + /* Timer reload and current value registers */ struct kwtmr_val { u32 reload; /* Timer reload reg */ @@ -112,6 +114,11 @@ void __udelay(unsigned long usec) */ int timer_init(void) { + /* Only init the timer once */ + if (init_done) + return 0; + init_done = 1; + /* load value into timer */ writel(TIMER_LOAD_VAL, CNTMR_RELOAD_REG(UBOOT_CNTR)); writel(TIMER_LOAD_VAL, CNTMR_VAL_REG(UBOOT_CNTR)); diff --git a/arch/arm/mach-zynq/clk.c b/arch/arm/mach-zynq/clk.c index d2885dc..6444be8 100644 --- a/arch/arm/mach-zynq/clk.c +++ b/arch/arm/mach-zynq/clk.c @@ -48,11 +48,11 @@ DECLARE_GLOBAL_DATA_PTR; struct clk; /** - * struct clk_ops: + * struct zynq_clk_ops: * @set_rate: Function pointer to set_rate() implementation * @get_rate: Function pointer to get_rate() implementation */ -struct clk_ops { +struct zynq_clk_ops { int (*set_rate)(struct clk *clk, unsigned long rate); unsigned long (*get_rate)(struct clk *clk); }; @@ -72,7 +72,7 @@ struct clk { enum zynq_clk parent; unsigned int flags; u32 *reg; - struct clk_ops ops; + struct zynq_clk_ops ops; }; #define ZYNQ_CLK_FLAGS_HAS_2_DIVS 1 diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index e6ddb17..3a7f5a0 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -20,7 +20,7 @@ static struct udevice *map_dev; unsigned long map_len; #endif -void reset_cpu(ulong ignored) +void sandbox_exit(void) { /* Do this here while it still has an effect */ os_fd_restore(); @@ -34,13 +34,6 @@ void reset_cpu(ulong ignored) os_exit(0); } -int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - reset_cpu(0); - - return 0; -} - /* delay x useconds */ void __udelay(unsigned long usec) { diff --git a/arch/sandbox/cpu/state.c b/arch/sandbox/cpu/state.c index cae731c..7e5d03e 100644 --- a/arch/sandbox/cpu/state.c +++ b/arch/sandbox/cpu/state.c @@ -345,6 +345,10 @@ int state_init(void) state->ram_buf = os_malloc(state->ram_size); assert(state->ram_buf); + /* No reset yet, so mark it as such. Always allow power reset */ + state->last_reset = RESET_COUNT; + state->reset_allowed[RESET_POWER] = true; + /* * Example of how to use GPIOs: * diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index c25614a..c948df8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -4,7 +4,7 @@ model = "sandbox"; compatible = "sandbox"; #address-cells = <1>; - #size-cells = <0>; + #size-cells = <1>; aliases { console = &uart0; @@ -28,7 +28,7 @@ }; a-test { - reg = <0>; + reg = <0 1>; compatible = "denx,u-boot-fdt-test"; ping-expect = <0>; ping-add = <0>; @@ -41,16 +41,16 @@ }; junk { - reg = <1>; + reg = <1 1>; compatible = "not,compatible"; }; no-compatible { - reg = <2>; + reg = <2 1>; }; b-test { - reg = <3>; + reg = <3 1>; compatible = "denx,u-boot-fdt-test"; ping-expect = <3>; ping-add = <3>; @@ -60,7 +60,7 @@ #address-cells = <1>; #size-cells = <0>; compatible = "denx,u-boot-test-bus"; - reg = <3>; + reg = <3 1>; ping-expect = <4>; ping-add = <4>; c-test@5 { @@ -84,14 +84,14 @@ }; d-test { - reg = <3>; + reg = <3 1>; ping-expect = <6>; ping-add = <6>; compatible = "google,another-fdt-test"; }; e-test { - reg = <3>; + reg = <3 1>; ping-expect = <6>; ping-add = <6>; compatible = "google,another-fdt-test"; @@ -105,6 +105,10 @@ compatible = "denx,u-boot-fdt-test"; }; + clk@0 { + compatible = "sandbox,clk"; + }; + eth@10002000 { compatible = "sandbox,eth"; reg = <0x10002000 0x1000>; @@ -142,7 +146,7 @@ i2c@0 { #address-cells = <1>; #size-cells = <0>; - reg = <0>; + reg = <0 1>; compatible = "sandbox,i2c"; clock-frequency = <100000>; eeprom@2c { @@ -176,6 +180,24 @@ }; }; + leds { + compatible = "gpio-leds"; + + iracibble { + gpios = <&gpio_a 1 0>; + label = "sandbox:red"; + }; + + martinet { + gpios = <&gpio_a 2 0>; + label = "sandbox:green"; + }; + }; + + mmc { + compatible = "sandbox,mmc"; + }; + pci: pci-controller { compatible = "sandbox,pci"; device_type = "pci"; @@ -192,10 +214,22 @@ }; }; + ram { + compatible = "sandbox,ram"; + }; + + reset@0 { + compatible = "sandbox,warm-reset"; + }; + + reset@1 { + compatible = "sandbox,reset"; + }; + spi@0 { #address-cells = <1>; #size-cells = <0>; - reg = <0>; + reg = <0 1>; compatible = "sandbox,spi"; cs-gpios = <0>, <&gpio_a 0>; spi.bin@0 { @@ -206,6 +240,19 @@ }; }; + syscon@0 { + compatible = "sandbox,syscon0"; + reg = <0x10 4>; + }; + + syscon@1 { + compatible = "sandbox,syscon1"; + reg = <0x20 5 + 0x28 6 + 0x30 7 + 0x38 8>; + }; + uart0: serial { compatible = "sandbox,serial"; u-boot,dm-pre-reloc; diff --git a/arch/sandbox/include/asm/state.h b/arch/sandbox/include/asm/state.h index a57480a..2bd28f6 100644 --- a/arch/sandbox/include/asm/state.h +++ b/arch/sandbox/include/asm/state.h @@ -7,6 +7,7 @@ #define __SANDBOX_STATE_H #include <config.h> +#include <reset.h> #include <stdbool.h> #include <linux/stringify.h> @@ -59,6 +60,8 @@ struct sandbox_state { bool write_state; /* Write sandbox state on exit */ bool ignore_missing_state_on_read; /* No error if state missing */ bool show_lcd; /* Show LCD on start-up */ + enum reset_t last_reset; /* Last reset type */ + bool reset_allowed[RESET_COUNT]; /* Allowed reset types */ enum state_terminal_raw term_raw; /* Terminal raw/cooked */ /* Pointer to information for each SPI bus/cs */ diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 91a5c79..d3c7851 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -17,6 +17,25 @@ #define SANDBOX_PCI_CLASS_CODE PCI_CLASS_CODE_COMM #define SANDBOX_PCI_CLASS_SUB_CODE PCI_CLASS_SUB_CODE_COMM_SERIAL +#define SANDBOX_CLK_RATE 32768 + +enum { + PERIPH_ID_FIRST = 0, + PERIPH_ID_SPI = PERIPH_ID_FIRST, + PERIPH_ID_I2C, + PERIPH_ID_PCI, + + PERIPH_ID_COUNT, +}; + +/* System controller driver data */ +enum { + SYSCON0 = 32, + SYSCON1, + + SYSCON_COUNT +}; + /** * sandbox_i2c_set_test_mode() - set test mode for running unit tests * diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h index da87cc3..2f3c3f9 100644 --- a/arch/sandbox/include/asm/u-boot-sandbox.h +++ b/arch/sandbox/include/asm/u-boot-sandbox.h @@ -83,4 +83,7 @@ void sandbox_set_enable_pci_map(int enable); */ int sandbox_read_fdt_from_file(void); +/* Exit sandbox (quit U-Boot) */ +void sandbox_exit(void); + #endif /* _U_BOOT_SANDBOX_H_ */ diff --git a/arch/x86/include/asm/interrupt.h b/arch/x86/include/asm/interrupt.h index 0a75f89..00cbe07 100644 --- a/arch/x86/include/asm/interrupt.h +++ b/arch/x86/include/asm/interrupt.h @@ -16,10 +16,6 @@ /* arch/x86/cpu/interrupts.c */ void set_vector(u8 intnum, void *routine); -/* arch/x86/lib/interrupts.c */ -void disable_irq(int irq); -void enable_irq(int irq); - /* Architecture specific functions */ void mask_irq(int irq); void unmask_irq(int irq); diff --git a/board/Marvell/db-88f6820-gp/README b/board/Marvell/db-88f6820-gp/README new file mode 100644 index 0000000..9bea5b3 --- /dev/null +++ b/board/Marvell/db-88f6820-gp/README @@ -0,0 +1,18 @@ +Update from original Marvell U-Boot to mainline U-Boot: +------------------------------------------------------- + +The resulting image including the SPL binary with the +full DDR setup is "u-boot-spl.kwb". + +To update the SPI NOR flash, please use the following +command: + +=> sf probe;tftpboot 2000000 db-88f6820-gp/u-boot-spl.kwb;\ +sf update 2000000 0 60000 + +Note that the original Marvell U-Boot seems to have +problems with the "sf update" command. This does not +work reliable. So here this command should be used: + +=> sf probe;tftpboot 2000000 db-88f6820-gp/u-boot-spl.kwb;\ +sf erase 0 60000;sf write 2000000 0 60000 diff --git a/board/Marvell/db-88f6820-gp/db-88f6820-gp.c b/board/Marvell/db-88f6820-gp/db-88f6820-gp.c index 51ac495..e661fa1 100644 --- a/board/Marvell/db-88f6820-gp/db-88f6820-gp.c +++ b/board/Marvell/db-88f6820-gp/db-88f6820-gp.c @@ -11,6 +11,8 @@ #include <asm/arch/cpu.h> #include <asm/arch/soc.h> +#include "../drivers/ddr/marvell/a38x/ddr3_a38x_topology.h" + DECLARE_GLOBAL_DATA_PTR; #define BIT(nr) (1UL << (nr)) @@ -54,6 +56,35 @@ static struct marvell_io_exp io_exp[] = { { 0x21, 3, 0xC0 } /* Output Data, register#1 */ }; +/* + * Define the DDR layout / topology here in the board file. This will + * be used by the DDR3 init code in the SPL U-Boot version to configure + * the DDR3 controller. + */ +static struct hws_topology_map board_topology_map = { + 0x1, /* active interfaces */ + /* cs_mask, mirror, dqs_swap, ck_swap X PUPs */ + { { { {0x1, 0, 0, 0}, + {0x1, 0, 0, 0}, + {0x1, 0, 0, 0}, + {0x1, 0, 0, 0}, + {0x1, 0, 0, 0} }, + SPEED_BIN_DDR_1866L, /* speed_bin */ + BUS_WIDTH_8, /* memory_width */ + MEM_4G, /* mem_size */ + DDR_FREQ_800, /* frequency */ + 0, 0, /* cas_l cas_wl */ + HWS_TEMP_LOW} }, /* temperature */ + 5, /* Num Of Bus Per Interface*/ + BUS_MASK_32BIT /* Busses mask */ +}; + +struct hws_topology_map *ddr3_get_topology_map(void) +{ + /* Return the board topology as defined in the board code */ + return &board_topology_map; +} + int board_early_init_f(void) { /* Configure MPP */ diff --git a/board/Marvell/db-88f6820-gp/kwbimage.cfg b/board/Marvell/db-88f6820-gp/kwbimage.cfg index e812454..cc05792 100644 --- a/board/Marvell/db-88f6820-gp/kwbimage.cfg +++ b/board/Marvell/db-88f6820-gp/kwbimage.cfg @@ -9,4 +9,4 @@ VERSION 1 BOOT_FROM spi # Binary Header (bin_hdr) with DDR3 training code -BINARY board/Marvell/db-88f6820-gp/binary.0 0000005b 00000068 +BINARY spl/u-boot-spl.bin 0000005b 00000068 diff --git a/board/maxbcm/maxbcm.c b/board/maxbcm/maxbcm.c index 2fbb90c..119ba4c 100644 --- a/board/maxbcm/maxbcm.c +++ b/board/maxbcm/maxbcm.c @@ -11,8 +11,8 @@ #include <asm/arch/soc.h> #include <linux/mbus.h> -#include "../drivers/ddr/mvebu/ddr3_hw_training.h" -#include "../arch/arm/mach-mvebu/serdes/high_speed_env_spec.h" +#include "../drivers/ddr/marvell/axp/ddr3_hw_training.h" +#include "../arch/arm/mach-mvebu/serdes/axp/high_speed_env_spec.h" DECLARE_GLOBAL_DATA_PTR; diff --git a/common/cmd_tsi148.c b/common/cmd_tsi148.c index dc488b2..ea96d0f 100644 --- a/common/cmd_tsi148.c +++ b/common/cmd_tsi148.c @@ -16,8 +16,8 @@ #include <tsi148.h> -#define PCI_VENDOR PCI_VENDOR_ID_TUNDRA -#define PCI_DEVICE PCI_DEVICE_ID_TUNDRA_TSI148 +#define LPCI_VENDOR PCI_VENDOR_ID_TUNDRA +#define LPCI_DEVICE PCI_DEVICE_ID_TUNDRA_TSI148 typedef struct _TSI148_DEV TSI148_DEV; @@ -41,7 +41,7 @@ int tsi148_init(void) pci_dev_t busdevfn; unsigned int val; - busdevfn = pci_find_device(PCI_VENDOR, PCI_DEVICE, 0); + busdevfn = pci_find_device(LPCI_VENDOR, LPCI_DEVICE, 0); if (busdevfn == -1) { puts("Tsi148: No Tundra Tsi148 found!\n"); return -1; @@ -68,7 +68,7 @@ int tsi148_init(void) /* check mapping */ debug("Tsi148: Read via mapping, PCI_ID = %08X\n", readl(&dev->uregs->pci_id)); - if (((PCI_DEVICE << 16) | PCI_VENDOR) != readl(&dev->uregs->pci_id)) { + if (((LPCI_DEVICE << 16) | LPCI_VENDOR) != readl(&dev->uregs->pci_id)) { printf("Tsi148: Cannot read PCI-ID via Mapping: %08x\n", readl(&dev->uregs->pci_id)); result = -1; diff --git a/common/cmd_usb.c b/common/cmd_usb.c index eab55cd..0ade775 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -22,8 +22,8 @@ #ifdef CONFIG_USB_STORAGE static int usb_stor_curr_dev = -1; /* current device */ #endif -#ifdef CONFIG_USB_HOST_ETHER -static int usb_ether_curr_dev = -1; /* current ethernet device */ +#if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH) +static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */ #endif /* some display routines (info command) */ @@ -355,12 +355,12 @@ static void usb_show_tree_graph(struct usb_device *dev, char *pre) #endif /* check if we are the last one */ #ifdef CONFIG_DM_USB - last_child = device_is_last_sibling(dev->dev); + /* Not the root of the usb tree? */ + if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) { + last_child = device_is_last_sibling(dev->dev); #else - last_child = (dev->parent != NULL); -#endif - if (last_child) { -#ifndef CONFIG_DM_USB + if (dev->parent != NULL) { /* not root? */ + last_child = 1; for (i = 0; i < dev->parent->maxchild; i++) { /* search for children */ if (dev->parent->children[i] == dev) { @@ -530,11 +530,14 @@ static void do_usb_start(void) /* try to recognize storage devices immediately */ usb_stor_curr_dev = usb_stor_scan(1); #endif -#endif #ifdef CONFIG_USB_HOST_ETHER +# ifdef CONFIG_DM_ETH +# error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" +# endif /* try to recognize ethernet devices immediately */ usb_ether_curr_dev = usb_host_eth_scan(1); #endif +#endif #ifdef CONFIG_USB_KEYBOARD drv_usb_kbd_init(); #endif @@ -630,12 +633,11 @@ static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) bus; uclass_next_device(&bus)) { struct usb_device *udev; - struct udevice *hub; + struct udevice *dev; - device_find_first_child(bus, &hub); - if (device_get_uclass_id(hub) == UCLASS_USB_HUB && - device_active(hub)) { - udev = dev_get_parentdata(hub); + device_find_first_child(bus, &dev); + if (dev && device_active(dev)) { + udev = dev_get_parentdata(dev); usb_show_tree(udev); } } diff --git a/common/console.c b/common/console.c index 0058222..ace206c 100644 --- a/common/console.c +++ b/common/console.c @@ -6,6 +6,7 @@ */ #include <common.h> +#include <debug_uart.h> #include <stdarg.h> #include <iomux.h> #include <malloc.h> @@ -455,11 +456,19 @@ static inline void print_pre_console_buffer(int flushpoint) {} void putc(const char c) { #ifdef CONFIG_SANDBOX + /* sandbox can send characters to stdout before it has a console */ if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { os_putc(c); return; } #endif +#ifdef CONFIG_DEBUG_UART + /* if we don't have a console yet, use the debug UART */ + if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { + printch(c); + return; + } +#endif #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) return; @@ -491,7 +500,18 @@ void puts(const char *s) return; } #endif +#ifdef CONFIG_DEBUG_UART + if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { + while (*s) { + int ch = *s++; + printch(ch); + if (ch == '\n') + printch('\r'); + } + return; + } +#endif #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) return; @@ -521,11 +541,6 @@ int printf(const char *fmt, ...) uint i; char printbuffer[CONFIG_SYS_PBSIZE]; -#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER) - if (!gd->have_console) - return 0; -#endif - va_start(args, fmt); /* For this to work, printbuffer must be larger than diff --git a/common/image.c b/common/image.c index f0f0135..9efacf8 100644 --- a/common/image.c +++ b/common/image.c @@ -543,6 +543,15 @@ void genimg_print_time(time_t timestamp) } #endif +const table_entry_t *get_table_entry(const table_entry_t *table, int id) +{ + for (; table->id >= 0; ++table) { + if (table->id == id) + return table; + } + return NULL; +} + /** * get_table_entry_name - translate entry id to long name * @table: pointer to a translation table for entries of a specific type @@ -559,15 +568,14 @@ void genimg_print_time(time_t timestamp) */ char *get_table_entry_name(const table_entry_t *table, char *msg, int id) { - for (; table->id >= 0; ++table) { - if (table->id == id) + table = get_table_entry(table, id); + if (!table) + return msg; #if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) - return table->lname; + return table->lname; #else - return table->lname + gd->reloc_off; + return table->lname + gd->reloc_off; #endif - } - return (msg); } const char *genimg_get_os_name(uint8_t os) @@ -586,6 +594,20 @@ const char *genimg_get_type_name(uint8_t type) return (get_table_entry_name(uimage_type, "Unknown Image", type)); } +const char *genimg_get_type_short_name(uint8_t type) +{ + const table_entry_t *table; + + table = get_table_entry(uimage_type, type); + if (!table) + return "unknown"; +#if defined(USE_HOSTCC) || !defined(CONFIG_NEEDS_MANUAL_RELOC) + return table->sname; +#else + return table->sname + gd->reloc_off; +#endif +} + const char *genimg_get_comp_name(uint8_t comp) { return (get_table_entry_name(uimage_comp, "Unknown Compression", @@ -610,34 +632,18 @@ int get_table_entry_id(const table_entry_t *table, const char *table_name, const char *name) { const table_entry_t *t; -#ifdef USE_HOSTCC - int first = 1; - - for (t = table; t->id >= 0; ++t) { - if (t->sname && strcasecmp(t->sname, name) == 0) - return(t->id); - } - fprintf(stderr, "\nInvalid %s Type - valid names are", table_name); - for (t = table; t->id >= 0; ++t) { - if (t->sname == NULL) - continue; - fprintf(stderr, "%c %s", (first) ? ':' : ',', t->sname); - first = 0; - } - fprintf(stderr, "\n"); -#else for (t = table; t->id >= 0; ++t) { #ifdef CONFIG_NEEDS_MANUAL_RELOC - if (t->sname && strcmp(t->sname + gd->reloc_off, name) == 0) + if (t->sname && strcasecmp(t->sname + gd->reloc_off, name) == 0) #else - if (t->sname && strcmp(t->sname, name) == 0) + if (t->sname && strcasecmp(t->sname, name) == 0) #endif return (t->id); } debug("Invalid %s Type: %s\n", table_name, name); -#endif /* USE_HOSTCC */ - return (-1); + + return -1; } int genimg_get_os_id(const char *name) diff --git a/common/spl/spl.c b/common/spl/spl.c index aeb0645..94b01da 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -148,18 +148,12 @@ static void spl_ram_load_image(void) } #endif -void board_init_r(gd_t *dummy1, ulong dummy2) +int spl_init(void) { - u32 boot_device; int ret; - debug(">>spl:board_init_r()\n"); - -#if defined(CONFIG_SYS_SPL_MALLOC_START) - mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, - CONFIG_SYS_SPL_MALLOC_SIZE); - gd->flags |= GD_FLG_FULL_MALLOC_INIT; -#elif defined(CONFIG_SYS_MALLOC_F_LEN) + debug("spl_init()\n"); +#if defined(CONFIG_SYS_MALLOC_F_LEN) gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN; gd->malloc_ptr = 0; #endif @@ -168,17 +162,36 @@ void board_init_r(gd_t *dummy1, ulong dummy2) ret = fdtdec_setup(); if (ret) { debug("fdtdec_setup() returned error %d\n", ret); - hang(); + return ret; } } if (IS_ENABLED(CONFIG_SPL_DM)) { ret = dm_init_and_scan(true); if (ret) { debug("dm_init_and_scan() returned error %d\n", ret); - hang(); + return ret; } } + gd->flags |= GD_FLG_SPL_INIT; + + return 0; +} +void board_init_r(gd_t *dummy1, ulong dummy2) +{ + u32 boot_device; + + debug(">>spl:board_init_r()\n"); + +#if defined(CONFIG_SYS_SPL_MALLOC_START) + mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, + CONFIG_SYS_SPL_MALLOC_SIZE); + gd->flags |= GD_FLG_FULL_MALLOC_INIT; +#endif + if (!(gd->flags & GD_FLG_SPL_INIT)) { + if (spl_init()) + hang(); + } #ifndef CONFIG_PPC /* * timer_init() does not exist on PPC systems. The timer is initialized @@ -285,6 +298,7 @@ void board_init_r(gd_t *dummy1, ulong dummy2) gd->malloc_ptr / 1024); #endif + debug("loaded - jumping to U-Boot..."); jump_to_image_no_args(&spl_image); } diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index 552f80d..5f1cfbf 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -7,6 +7,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <dm.h> #include <spl.h> #include <linux/compiler.h> #include <asm/u-boot.h> @@ -26,11 +27,14 @@ static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector) /* read image header to find the image size & load address */ count = mmc->block_dev.block_read(0, sector, 1, header); + debug("read sector %lx, count=%lu\n", sector, count); if (count == 0) goto end; - if (image_get_magic(header) != IH_MAGIC) + if (image_get_magic(header) != IH_MAGIC) { + puts("bad magic\n"); return -1; + } spl_parse_image_header(header); @@ -40,7 +44,9 @@ static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector) /* Read the header too to avoid extra memcpy */ count = mmc->block_dev.block_read(0, sector, image_size_sectors, - (void *) spl_image.load_addr); + (void *)spl_image.load_addr); + debug("read %x sectors to %x\n", image_size_sectors, + spl_image.load_addr); end: if (count == 0) { @@ -96,9 +102,18 @@ void spl_mmc_load_image(void) { struct mmc *mmc; u32 boot_mode; - int err; + int err = 0; __maybe_unused int part; +#ifdef CONFIG_DM_MMC + struct udevice *dev; + + mmc_initialize(NULL); + err = uclass_get_device(UCLASS_MMC, 0, &dev); + mmc = NULL; + if (!err) + mmc = mmc_get_mmc_dev(dev); +#else mmc_initialize(gd->bd); /* We register only one device. So, the dev id is always 0 */ @@ -109,8 +124,11 @@ void spl_mmc_load_image(void) #endif hang(); } +#endif + + if (!err) + err = mmc_init(mmc); - err = mmc_init(mmc); if (err) { #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT printf("spl: mmc init failed with error: %d\n", err); diff --git a/common/usb.c b/common/usb.c index 7ff8ac5..fbaf8ec 100644 --- a/common/usb.c +++ b/common/usb.c @@ -911,26 +911,24 @@ __weak int usb_alloc_device(struct usb_device *udev) } #endif /* !CONFIG_DM_USB */ -#ifndef CONFIG_DM_USB -int usb_legacy_port_reset(struct usb_device *hub, int portnr) +static int usb_hub_port_reset(struct usb_device *dev, struct usb_device *hub) { if (hub) { unsigned short portstatus; int err; /* reset the port for the second time */ - err = legacy_hub_port_reset(hub, portnr - 1, &portstatus); + err = legacy_hub_port_reset(hub, dev->portnr - 1, &portstatus); if (err < 0) { - printf("\n Couldn't reset port %i\n", portnr); + printf("\n Couldn't reset port %i\n", dev->portnr); return err; } } else { - usb_reset_root_port(); + usb_reset_root_port(dev); } return 0; } -#endif static int get_descriptor_len(struct usb_device *dev, int len, int expect_len) { @@ -1032,7 +1030,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) } static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, - struct usb_device *parent, int portnr) + struct usb_device *parent) { int err; @@ -1050,7 +1048,7 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, err = usb_setup_descriptor(dev, do_read); if (err) return err; - err = usb_legacy_port_reset(parent, portnr); + err = usb_hub_port_reset(dev, parent); if (err) return err; @@ -1128,7 +1126,7 @@ int usb_select_config(struct usb_device *dev) } int usb_setup_device(struct usb_device *dev, bool do_read, - struct usb_device *parent, int portnr) + struct usb_device *parent) { int addr; int ret; @@ -1137,7 +1135,7 @@ int usb_setup_device(struct usb_device *dev, bool do_read, addr = dev->devnum; dev->devnum = 0; - ret = usb_prepare_device(dev, addr, do_read, parent, portnr); + ret = usb_prepare_device(dev, addr, do_read, parent); if (ret) return ret; ret = usb_select_config(dev); @@ -1167,7 +1165,7 @@ int usb_new_device(struct usb_device *dev) #ifdef CONFIG_USB_XHCI do_read = false; #endif - err = usb_setup_device(dev, do_read, dev->parent, dev->portnr); + err = usb_setup_device(dev, do_read, dev->parent); if (err) return err; diff --git a/common/usb_hub.c b/common/usb_hub.c index be01f4f..f621ddb 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -652,6 +652,6 @@ static const struct usb_device_id hub_id_table[] = { { } /* Terminating entry */ }; -USB_DEVICE(usb_generic_hub, hub_id_table); +U_BOOT_USB_DEVICE(usb_generic_hub, hub_id_table); #endif diff --git a/common/usb_kbd.c b/common/usb_kbd.c index e2af67d..0227024 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -540,8 +540,8 @@ int drv_usb_kbd_init(void) debug("%s: Probing for keyboard\n", __func__); #ifdef CONFIG_DM_USB /* - * TODO: We should add USB_DEVICE() declarations to each USB ethernet - * driver and then most of this file can be removed. + * TODO: We should add U_BOOT_USB_DEVICE() declarations to each USB + * keyboard driver and then most of this file can be removed. */ struct udevice *bus; struct uclass *uc; diff --git a/common/usb_storage.c b/common/usb_storage.c index cc9b3e3..b978430 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -1442,6 +1442,6 @@ static const struct usb_device_id mass_storage_id_table[] = { { } /* Terminating entry */ }; -USB_DEVICE(usb_mass_storage, mass_storage_id_table); +U_BOOT_USB_DEVICE(usb_mass_storage, mass_storage_id_table); #endif diff --git a/configs/db-88f6820-gp_defconfig b/configs/db-88f6820-gp_defconfig index 569ddfd..0ff6706 100644 --- a/configs/db-88f6820-gp_defconfig +++ b/configs/db-88f6820-gp_defconfig @@ -1,3 +1,4 @@ +CONFIG_SPL=y CONFIG_ARM=y CONFIG_TARGET_DB_88F6820_GP=y # CONFIG_CMD_IMLS is not set diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 598519d..5535746 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -18,6 +18,7 @@ CONFIG_PCI_SANDBOX=y CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_SANDBOX=y CONFIG_CMD_CROS_EC=y +CONFIG_CMD_DHRYSTONE=y CONFIG_CROS_EC=y CONFIG_CROS_EC_SANDBOX=y CONFIG_DM_ETH=y @@ -44,3 +45,10 @@ CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y CONFIG_UT_ENV=y +CONFIG_CLK=y +CONFIG_RESET=y +CONFIG_RAM=y +CONFIG_DM_MMC=y +CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_SYSCON=y diff --git a/doc/device-tree-bindings/leds/common.txt b/doc/device-tree-bindings/leds/common.txt new file mode 100644 index 0000000..2d88816 --- /dev/null +++ b/doc/device-tree-bindings/leds/common.txt @@ -0,0 +1,23 @@ +Common leds properties. + +Optional properties for child nodes: +- label : The label for this LED. If omitted, the label is + taken from the node name (excluding the unit address). + +- linux,default-trigger : This parameter, if present, is a + string defining the trigger assigned to the LED. Current triggers are: + "backlight" - LED will act as a back-light, controlled by the framebuffer + system + "default-on" - LED will turn on (but for leds-gpio see "default-state" + property in Documentation/devicetree/bindings/gpio/led.txt) + "heartbeat" - LED "double" flashes at a load average based rate + "ide-disk" - LED indicates disk activity + "timer" - LED flashes at a fixed, configurable rate + +Examples: + +system-status { + label = "Status"; + linux,default-trigger = "heartbeat"; + ... +}; diff --git a/doc/device-tree-bindings/leds/leds-gpio.txt b/doc/device-tree-bindings/leds/leds-gpio.txt new file mode 100644 index 0000000..df1b308 --- /dev/null +++ b/doc/device-tree-bindings/leds/leds-gpio.txt @@ -0,0 +1,52 @@ +LEDs connected to GPIO lines + +Required properties: +- compatible : should be "gpio-leds". + +Each LED is represented as a sub-node of the gpio-leds device. Each +node's name represents the name of the corresponding LED. + +LED sub-node properties: +- gpios : Should specify the LED's GPIO, see "gpios property" in + Documentation/devicetree/bindings/gpio/gpio.txt. Active low LEDs should be + indicated using flags in the GPIO specifier. +- label : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt +- default-state: (optional) The initial state of the LED. Valid + values are "on", "off", and "keep". If the LED is already on or off + and the default-state property is set the to same value, then no + glitch should be produced where the LED momentarily turns off (or + on). The "keep" setting will keep the LED at whatever its current + state is, without producing a glitch. The default is off if this + property is not present. + +Examples: + +leds { + compatible = "gpio-leds"; + hdd { + label = "IDE Activity"; + gpios = <&mcu_pio 0 1>; /* Active low */ + linux,default-trigger = "ide-disk"; + }; + + fault { + gpios = <&mcu_pio 1 0>; + /* Keep LED on if BIOS detected hardware fault */ + default-state = "keep"; + }; +}; + +run-control { + compatible = "gpio-leds"; + red { + gpios = <&mpc8572 6 0>; + default-state = "off"; + }; + green { + gpios = <&mpc8572 7 0>; + default-state = "on"; + }; +}; diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt index f0276b1..b891e84 100644 --- a/doc/driver-model/README.txt +++ b/doc/driver-model/README.txt @@ -301,6 +301,15 @@ device tree) and probe. Platform Data ------------- +*** Note: platform data is the old way of doing things. It is +*** basically a C structure which is passed to drivers to tell them about +*** platform-specific settings like the address of its registers, bus +*** speed, etc. Device tree is now the preferred way of handling this. +*** Unless you have a good reason not to use device tree (the main one +*** being you need serial support in SPL and don't have enough SRAM for +*** the cut-down device tree and libfdt libraries) you should stay away +*** from platform data. + Platform data is like Linux platform data, if you are familiar with that. It provides the board-specific information to start up a device. @@ -366,8 +375,12 @@ Device Tree ----------- While platdata is useful, a more flexible way of providing device data is -by using device tree. With device tree we replace the above code with the -following device tree fragment: +by using device tree. In U-Boot you should use this where possible. Avoid +sending patches which make use of the U_BOOT_DEVICE() macro unless strictly +necessary. + +With device tree we replace the above code with the following device tree +fragment: red-square { compatible = "demo-shape"; diff --git a/drivers/Kconfig b/drivers/Kconfig index c7e526c..092bc02 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -1,5 +1,7 @@ menu "Device Drivers" +source "drivers/clk/Kconfig" + source "drivers/core/Kconfig" source "drivers/cpu/Kconfig" @@ -20,6 +22,8 @@ source "drivers/net/Kconfig" source "drivers/input/Kconfig" +source "drivers/led/Kconfig" + source "drivers/serial/Kconfig" source "drivers/tpm/Kconfig" @@ -32,6 +36,8 @@ source "drivers/gpio/Kconfig" source "drivers/power/Kconfig" +source "drivers/ram/Kconfig" + source "drivers/hwmon/Kconfig" source "drivers/watchdog/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 405b64b..5a35148 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_CLK) += clk/ obj-$(CONFIG_DM) += core/ obj-$(CONFIG_DM_DEMO) += demo/ obj-$(CONFIG_BIOSEMU) += bios_emulator/ @@ -7,9 +8,11 @@ obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ +obj-$(CONFIG_LED) += led/ obj-y += misc/ obj-y += pcmcia/ obj-y += dfu/ +obj-$(CONFIG_RAM) += ram/ obj-y += rtc/ obj-y += sound/ obj-y += tpm/ diff --git a/drivers/block/mvsata_ide.c b/drivers/block/mvsata_ide.c index e54d564..52c1602 100644 --- a/drivers/block/mvsata_ide.c +++ b/drivers/block/mvsata_ide.c @@ -13,6 +13,8 @@ #include <asm/arch/orion5x.h> #elif defined(CONFIG_KIRKWOOD) #include <asm/arch/soc.h> +#elif defined(CONFIG_ARMADA_XP) +#include <linux/mbus.h> #endif /* SATA port registers */ @@ -90,6 +92,41 @@ struct mvsata_port_registers { #define MVSATA_STATUS_TIMEOUT -1 /* + * Registers for SATA MBUS memory windows + */ + +#define MVSATA_WIN_CONTROL(w) (MVEBU_AXP_SATA_BASE + 0x30 + ((w) << 4)) +#define MVSATA_WIN_BASE(w) (MVEBU_AXP_SATA_BASE + 0x34 + ((w) << 4)) + +/* + * Initialize SATA memory windows for Armada XP + */ + +#ifdef CONFIG_ARMADA_XP +static void mvsata_ide_conf_mbus_windows(void) +{ + const struct mbus_dram_target_info *dram; + int i; + + dram = mvebu_mbus_dram_info(); + + /* Disable windows, Set Size/Base to 0 */ + for (i = 0; i < 4; i++) { + writel(0, MVSATA_WIN_CONTROL(i)); + writel(0, MVSATA_WIN_BASE(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + MVSATA_WIN_CONTROL(i)); + writel(cs->base & 0xffff0000, MVSATA_WIN_BASE(i)); + } +} +#endif + +/* * Initialize one MVSATAHC port: set SControl's IPM to "always active" * and DET to "reset", then wait for SStatus's DET to become "device and * comm ok" (or time out after 50 us if no device), then set SControl's @@ -137,6 +174,10 @@ int ide_preinit(void) int ret = MVSATA_STATUS_TIMEOUT; int status; +#ifdef CONFIG_ARMADA_XP + mvsata_ide_conf_mbus_windows(); +#endif + /* Enable ATA port 0 (could be SATA port 0 or 1) if declared */ #if defined(CONFIG_SYS_ATA_IDE0_OFFSET) status = mvsata_ide_initialize_port( diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig new file mode 100644 index 0000000..07eb54c --- /dev/null +++ b/drivers/clk/Kconfig @@ -0,0 +1,19 @@ +config CLK + bool "Enable clock driver support" + depends on DM + help + This allows drivers to be provided for clock generators, including + oscillators and PLLs. Devices can use a common clock API to request + a particular clock rate and check on available clocks. Clocks can + feed into other clocks in a tree structure, with multiplexers to + choose the source for each clock. + +config SPL_CLK_SUPPORT + bool "Enable clock support in SPL" + depends on CLK + help + The clock subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use clock drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up clocks within SPL, and allows the same drivers to be + used as U-Boot proper. diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile new file mode 100644 index 0000000..bb89fb9 --- /dev/null +++ b/drivers/clk/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2015 Google, Inc +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CLK) += clk-uclass.o +obj-$(CONFIG_SANDBOX) += clk_sandbox.o diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c new file mode 100644 index 0000000..73dfd7d --- /dev/null +++ b/drivers/clk/clk-uclass.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <dm/lists.h> +#include <dm/root.h> + +ulong clk_get_rate(struct udevice *dev) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->get_rate) + return -ENOSYS; + + return ops->get_rate(dev); +} + +ulong clk_set_rate(struct udevice *dev, ulong rate) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->set_rate) + return -ENOSYS; + + return ops->set_rate(dev, rate); +} + +ulong clk_get_periph_rate(struct udevice *dev, int periph) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->get_periph_rate) + return -ENOSYS; + + return ops->get_periph_rate(dev, periph); +} + +ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->set_periph_rate) + return -ENOSYS; + + return ops->set_periph_rate(dev, periph, rate); +} + +UCLASS_DRIVER(clk) = { + .id = UCLASS_CLK, + .name = "clk", +}; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c new file mode 100644 index 0000000..058225a --- /dev/null +++ b/drivers/clk/clk_sandbox.c @@ -0,0 +1,85 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <asm/test.h> + +struct sandbox_clk_priv { + ulong rate; + ulong periph_rate[PERIPH_ID_COUNT]; +}; + +static ulong sandbox_clk_get_rate(struct udevice *dev) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + return priv->rate; +} + +static ulong sandbox_clk_set_rate(struct udevice *dev, ulong rate) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + if (!rate) + return -EINVAL; + priv->rate = rate; + return 0; +} + +ulong sandbox_get_periph_rate(struct udevice *dev, int periph) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + if (periph < PERIPH_ID_FIRST || periph >= PERIPH_ID_COUNT) + return -EINVAL; + return priv->periph_rate[periph]; +} + +ulong sandbox_set_periph_rate(struct udevice *dev, int periph, ulong rate) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + ulong old_rate; + + if (periph < PERIPH_ID_FIRST || periph >= PERIPH_ID_COUNT) + return -EINVAL; + old_rate = priv->periph_rate[periph]; + priv->periph_rate[periph] = rate; + + return old_rate; +} + +static int sandbox_clk_probe(struct udevice *dev) +{ + struct sandbox_clk_priv *priv = dev_get_priv(dev); + + priv->rate = SANDBOX_CLK_RATE; + + return 0; +} + +static struct clk_ops sandbox_clk_ops = { + .get_rate = sandbox_clk_get_rate, + .set_rate = sandbox_clk_set_rate, + .get_periph_rate = sandbox_get_periph_rate, + .set_periph_rate = sandbox_set_periph_rate, +}; + +static const struct udevice_id sandbox_clk_ids[] = { + { .compatible = "sandbox,clk" }, + { } +}; + +U_BOOT_DRIVER(clk_sandbox) = { + .name = "clk_sandbox", + .id = UCLASS_CLK, + .of_match = sandbox_clk_ids, + .ops = &sandbox_clk_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_clk_priv), + .probe = sandbox_clk_probe, +}; diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index 2861b43..e40372d 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -38,6 +38,10 @@ config DM_DEVICE_REMOVE device. This is not normally required in SPL, so by default this option is disabled for SPL. + Note that this may have undesirable results in the USB subsystem as + it causes unplugged devices to linger around in the dm-tree, and it + causes USB host controllers to not be stopped when booting the OS. + config DM_STDIO bool "Support stdio registration" depends on DM diff --git a/drivers/core/Makefile b/drivers/core/Makefile index a3fec38..5c2ead8 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -4,8 +4,11 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_DM) += device.o lists.o root.o uclass.o util.o +obj-y += device.o lists.o root.o uclass.o util.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_OF_CONTROL) += simple-bus.o endif obj-$(CONFIG_DM_DEVICE_REMOVE) += device-remove.o +obj-$(CONFIG_DM) += dump.o +obj-$(CONFIG_OF_CONTROL) += regmap.o +obj-$(CONFIG_OF_CONTROL) += syscon-uclass.o diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c index 6a16b4f..6b87f86 100644 --- a/drivers/core/device-remove.c +++ b/drivers/core/device-remove.c @@ -18,16 +18,7 @@ #include <dm/uclass-internal.h> #include <dm/util.h> -/** - * device_chld_unbind() - Unbind all device's children from the device - * - * On error, the function continues to unbind all children, and reports the - * first error. - * - * @dev: The device that is to be stripped of its children - * @return 0 on success, -ve on error - */ -static int device_chld_unbind(struct udevice *dev) +int device_unbind_children(struct udevice *dev) { struct udevice *pos, *n; int ret, saved_ret = 0; @@ -43,12 +34,7 @@ static int device_chld_unbind(struct udevice *dev) return saved_ret; } -/** - * device_chld_remove() - Stop all device's children - * @dev: The device whose children are to be removed - * @return 0 on success, -ve on error - */ -static int device_chld_remove(struct udevice *dev) +int device_remove_children(struct udevice *dev) { struct udevice *pos, *n; int ret; @@ -84,7 +70,7 @@ int device_unbind(struct udevice *dev) return ret; } - ret = device_chld_unbind(dev); + ret = device_unbind_children(dev); if (ret) return ret; @@ -159,7 +145,7 @@ int device_remove(struct udevice *dev) if (ret) return ret; - ret = device_chld_remove(dev); + ret = device_remove_children(dev); if (ret) goto err; diff --git a/drivers/core/device.c b/drivers/core/device.c index 85fd1fc..51b1b44 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -284,7 +284,6 @@ int device_probe_child(struct udevice *dev, void *parent_priv) goto fail; } - dev->flags |= DM_FLAG_ACTIVATED; if (drv->probe) { ret = drv->probe(dev); if (ret) { @@ -330,7 +329,7 @@ void *dev_get_platdata(struct udevice *dev) void *dev_get_parent_platdata(struct udevice *dev) { if (!dev) { - dm_warn("%s: null device", __func__); + dm_warn("%s: null device\n", __func__); return NULL; } @@ -340,7 +339,7 @@ void *dev_get_parent_platdata(struct udevice *dev) void *dev_get_uclass_platdata(struct udevice *dev) { if (!dev) { - dm_warn("%s: null device", __func__); + dm_warn("%s: null device\n", __func__); return NULL; } @@ -459,17 +458,42 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset, return -ENODEV; } -int device_get_child_by_of_offset(struct udevice *parent, int seq, +int device_get_child_by_of_offset(struct udevice *parent, int node, struct udevice **devp) { struct udevice *dev; int ret; *devp = NULL; - ret = device_find_child_by_of_offset(parent, seq, &dev); + ret = device_find_child_by_of_offset(parent, node, &dev); return device_get_device_tail(dev, ret, devp); } +static struct udevice *_device_find_global_by_of_offset(struct udevice *parent, + int of_offset) +{ + struct udevice *dev, *found; + + if (parent->of_offset == of_offset) + return parent; + + list_for_each_entry(dev, &parent->child_head, sibling_node) { + found = _device_find_global_by_of_offset(dev, of_offset); + if (found) + return found; + } + + return NULL; +} + +int device_get_global_by_of_offset(int of_offset, struct udevice **devp) +{ + struct udevice *dev; + + dev = _device_find_global_by_of_offset(gd->dm_root, of_offset); + return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp); +} + int device_find_first_child(struct udevice *parent, struct udevice **devp) { if (list_empty(&parent->child_head)) { diff --git a/drivers/core/dump.c b/drivers/core/dump.c new file mode 100644 index 0000000..fd4596e --- /dev/null +++ b/drivers/core/dump.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <dm/root.h> + +static void show_devices(struct udevice *dev, int depth, int last_flag) +{ + int i, is_last; + struct udevice *child; + char class_name[12]; + + /* print the first 11 characters to not break the tree-format. */ + strlcpy(class_name, dev->uclass->uc_drv->name, sizeof(class_name)); + printf(" %-11s [ %c ] ", class_name, + dev->flags & DM_FLAG_ACTIVATED ? '+' : ' '); + + for (i = depth; i >= 0; i--) { + is_last = (last_flag >> i) & 1; + if (i) { + if (is_last) + printf(" "); + else + printf("| "); + } else { + if (is_last) + printf("`-- "); + else + printf("|-- "); + } + } + + printf("%s\n", dev->name); + + list_for_each_entry(child, &dev->child_head, sibling_node) { + is_last = list_is_last(&child->sibling_node, &dev->child_head); + show_devices(child, depth + 1, (last_flag << 1) | is_last); + } +} + +void dm_dump_all(void) +{ + struct udevice *root; + + root = dm_root(); + if (root) { + printf(" Class Probed Name\n"); + printf("----------------------------------------\n"); + show_devices(root, -1, 0); + } +} + +/** + * dm_display_line() - Display information about a single device + * + * Displays a single line of information with an option prefix + * + * @dev: Device to display + */ +static void dm_display_line(struct udevice *dev) +{ + printf("- %c %s @ %08lx", + dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ', + dev->name, (ulong)map_to_sysmem(dev)); + if (dev->seq != -1 || dev->req_seq != -1) + printf(", seq %d, (req %d)", dev->seq, dev->req_seq); + puts("\n"); +} + +void dm_dump_uclass(void) +{ + struct uclass *uc; + int ret; + int id; + + for (id = 0; id < UCLASS_COUNT; id++) { + struct udevice *dev; + + ret = uclass_get(id, &uc); + if (ret) + continue; + + printf("uclass %d: %s\n", id, uc->uc_drv->name); + if (list_empty(&uc->dev_head)) + continue; + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + dm_display_line(dev); + } + puts("\n"); + } +} diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 0c49d99..2e52500 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -86,13 +86,13 @@ int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, drv = lists_driver_lookup_name(drv_name); if (!drv) { - printf("Cannot find driver '%s'\n", drv_name); + debug("Cannot find driver '%s'\n", drv_name); return -ENOENT; } ret = device_bind(parent, drv, dev_name, NULL, node, devp); if (ret) { - printf("Cannot create device named '%s' (err=%d)\n", - dev_name, ret); + debug("Cannot create device named '%s' (err=%d)\n", + dev_name, ret); return ret; } diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c new file mode 100644 index 0000000..519832f --- /dev/null +++ b/drivers/core/regmap.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <libfdt.h> +#include <malloc.h> +#include <mapmem.h> +#include <regmap.h> + +DECLARE_GLOBAL_DATA_PTR; + +int regmap_init_mem(struct udevice *dev, struct regmap **mapp) +{ + const void *blob = gd->fdt_blob; + struct regmap_range *range; + const fdt32_t *cell; + struct regmap *map; + int count; + int addr_len, size_len, both_len; + int parent; + int len; + + parent = dev->parent->of_offset; + addr_len = fdt_address_cells(blob, parent); + size_len = fdt_size_cells(blob, parent); + both_len = addr_len + size_len; + + cell = fdt_getprop(blob, dev->of_offset, "reg", &len); + len /= sizeof(*cell); + count = len / both_len; + if (!cell || !count) + return -EINVAL; + + map = malloc(sizeof(struct regmap)); + if (!map) + return -ENOMEM; + + if (count <= 1) { + map->range = &map->base_range; + } else { + map->range = malloc(count * sizeof(struct regmap_range)); + if (!map->range) { + free(map); + return -ENOMEM; + } + } + + map->base = fdtdec_get_number(cell, addr_len); + map->range_count = count; + + for (range = map->range; count > 0; + count--, cell += both_len, range++) { + range->start = fdtdec_get_number(cell, addr_len); + range->size = fdtdec_get_number(cell + addr_len, size_len); + } + + *mapp = map; + + return 0; +} + +void *regmap_get_range(struct regmap *map, unsigned int range_num) +{ + struct regmap_range *range; + + if (range_num >= map->range_count) + return NULL; + range = &map->range[range_num]; + + return map_sysmem(range->start, range->size); +} + +int regmap_uninit(struct regmap *map) +{ + if (map->range_count > 1) + free(map->range); + free(map); + + return 0; +} diff --git a/drivers/core/syscon-uclass.c b/drivers/core/syscon-uclass.c new file mode 100644 index 0000000..686c320 --- /dev/null +++ b/drivers/core/syscon-uclass.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <syscon.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/err.h> + +struct regmap *syscon_get_regmap(struct udevice *dev) +{ + struct syscon_uc_info *priv; + + if (device_get_uclass_id(dev) != UCLASS_SYSCON) + return ERR_PTR(-ENOEXEC); + priv = dev_get_uclass_priv(dev); + return priv->regmap; +} + +static int syscon_pre_probe(struct udevice *dev) +{ + struct syscon_uc_info *priv = dev_get_uclass_priv(dev); + + return regmap_init_mem(dev, &priv->regmap); +} + +struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_SYSCON, &uc); + if (ret) + return ERR_PTR(ret); + uclass_foreach_dev(dev, uc) { + if (dev->driver_data == driver_data) { + struct syscon_uc_info *priv; + int ret; + + ret = device_probe(dev); + if (ret) + return ERR_PTR(ret); + priv = dev_get_uclass_priv(dev); + + return priv->regmap; + } + } + + return ERR_PTR(-ENODEV); +} + +void *syscon_get_first_range(ulong driver_data) +{ + struct regmap *map; + + map = syscon_get_regmap_by_driver_data(driver_data); + if (IS_ERR(map)) + return map; + return regmap_get_range(map, 0); +} + +UCLASS_DRIVER(syscon) = { + .id = UCLASS_SYSCON, + .name = "syscon", + .per_device_auto_alloc_size = sizeof(struct syscon_uc_info), + .pre_probe = syscon_pre_probe, +}; diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 7de8173..aba9880 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -56,8 +56,8 @@ static int uclass_add(enum uclass_id id, struct uclass **ucp) *ucp = NULL; uc_drv = lists_uclass_lookup(id); if (!uc_drv) { - dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", - id); + debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", + id); return -ENOENT; } uc = calloc(1, sizeof(*uc)); diff --git a/drivers/ddr/marvell/a38x/Makefile b/drivers/ddr/marvell/a38x/Makefile new file mode 100644 index 0000000..bf6ea49 --- /dev/null +++ b/drivers/ddr/marvell/a38x/Makefile @@ -0,0 +1,19 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SPL_BUILD) += ddr3_a38x.o +obj-$(CONFIG_SPL_BUILD) += ddr3_a38x_training.o +obj-$(CONFIG_SPL_BUILD) += ddr3_debug.o +obj-$(CONFIG_SPL_BUILD) += ddr3_hws_hw_training.o +obj-$(CONFIG_SPL_BUILD) += ddr3_init.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_bist.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_centralization.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_db.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_hw_algo.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_ip_engine.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_leveling.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_pbs.o +obj-$(CONFIG_SPL_BUILD) += ddr3_training_static.o +obj-$(CONFIG_SPL_BUILD) += xor.o diff --git a/drivers/ddr/marvell/a38x/ddr3_a38x.c b/drivers/ddr/marvell/a38x/ddr3_a38x.c new file mode 100644 index 0000000..f469907 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_a38x.c @@ -0,0 +1,741 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define A38X_NUMBER_OF_INTERFACES 5 + +#define SAR_DEV_ID_OFFS 27 +#define SAR_DEV_ID_MASK 0x7 + +/* Termal Sensor Registers */ +#define TSEN_STATE_REG 0xe4070 +#define TSEN_STATE_OFFSET 31 +#define TSEN_STATE_MASK (0x1 << TSEN_STATE_OFFSET) +#define TSEN_CONF_REG 0xe4074 +#define TSEN_CONF_RST_OFFSET 8 +#define TSEN_CONF_RST_MASK (0x1 << TSEN_CONF_RST_OFFSET) +#define TSEN_STATUS_REG 0xe4078 +#define TSEN_STATUS_READOUT_VALID_OFFSET 10 +#define TSEN_STATUS_READOUT_VALID_MASK (0x1 << \ + TSEN_STATUS_READOUT_VALID_OFFSET) +#define TSEN_STATUS_TEMP_OUT_OFFSET 0 +#define TSEN_STATUS_TEMP_OUT_MASK (0x3ff << TSEN_STATUS_TEMP_OUT_OFFSET) + +static struct dfx_access interface_map[] = { + /* Pipe Client */ + { 0, 17 }, + { 1, 7 }, + { 1, 11 }, + { 0, 3 }, + { 1, 25 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } +}; + +/* This array hold the board round trip delay (DQ and CK) per <interface,bus> */ +struct trip_delay_element a38x_board_round_trip_delay_array[] = { + /* 1st board */ + /* Interface bus DQS-delay CK-delay */ + { 3952, 5060 }, + { 3192, 4493 }, + { 4785, 6677 }, + { 3413, 7267 }, + { 4282, 6086 }, /* ECC PUP */ + { 3952, 5134 }, + { 3192, 4567 }, + { 4785, 6751 }, + { 3413, 7341 }, + { 4282, 6160 }, /* ECC PUP */ + + /* 2nd board */ + /* Interface bus DQS-delay CK-delay */ + { 3952, 5060 }, + { 3192, 4493 }, + { 4785, 6677 }, + { 3413, 7267 }, + { 4282, 6086 }, /* ECC PUP */ + { 3952, 5134 }, + { 3192, 4567 }, + { 4785, 6751 }, + { 3413, 7341 }, + { 4282, 6160 } /* ECC PUP */ +}; + +#ifdef STATIC_ALGO_SUPPORT +/* package trace */ +static struct trip_delay_element a38x_package_round_trip_delay_array[] = { + /* IF BUS DQ_DELAY CK_DELAY */ + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } +}; + +static int a38x_silicon_delay_offset[] = { + /* board 0 */ + 0, + /* board 1 */ + 0, + /* board 2 */ + 0 +}; +#endif + +static u8 a38x_bw_per_freq[DDR_FREQ_LIMIT] = { + 0x3, /* DDR_FREQ_100 */ + 0x4, /* DDR_FREQ_400 */ + 0x4, /* DDR_FREQ_533 */ + 0x5, /* DDR_FREQ_667 */ + 0x5, /* DDR_FREQ_800 */ + 0x5, /* DDR_FREQ_933 */ + 0x5, /* DDR_FREQ_1066 */ + 0x3, /* DDR_FREQ_311 */ + 0x3, /* DDR_FREQ_333 */ + 0x4, /* DDR_FREQ_467 */ + 0x5, /* DDR_FREQ_850 */ + 0x5, /* DDR_FREQ_600 */ + 0x3, /* DDR_FREQ_300 */ + 0x5, /* DDR_FREQ_900 */ + 0x3, /* DDR_FREQ_360 */ + 0x5 /* DDR_FREQ_1000 */ +}; + +static u8 a38x_rate_per_freq[DDR_FREQ_LIMIT] = { + /*TBD*/ 0x1, /* DDR_FREQ_100 */ + 0x2, /* DDR_FREQ_400 */ + 0x2, /* DDR_FREQ_533 */ + 0x2, /* DDR_FREQ_667 */ + 0x2, /* DDR_FREQ_800 */ + 0x3, /* DDR_FREQ_933 */ + 0x3, /* DDR_FREQ_1066 */ + 0x1, /* DDR_FREQ_311 */ + 0x1, /* DDR_FREQ_333 */ + 0x2, /* DDR_FREQ_467 */ + 0x2, /* DDR_FREQ_850 */ + 0x2, /* DDR_FREQ_600 */ + 0x1, /* DDR_FREQ_300 */ + 0x2, /* DDR_FREQ_900 */ + 0x1, /* DDR_FREQ_360 */ + 0x2 /* DDR_FREQ_1000 */ +}; + +static u16 a38x_vco_freq_per_sar[] = { + 666, /* 0 */ + 1332, + 800, + 1600, + 1066, + 2132, + 1200, + 2400, + 1332, + 1332, + 1500, + 1500, + 1600, /* 12 */ + 1600, + 1700, + 1700, + 1866, + 1866, + 1800, /* 18 */ + 2000, + 2000, + 4000, + 2132, + 2132, + 2300, + 2300, + 2400, + 2400, + 2500, + 2500, + 800 +}; + +u32 pipe_multicast_mask; + +u32 dq_bit_map_2_phy_pin[] = { + 1, 0, 2, 6, 9, 8, 3, 7, /* 0 */ + 8, 9, 1, 7, 2, 6, 3, 0, /* 1 */ + 3, 9, 7, 8, 1, 0, 2, 6, /* 2 */ + 1, 0, 6, 2, 8, 3, 7, 9, /* 3 */ + 0, 1, 2, 9, 7, 8, 3, 6, /* 4 */ +}; + +static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id, + enum hws_ddr_freq freq); + +/* + * Read temperature TJ value + */ +u32 ddr3_ctrl_get_junc_temp(u8 dev_num) +{ + int reg = 0; + + /* Initiates TSEN hardware reset once */ + if ((reg_read(TSEN_CONF_REG) & TSEN_CONF_RST_MASK) == 0) + reg_bit_set(TSEN_CONF_REG, TSEN_CONF_RST_MASK); + mdelay(10); + + /* Check if the readout field is valid */ + if ((reg_read(TSEN_STATUS_REG) & TSEN_STATUS_READOUT_VALID_MASK) == 0) { + printf("%s: TSEN not ready\n", __func__); + return 0; + } + + reg = reg_read(TSEN_STATUS_REG); + reg = (reg & TSEN_STATUS_TEMP_OUT_MASK) >> TSEN_STATUS_TEMP_OUT_OFFSET; + + return ((((10000 * reg) / 21445) * 1000) - 272674) / 1000; +} + +/* + * Name: ddr3_tip_a38x_get_freq_config. + * Desc: + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_tip_a38x_get_freq_config(u8 dev_num, enum hws_ddr_freq freq, + struct hws_tip_freq_config_info + *freq_config_info) +{ + if (a38x_bw_per_freq[freq] == 0xff) + return MV_NOT_SUPPORTED; + + if (freq_config_info == NULL) + return MV_BAD_PARAM; + + freq_config_info->bw_per_freq = a38x_bw_per_freq[freq]; + freq_config_info->rate_per_freq = a38x_rate_per_freq[freq]; + freq_config_info->is_supported = 1; + + return MV_OK; +} + +/* + * Name: ddr3_tip_a38x_pipe_enable. + * Desc: + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_tip_a38x_pipe_enable(u8 dev_num, enum hws_access_type interface_access, + u32 if_id, int enable) +{ + u32 data_value, pipe_enable_mask = 0; + + if (enable == 0) { + pipe_enable_mask = 0; + } else { + if (interface_access == ACCESS_TYPE_MULTICAST) + pipe_enable_mask = pipe_multicast_mask; + else + pipe_enable_mask = (1 << interface_map[if_id].pipe); + } + + CHECK_STATUS(ddr3_tip_reg_read + (dev_num, PIPE_ENABLE_ADDR, &data_value, MASK_ALL_BITS)); + data_value = (data_value & (~0xff)) | pipe_enable_mask; + CHECK_STATUS(ddr3_tip_reg_write(dev_num, PIPE_ENABLE_ADDR, data_value)); + + return MV_OK; +} + +/* + * Name: ddr3_tip_a38x_if_write. + * Desc: + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_tip_a38x_if_write(u8 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 data_value, + u32 mask) +{ + u32 ui_data_read; + + if (mask != MASK_ALL_BITS) { + CHECK_STATUS(ddr3_tip_a38x_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, reg_addr, + &ui_data_read, MASK_ALL_BITS)); + data_value = (ui_data_read & (~mask)) | (data_value & mask); + } + + reg_write(reg_addr, data_value); + + return MV_OK; +} + +/* + * Name: ddr3_tip_a38x_if_read. + * Desc: + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_tip_a38x_if_read(u8 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 *data, u32 mask) +{ + *data = reg_read(reg_addr) & mask; + + return MV_OK; +} + +/* + * Name: ddr3_tip_a38x_select_ddr_controller. + * Desc: Enable/Disable access to Marvell's server. + * Args: dev_num - device number + * enable - whether to enable or disable the server + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_tip_a38x_select_ddr_controller(u8 dev_num, int enable) +{ + u32 reg; + + reg = reg_read(CS_ENABLE_REG); + + if (enable) + reg |= (1 << 6); + else + reg &= ~(1 << 6); + + reg_write(CS_ENABLE_REG, reg); + + return MV_OK; +} + +/* + * Name: ddr3_tip_init_a38x_silicon. + * Desc: init Training SW DB. + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_tip_init_a38x_silicon(u32 dev_num, u32 board_id) +{ + struct hws_tip_config_func_db config_func; + enum hws_ddr_freq ddr_freq; + int status; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* new read leveling version */ + config_func.tip_dunit_read_func = ddr3_tip_a38x_if_read; + config_func.tip_dunit_write_func = ddr3_tip_a38x_if_write; + config_func.tip_dunit_mux_select_func = + ddr3_tip_a38x_select_ddr_controller; + config_func.tip_get_freq_config_info_func = + ddr3_tip_a38x_get_freq_config; + config_func.tip_set_freq_divider_func = ddr3_tip_a38x_set_divider; + config_func.tip_get_device_info_func = ddr3_tip_a38x_get_device_info; + config_func.tip_get_temperature = ddr3_ctrl_get_junc_temp; + + ddr3_tip_init_config_func(dev_num, &config_func); + + ddr3_tip_register_dq_table(dev_num, dq_bit_map_2_phy_pin); + +#ifdef STATIC_ALGO_SUPPORT + { + struct hws_tip_static_config_info static_config; + u32 board_offset = + board_id * A38X_NUMBER_OF_INTERFACES * + tm->num_of_bus_per_interface; + + static_config.silicon_delay = + a38x_silicon_delay_offset[board_id]; + static_config.package_trace_arr = + a38x_package_round_trip_delay_array; + static_config.board_trace_arr = + &a38x_board_round_trip_delay_array[board_offset]; + ddr3_tip_init_static_config_db(dev_num, &static_config); + } +#endif + status = ddr3_tip_a38x_get_init_freq(dev_num, &ddr_freq); + if (MV_OK != status) { + DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR, + ("DDR3 silicon get target frequency - FAILED 0x%x\n", + status)); + return status; + } + + rl_version = 1; + mask_tune_func = (SET_LOW_FREQ_MASK_BIT | + LOAD_PATTERN_MASK_BIT | + SET_MEDIUM_FREQ_MASK_BIT | WRITE_LEVELING_MASK_BIT | + /* LOAD_PATTERN_2_MASK_BIT | */ + WRITE_LEVELING_SUPP_MASK_BIT | + READ_LEVELING_MASK_BIT | + PBS_RX_MASK_BIT | + PBS_TX_MASK_BIT | + SET_TARGET_FREQ_MASK_BIT | + WRITE_LEVELING_TF_MASK_BIT | + WRITE_LEVELING_SUPP_TF_MASK_BIT | + READ_LEVELING_TF_MASK_BIT | + CENTRALIZATION_RX_MASK_BIT | + CENTRALIZATION_TX_MASK_BIT); + rl_mid_freq_wa = 1; + + if ((ddr_freq == DDR_FREQ_333) || (ddr_freq == DDR_FREQ_400)) { + mask_tune_func = (WRITE_LEVELING_MASK_BIT | + LOAD_PATTERN_2_MASK_BIT | + WRITE_LEVELING_SUPP_MASK_BIT | + READ_LEVELING_MASK_BIT | + PBS_RX_MASK_BIT | + PBS_TX_MASK_BIT | + CENTRALIZATION_RX_MASK_BIT | + CENTRALIZATION_TX_MASK_BIT); + rl_mid_freq_wa = 0; /* WA not needed if 333/400 is TF */ + } + + /* Supplementary not supported for ECC modes */ + if (1 == ddr3_if_ecc_enabled()) { + mask_tune_func &= ~WRITE_LEVELING_SUPP_TF_MASK_BIT; + mask_tune_func &= ~WRITE_LEVELING_SUPP_MASK_BIT; + mask_tune_func &= ~PBS_TX_MASK_BIT; + mask_tune_func &= ~PBS_RX_MASK_BIT; + } + + if (ck_delay == -1) + ck_delay = 160; + if (ck_delay_16 == -1) + ck_delay_16 = 160; + ca_delay = 0; + delay_enable = 1; + + calibration_update_control = 1; + + init_freq = tm->interface_params[first_active_if].memory_freq; + + ddr3_tip_a38x_get_medium_freq(dev_num, &medium_freq); + + return MV_OK; +} + +int ddr3_a38x_update_topology_map(u32 dev_num, struct hws_topology_map *tm) +{ + u32 if_id = 0; + enum hws_ddr_freq freq; + + ddr3_tip_a38x_get_init_freq(dev_num, &freq); + tm->interface_params[if_id].memory_freq = freq; + + /* + * re-calc topology parameters according to topology updates + * (if needed) + */ + CHECK_STATUS(hws_ddr3_tip_load_topology_map(dev_num, tm)); + + return MV_OK; +} + +int ddr3_tip_init_a38x(u32 dev_num, u32 board_id) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (NULL == tm) + return MV_FAIL; + + ddr3_a38x_update_topology_map(dev_num, tm); + ddr3_tip_init_a38x_silicon(dev_num, board_id); + + return MV_OK; +} + +int ddr3_tip_a38x_get_init_freq(int dev_num, enum hws_ddr_freq *freq) +{ + u32 reg; + + /* Read sample at reset setting */ + reg = (reg_read(REG_DEVICE_SAR1_ADDR) >> + RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) & + RST2_CPU_DDR_CLOCK_SELECT_IN_MASK; + switch (reg) { + case 0x0: + case 0x1: + *freq = DDR_FREQ_333; + break; + case 0x2: + case 0x3: + *freq = DDR_FREQ_400; + break; + case 0x4: + case 0xd: + *freq = DDR_FREQ_533; + break; + case 0x6: + *freq = DDR_FREQ_600; + break; + case 0x8: + case 0x11: + case 0x14: + *freq = DDR_FREQ_667; + break; + case 0xc: + case 0x15: + case 0x1b: + *freq = DDR_FREQ_800; + break; + case 0x10: + *freq = DDR_FREQ_933; + break; + case 0x12: + *freq = DDR_FREQ_900; + break; + case 0x13: + *freq = DDR_FREQ_900; + break; + default: + *freq = 0; + return MV_NOT_SUPPORTED; + } + + return MV_OK; +} + +int ddr3_tip_a38x_get_medium_freq(int dev_num, enum hws_ddr_freq *freq) +{ + u32 reg; + + /* Read sample at reset setting */ + reg = (reg_read(REG_DEVICE_SAR1_ADDR) >> + RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) & + RST2_CPU_DDR_CLOCK_SELECT_IN_MASK; + switch (reg) { + case 0x0: + case 0x1: + /* Medium is same as TF to run PBS in this freq */ + *freq = DDR_FREQ_333; + break; + case 0x2: + case 0x3: + /* Medium is same as TF to run PBS in this freq */ + *freq = DDR_FREQ_400; + break; + case 0x4: + case 0xd: + *freq = DDR_FREQ_533; + break; + case 0x8: + case 0x11: + case 0x14: + *freq = DDR_FREQ_333; + break; + case 0xc: + case 0x15: + case 0x1b: + *freq = DDR_FREQ_400; + break; + case 0x6: + *freq = DDR_FREQ_300; + break; + case 0x12: + *freq = DDR_FREQ_360; + break; + case 0x13: + *freq = DDR_FREQ_400; + break; + default: + *freq = 0; + return MV_NOT_SUPPORTED; + } + + return MV_OK; +} + +u32 ddr3_tip_get_init_freq(void) +{ + enum hws_ddr_freq freq; + + ddr3_tip_a38x_get_init_freq(0, &freq); + + return freq; +} + +static int ddr3_tip_a38x_set_divider(u8 dev_num, u32 if_id, + enum hws_ddr_freq frequency) +{ + u32 divider = 0; + u32 sar_val; + + if (if_id != 0) { + DEBUG_TRAINING_ACCESS(DEBUG_LEVEL_ERROR, + ("A38x does not support interface 0x%x\n", + if_id)); + return MV_BAD_PARAM; + } + + /* get VCO freq index */ + sar_val = (reg_read(REG_DEVICE_SAR1_ADDR) >> + RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET) & + RST2_CPU_DDR_CLOCK_SELECT_IN_MASK; + divider = a38x_vco_freq_per_sar[sar_val] / freq_val[frequency]; + + /* Set Sync mode */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x20220, 0x0, + 0x1000)); + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe42f4, 0x0, + 0x200)); + + /* cpupll_clkdiv_reset_mask */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4264, 0x1f, + 0xff)); + + /* cpupll_clkdiv_reload_smooth */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4260, + (0x2 << 8), (0xff << 8))); + + /* cpupll_clkdiv_relax_en */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4260, + (0x2 << 24), (0xff << 24))); + + /* write the divider */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4268, + (divider << 8), (0x3f << 8))); + + /* set cpupll_clkdiv_reload_ratio */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4264, + (1 << 8), (1 << 8))); + + /* undet cpupll_clkdiv_reload_ratio */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4264, 0, + (1 << 8))); + + /* clear cpupll_clkdiv_reload_force */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4260, 0, + (0xff << 8))); + + /* clear cpupll_clkdiv_relax_en */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4260, 0, + (0xff << 24))); + + /* clear cpupll_clkdiv_reset_mask */ + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0xe4264, 0, + 0xff)); + + /* Dunit training clock + 1:1 mode */ + if ((frequency == DDR_FREQ_LOW_FREQ) || (freq_val[frequency] <= 400)) { + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x18488, + (1 << 16), (1 << 16))); + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1524, + (0 << 15), (1 << 15))); + } else { + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x18488, + 0, (1 << 16))); + CHECK_STATUS(ddr3_tip_a38x_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1524, + (1 << 15), (1 << 15))); + } + + return MV_OK; +} + +/* + * external read from memory + */ +int ddr3_tip_ext_read(u32 dev_num, u32 if_id, u32 reg_addr, + u32 num_of_bursts, u32 *data) +{ + u32 burst_num; + + for (burst_num = 0; burst_num < num_of_bursts * 8; burst_num++) + data[burst_num] = readl(reg_addr + 4 * burst_num); + + return MV_OK; +} + +/* + * external write to memory + */ +int ddr3_tip_ext_write(u32 dev_num, u32 if_id, u32 reg_addr, + u32 num_of_bursts, u32 *data) { + u32 burst_num; + + for (burst_num = 0; burst_num < num_of_bursts * 8; burst_num++) + writel(data[burst_num], reg_addr + 4 * burst_num); + + return MV_OK; +} + +int ddr3_silicon_pre_init(void) +{ + int result; + + result = ddr3_silicon_init(); + + return result; +} + +int ddr3_post_run_alg(void) +{ + return MV_OK; +} + +int ddr3_silicon_post_init(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* Set half bus width */ + if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask)) { + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + REG_SDRAM_CONFIG_ADDR, 0x0, 0x8000)); + } + + return MV_OK; +} + +int ddr3_tip_a38x_get_device_info(u8 dev_num, struct ddr3_device_info *info_ptr) +{ + info_ptr->device_id = 0x6800; + info_ptr->ck_delay = ck_delay; + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_a38x.h b/drivers/ddr/marvell/a38x/ddr3_a38x.h new file mode 100644 index 0000000..49621bc --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_a38x.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_A38X_H +#define _DDR3_A38X_H + +#define MAX_INTERFACE_NUM 1 +#define MAX_BUS_NUM 5 + +#include "ddr3_hws_hw_training_def.h" + +/* Allow topolgy update from board TWSI device*/ +#if !defined(CONFIG_CUSTOMER_BOARD_SUPPORT) +#define MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI +#endif + +#define ECC_SUPPORT + +/* right now, we're not supporting this in mainline */ +#undef SUPPORT_STATIC_DUNIT_CONFIG + +/* Controler bus divider 1 for 32 bit, 2 for 64 bit */ +#define DDR_CONTROLLER_BUS_WIDTH_MULTIPLIER 1 + +/* Tune internal training params values */ +#define TUNE_TRAINING_PARAMS_CK_DELAY 160 +#define TUNE_TRAINING_PARAMS_CK_DELAY_16 160 +#define TUNE_TRAINING_PARAMS_PFINGER 41 +#define TUNE_TRAINING_PARAMS_NFINGER 43 +#define TUNE_TRAINING_PARAMS_PHYREG3VAL 0xa + +#define MARVELL_BOARD MARVELL_BOARD_ID_BASE + + +#define REG_DEVICE_SAR1_ADDR 0xe4204 +#define RST2_CPU_DDR_CLOCK_SELECT_IN_OFFSET 17 +#define RST2_CPU_DDR_CLOCK_SELECT_IN_MASK 0x1f + +/* DRAM Windows */ +#define REG_XBAR_WIN_5_CTRL_ADDR 0x20050 +#define REG_XBAR_WIN_5_BASE_ADDR 0x20054 + +/* DRAM Windows */ +#define REG_XBAR_WIN_4_CTRL_ADDR 0x20040 +#define REG_XBAR_WIN_4_BASE_ADDR 0x20044 +#define REG_XBAR_WIN_4_REMAP_ADDR 0x20048 +#define REG_XBAR_WIN_7_REMAP_ADDR 0x20078 +#define REG_XBAR_WIN_16_CTRL_ADDR 0x200d0 +#define REG_XBAR_WIN_16_BASE_ADDR 0x200d4 +#define REG_XBAR_WIN_16_REMAP_ADDR 0x200dc +#define REG_XBAR_WIN_19_CTRL_ADDR 0x200e8 + +#define REG_FASTPATH_WIN_BASE_ADDR(win) (0x20180 + (0x8 * win)) +#define REG_FASTPATH_WIN_CTRL_ADDR(win) (0x20184 + (0x8 * win)) + +/* SatR defined too change topology busWidth and ECC configuration */ +#define DDR_SATR_CONFIG_MASK_WIDTH 0x8 +#define DDR_SATR_CONFIG_MASK_ECC 0x10 +#define DDR_SATR_CONFIG_MASK_ECC_PUP 0x20 + +#define REG_SAMPLE_RESET_HIGH_ADDR 0x18600 + +#define MV_BOARD_REFCLK MV_BOARD_REFCLK_25MHZ + +/* Matrix enables DRAM modes (bus width/ECC) per boardId */ +#define TOPOLOGY_UPDATE_32BIT 0 +#define TOPOLOGY_UPDATE_32BIT_ECC 1 +#define TOPOLOGY_UPDATE_16BIT 2 +#define TOPOLOGY_UPDATE_16BIT_ECC 3 +#define TOPOLOGY_UPDATE_16BIT_ECC_PUP3 4 +#define TOPOLOGY_UPDATE { \ + /* 32Bit, 32bit ECC, 16bit, 16bit ECC PUP4, 16bit ECC PUP3 */ \ + {1, 1, 1, 1, 1}, /* RD_NAS_68XX_ID */ \ + {1, 1, 1, 1, 1}, /* DB_68XX_ID */ \ + {1, 0, 1, 0, 1}, /* RD_AP_68XX_ID */ \ + {1, 0, 1, 0, 1}, /* DB_AP_68XX_ID */ \ + {1, 0, 1, 0, 1}, /* DB_GP_68XX_ID */ \ + {0, 0, 1, 1, 0}, /* DB_BP_6821_ID */ \ + {1, 1, 1, 1, 1} /* DB_AMC_6820_ID */ \ + }; + +enum { + CPU_1066MHZ_DDR_400MHZ, + CPU_RESERVED_DDR_RESERVED0, + CPU_667MHZ_DDR_667MHZ, + CPU_800MHZ_DDR_800MHZ, + CPU_RESERVED_DDR_RESERVED1, + CPU_RESERVED_DDR_RESERVED2, + CPU_RESERVED_DDR_RESERVED3, + LAST_FREQ +}; + +#define ACTIVE_INTERFACE_MASK 0x1 + +#endif /* _DDR3_A38X_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_a38x_mc_static.h b/drivers/ddr/marvell/a38x/ddr3_a38x_mc_static.h new file mode 100644 index 0000000..b879a01 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_a38x_mc_static.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_A38X_MC_STATIC_H +#define _DDR3_A38X_MC_STATIC_H + +#include "ddr3_a38x.h" + +#ifdef SUPPORT_STATIC_DUNIT_CONFIG + +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT +static struct reg_data ddr3_customer_800[] = { + /* parameters for customer board (based on 800MHZ) */ + {0x1400, 0x7b00cc30, 0xffffffff}, + {0x1404, 0x36301820, 0xffffffff}, + {0x1408, 0x5415baab, 0xffffffff}, + {0x140c, 0x38411def, 0xffffffff}, + {0x1410, 0x18300000, 0xffffffff}, + {0x1414, 0x00000700, 0xffffffff}, + {0x1424, 0x0060f3ff, 0xffffffff}, + {0x1428, 0x0011a940, 0xffffffff}, + {0x142c, 0x28c5134, 0xffffffff}, + {0x1474, 0x00000000, 0xffffffff}, + {0x147c, 0x0000d771, 0xffffffff}, + {0x1494, 0x00030000, 0xffffffff}, + {0x149c, 0x00000300, 0xffffffff}, + {0x14a8, 0x00000000, 0xffffffff}, + {0x14cc, 0xbd09000d, 0xffffffff}, + {0x1504, 0xfffffff1, 0xffffffff}, + {0x150c, 0xffffffe5, 0xffffffff}, + {0x1514, 0x00000000, 0xffffffff}, + {0x151c, 0x00000000, 0xffffffff}, + {0x1538, 0x00000b0b, 0xffffffff}, + {0x153c, 0x00000c0c, 0xffffffff}, + {0x15d0, 0x00000670, 0xffffffff}, + {0x15d4, 0x00000046, 0xffffffff}, + {0x15d8, 0x00000010, 0xffffffff}, + {0x15dc, 0x00000000, 0xffffffff}, + {0x15e0, 0x00000023, 0xffffffff}, + {0x15e4, 0x00203c18, 0xffffffff}, + {0x15ec, 0xf8000019, 0xffffffff}, + {0x16a0, 0xcc000006, 0xffffffff}, /* Clock Delay */ + {0xe4124, 0x08008073, 0xffffffff}, /* AVS BG default */ + {0, 0, 0} +}; + +#else /* CONFIG_CUSTOMER_BOARD_SUPPORT */ + +struct reg_data ddr3_a38x_933[MV_MAX_DDR3_STATIC_SIZE] = { + /* parameters for 933MHZ */ + {0x1400, 0x7b00ce3a, 0xffffffff}, + {0x1404, 0x36301820, 0xffffffff}, + {0x1408, 0x7417eccf, 0xffffffff}, + {0x140c, 0x3e421f98, 0xffffffff}, + {0x1410, 0x1a300000, 0xffffffff}, + {0x1414, 0x00000700, 0xffffffff}, + {0x1424, 0x0060f3ff, 0xffffffff}, + {0x1428, 0x0013ca50, 0xffffffff}, + {0x142c, 0x028c5165, 0xffffffff}, + {0x1474, 0x00000000, 0xffffffff}, + {0x147c, 0x0000e871, 0xffffffff}, + {0x1494, 0x00010000, 0xffffffff}, + {0x149c, 0x00000001, 0xffffffff}, + {0x14a8, 0x00000000, 0xffffffff}, + {0x14cc, 0xbd09000d, 0xffffffff}, + {0x1504, 0xffffffe1, 0xffffffff}, + {0x150c, 0xffffffe5, 0xffffffff}, + {0x1514, 0x00000000, 0xffffffff}, + {0x151c, 0x00000000, 0xffffffff}, + {0x1538, 0x00000d0d, 0xffffffff}, + {0x153c, 0x00000d0d, 0xffffffff}, + {0x15d0, 0x00000608, 0xffffffff}, + {0x15d4, 0x00000044, 0xffffffff}, + {0x15d8, 0x00000020, 0xffffffff}, + {0x15dc, 0x00000000, 0xffffffff}, + {0x15e0, 0x00000021, 0xffffffff}, + {0x15e4, 0x00203c18, 0xffffffff}, + {0x15ec, 0xf8000019, 0xffffffff}, + {0x16a0, 0xcc000006, 0xffffffff}, /* Clock Delay */ + {0xe4124, 0x08008073, 0xffffffff}, /* AVS BG default */ + {0, 0, 0} +}; + +static struct reg_data ddr3_a38x_800[] = { + /* parameters for 800MHZ */ + {0x1400, 0x7b00cc30, 0xffffffff}, + {0x1404, 0x36301820, 0xffffffff}, + {0x1408, 0x5415baab, 0xffffffff}, + {0x140c, 0x38411def, 0xffffffff}, + {0x1410, 0x18300000, 0xffffffff}, + {0x1414, 0x00000700, 0xffffffff}, + {0x1424, 0x0060f3ff, 0xffffffff}, + {0x1428, 0x0011a940, 0xffffffff}, + {0x142c, 0x28c5134, 0xffffffff}, + {0x1474, 0x00000000, 0xffffffff}, + {0x147c, 0x0000d771, 0xffffffff}, + {0x1494, 0x00030000, 0xffffffff}, + {0x149c, 0x00000300, 0xffffffff}, + {0x14a8, 0x00000000, 0xffffffff}, + {0x14cc, 0xbd09000d, 0xffffffff}, + {0x1504, 0xfffffff1, 0xffffffff}, + {0x150c, 0xffffffe5, 0xffffffff}, + {0x1514, 0x00000000, 0xffffffff}, + {0x151c, 0x00000000, 0xffffffff}, + {0x1538, 0x00000b0b, 0xffffffff}, + {0x153c, 0x00000c0c, 0xffffffff}, + {0x15d0, 0x00000670, 0xffffffff}, + {0x15d4, 0x00000046, 0xffffffff}, + {0x15d8, 0x00000010, 0xffffffff}, + {0x15dc, 0x00000000, 0xffffffff}, + {0x15e0, 0x00000023, 0xffffffff}, + {0x15e4, 0x00203c18, 0xffffffff}, + {0x15ec, 0xf8000019, 0xffffffff}, + {0x16a0, 0xcc000006, 0xffffffff}, /* Clock Delay */ + {0xe4124, 0x08008073, 0xffffffff}, /* AVS BG default */ + {0, 0, 0} +}; + +static struct reg_data ddr3_a38x_667[] = { + /* parameters for 667MHZ */ + /* DDR SDRAM Configuration Register */ + {0x1400, 0x7b00ca28, 0xffffffff}, + /* Dunit Control Low Register - kw28 bit12 low (disable CLK1) */ + {0x1404, 0x36301820, 0xffffffff}, + /* DDR SDRAM Timing (Low) Register */ + {0x1408, 0x43149997, 0xffffffff}, + /* DDR SDRAM Timing (High) Register */ + {0x140c, 0x38411bc7, 0xffffffff}, + /* DDR SDRAM Address Control Register */ + {0x1410, 0x14330000, 0xffffffff}, + /* DDR SDRAM Open Pages Control Register */ + {0x1414, 0x00000700, 0xffffffff}, + /* Dunit Control High Register (2 :1 - bits 15:12 = 0xd) */ + {0x1424, 0x0060f3ff, 0xffffffff}, + /* Dunit Control High Register */ + {0x1428, 0x000f8830, 0xffffffff}, + /* Dunit Control High Register (2:1 - bit 29 = '1') */ + {0x142c, 0x28c50f8, 0xffffffff}, + {0x147c, 0x0000c671, 0xffffffff}, + /* DDR SDRAM ODT Control (Low) Register */ + {0x1494, 0x00030000, 0xffffffff}, + /* DDR SDRAM ODT Control (High) Register, will be configured at WL */ + {0x1498, 0x00000000, 0xffffffff}, + /* DDR Dunit ODT Control Register */ + {0x149c, 0x00000300, 0xffffffff}, + {0x14a8, 0x00000000, 0xffffffff}, /* */ + {0x14cc, 0xbd09000d, 0xffffffff}, /* */ + {0x1474, 0x00000000, 0xffffffff}, + /* Read Data Sample Delays Register */ + {0x1538, 0x00000009, 0xffffffff}, + /* Read Data Ready Delay Register */ + {0x153c, 0x0000000c, 0xffffffff}, + {0x1504, 0xfffffff1, 0xffffffff}, /* */ + {0x150c, 0xffffffe5, 0xffffffff}, /* */ + {0x1514, 0x00000000, 0xffffffff}, /* */ + {0x151c, 0x0, 0xffffffff}, /* */ + {0x15d0, 0x00000650, 0xffffffff}, /* MR0 */ + {0x15d4, 0x00000046, 0xffffffff}, /* MR1 */ + {0x15d8, 0x00000010, 0xffffffff}, /* MR2 */ + {0x15dc, 0x00000000, 0xffffffff}, /* MR3 */ + {0x15e0, 0x23, 0xffffffff}, /* */ + {0x15e4, 0x00203c18, 0xffffffff}, /* ZQC Configuration Register */ + {0x15ec, 0xf8000019, 0xffffffff}, /* DDR PHY */ + {0x16a0, 0xcc000006, 0xffffffff}, /* Clock Delay */ + {0xe4124, 0x08008073, 0xffffffff}, /* AVS BG default */ + {0, 0, 0} +}; + +static struct reg_data ddr3_a38x_533[] = { + /* parameters for 533MHZ */ + /* DDR SDRAM Configuration Register */ + {0x1400, 0x7b00d040, 0xffffffff}, + /* Dunit Control Low Register - kw28 bit12 low (disable CLK1) */ + {0x1404, 0x36301820, 0xffffffff}, + /* DDR SDRAM Timing (Low) Register */ + {0x1408, 0x33137772, 0xffffffff}, + /* DDR SDRAM Timing (High) Register */ + {0x140c, 0x3841199f, 0xffffffff}, + /* DDR SDRAM Address Control Register */ + {0x1410, 0x10330000, 0xffffffff}, + /* DDR SDRAM Open Pages Control Register */ + {0x1414, 0x00000700, 0xffffffff}, + /* Dunit Control High Register (2 :1 - bits 15:12 = 0xd) */ + {0x1424, 0x0060f3ff, 0xffffffff}, + /* Dunit Control High Register */ + {0x1428, 0x000d6720, 0xffffffff}, + /* Dunit Control High Register (2:1 - bit 29 = '1') */ + {0x142c, 0x028c50c3, 0xffffffff}, + {0x147c, 0x0000b571, 0xffffffff}, + /* DDR SDRAM ODT Control (Low) Register */ + {0x1494, 0x00030000, 0xffffffff}, + /* DDR SDRAM ODT Control (High) Register, will be configured at WL */ + {0x1498, 0x00000000, 0xffffffff}, + /* DDR Dunit ODT Control Register */ + {0x149c, 0x00000003, 0xffffffff}, + {0x14a8, 0x00000000, 0xffffffff}, /* */ + {0x14cc, 0xbd09000d, 0xffffffff}, /* */ + {0x1474, 0x00000000, 0xffffffff}, + /* Read Data Sample Delays Register */ + {0x1538, 0x00000707, 0xffffffff}, + /* Read Data Ready Delay Register */ + {0x153c, 0x00000707, 0xffffffff}, + {0x1504, 0xffffffe1, 0xffffffff}, /* */ + {0x150c, 0xffffffe5, 0xffffffff}, /* */ + {0x1514, 0x00000000, 0xffffffff}, /* */ + {0x151c, 0x00000000, 0xffffffff}, /* */ + {0x15d0, 0x00000630, 0xffffffff}, /* MR0 */ + {0x15d4, 0x00000046, 0xffffffff}, /* MR1 */ + {0x15d8, 0x00000008, 0xffffffff}, /* MR2 */ + {0x15dc, 0x00000000, 0xffffffff}, /* MR3 */ + {0x15e0, 0x00000023, 0xffffffff}, /* */ + {0x15e4, 0x00203c18, 0xffffffff}, /* ZQC Configuration Register */ + {0x15ec, 0xf8000019, 0xffffffff}, /* DDR PHY */ + {0x16a0, 0xcc000006, 0xffffffff}, /* Clock Delay */ + {0xe4124, 0x08008073, 0xffffffff}, /* AVS BG default */ + {0, 0, 0} +}; + +#endif /* CONFIG_CUSTOMER_BOARD_SUPPORT */ + +#endif /* SUPPORT_STATIC_DUNIT_CONFIG */ + +#endif /* _DDR3_A38X_MC_STATIC_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_a38x_topology.h b/drivers/ddr/marvell/a38x/ddr3_a38x_topology.h new file mode 100644 index 0000000..f27bbff --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_a38x_topology.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_A38X_TOPOLOGY_H +#define _DDR3_A38X_TOPOLOGY_H + +#include "ddr_topology_def.h" + +/* Bus mask variants */ +#define BUS_MASK_32BIT 0xf +#define BUS_MASK_32BIT_ECC 0x1f +#define BUS_MASK_16BIT 0x3 +#define BUS_MASK_16BIT_ECC 0x13 +#define BUS_MASK_16BIT_ECC_PUP3 0xb + +#define DYNAMIC_CS_SIZE_CONFIG +#define DISABLE_L2_FILTERING_DURING_DDR_TRAINING + +#endif /* _DDR3_A38X_TOPOLOGY_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_a38x_training.c b/drivers/ddr/marvell/a38x/ddr3_a38x_training.c new file mode 100644 index 0000000..52c43f7 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_a38x_training.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +/* + * Name: ddr3_tip_init_silicon + * Desc: initiate silicon parameters + * Args: + * Notes: + * Returns: required value + */ +int ddr3_silicon_init(void) +{ + int status; + static int init_done; + + if (init_done == 1) + return MV_OK; + + status = ddr3_tip_init_a38x(0, 0); + if (MV_OK != status) { + printf("DDR3 A38x silicon init - FAILED 0x%x\n", status); + return status; + } + + init_done = 1; + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_debug.c b/drivers/ddr/marvell/a38x/ddr3_debug.c new file mode 100644 index 0000000..1d72bc5 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_debug.c @@ -0,0 +1,1551 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +u8 is_reg_dump = 0; +u8 debug_pbs = DEBUG_LEVEL_ERROR; + +/* + * API to change flags outside of the lib + */ +#ifndef SILENT_LIB +/* Debug flags for other Training modules */ +u8 debug_training_static = DEBUG_LEVEL_ERROR; +u8 debug_training = DEBUG_LEVEL_ERROR; +u8 debug_leveling = DEBUG_LEVEL_ERROR; +u8 debug_centralization = DEBUG_LEVEL_ERROR; +u8 debug_training_ip = DEBUG_LEVEL_ERROR; +u8 debug_training_bist = DEBUG_LEVEL_ERROR; +u8 debug_training_hw_alg = DEBUG_LEVEL_ERROR; +u8 debug_training_access = DEBUG_LEVEL_ERROR; +u8 debug_training_a38x = DEBUG_LEVEL_ERROR; + +void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level) +{ + switch (block) { + case DEBUG_BLOCK_STATIC: + debug_training_static = level; + break; + case DEBUG_BLOCK_TRAINING_MAIN: + debug_training = level; + break; + case DEBUG_BLOCK_LEVELING: + debug_leveling = level; + break; + case DEBUG_BLOCK_CENTRALIZATION: + debug_centralization = level; + break; + case DEBUG_BLOCK_PBS: + debug_pbs = level; + break; + case DEBUG_BLOCK_ALG: + debug_training_hw_alg = level; + break; + case DEBUG_BLOCK_DEVICE: + debug_training_a38x = level; + break; + case DEBUG_BLOCK_ACCESS: + debug_training_access = level; + break; + case DEBUG_STAGES_REG_DUMP: + if (level == DEBUG_LEVEL_TRACE) + is_reg_dump = 1; + else + is_reg_dump = 0; + break; + case DEBUG_BLOCK_ALL: + default: + debug_training_static = level; + debug_training = level; + debug_leveling = level; + debug_centralization = level; + debug_pbs = level; + debug_training_hw_alg = level; + debug_training_access = level; + debug_training_a38x = level; + } +} +#else +void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level) +{ + return; +} +#endif + +struct hws_tip_config_func_db config_func_info[HWS_MAX_DEVICE_NUM]; +u8 is_default_centralization = 0; +u8 is_tune_result = 0; +u8 is_validate_window_per_if = 0; +u8 is_validate_window_per_pup = 0; +u8 sweep_cnt = 1; +u32 is_bist_reset_bit = 1; +static struct hws_xsb_info xsb_info[HWS_MAX_DEVICE_NUM]; + +/* + * Dump Dunit & Phy registers + */ +int ddr3_tip_reg_dump(u32 dev_num) +{ + u32 if_id, reg_addr, data_value, bus_id; + u32 read_data[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + printf("-- dunit registers --\n"); + for (reg_addr = 0x1400; reg_addr < 0x19f0; reg_addr += 4) { + printf("0x%x ", reg_addr); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, reg_addr, read_data, + MASK_ALL_BITS)); + printf("0x%x ", read_data[if_id]); + } + printf("\n"); + } + + printf("-- Phy registers --\n"); + for (reg_addr = 0; reg_addr <= 0xff; reg_addr++) { + printf("0x%x ", reg_addr); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; + bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, reg_addr, + &data_value)); + printf("0x%x ", data_value); + } + for (bus_id = 0; + bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_CONTROL, reg_addr, + &data_value)); + printf("0x%x ", data_value); + } + } + printf("\n"); + } + + return MV_OK; +} + +/* + * Register access func registration + */ +int ddr3_tip_init_config_func(u32 dev_num, + struct hws_tip_config_func_db *config_func) +{ + if (config_func == NULL) + return MV_BAD_PARAM; + + memcpy(&config_func_info[dev_num], config_func, + sizeof(struct hws_tip_config_func_db)); + + return MV_OK; +} + +/* + * Read training result table + */ +int hws_ddr3_tip_read_training_result( + u32 dev_num, enum hws_result result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]) +{ + dev_num = dev_num; + + if (result == NULL) + return MV_BAD_PARAM; + memcpy(result, training_result, sizeof(result)); + + return MV_OK; +} + +/* + * Get training result info pointer + */ +enum hws_result *ddr3_tip_get_result_ptr(u32 stage) +{ + return training_result[stage]; +} + +/* + * Device info read + */ +int ddr3_tip_get_device_info(u32 dev_num, struct ddr3_device_info *info_ptr) +{ + if (config_func_info[dev_num].tip_get_device_info_func != NULL) { + return config_func_info[dev_num]. + tip_get_device_info_func((u8) dev_num, info_ptr); + } + + return MV_FAIL; +} + +#ifndef EXCLUDE_SWITCH_DEBUG +/* + * Convert freq to character string + */ +static char *convert_freq(enum hws_ddr_freq freq) +{ + switch (freq) { + case DDR_FREQ_LOW_FREQ: + return "DDR_FREQ_LOW_FREQ"; + case DDR_FREQ_400: + return "400"; + + case DDR_FREQ_533: + return "533"; + case DDR_FREQ_667: + return "667"; + + case DDR_FREQ_800: + return "800"; + + case DDR_FREQ_933: + return "933"; + + case DDR_FREQ_1066: + return "1066"; + case DDR_FREQ_311: + return "311"; + + case DDR_FREQ_333: + return "333"; + + case DDR_FREQ_467: + return "467"; + + case DDR_FREQ_850: + return "850"; + + case DDR_FREQ_900: + return "900"; + + case DDR_FREQ_360: + return "DDR_FREQ_360"; + + case DDR_FREQ_1000: + return "DDR_FREQ_1000"; + default: + return "Unknown Frequency"; + } +} + +/* + * Convert device ID to character string + */ +static char *convert_dev_id(u32 dev_id) +{ + switch (dev_id) { + case 0x6800: + return "A38xx"; + case 0x6900: + return "A39XX"; + case 0xf400: + return "AC3"; + case 0xfc00: + return "BC2"; + + default: + return "Unknown Device"; + } +} + +/* + * Convert device ID to character string + */ +static char *convert_mem_size(u32 dev_id) +{ + switch (dev_id) { + case 0: + return "512 MB"; + case 1: + return "1 GB"; + case 2: + return "2 GB"; + case 3: + return "4 GB"; + case 4: + return "8 GB"; + + default: + return "wrong mem size"; + } +} + +int print_device_info(u8 dev_num) +{ + struct ddr3_device_info info_ptr; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_get_device_info(dev_num, &info_ptr)); + printf("=== DDR setup START===\n"); + printf("\tDevice ID: %s\n", convert_dev_id(info_ptr.device_id)); + printf("\tDDR3 CK delay: %d\n", info_ptr.ck_delay); + print_topology(tm); + printf("=== DDR setup END===\n"); + + return MV_OK; +} + +void hws_ddr3_tip_sweep_test(int enable) +{ + if (enable) { + is_validate_window_per_if = 1; + is_validate_window_per_pup = 1; + debug_training = DEBUG_LEVEL_TRACE; + } else { + is_validate_window_per_if = 0; + is_validate_window_per_pup = 0; + } +} +#endif + +char *ddr3_tip_convert_tune_result(enum hws_result tune_result) +{ + switch (tune_result) { + case TEST_FAILED: + return "FAILED"; + case TEST_SUCCESS: + return "PASS"; + case NO_TEST_DONE: + return "NOT COMPLETED"; + default: + return "Un-KNOWN"; + } +} + +/* + * Print log info + */ +int ddr3_tip_print_log(u32 dev_num, u32 mem_addr) +{ + u32 if_id = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + mem_addr = mem_addr; + +#ifndef EXCLUDE_SWITCH_DEBUG + if ((is_validate_window_per_if != 0) || + (is_validate_window_per_pup != 0)) { + u32 is_pup_log = 0; + enum hws_ddr_freq freq; + + freq = tm->interface_params[first_active_if].memory_freq; + + is_pup_log = (is_validate_window_per_pup != 0) ? 1 : 0; + printf("===VALIDATE WINDOW LOG START===\n"); + printf("DDR Frequency: %s ======\n", convert_freq(freq)); + /* print sweep windows */ + ddr3_tip_run_sweep_test(dev_num, sweep_cnt, 1, is_pup_log); + ddr3_tip_run_sweep_test(dev_num, sweep_cnt, 0, is_pup_log); + ddr3_tip_print_all_pbs_result(dev_num); + ddr3_tip_print_wl_supp_result(dev_num); + printf("===VALIDATE WINDOW LOG END ===\n"); + CHECK_STATUS(ddr3_tip_restore_dunit_regs(dev_num)); + ddr3_tip_reg_dump(dev_num); + } +#endif + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("IF %d Status:\n", if_id)); + + if (mask_tune_func & INIT_CONTROLLER_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tInit Controller: %s\n", + ddr3_tip_convert_tune_result + (training_result[INIT_CONTROLLER] + [if_id]))); + } + if (mask_tune_func & SET_LOW_FREQ_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tLow freq Config: %s\n", + ddr3_tip_convert_tune_result + (training_result[SET_LOW_FREQ] + [if_id]))); + } + if (mask_tune_func & LOAD_PATTERN_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tLoad Pattern: %s\n", + ddr3_tip_convert_tune_result + (training_result[LOAD_PATTERN] + [if_id]))); + } + if (mask_tune_func & SET_MEDIUM_FREQ_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tMedium freq Config: %s\n", + ddr3_tip_convert_tune_result + (training_result[SET_MEDIUM_FREQ] + [if_id]))); + } + if (mask_tune_func & WRITE_LEVELING_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tWL: %s\n", + ddr3_tip_convert_tune_result + (training_result[WRITE_LEVELING] + [if_id]))); + } + if (mask_tune_func & LOAD_PATTERN_2_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tLoad Pattern: %s\n", + ddr3_tip_convert_tune_result + (training_result[LOAD_PATTERN_2] + [if_id]))); + } + if (mask_tune_func & READ_LEVELING_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tRL: %s\n", + ddr3_tip_convert_tune_result + (training_result[READ_LEVELING] + [if_id]))); + } + if (mask_tune_func & WRITE_LEVELING_SUPP_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tWL Supp: %s\n", + ddr3_tip_convert_tune_result + (training_result[WRITE_LEVELING_SUPP] + [if_id]))); + } + if (mask_tune_func & PBS_RX_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tPBS RX: %s\n", + ddr3_tip_convert_tune_result + (training_result[PBS_RX] + [if_id]))); + } + if (mask_tune_func & PBS_TX_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tPBS TX: %s\n", + ddr3_tip_convert_tune_result + (training_result[PBS_TX] + [if_id]))); + } + if (mask_tune_func & SET_TARGET_FREQ_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tTarget freq Config: %s\n", + ddr3_tip_convert_tune_result + (training_result[SET_TARGET_FREQ] + [if_id]))); + } + if (mask_tune_func & WRITE_LEVELING_TF_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tWL TF: %s\n", + ddr3_tip_convert_tune_result + (training_result[WRITE_LEVELING_TF] + [if_id]))); + } + if (mask_tune_func & READ_LEVELING_TF_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tRL TF: %s\n", + ddr3_tip_convert_tune_result + (training_result[READ_LEVELING_TF] + [if_id]))); + } + if (mask_tune_func & WRITE_LEVELING_SUPP_TF_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tWL TF Supp: %s\n", + ddr3_tip_convert_tune_result + (training_result + [WRITE_LEVELING_SUPP_TF] + [if_id]))); + } + if (mask_tune_func & CENTRALIZATION_RX_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tCentr RX: %s\n", + ddr3_tip_convert_tune_result + (training_result[CENTRALIZATION_RX] + [if_id]))); + } + if (mask_tune_func & VREF_CALIBRATION_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tVREF_CALIBRATION: %s\n", + ddr3_tip_convert_tune_result + (training_result[VREF_CALIBRATION] + [if_id]))); + } + if (mask_tune_func & CENTRALIZATION_TX_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("\tCentr TX: %s\n", + ddr3_tip_convert_tune_result + (training_result[CENTRALIZATION_TX] + [if_id]))); + } + } + + return MV_OK; +} + +/* + * Print stability log info + */ +int ddr3_tip_print_stability_log(u32 dev_num) +{ + u8 if_id = 0, csindex = 0, bus_id = 0, idx = 0; + u32 reg_data; + u32 read_data[MAX_INTERFACE_NUM]; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* Title print */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + printf("Title: I/F# , Tj, Calibration_n0, Calibration_p0, Calibration_n1, Calibration_p1, Calibration_n2, Calibration_p2,"); + for (csindex = 0; csindex < max_cs; csindex++) { + printf("CS%d , ", csindex); + printf("\n"); + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + printf("VWTx, VWRx, WL_tot, WL_ADLL, WL_PH, RL_Tot, RL_ADLL, RL_PH, RL_Smp, Cen_tx, Cen_rx, Vref, DQVref,"); + printf("\t\t"); + for (idx = 0; idx < 11; idx++) + printf("PBSTx-Pad%d,", idx); + printf("\t\t"); + for (idx = 0; idx < 11; idx++) + printf("PBSRx-Pad%d,", idx); + } + } + printf("\n"); + + /* Data print */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + printf("Data: %d,%d,", if_id, + (config_func_info[dev_num].tip_get_temperature != NULL) + ? (config_func_info[dev_num]. + tip_get_temperature(dev_num)) : (0)); + + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x14c8, + read_data, MASK_ALL_BITS)); + printf("%d,%d,", ((read_data[if_id] & 0x3f0) >> 4), + ((read_data[if_id] & 0xfc00) >> 10)); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x17c8, + read_data, MASK_ALL_BITS)); + printf("%d,%d,", ((read_data[if_id] & 0x3f0) >> 4), + ((read_data[if_id] & 0xfc00) >> 10)); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1dc8, + read_data, MASK_ALL_BITS)); + printf("%d,%d,", ((read_data[if_id] & 0x3f0000) >> 16), + ((read_data[if_id] & 0xfc00000) >> 22)); + + for (csindex = 0; csindex < max_cs; csindex++) { + printf("CS%d , ", csindex); + for (bus_id = 0; bus_id < MAX_BUS_NUM; bus_id++) { + printf("\n"); + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + RESULT_DB_PHY_REG_ADDR + + csindex, ®_data); + printf("%d,%d,", (reg_data & 0x1f), + ((reg_data & 0x3e0) >> 5)); + /* WL */ + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + WL_PHY_REG + + csindex * 4, ®_data); + printf("%d,%d,%d,", + (reg_data & 0x1f) + + ((reg_data & 0x1c0) >> 6) * 32, + (reg_data & 0x1f), + (reg_data & 0x1c0) >> 6); + /* RL */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, + READ_DATA_SAMPLE_DELAY, + read_data, MASK_ALL_BITS)); + read_data[if_id] = + (read_data[if_id] & + (0xf << (4 * csindex))) >> + (4 * csindex); + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, + RL_PHY_REG + csindex * 4, + ®_data); + printf("%d,%d,%d,%d,", + (reg_data & 0x1f) + + ((reg_data & 0x1c0) >> 6) * 32 + + read_data[if_id] * 64, + (reg_data & 0x1f), + ((reg_data & 0x1c0) >> 6), + read_data[if_id]); + /* Centralization */ + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + csindex * 4, ®_data); + printf("%d,", (reg_data & 0x3f)); + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG + + csindex * 4, ®_data); + printf("%d,", (reg_data & 0x1f)); + /* Vref */ + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, + PAD_CONFIG_PHY_REG, + ®_data); + printf("%d,", (reg_data & 0x7)); + /* DQVref */ + /* Need to add the Read Function from device */ + printf("%d,", 0); + printf("\t\t"); + for (idx = 0; idx < 11; idx++) { + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + 0xd0 + + 12 * csindex + + idx, ®_data); + printf("%d,", (reg_data & 0x3f)); + } + printf("\t\t"); + for (idx = 0; idx < 11; idx++) { + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + 0x10 + + 16 * csindex + + idx, ®_data); + printf("%d,", (reg_data & 0x3f)); + } + printf("\t\t"); + for (idx = 0; idx < 11; idx++) { + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + 0x50 + + 16 * csindex + + idx, ®_data); + printf("%d,", (reg_data & 0x3f)); + } + } + } + } + printf("\n"); + + return MV_OK; +} + +/* + * Register XSB information + */ +int ddr3_tip_register_xsb_info(u32 dev_num, struct hws_xsb_info *xsb_info_table) +{ + memcpy(&xsb_info[dev_num], xsb_info_table, sizeof(struct hws_xsb_info)); + return MV_OK; +} + +/* + * Read ADLL Value + */ +int read_adll_value(u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], + int reg_addr, u32 mask) +{ + u32 data_value; + u32 if_id = 0, bus_id = 0; + u32 dev_num = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * multi CS support - reg_addr is calucalated in calling function + * with CS offset + */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, + bus_id, + DDR_PHY_DATA, reg_addr, + &data_value)); + pup_values[if_id * + tm->num_of_bus_per_interface + bus_id] = + data_value & mask; + } + } + + return 0; +} + +/* + * Write ADLL Value + */ +int write_adll_value(u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], + int reg_addr) +{ + u32 if_id = 0, bus_id = 0; + u32 dev_num = 0, data; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * multi CS support - reg_addr is calucalated in calling function + * with CS offset + */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + data = pup_values[if_id * + tm->num_of_bus_per_interface + + bus_id]; + CHECK_STATUS(ddr3_tip_bus_write(dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + reg_addr, data)); + } + } + + return 0; +} + +#ifndef EXCLUDE_SWITCH_DEBUG +u32 rl_version = 1; /* 0 - old RL machine */ +struct hws_tip_config_func_db config_func_info[HWS_MAX_DEVICE_NUM]; +u32 start_xsb_offset = 0; +u8 is_rl_old = 0; +u8 is_freq_old = 0; +u8 is_dfs_disabled = 0; +u32 default_centrlization_value = 0x12; +u32 vref = 0x4; +u32 activate_select_before_run_alg = 1, activate_deselect_after_run_alg = 1, + rl_test = 0, reset_read_fifo = 0; +int debug_acc = 0; +u32 ctrl_sweepres[ADLL_LENGTH][MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u32 ctrl_adll[MAX_CS_NUM * MAX_INTERFACE_NUM * MAX_BUS_NUM]; +u8 cs_mask_reg[] = { + 0, 4, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +u32 xsb_test_table[][8] = { + {0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, + 0x66666666, 0x77777777}, + {0x88888888, 0x99999999, 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd, + 0xeeeeeeee, 0xffffffff}, + {0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff}, + {0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff}, + {0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff}, + {0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff}, + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff}, + {0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000}, + {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff} +}; + +static int ddr3_tip_access_atr(u32 dev_num, u32 flag_id, u32 value, u32 **ptr); + +int ddr3_tip_print_adll(void) +{ + u32 bus_cnt = 0, if_id, data_p1, data_p2, ui_data3, dev_num = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_cnt, + DDR_PHY_DATA, 0x1, &data_p1)); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, 0x2, &data_p2)); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, 0x3, &ui_data3)); + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + (" IF %d bus_cnt %d phy_reg_1_data 0x%x phy_reg_2_data 0x%x phy_reg_3_data 0x%x\n", + if_id, bus_cnt, data_p1, data_p2, + ui_data3)); + } + } + + return MV_OK; +} + +/* + * Set attribute value + */ +int ddr3_tip_set_atr(u32 dev_num, u32 flag_id, u32 value) +{ + int ret; + u32 *ptr_flag = NULL; + + ret = ddr3_tip_access_atr(dev_num, flag_id, value, &ptr_flag); + if (ptr_flag != NULL) { + printf("ddr3_tip_set_atr Flag ID 0x%x value is set to 0x%x (was 0x%x)\n", + flag_id, value, *ptr_flag); + *ptr_flag = value; + } else { + printf("ddr3_tip_set_atr Flag ID 0x%x value is set to 0x%x\n", + flag_id, value); + } + + return ret; +} + +/* + * Access attribute + */ +static int ddr3_tip_access_atr(u32 dev_num, u32 flag_id, u32 value, u32 **ptr) +{ + u32 tmp_val = 0, if_id = 0, pup_id = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + dev_num = dev_num; + *ptr = NULL; + + switch (flag_id) { + case 0: + *ptr = (u32 *)&(tm->if_act_mask); + break; + + case 0x1: + *ptr = (u32 *)&mask_tune_func; + break; + + case 0x2: + *ptr = (u32 *)&low_freq; + break; + + case 0x3: + *ptr = (u32 *)&medium_freq; + break; + + case 0x4: + *ptr = (u32 *)&generic_init_controller; + break; + + case 0x5: + *ptr = (u32 *)&rl_version; + break; + + case 0x8: + *ptr = (u32 *)&start_xsb_offset; + break; + + case 0x20: + *ptr = (u32 *)&is_rl_old; + break; + + case 0x21: + *ptr = (u32 *)&is_freq_old; + break; + + case 0x23: + *ptr = (u32 *)&is_dfs_disabled; + break; + + case 0x24: + *ptr = (u32 *)&is_pll_before_init; + break; + + case 0x25: + *ptr = (u32 *)&is_adll_calib_before_init; + break; +#ifdef STATIC_ALGO_SUPPORT + case 0x26: + *ptr = (u32 *)&(silicon_delay[0]); + break; + + case 0x27: + *ptr = (u32 *)&wl_debug_delay; + break; +#endif + case 0x28: + *ptr = (u32 *)&is_tune_result; + break; + + case 0x29: + *ptr = (u32 *)&is_validate_window_per_if; + break; + + case 0x2a: + *ptr = (u32 *)&is_validate_window_per_pup; + break; + + case 0x30: + *ptr = (u32 *)&sweep_cnt; + break; + + case 0x31: + *ptr = (u32 *)&is_bist_reset_bit; + break; + + case 0x32: + *ptr = (u32 *)&is_dfs_in_init; + break; + + case 0x33: + *ptr = (u32 *)&p_finger; + break; + + case 0x34: + *ptr = (u32 *)&n_finger; + break; + + case 0x35: + *ptr = (u32 *)&init_freq; + break; + + case 0x36: + *ptr = (u32 *)&(freq_val[DDR_FREQ_LOW_FREQ]); + break; + + case 0x37: + *ptr = (u32 *)&start_pattern; + break; + + case 0x38: + *ptr = (u32 *)&end_pattern; + break; + + case 0x39: + *ptr = (u32 *)&phy_reg0_val; + break; + + case 0x4a: + *ptr = (u32 *)&phy_reg1_val; + break; + + case 0x4b: + *ptr = (u32 *)&phy_reg2_val; + break; + + case 0x4c: + *ptr = (u32 *)&phy_reg3_val; + break; + + case 0x4e: + *ptr = (u32 *)&sweep_pattern; + break; + + case 0x50: + *ptr = (u32 *)&is_rzq6; + break; + + case 0x51: + *ptr = (u32 *)&znri_data_phy_val; + break; + + case 0x52: + *ptr = (u32 *)&zpri_data_phy_val; + break; + + case 0x53: + *ptr = (u32 *)&finger_test; + break; + + case 0x54: + *ptr = (u32 *)&n_finger_start; + break; + + case 0x55: + *ptr = (u32 *)&n_finger_end; + break; + + case 0x56: + *ptr = (u32 *)&p_finger_start; + break; + + case 0x57: + *ptr = (u32 *)&p_finger_end; + break; + + case 0x58: + *ptr = (u32 *)&p_finger_step; + break; + + case 0x59: + *ptr = (u32 *)&n_finger_step; + break; + + case 0x5a: + *ptr = (u32 *)&znri_ctrl_phy_val; + break; + + case 0x5b: + *ptr = (u32 *)&zpri_ctrl_phy_val; + break; + + case 0x5c: + *ptr = (u32 *)&is_reg_dump; + break; + + case 0x5d: + *ptr = (u32 *)&vref; + break; + + case 0x5e: + *ptr = (u32 *)&mode2_t; + break; + + case 0x5f: + *ptr = (u32 *)&xsb_validate_type; + break; + + case 0x60: + *ptr = (u32 *)&xsb_validation_base_address; + break; + + case 0x67: + *ptr = (u32 *)&activate_select_before_run_alg; + break; + + case 0x68: + *ptr = (u32 *)&activate_deselect_after_run_alg; + break; + + case 0x69: + *ptr = (u32 *)&odt_additional; + break; + + case 0x70: + *ptr = (u32 *)&debug_mode; + break; + + case 0x71: + *ptr = (u32 *)&pbs_pattern; + break; + + case 0x72: + *ptr = (u32 *)&delay_enable; + break; + + case 0x73: + *ptr = (u32 *)&ck_delay; + break; + + case 0x74: + *ptr = (u32 *)&ck_delay_16; + break; + + case 0x75: + *ptr = (u32 *)&ca_delay; + break; + + case 0x100: + *ptr = (u32 *)&debug_dunit; + break; + + case 0x101: + debug_acc = (int)value; + break; + + case 0x102: + debug_training = (u8)value; + break; + + case 0x103: + debug_training_bist = (u8)value; + break; + + case 0x104: + debug_centralization = (u8)value; + break; + + case 0x105: + debug_training_ip = (u8)value; + break; + + case 0x106: + debug_leveling = (u8)value; + break; + + case 0x107: + debug_pbs = (u8)value; + break; + + case 0x108: + debug_training_static = (u8)value; + break; + + case 0x109: + debug_training_access = (u8)value; + break; + + case 0x112: + *ptr = &start_pattern; + break; + + case 0x113: + *ptr = &end_pattern; + break; + + default: + if ((flag_id >= 0x200) && (flag_id < 0x210)) { + if_id = flag_id - 0x200; + *ptr = (u32 *)&(tm->interface_params + [if_id].memory_freq); + } else if ((flag_id >= 0x210) && (flag_id < 0x220)) { + if_id = flag_id - 0x210; + *ptr = (u32 *)&(tm->interface_params + [if_id].speed_bin_index); + } else if ((flag_id >= 0x220) && (flag_id < 0x230)) { + if_id = flag_id - 0x220; + *ptr = (u32 *)&(tm->interface_params + [if_id].bus_width); + } else if ((flag_id >= 0x230) && (flag_id < 0x240)) { + if_id = flag_id - 0x230; + *ptr = (u32 *)&(tm->interface_params + [if_id].memory_size); + } else if ((flag_id >= 0x240) && (flag_id < 0x250)) { + if_id = flag_id - 0x240; + *ptr = (u32 *)&(tm->interface_params + [if_id].cas_l); + } else if ((flag_id >= 0x250) && (flag_id < 0x260)) { + if_id = flag_id - 0x250; + *ptr = (u32 *)&(tm->interface_params + [if_id].cas_wl); + } else if ((flag_id >= 0x270) && (flag_id < 0x2cf)) { + if_id = (flag_id - 0x270) / MAX_BUS_NUM; + pup_id = (flag_id - 0x270) % MAX_BUS_NUM; + *ptr = (u32 *)&(tm->interface_params[if_id]. + as_bus_params[pup_id].is_ck_swap); + } else if ((flag_id >= 0x2d0) && (flag_id < 0x32f)) { + if_id = (flag_id - 0x2d0) / MAX_BUS_NUM; + pup_id = (flag_id - 0x2d0) % MAX_BUS_NUM; + *ptr = (u32 *)&(tm->interface_params[if_id]. + as_bus_params[pup_id].is_dqs_swap); + } else if ((flag_id >= 0x330) && (flag_id < 0x38f)) { + if_id = (flag_id - 0x330) / MAX_BUS_NUM; + pup_id = (flag_id - 0x330) % MAX_BUS_NUM; + *ptr = (u32 *)&(tm->interface_params[if_id]. + as_bus_params[pup_id].cs_bitmask); + } else if ((flag_id >= 0x390) && (flag_id < 0x3ef)) { + if_id = (flag_id - 0x390) / MAX_BUS_NUM; + pup_id = (flag_id - 0x390) % MAX_BUS_NUM; + *ptr = (u32 *)&(tm->interface_params + [if_id].as_bus_params + [pup_id].mirror_enable_bitmask); + } else if ((flag_id >= 0x500) && (flag_id <= 0x50f)) { + tmp_val = flag_id - 0x320; + *ptr = (u32 *)&(clamp_tbl[tmp_val]); + } else { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("flag_id out of boundary %d\n", + flag_id)); + return MV_BAD_PARAM; + } + } + + return MV_OK; +} + +#ifndef EXCLUDE_SWITCH_DEBUG +/* + * Print ADLL + */ +int print_adll(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]) +{ + u32 i, j; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + dev_num = dev_num; + + for (j = 0; j < tm->num_of_bus_per_interface; j++) { + VALIDATE_ACTIVE(tm->bus_act_mask, j); + for (i = 0; i < MAX_INTERFACE_NUM; i++) { + printf("%d ,", + adll[i * tm->num_of_bus_per_interface + j]); + } + } + printf("\n"); + + return MV_OK; +} +#endif + +/* byte_index - only byte 0, 1, 2, or 3, oxff - test all bytes */ +static u32 ddr3_tip_compare(u32 if_id, u32 *p_src, u32 *p_dst, + u32 byte_index) +{ + u32 burst_cnt = 0, addr_offset, i_id; + int b_is_fail = 0; + + addr_offset = + (byte_index == + 0xff) ? (u32) 0xffffffff : (u32) (0xff << (byte_index * 8)); + for (burst_cnt = 0; burst_cnt < EXT_ACCESS_BURST_LENGTH; burst_cnt++) { + if ((p_src[burst_cnt] & addr_offset) != + (p_dst[burst_cnt] & addr_offset)) + b_is_fail = 1; + } + + if (b_is_fail == 1) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("IF %d exp: ", if_id)); + for (i_id = 0; i_id <= MAX_INTERFACE_NUM - 1; i_id++) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("0x%8x ", p_src[i_id])); + } + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("\n_i_f %d rcv: ", if_id)); + for (i_id = 0; i_id <= MAX_INTERFACE_NUM - 1; i_id++) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("(0x%8x ", p_dst[i_id])); + } + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, ("\n ")); + } + + return b_is_fail; +} + +/* test_type = 0-tx , 1-rx */ +int ddr3_tip_sweep_test(u32 dev_num, u32 test_type, + u32 mem_addr, u32 is_modify_adll, + u32 start_if, u32 end_if, u32 startpup, u32 endpup) +{ + u32 bus_cnt = 0, adll_val = 0, if_id, ui_prev_adll, ui_mask_bit, + end_adll, start_adll; + u32 reg_addr = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + mem_addr = mem_addr; + + if (test_type == 0) { + reg_addr = 1; + ui_mask_bit = 0x3f; + start_adll = 0; + end_adll = ui_mask_bit; + } else { + reg_addr = 3; + ui_mask_bit = 0x1f; + start_adll = 0; + end_adll = ui_mask_bit; + } + + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("==============================\n")); + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("Test type %d (0-tx, 1-rx)\n", test_type)); + + for (if_id = start_if; if_id <= end_if; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_cnt = startpup; bus_cnt < endpup; bus_cnt++) { + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, reg_addr, + &ui_prev_adll)); + + for (adll_val = start_adll; adll_val <= end_adll; + adll_val++) { + if (is_modify_adll == 1) { + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, bus_cnt, + DDR_PHY_DATA, reg_addr, + adll_val, ui_mask_bit)); + } + } + if (is_modify_adll == 1) { + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, reg_addr, + ui_prev_adll)); + } + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("\n")); + } + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("\n")); + } + + return MV_OK; +} + +#ifndef EXCLUDE_SWITCH_DEBUG +/* + * Sweep validation + */ +int ddr3_tip_run_sweep_test(int dev_num, u32 repeat_num, u32 direction, + u32 mode) +{ + u32 pup = 0, start_pup = 0, end_pup = 0; + u32 adll = 0; + u32 res[MAX_INTERFACE_NUM] = { 0 }; + int if_id = 0; + u32 adll_value = 0; + int reg = (direction == 0) ? WRITE_CENTRALIZATION_PHY_REG : + READ_CENTRALIZATION_PHY_REG; + enum hws_access_type pup_access; + u32 cs; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + repeat_num = repeat_num; + + if (mode == 1) { + /* per pup */ + start_pup = 0; + end_pup = tm->num_of_bus_per_interface - 1; + pup_access = ACCESS_TYPE_UNICAST; + } else { + start_pup = 0; + end_pup = 0; + pup_access = ACCESS_TYPE_MULTICAST; + } + + for (cs = 0; cs < max_cs; cs++) { + for (adll = 0; adll < ADLL_LENGTH; adll++) { + for (if_id = 0; + if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE + (tm->if_act_mask, + if_id); + for (pup = start_pup; pup <= end_pup; pup++) { + ctrl_sweepres[adll][if_id][pup] = + 0; + } + } + } + + for (adll = 0; adll < (MAX_INTERFACE_NUM * MAX_BUS_NUM); adll++) + ctrl_adll[adll] = 0; + /* Save DQS value(after algorithm run) */ + read_adll_value(ctrl_adll, + (reg + (cs * CS_REGISTER_ADDR_OFFSET)), + MASK_ALL_BITS); + + /* + * Sweep ADLL from 0:31 on all I/F on all Pup and perform + * BIST on each stage. + */ + for (pup = start_pup; pup <= end_pup; pup++) { + for (adll = 0; adll < ADLL_LENGTH; adll++) { + adll_value = + (direction == 0) ? (adll * 2) : adll; + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, 0, + pup_access, pup, DDR_PHY_DATA, + reg + CS_REG_VALUE(cs), + adll_value)); + hws_ddr3_run_bist(dev_num, sweep_pattern, res, + cs); + /* ddr3_tip_reset_fifo_ptr(dev_num); */ + for (if_id = 0; + if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE + (tm->if_act_mask, + if_id); + ctrl_sweepres[adll][if_id][pup] + = res[if_id]; + if (mode == 1) { + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + pup, + DDR_PHY_DATA, + reg + CS_REG_VALUE(cs), + ctrl_adll[if_id * + cs * + tm->num_of_bus_per_interface + + pup])); + } + } + } + } + printf("Final, CS %d,%s, Sweep, Result, Adll,", cs, + ((direction == 0) ? "TX" : "RX")); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (mode == 1) { + for (pup = start_pup; pup <= end_pup; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + printf("I/F%d-PHY%d , ", if_id, pup); + } + } else { + printf("I/F%d , ", if_id); + } + } + printf("\n"); + + for (adll = 0; adll < ADLL_LENGTH; adll++) { + adll_value = (direction == 0) ? (adll * 2) : adll; + printf("Final,%s, Sweep, Result, %d ,", + ((direction == 0) ? "TX" : "RX"), adll_value); + + for (if_id = 0; + if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = start_pup; pup <= end_pup; pup++) { + printf("%d , ", + ctrl_sweepres[adll][if_id] + [pup]); + } + } + printf("\n"); + } + + /* + * Write back to the phy the Rx DQS value, we store in + * the beginning. + */ + write_adll_value(ctrl_adll, + (reg + cs * CS_REGISTER_ADDR_OFFSET)); + /* print adll results */ + read_adll_value(ctrl_adll, (reg + cs * CS_REGISTER_ADDR_OFFSET), + MASK_ALL_BITS); + printf("%s, DQS, ADLL,,,", (direction == 0) ? "Tx" : "Rx"); + print_adll(dev_num, ctrl_adll); + } + ddr3_tip_reset_fifo_ptr(dev_num); + + return 0; +} + +void print_topology(struct hws_topology_map *topology_db) +{ + u32 ui, uj; + + printf("\tinterface_mask: 0x%x\n", topology_db->if_act_mask); + printf("\tNum Bus: %d\n", topology_db->num_of_bus_per_interface); + printf("\tbus_act_mask: 0x%x\n", topology_db->bus_act_mask); + + for (ui = 0; ui < MAX_INTERFACE_NUM; ui++) { + VALIDATE_ACTIVE(topology_db->if_act_mask, ui); + printf("\n\tInterface ID: %d\n", ui); + printf("\t\tDDR Frequency: %s\n", + convert_freq(topology_db-> + interface_params[ui].memory_freq)); + printf("\t\tSpeed_bin: %d\n", + topology_db->interface_params[ui].speed_bin_index); + printf("\t\tBus_width: %d\n", + (4 << topology_db->interface_params[ui].bus_width)); + printf("\t\tMem_size: %s\n", + convert_mem_size(topology_db-> + interface_params[ui].memory_size)); + printf("\t\tCAS-WL: %d\n", + topology_db->interface_params[ui].cas_wl); + printf("\t\tCAS-L: %d\n", + topology_db->interface_params[ui].cas_l); + printf("\t\tTemperature: %d\n", + topology_db->interface_params[ui].interface_temp); + printf("\n"); + for (uj = 0; uj < 4; uj++) { + printf("\t\tBus %d parameters- CS Mask: 0x%x\t", uj, + topology_db->interface_params[ui]. + as_bus_params[uj].cs_bitmask); + printf("Mirror: 0x%x\t", + topology_db->interface_params[ui]. + as_bus_params[uj].mirror_enable_bitmask); + printf("DQS Swap is %s \t", + (topology_db-> + interface_params[ui].as_bus_params[uj]. + is_dqs_swap == 1) ? "enabled" : "disabled"); + printf("Ck Swap:%s\t", + (topology_db-> + interface_params[ui].as_bus_params[uj]. + is_ck_swap == 1) ? "enabled" : "disabled"); + printf("\n"); + } + } +} +#endif + +/* + * Execute XSB Test transaction (rd/wr/both) + */ +int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type, + u32 read_type, u32 burst_length) +{ + u32 seq = 0, if_id = 0, addr, cnt; + int ret = MV_OK, ret_tmp; + u32 data_read[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + addr = mem_addr; + for (cnt = 0; cnt <= burst_length; cnt++) { + seq = (seq + 1) % 8; + if (write_type != 0) { + CHECK_STATUS(ddr3_tip_ext_write + (dev_num, if_id, addr, 1, + xsb_test_table[seq])); + } + if (read_type != 0) { + CHECK_STATUS(ddr3_tip_ext_read + (dev_num, if_id, addr, 1, + data_read)); + } + if ((read_type != 0) && (write_type != 0)) { + ret_tmp = + ddr3_tip_compare(if_id, + xsb_test_table[seq], + data_read, + 0xff); + addr += (EXT_ACCESS_BURST_LENGTH * 4); + ret = (ret != MV_OK) ? ret : ret_tmp; + } + } + } + + return ret; +} + +#else /*EXCLUDE_SWITCH_DEBUG */ + +u32 rl_version = 1; /* 0 - old RL machine */ +u32 vref = 0x4; +u32 start_xsb_offset = 0; +u8 cs_mask_reg[] = { + 0, 4, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type, + u32 read_type, u32 burst_length) +{ + return MV_OK; +} + +#endif diff --git a/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.c b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.c new file mode 100644 index 0000000..560da7e --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define REG_READ_DATA_SAMPLE_DELAYS_ADDR 0x1538 +#define REG_READ_DATA_SAMPLE_DELAYS_MASK 0x1f +#define REG_READ_DATA_SAMPLE_DELAYS_OFFS 8 + +#define REG_READ_DATA_READY_DELAYS_ADDR 0x153c +#define REG_READ_DATA_READY_DELAYS_MASK 0x1f +#define REG_READ_DATA_READY_DELAYS_OFFS 8 + +int ddr3_if_ecc_enabled(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask) || + DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) + return 1; + else + return 0; +} + +int ddr3_pre_algo_config(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* Set Bus3 ECC training mode */ + if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) { + /* Set Bus3 ECC MUX */ + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + REG_SDRAM_PINS_MUX, 0x100, 0x100)); + } + + /* Set regular ECC training mode (bus4 and bus 3) */ + if ((DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask)) || + (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))) { + /* Enable ECC Write MUX */ + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x100, 0x100)); + /* General ECC enable */ + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + REG_SDRAM_CONFIG_ADDR, 0x40000, 0x40000)); + /* Disable Read Data ECC MUX */ + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x0, 0x2)); + } + + return MV_OK; +} + +int ddr3_post_algo_config(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + int status; + + status = ddr3_post_run_alg(); + if (MV_OK != status) { + printf("DDR3 Post Run Alg - FAILED 0x%x\n", status); + return status; + } + + /* Un_set ECC training mode */ + if ((DDR3_IS_ECC_PUP4_MODE(tm->bus_act_mask)) || + (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask))) { + /* Disable ECC Write MUX */ + CHECK_STATUS(ddr3_tip_if_write + (0, ACCESS_TYPE_UNICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x0, 0x100)); + /* General ECC and Bus3 ECC MUX remains enabled */ + } + + return MV_OK; +} + +int ddr3_hws_hw_training(void) +{ + enum hws_algo_type algo_mode = ALGO_TYPE_DYNAMIC; + int status; + struct init_cntr_param init_param; + + status = ddr3_silicon_pre_init(); + if (MV_OK != status) { + printf("DDR3 Pre silicon Config - FAILED 0x%x\n", status); + return status; + } + + init_param.do_mrs_phy = 1; +#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X) + init_param.is_ctrl64_bit = 0; +#else + init_param.is_ctrl64_bit = 1; +#endif +#if defined(CONFIG_ALLEYCAT3) || defined(CONFIG_ARMADA_38X) || \ + defined(CONFIG_ARMADA_39X) + init_param.init_phy = 1; +#else + init_param.init_phy = 0; +#endif + init_param.msys_init = 1; + status = hws_ddr3_tip_init_controller(0, &init_param); + if (MV_OK != status) { + printf("DDR3 init controller - FAILED 0x%x\n", status); + return status; + } + + status = ddr3_silicon_post_init(); + if (MV_OK != status) { + printf("DDR3 Post Init - FAILED 0x%x\n", status); + return status; + } + + status = ddr3_pre_algo_config(); + if (MV_OK != status) { + printf("DDR3 Pre Algo Config - FAILED 0x%x\n", status); + return status; + } + + /* run algorithm in order to configure the PHY */ + status = hws_ddr3_tip_run_alg(0, algo_mode); + if (MV_OK != status) { + printf("DDR3 run algorithm - FAILED 0x%x\n", status); + return status; + } + + status = ddr3_post_algo_config(); + if (MV_OK != status) { + printf("DDR3 Post Algo Config - FAILED 0x%x\n", status); + return status; + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.h b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.h new file mode 100644 index 0000000..17a0953 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_HWS_HW_TRAINING_H +#define _DDR3_HWS_HW_TRAINING_H + +/* struct used for DLB configuration array */ +struct dlb_config { + u32 reg_addr; + u32 reg_data; +}; + +/* Topology update structure */ +struct topology_update_info { + int update_ecc; + u8 ecc; + int update_width; + u8 width; + int update_ecc_pup3_mode; + u8 ecc_pup_mode_offset; +}; + +/* Topology update defines */ +#define TOPOLOGY_UPDATE_WIDTH_16BIT 1 +#define TOPOLOGY_UPDATE_WIDTH_32BIT 0 +#define TOPOLOGY_UPDATE_WIDTH_32BIT_MASK 0xf +#define TOPOLOGY_UPDATE_WIDTH_16BIT_MASK 0x3 + +#define TOPOLOGY_UPDATE_ECC_ON 1 +#define TOPOLOGY_UPDATE_ECC_OFF 0 +#define TOPOLOGY_UPDATE_ECC_OFFSET_PUP4 4 +#define TOPOLOGY_UPDATE_ECC_OFFSET_PUP3 3 + +/* + * 1. L2 filter should be set at binary header to 0xd000000, + * to avoid conflict with internal register IO. + * 2. U-Boot modifies internal registers base to 0xf100000, + * and than should update L2 filter accordingly to 0xf000000 (3.75 GB) + */ +/* temporary limit l2 filter to 3GiB (LSP issue) */ +#define L2_FILTER_FOR_MAX_MEMORY_SIZE 0xc0000000 +#define ADDRESS_FILTERING_END_REGISTER 0x8c04 + +#define SUB_VERSION 0 + +#endif /* _DDR3_HWS_HW_TRAINING_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_hws_hw_training_def.h b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training_def.h new file mode 100644 index 0000000..7500a72 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_hws_hw_training_def.h @@ -0,0 +1,467 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_HWS_HW_TRAINING_DEF_H +#define _DDR3_HWS_HW_TRAINING_DEF_H + +#define SAR_DDR3_FREQ_MASK 0xfe00000 +#define SAR_CPU_FAB_GET(cpu, fab) (((cpu & 0x7) << 21) | \ + ((fab & 0xf) << 24)) + +#define MAX_CS 4 + +#define MIN_DIMM_ADDR 0x50 +#define FAR_END_DIMM_ADDR 0x50 +#define MAX_DIMM_ADDR 0x60 + +#define SDRAM_CS_SIZE 0xfffffff +#define SDRAM_CS_BASE 0x0 +#define SDRAM_DIMM_SIZE 0x80000000 + +#define CPU_CONFIGURATION_REG(id) (0x21800 + (id * 0x100)) +#define CPU_MRVL_ID_OFFSET 0x10 +#define SAR1_CPU_CORE_MASK 0x00000018 +#define SAR1_CPU_CORE_OFFSET 3 + +#define NEW_FABRIC_TWSI_ADDR 0x4e +#ifdef DB_784MP_GP +#define BUS_WIDTH_ECC_TWSI_ADDR 0x4e +#else +#define BUS_WIDTH_ECC_TWSI_ADDR 0x4f +#endif +#define MV_MAX_DDR3_STATIC_SIZE 50 +#define MV_DDR3_MODES_NUMBER 30 + +#define RESUME_RL_PATTERNS_ADDR 0xfe0000 +#define RESUME_RL_PATTERNS_SIZE 0x100 +#define RESUME_TRAINING_VALUES_ADDR (RESUME_RL_PATTERNS_ADDR + \ + RESUME_RL_PATTERNS_SIZE) +#define RESUME_TRAINING_VALUES_MAX 0xcd0 +#define BOOT_INFO_ADDR (RESUME_RL_PATTERNS_ADDR + 0x1000) +#define CHECKSUM_RESULT_ADDR (BOOT_INFO_ADDR + 0x1000) +#define NUM_OF_REGISTER_ADDR (CHECKSUM_RESULT_ADDR + 4) +#define SUSPEND_MAGIC_WORD 0xdeadb002 +#define REGISTER_LIST_END 0xffffffff + +/* MISC */ +#define INTER_REGS_BASE SOC_REGS_PHY_BASE + +/* DDR */ +#define REG_SDRAM_CONFIG_ADDR 0x1400 +#define REG_SDRAM_CONFIG_MASK 0x9fffffff +#define REG_SDRAM_CONFIG_RFRS_MASK 0x3fff +#define REG_SDRAM_CONFIG_WIDTH_OFFS 15 +#define REG_SDRAM_CONFIG_REGDIMM_OFFS 17 +#define REG_SDRAM_CONFIG_ECC_OFFS 18 +#define REG_SDRAM_CONFIG_IERR_OFFS 19 +#define REG_SDRAM_CONFIG_PUPRSTDIV_OFFS 28 +#define REG_SDRAM_CONFIG_RSTRD_OFFS 30 + +#define REG_SDRAM_PINS_MUX 0x19d4 + +#define REG_DUNIT_CTRL_LOW_ADDR 0x1404 +#define REG_DUNIT_CTRL_LOW_2T_OFFS 3 +#define REG_DUNIT_CTRL_LOW_2T_MASK 0x3 +#define REG_DUNIT_CTRL_LOW_DPDE_OFFS 14 + +#define REG_SDRAM_TIMING_LOW_ADDR 0x1408 +#define REG_SDRAM_TIMING_HIGH_ADDR 0x140c +#define REG_SDRAM_TIMING_H_R2R_OFFS 7 +#define REG_SDRAM_TIMING_H_R2R_MASK 0x3 +#define REG_SDRAM_TIMING_H_R2W_W2R_OFFS 9 +#define REG_SDRAM_TIMING_H_R2W_W2R_MASK 0x3 +#define REG_SDRAM_TIMING_H_W2W_OFFS 11 +#define REG_SDRAM_TIMING_H_W2W_MASK 0x1f +#define REG_SDRAM_TIMING_H_R2R_H_OFFS 19 +#define REG_SDRAM_TIMING_H_R2R_H_MASK 0x7 +#define REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS 22 +#define REG_SDRAM_TIMING_H_R2W_W2R_H_MASK 0x7 + +#define REG_SDRAM_ADDRESS_CTRL_ADDR 0x1410 +#define REG_SDRAM_ADDRESS_SIZE_OFFS 2 +#define REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS 18 +#define REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS 4 + +#define REG_SDRAM_OPEN_PAGES_ADDR 0x1414 +#define REG_SDRAM_OPERATION_CS_OFFS 8 + +#define REG_SDRAM_OPERATION_ADDR 0x1418 +#define REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS 24 +#define REG_SDRAM_OPERATION_CWA_DATA_OFFS 20 +#define REG_SDRAM_OPERATION_CWA_DATA_MASK 0xf +#define REG_SDRAM_OPERATION_CWA_RC_OFFS 16 +#define REG_SDRAM_OPERATION_CWA_RC_MASK 0xf +#define REG_SDRAM_OPERATION_CMD_MR0 0xf03 +#define REG_SDRAM_OPERATION_CMD_MR1 0xf04 +#define REG_SDRAM_OPERATION_CMD_MR2 0xf08 +#define REG_SDRAM_OPERATION_CMD_MR3 0xf09 +#define REG_SDRAM_OPERATION_CMD_RFRS 0xf02 +#define REG_SDRAM_OPERATION_CMD_CWA 0xf0e +#define REG_SDRAM_OPERATION_CMD_RFRS_DONE 0xf +#define REG_SDRAM_OPERATION_CMD_MASK 0xf +#define REG_SDRAM_OPERATION_CS_OFFS 8 + +#define REG_OUDDR3_TIMING_ADDR 0x142c + +#define REG_SDRAM_MODE_ADDR 0x141c + +#define REG_SDRAM_EXT_MODE_ADDR 0x1420 + +#define REG_DDR_CONT_HIGH_ADDR 0x1424 + +#define REG_ODT_TIME_LOW_ADDR 0x1428 +#define REG_ODT_ON_CTL_RD_OFFS 12 +#define REG_ODT_OFF_CTL_RD_OFFS 16 +#define REG_SDRAM_ERROR_ADDR 0x1454 +#define REG_SDRAM_AUTO_PWR_SAVE_ADDR 0x1474 +#define REG_ODT_TIME_HIGH_ADDR 0x147c + +#define REG_SDRAM_INIT_CTRL_ADDR 0x1480 +#define REG_SDRAM_INIT_CTRL_OFFS 0 +#define REG_SDRAM_INIT_CKE_ASSERT_OFFS 2 +#define REG_SDRAM_INIT_RESET_DEASSERT_OFFS 3 +#define REG_SDRAM_INIT_RESET_MASK_OFFS 1 + +#define REG_SDRAM_ODT_CTRL_LOW_ADDR 0x1494 + +#define REG_SDRAM_ODT_CTRL_HIGH_ADDR 0x1498 +#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK 0x0 +#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_ENA 0x3 + +#define REG_DUNIT_ODT_CTRL_ADDR 0x149c +#define REG_DUNIT_ODT_CTRL_OVRD_OFFS 8 +#define REG_DUNIT_ODT_CTRL_OVRD_VAL_OFFS 9 + +#define REG_DRAM_FIFO_CTRL_ADDR 0x14a0 + +#define REG_DRAM_AXI_CTRL_ADDR 0x14a8 +#define REG_DRAM_AXI_CTRL_AXIDATABUSWIDTH_OFFS 0 + +#define REG_METAL_MASK_ADDR 0x14b0 +#define REG_METAL_MASK_MASK 0xdfffffff +#define REG_METAL_MASK_RETRY_OFFS 0 + +#define REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR 0x14c0 + +#define REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR 0x14c4 +#define REG_DRAM_VER_CAL_MACHINE_CTRL_ADDR 0x14c8 +#define REG_DRAM_MAIN_PADS_CAL_ADDR 0x14cc + +#define REG_DRAM_HOR_CAL_MACHINE_CTRL_ADDR 0x17c8 + +#define REG_CS_SIZE_SCRATCH_ADDR 0x1504 +#define REG_DYNAMIC_POWER_SAVE_ADDR 0x1520 +#define REG_DDR_IO_ADDR 0x1524 +#define REG_DDR_IO_CLK_RATIO_OFFS 15 + +#define REG_DFS_ADDR 0x1528 +#define REG_DFS_DLLNEXTSTATE_OFFS 0 +#define REG_DFS_BLOCK_OFFS 1 +#define REG_DFS_SR_OFFS 2 +#define REG_DFS_ATSR_OFFS 3 +#define REG_DFS_RECONF_OFFS 4 +#define REG_DFS_CL_NEXT_STATE_OFFS 8 +#define REG_DFS_CL_NEXT_STATE_MASK 0xf +#define REG_DFS_CWL_NEXT_STATE_OFFS 12 +#define REG_DFS_CWL_NEXT_STATE_MASK 0x7 + +#define REG_READ_DATA_SAMPLE_DELAYS_ADDR 0x1538 +#define REG_READ_DATA_SAMPLE_DELAYS_MASK 0x1f +#define REG_READ_DATA_SAMPLE_DELAYS_OFFS 8 + +#define REG_READ_DATA_READY_DELAYS_ADDR 0x153c +#define REG_READ_DATA_READY_DELAYS_MASK 0x1f +#define REG_READ_DATA_READY_DELAYS_OFFS 8 + +#define START_BURST_IN_ADDR 1 + +#define REG_DRAM_TRAINING_SHADOW_ADDR 0x18488 +#define REG_DRAM_TRAINING_ADDR 0x15b0 +#define REG_DRAM_TRAINING_LOW_FREQ_OFFS 0 +#define REG_DRAM_TRAINING_PATTERNS_OFFS 4 +#define REG_DRAM_TRAINING_MED_FREQ_OFFS 2 +#define REG_DRAM_TRAINING_WL_OFFS 3 +#define REG_DRAM_TRAINING_RL_OFFS 6 +#define REG_DRAM_TRAINING_DQS_RX_OFFS 15 +#define REG_DRAM_TRAINING_DQS_TX_OFFS 16 +#define REG_DRAM_TRAINING_CS_OFFS 20 +#define REG_DRAM_TRAINING_RETEST_OFFS 24 +#define REG_DRAM_TRAINING_DFS_FREQ_OFFS 27 +#define REG_DRAM_TRAINING_DFS_REQ_OFFS 29 +#define REG_DRAM_TRAINING_ERROR_OFFS 30 +#define REG_DRAM_TRAINING_AUTO_OFFS 31 +#define REG_DRAM_TRAINING_RETEST_PAR 0x3 +#define REG_DRAM_TRAINING_RETEST_MASK 0xf8ffffff +#define REG_DRAM_TRAINING_CS_MASK 0xff0fffff +#define REG_DRAM_TRAINING_PATTERNS_MASK 0xff0f0000 + +#define REG_DRAM_TRAINING_1_ADDR 0x15b4 +#define REG_DRAM_TRAINING_1_TRNBPOINT_OFFS 16 + +#define REG_DRAM_TRAINING_2_ADDR 0x15b8 +#define REG_DRAM_TRAINING_2_OVERRUN_OFFS 17 +#define REG_DRAM_TRAINING_2_FIFO_RST_OFFS 4 +#define REG_DRAM_TRAINING_2_RL_MODE_OFFS 3 +#define REG_DRAM_TRAINING_2_WL_MODE_OFFS 2 +#define REG_DRAM_TRAINING_2_ECC_MUX_OFFS 1 +#define REG_DRAM_TRAINING_2_SW_OVRD_OFFS 0 + +#define REG_DRAM_TRAINING_PATTERN_BASE_ADDR 0x15bc +#define REG_DRAM_TRAINING_PATTERN_BASE_OFFS 3 + +#define REG_TRAINING_DEBUG_2_ADDR 0x15c4 +#define REG_TRAINING_DEBUG_2_OFFS 16 +#define REG_TRAINING_DEBUG_2_MASK 0x3 + +#define REG_TRAINING_DEBUG_3_ADDR 0x15c8 +#define REG_TRAINING_DEBUG_3_OFFS 3 +#define REG_TRAINING_DEBUG_3_MASK 0x7 + +#define MR_CS_ADDR_OFFS 4 + +#define REG_DDR3_MR0_ADDR 0x15d0 +#define REG_DDR3_MR0_CS_ADDR 0x1870 +#define REG_DDR3_MR0_CL_MASK 0x74 +#define REG_DDR3_MR0_CL_OFFS 2 +#define REG_DDR3_MR0_CL_HIGH_OFFS 3 +#define CL_MASK 0xf + +#define REG_DDR3_MR1_ADDR 0x15d4 +#define REG_DDR3_MR1_CS_ADDR 0x1874 +#define REG_DDR3_MR1_RTT_MASK 0xfffffdbb +#define REG_DDR3_MR1_DLL_ENA_OFFS 0 +#define REG_DDR3_MR1_RTT_DISABLED 0x0 +#define REG_DDR3_MR1_RTT_RZQ2 0x40 +#define REG_DDR3_MR1_RTT_RZQ4 0x2 +#define REG_DDR3_MR1_RTT_RZQ6 0x42 +#define REG_DDR3_MR1_RTT_RZQ8 0x202 +#define REG_DDR3_MR1_RTT_RZQ12 0x4 +/* WL-disabled, OB-enabled */ +#define REG_DDR3_MR1_OUTBUF_WL_MASK 0xffffef7f +/* Output Buffer Disabled */ +#define REG_DDR3_MR1_OUTBUF_DIS_OFFS 12 +#define REG_DDR3_MR1_WL_ENA_OFFS 7 +#define REG_DDR3_MR1_WL_ENA 0x80 /* WL Enabled */ +#define REG_DDR3_MR1_ODT_MASK 0xfffffdbb + +#define REG_DDR3_MR2_ADDR 0x15d8 +#define REG_DDR3_MR2_CS_ADDR 0x1878 +#define REG_DDR3_MR2_CWL_OFFS 3 +#define REG_DDR3_MR2_CWL_MASK 0x7 +#define REG_DDR3_MR2_ODT_MASK 0xfffff9ff +#define REG_DDR3_MR3_ADDR 0x15dc +#define REG_DDR3_MR3_CS_ADDR 0x187c + +#define REG_DDR3_RANK_CTRL_ADDR 0x15e0 +#define REG_DDR3_RANK_CTRL_CS_ENA_MASK 0xf +#define REG_DDR3_RANK_CTRL_MIRROR_OFFS 4 + +#define REG_ZQC_CONF_ADDR 0x15e4 + +#define REG_DRAM_PHY_CONFIG_ADDR 0x15ec +#define REG_DRAM_PHY_CONFIG_MASK 0x3fffffff + +#define REG_ODPG_CNTRL_ADDR 0x1600 +#define REG_ODPG_CNTRL_OFFS 21 + +#define REG_PHY_LOCK_MASK_ADDR 0x1670 +#define REG_PHY_LOCK_MASK_MASK 0xfffff000 + +#define REG_PHY_LOCK_STATUS_ADDR 0x1674 +#define REG_PHY_LOCK_STATUS_LOCK_OFFS 9 +#define REG_PHY_LOCK_STATUS_LOCK_MASK 0xfff +#define REG_PHY_LOCK_APLL_ADLL_STATUS_MASK 0x7ff + +#define REG_PHY_REGISTRY_FILE_ACCESS_ADDR 0x16a0 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_WR 0xc0000000 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_RD 0x80000000 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE 0x80000000 +#define REG_PHY_BC_OFFS 27 +#define REG_PHY_CNTRL_OFFS 26 +#define REG_PHY_CS_OFFS 16 +#define REG_PHY_DQS_REF_DLY_OFFS 10 +#define REG_PHY_PHASE_OFFS 8 +#define REG_PHY_PUP_OFFS 22 + +#define REG_TRAINING_WL_ADDR 0x16ac +#define REG_TRAINING_WL_CS_MASK 0xfffffffc +#define REG_TRAINING_WL_UPD_OFFS 2 +#define REG_TRAINING_WL_CS_DONE_OFFS 3 +#define REG_TRAINING_WL_RATIO_MASK 0xffffff0f +#define REG_TRAINING_WL_1TO1 0x50 +#define REG_TRAINING_WL_2TO1 0x10 +#define REG_TRAINING_WL_DELAYEXP_MASK 0x20000000 +#define REG_TRAINING_WL_RESULTS_MASK 0x000001ff +#define REG_TRAINING_WL_RESULTS_OFFS 20 + +#define REG_REGISTERED_DRAM_CTRL_ADDR 0x16d0 +#define REG_REGISTERED_DRAM_CTRL_SR_FLOAT_OFFS 15 +#define REG_REGISTERED_DRAM_CTRL_PARITY_MASK 0x3f + +/* DLB */ +#define REG_STATIC_DRAM_DLB_CONTROL 0x1700 +#define DLB_BUS_OPTIMIZATION_WEIGHTS_REG 0x1704 +#define DLB_AGING_REGISTER 0x1708 +#define DLB_EVICTION_CONTROL_REG 0x170c +#define DLB_EVICTION_TIMERS_REGISTER_REG 0x1710 +#define DLB_USER_COMMAND_REG 0x1714 +#define DLB_BUS_WEIGHTS_DIFF_CS 0x1770 +#define DLB_BUS_WEIGHTS_DIFF_BG 0x1774 +#define DLB_BUS_WEIGHTS_SAME_BG 0x1778 +#define DLB_BUS_WEIGHTS_RD_WR 0x177c +#define DLB_BUS_WEIGHTS_ATTR_SYS_PRIO 0x1780 +#define DLB_MAIN_QUEUE_MAP 0x1784 +#define DLB_LINE_SPLIT 0x1788 + +#define DLB_ENABLE 0x1 +#define DLB_WRITE_COALESING (0x1 << 2) +#define DLB_AXI_PREFETCH_EN (0x1 << 3) +#define DLB_MBUS_PREFETCH_EN (0x1 << 4) +#define PREFETCH_N_LN_SZ_TR (0x1 << 6) +#define DLB_INTERJECTION_ENABLE (0x1 << 3) + +/* CPU */ +#define REG_BOOTROM_ROUTINE_ADDR 0x182d0 +#define REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS 12 + +#define REG_DRAM_INIT_CTRL_STATUS_ADDR 0x18488 +#define REG_DRAM_INIT_CTRL_TRN_CLK_OFFS 16 +#define REG_CPU_DIV_CLK_CTRL_0_NEW_RATIO 0x000200ff +#define REG_DRAM_INIT_CTRL_STATUS_2_ADDR 0x1488 + +#define REG_CPU_DIV_CLK_CTRL_0_ADDR 0x18700 + +#define REG_CPU_DIV_CLK_CTRL_1_ADDR 0x18704 +#define REG_CPU_DIV_CLK_CTRL_2_ADDR 0x18708 + +#define REG_CPU_DIV_CLK_CTRL_3_ADDR 0x1870c +#define REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK 0xffffc0ff +#define REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS 8 + +#define REG_CPU_DIV_CLK_CTRL_4_ADDR 0x18710 + +#define REG_CPU_DIV_CLK_STATUS_0_ADDR 0x18718 +#define REG_CPU_DIV_CLK_ALL_STABLE_OFFS 8 + +#define REG_CPU_PLL_CTRL_0_ADDR 0x1871c +#define REG_CPU_PLL_STATUS_0_ADDR 0x18724 +#define REG_CORE_DIV_CLK_CTRL_ADDR 0x18740 +#define REG_CORE_DIV_CLK_STATUS_ADDR 0x18744 +#define REG_DDRPHY_APLL_CTRL_ADDR 0x18780 + +#define REG_DDRPHY_APLL_CTRL_2_ADDR 0x18784 +#define REG_SFABRIC_CLK_CTRL_ADDR 0x20858 +#define REG_SFABRIC_CLK_CTRL_SMPL_OFFS 8 + +/* DRAM Windows */ +#define REG_XBAR_WIN_19_CTRL_ADDR 0x200e8 +#define REG_XBAR_WIN_4_CTRL_ADDR 0x20040 +#define REG_XBAR_WIN_4_BASE_ADDR 0x20044 +#define REG_XBAR_WIN_4_REMAP_ADDR 0x20048 +#define REG_FASTPATH_WIN_0_CTRL_ADDR 0x20184 +#define REG_XBAR_WIN_7_REMAP_ADDR 0x20078 + +/* SRAM */ +#define REG_CDI_CONFIG_ADDR 0x20220 +#define REG_SRAM_WINDOW_0_ADDR 0x20240 +#define REG_SRAM_WINDOW_0_ENA_OFFS 0 +#define REG_SRAM_WINDOW_1_ADDR 0x20244 +#define REG_SRAM_L2_ENA_ADDR 0x8500 +#define REG_SRAM_CLEAN_BY_WAY_ADDR 0x87bc + +/* Timers */ +#define REG_TIMERS_CTRL_ADDR 0x20300 +#define REG_TIMERS_EVENTS_ADDR 0x20304 +#define REG_TIMER0_VALUE_ADDR 0x20314 +#define REG_TIMER1_VALUE_ADDR 0x2031c +#define REG_TIMER0_ENABLE_MASK 0x1 + +#define MV_BOARD_REFCLK_25MHZ 25000000 +#define CNTMR_RELOAD_REG(tmr) (REG_TIMERS_CTRL_ADDR + 0x10 + (tmr * 8)) +#define CNTMR_VAL_REG(tmr) (REG_TIMERS_CTRL_ADDR + 0x14 + (tmr * 8)) +#define CNTMR_CTRL_REG(tmr) (REG_TIMERS_CTRL_ADDR) +#define CTCR_ARM_TIMER_EN_OFFS(timer) (timer * 2) +#define CTCR_ARM_TIMER_EN_MASK(timer) (1 << CTCR_ARM_TIMER_EN_OFFS(timer)) +#define CTCR_ARM_TIMER_EN(timer) (1 << CTCR_ARM_TIMER_EN_OFFS(timer)) + +#define CTCR_ARM_TIMER_AUTO_OFFS(timer) (1 + (timer * 2)) +#define CTCR_ARM_TIMER_AUTO_MASK(timer) (1 << CTCR_ARM_TIMER_EN_OFFS(timer)) +#define CTCR_ARM_TIMER_AUTO_EN(timer) (1 << CTCR_ARM_TIMER_AUTO_OFFS(timer)) + +/* PMU */ +#define REG_PMU_I_F_CTRL_ADDR 0x1c090 +#define REG_PMU_DUNIT_BLK_OFFS 16 +#define REG_PMU_DUNIT_RFRS_OFFS 20 +#define REG_PMU_DUNIT_ACK_OFFS 24 + +/* MBUS */ +#define MBUS_UNITS_PRIORITY_CONTROL_REG (MBUS_REGS_OFFSET + 0x420) +#define FABRIC_UNITS_PRIORITY_CONTROL_REG (MBUS_REGS_OFFSET + 0x424) +#define MBUS_UNITS_PREFETCH_CONTROL_REG (MBUS_REGS_OFFSET + 0x428) +#define FABRIC_UNITS_PREFETCH_CONTROL_REG (MBUS_REGS_OFFSET + 0x42c) + +#define REG_PM_STAT_MASK_ADDR 0x2210c +#define REG_PM_STAT_MASK_CPU0_IDLE_MASK_OFFS 16 + +#define REG_PM_EVENT_STAT_MASK_ADDR 0x22120 +#define REG_PM_EVENT_STAT_MASK_DFS_DONE_OFFS 17 + +#define REG_PM_CTRL_CONFIG_ADDR 0x22104 +#define REG_PM_CTRL_CONFIG_DFS_REQ_OFFS 18 + +#define REG_FABRIC_LOCAL_IRQ_MASK_ADDR 0x218c4 +#define REG_FABRIC_LOCAL_IRQ_PMU_MASK_OFFS 18 + +/* Controller revision info */ +#define PCI_CLASS_CODE_AND_REVISION_ID 0x008 +#define PCCRIR_REVID_OFFS 0 /* Revision ID */ +#define PCCRIR_REVID_MASK (0xff << PCCRIR_REVID_OFFS) + +/* Power Management Clock Gating Control Register */ +#define POWER_MNG_CTRL_REG 0x18220 +#define PEX_DEVICE_AND_VENDOR_ID 0x000 +#define PEX_CFG_DIRECT_ACCESS(if, reg) (PEX_IF_REGS_BASE(if) + (reg)) +#define PMC_PEXSTOPCLOCK_OFFS(p) ((p) < 8 ? (5 + (p)) : (18 + (p))) +#define PMC_PEXSTOPCLOCK_MASK(p) (1 << PMC_PEXSTOPCLOCK_OFFS(p)) +#define PMC_PEXSTOPCLOCK_EN(p) (1 << PMC_PEXSTOPCLOCK_OFFS(p)) +#define PMC_PEXSTOPCLOCK_STOP(p) (0 << PMC_PEXSTOPCLOCK_OFFS(p)) + +/* TWSI */ +#define TWSI_DATA_ADDR_MASK 0x7 +#define TWSI_DATA_ADDR_OFFS 1 + +/* General */ +#define MAX_CS 4 + +/* Frequencies */ +#define FAB_OPT 21 +#define CLK_CPU 12 +#define CLK_VCO (2 * CLK_CPU) +#define CLK_DDR 12 + +/* CPU Frequencies: */ +#define CLK_CPU_1000 0 +#define CLK_CPU_1066 1 +#define CLK_CPU_1200 2 +#define CLK_CPU_1333 3 +#define CLK_CPU_1500 4 +#define CLK_CPU_1666 5 +#define CLK_CPU_1800 6 +#define CLK_CPU_2000 7 +#define CLK_CPU_600 8 +#define CLK_CPU_667 9 +#define CLK_CPU_800 0xa + +/* Extra Cpu Frequencies: */ +#define CLK_CPU_1600 11 +#define CLK_CPU_2133 12 +#define CLK_CPU_2200 13 +#define CLK_CPU_2400 14 + +#define SAR1_CPU_CORE_MASK 0x00000018 +#define SAR1_CPU_CORE_OFFSET 3 + +#endif /* _DDR3_HWS_HW_TRAINING_DEF_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_hws_sil_training.h b/drivers/ddr/marvell/a38x/ddr3_hws_sil_training.h new file mode 100644 index 0000000..544237a --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_hws_sil_training.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_HWS_SIL_TRAINING_H +#define _DDR3_HWS_SIL_TRAINING_H + +#include "ddr3_training_ip.h" +#include "ddr3_training_ip_prv_if.h" + +int ddr3_silicon_pre_config(void); +int ddr3_silicon_init(void); +int ddr3_silicon_get_ddr_target_freq(u32 *ddr_freq); + +#endif /* _DDR3_HWS_SIL_TRAINING_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_init.c b/drivers/ddr/marvell/a38x/ddr3_init.c new file mode 100644 index 0000000..d6ed8e0 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_init.c @@ -0,0 +1,852 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#include "../../../../arch/arm/mach-mvebu/serdes/a38x/sys_env_lib.h" + +static struct dlb_config ddr3_dlb_config_table[] = { + {REG_STATIC_DRAM_DLB_CONTROL, 0x2000005c}, + {DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0x00880000}, + {DLB_AGING_REGISTER, 0x0f7f007f}, + {DLB_EVICTION_CONTROL_REG, 0x0000129f}, + {DLB_EVICTION_TIMERS_REGISTER_REG, 0x00ff0000}, + {DLB_BUS_WEIGHTS_DIFF_CS, 0x04030802}, + {DLB_BUS_WEIGHTS_DIFF_BG, 0x00000a02}, + {DLB_BUS_WEIGHTS_SAME_BG, 0x09000a01}, + {DLB_BUS_WEIGHTS_RD_WR, 0x00020005}, + {DLB_BUS_WEIGHTS_ATTR_SYS_PRIO, 0x00060f10}, + {DLB_MAIN_QUEUE_MAP, 0x00000543}, + {DLB_LINE_SPLIT, 0x00000000}, + {DLB_USER_COMMAND_REG, 0x00000000}, + {0x0, 0x0} +}; + +static struct dlb_config ddr3_dlb_config_table_a0[] = { + {REG_STATIC_DRAM_DLB_CONTROL, 0x2000005c}, + {DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0x00880000}, + {DLB_AGING_REGISTER, 0x0f7f007f}, + {DLB_EVICTION_CONTROL_REG, 0x0000129f}, + {DLB_EVICTION_TIMERS_REGISTER_REG, 0x00ff0000}, + {DLB_BUS_WEIGHTS_DIFF_CS, 0x04030802}, + {DLB_BUS_WEIGHTS_DIFF_BG, 0x00000a02}, + {DLB_BUS_WEIGHTS_SAME_BG, 0x09000a01}, + {DLB_BUS_WEIGHTS_RD_WR, 0x00020005}, + {DLB_BUS_WEIGHTS_ATTR_SYS_PRIO, 0x00060f10}, + {DLB_MAIN_QUEUE_MAP, 0x00000543}, + {DLB_LINE_SPLIT, 0x00000000}, + {DLB_USER_COMMAND_REG, 0x00000000}, + {0x0, 0x0} +}; + +#if defined(CONFIG_ARMADA_38X) +struct dram_modes { + char *mode_name; + u8 cpu_freq; + u8 fab_freq; + u8 chip_id; + u8 chip_board_rev; + struct reg_data *regs; +}; + +struct dram_modes ddr_modes[] = { +#ifdef SUPPORT_STATIC_DUNIT_CONFIG + /* Conf name, CPUFreq, Fab_freq, Chip ID, Chip/Board, MC regs*/ +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT + {"a38x_customer_0_800", DDR_FREQ_800, 0, 0x0, A38X_CUSTOMER_BOARD_ID0, + ddr3_customer_800}, + {"a38x_customer_1_800", DDR_FREQ_800, 0, 0x0, A38X_CUSTOMER_BOARD_ID1, + ddr3_customer_800}, +#else + {"a38x_533", DDR_FREQ_533, 0, 0x0, MARVELL_BOARD, ddr3_a38x_533}, + {"a38x_667", DDR_FREQ_667, 0, 0x0, MARVELL_BOARD, ddr3_a38x_667}, + {"a38x_800", DDR_FREQ_800, 0, 0x0, MARVELL_BOARD, ddr3_a38x_800}, + {"a38x_933", DDR_FREQ_933, 0, 0x0, MARVELL_BOARD, ddr3_a38x_933}, +#endif +#endif +}; +#endif /* defined(CONFIG_ARMADA_38X) */ + +/* Translates topology map definitions to real memory size in bits */ +u32 mem_size[] = { + ADDR_SIZE_512MB, ADDR_SIZE_1GB, ADDR_SIZE_2GB, ADDR_SIZE_4GB, + ADDR_SIZE_8GB +}; + +static char *ddr_type = "DDR3"; + +/* + * Set 1 to use dynamic DUNIT configuration, + * set 0 (supported for A380 and AC3) to configure DUNIT in values set by + * ddr3_tip_init_specific_reg_config + */ +u8 generic_init_controller = 1; + +#ifdef SUPPORT_STATIC_DUNIT_CONFIG +static u32 ddr3_get_static_ddr_mode(void); +#endif +static int ddr3_hws_tune_training_params(u8 dev_num); +static int ddr3_update_topology_map(struct hws_topology_map *topology_map); + +/* device revision */ +#define DEV_VERSION_ID_REG 0x1823c +#define REVISON_ID_OFFS 8 +#define REVISON_ID_MASK 0xf00 + +/* A38x revisions */ +#define MV_88F68XX_Z1_ID 0x0 +#define MV_88F68XX_A0_ID 0x4 +/* A39x revisions */ +#define MV_88F69XX_Z1_ID 0x2 + +/* + * sys_env_device_rev_get - Get Marvell controller device revision number + * + * DESCRIPTION: + * This function returns 8bit describing the device revision as defined + * Revision ID Register. + * + * INPUT: + * None. + * + * OUTPUT: + * None. + * + * RETURN: + * 8bit desscribing Marvell controller revision number + */ +u8 sys_env_device_rev_get(void) +{ + u32 value; + + value = reg_read(DEV_VERSION_ID_REG); + return (value & (REVISON_ID_MASK)) >> REVISON_ID_OFFS; +} + +/* + * sys_env_dlb_config_ptr_get + * + * DESCRIPTION: defines pointer to to DLB COnfiguration table + * + * INPUT: none + * + * OUTPUT: pointer to DLB COnfiguration table + * + * RETURN: + * returns pointer to DLB COnfiguration table + */ +struct dlb_config *sys_env_dlb_config_ptr_get(void) +{ +#ifdef CONFIG_ARMADA_39X + return &ddr3_dlb_config_table_a0[0]; +#else + if (sys_env_device_rev_get() == MV_88F68XX_A0_ID) + return &ddr3_dlb_config_table_a0[0]; + else + return &ddr3_dlb_config_table[0]; +#endif +} + +/* + * sys_env_get_cs_ena_from_reg + * + * DESCRIPTION: Get bit mask of enabled CS + * + * INPUT: None + * + * OUTPUT: None + * + * RETURN: + * Bit mask of enabled CS, 1 if only CS0 enabled, + * 3 if both CS0 and CS1 enabled + */ +u32 sys_env_get_cs_ena_from_reg(void) +{ + return reg_read(REG_DDR3_RANK_CTRL_ADDR) & + REG_DDR3_RANK_CTRL_CS_ENA_MASK; +} + +static void ddr3_restore_and_set_final_windows(u32 *win) +{ + u32 win_ctrl_reg, num_of_win_regs; + u32 cs_ena = sys_env_get_cs_ena_from_reg(); + u32 ui; + + win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR; + num_of_win_regs = 16; + + /* Return XBAR windows 4-7 or 16-19 init configuration */ + for (ui = 0; ui < num_of_win_regs; ui++) + reg_write((win_ctrl_reg + 0x4 * ui), win[ui]); + + printf("%s Training Sequence - Switching XBAR Window to FastPath Window\n", + ddr_type); + +#if defined DYNAMIC_CS_SIZE_CONFIG + if (ddr3_fast_path_dynamic_cs_size_config(cs_ena) != MV_OK) + printf("ddr3_fast_path_dynamic_cs_size_config FAILED\n"); +#else + u32 reg, cs; + reg = 0x1fffffe1; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg |= (cs << 2); + break; + } + } + /* Open fast path Window to - 0.5G */ + reg_write(REG_FASTPATH_WIN_0_CTRL_ADDR, reg); +#endif +} + +static int ddr3_save_and_set_training_windows(u32 *win) +{ + u32 cs_ena; + u32 reg, tmp_count, cs, ui; + u32 win_ctrl_reg, win_base_reg, win_remap_reg; + u32 num_of_win_regs, win_jump_index; + win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR; + win_base_reg = REG_XBAR_WIN_4_BASE_ADDR; + win_remap_reg = REG_XBAR_WIN_4_REMAP_ADDR; + win_jump_index = 0x10; + num_of_win_regs = 16; + struct hws_topology_map *tm = ddr3_get_topology_map(); + +#ifdef DISABLE_L2_FILTERING_DURING_DDR_TRAINING + /* + * Disable L2 filtering during DDR training + * (when Cross Bar window is open) + */ + reg_write(ADDRESS_FILTERING_END_REGISTER, 0); +#endif + + cs_ena = tm->interface_params[0].as_bus_params[0].cs_bitmask; + + /* Close XBAR Window 19 - Not needed */ + /* {0x000200e8} - Open Mbus Window - 2G */ + reg_write(REG_XBAR_WIN_19_CTRL_ADDR, 0); + + /* Save XBAR Windows 4-19 init configurations */ + for (ui = 0; ui < num_of_win_regs; ui++) + win[ui] = reg_read(win_ctrl_reg + 0x4 * ui); + + /* Open XBAR Windows 4-7 or 16-19 for other CS */ + reg = 0; + tmp_count = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + switch (cs) { + case 0: + reg = 0x0e00; + break; + case 1: + reg = 0x0d00; + break; + case 2: + reg = 0x0b00; + break; + case 3: + reg = 0x0700; + break; + } + reg |= (1 << 0); + reg |= (SDRAM_CS_SIZE & 0xffff0000); + + reg_write(win_ctrl_reg + win_jump_index * tmp_count, + reg); + reg = (((SDRAM_CS_SIZE + 1) * (tmp_count)) & + 0xffff0000); + reg_write(win_base_reg + win_jump_index * tmp_count, + reg); + + if (win_remap_reg <= REG_XBAR_WIN_7_REMAP_ADDR) + reg_write(win_remap_reg + + win_jump_index * tmp_count, 0); + + tmp_count++; + } + } + + return MV_OK; +} + +/* + * Name: ddr3_init - Main DDR3 Init function + * Desc: This routine initialize the DDR3 MC and runs HW training. + * Args: None. + * Notes: + * Returns: None. + */ +int ddr3_init(void) +{ + u32 reg = 0; + u32 soc_num; + int status; + u32 win[16]; + + /* SoC/Board special Initializtions */ + /* Get version from internal library */ + ddr3_print_version(); + + /*Add sub_version string */ + DEBUG_INIT_C("", SUB_VERSION, 1); + + /* Switching CPU to MRVL ID */ + soc_num = (reg_read(REG_SAMPLE_RESET_HIGH_ADDR) & SAR1_CPU_CORE_MASK) >> + SAR1_CPU_CORE_OFFSET; + switch (soc_num) { + case 0x3: + reg_bit_set(CPU_CONFIGURATION_REG(3), CPU_MRVL_ID_OFFSET); + reg_bit_set(CPU_CONFIGURATION_REG(2), CPU_MRVL_ID_OFFSET); + case 0x1: + reg_bit_set(CPU_CONFIGURATION_REG(1), CPU_MRVL_ID_OFFSET); + case 0x0: + reg_bit_set(CPU_CONFIGURATION_REG(0), CPU_MRVL_ID_OFFSET); + default: + break; + } + + /* + * Set DRAM Reset Mask in case detected GPIO indication of wakeup from + * suspend i.e the DRAM values will not be overwritten / reset when + * waking from suspend + */ + if (sys_env_suspend_wakeup_check() == + SUSPEND_WAKEUP_ENABLED_GPIO_DETECTED) { + reg_bit_set(REG_SDRAM_INIT_CTRL_ADDR, + 1 << REG_SDRAM_INIT_RESET_MASK_OFFS); + } + + /* + * Stage 0 - Set board configuration + */ + + /* Check if DRAM is already initialized */ + if (reg_read(REG_BOOTROM_ROUTINE_ADDR) & + (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS)) { + printf("%s Training Sequence - 2nd boot - Skip\n", ddr_type); + return MV_OK; + } + + /* + * Stage 1 - Dunit Setup + */ + + /* Fix read ready phases for all SOC in reg 0x15c8 */ + reg = reg_read(REG_TRAINING_DEBUG_3_ADDR); + reg &= ~(REG_TRAINING_DEBUG_3_MASK); + reg |= 0x4; /* Phase 0 */ + reg &= ~(REG_TRAINING_DEBUG_3_MASK << REG_TRAINING_DEBUG_3_OFFS); + reg |= (0x4 << (1 * REG_TRAINING_DEBUG_3_OFFS)); /* Phase 1 */ + reg &= ~(REG_TRAINING_DEBUG_3_MASK << (3 * REG_TRAINING_DEBUG_3_OFFS)); + reg |= (0x6 << (3 * REG_TRAINING_DEBUG_3_OFFS)); /* Phase 3 */ + reg &= ~(REG_TRAINING_DEBUG_3_MASK << (4 * REG_TRAINING_DEBUG_3_OFFS)); + reg |= (0x6 << (4 * REG_TRAINING_DEBUG_3_OFFS)); + reg &= ~(REG_TRAINING_DEBUG_3_MASK << (5 * REG_TRAINING_DEBUG_3_OFFS)); + reg |= (0x6 << (5 * REG_TRAINING_DEBUG_3_OFFS)); + reg_write(REG_TRAINING_DEBUG_3_ADDR, reg); + + /* + * Axi_bresp_mode[8] = Compliant, + * Axi_addr_decode_cntrl[11] = Internal, + * Axi_data_bus_width[0] = 128bit + * */ + /* 0x14a8 - AXI Control Register */ + reg_write(REG_DRAM_AXI_CTRL_ADDR, 0); + + /* + * Stage 2 - Training Values Setup + */ + /* Set X-BAR windows for the training sequence */ + ddr3_save_and_set_training_windows(win); + +#ifdef SUPPORT_STATIC_DUNIT_CONFIG + /* + * Load static controller configuration (in case dynamic/generic init + * is not enabled + */ + if (generic_init_controller == 0) { + ddr3_tip_init_specific_reg_config(0, + ddr_modes + [ddr3_get_static_ddr_mode + ()].regs); + } +#endif + + /* Load topology for New Training IP */ + status = ddr3_load_topology_map(); + if (MV_OK != status) { + printf("%s Training Sequence topology load - FAILED\n", + ddr_type); + return status; + } + + /* Tune training algo paramteres */ + status = ddr3_hws_tune_training_params(0); + if (MV_OK != status) + return status; + + /* Set log level for training lib */ + ddr3_hws_set_log_level(DEBUG_BLOCK_ALL, DEBUG_LEVEL_ERROR); + + /* Start New Training IP */ + status = ddr3_hws_hw_training(); + if (MV_OK != status) { + printf("%s Training Sequence - FAILED\n", ddr_type); + return status; + } + + /* + * Stage 3 - Finish + */ + /* Restore and set windows */ + ddr3_restore_and_set_final_windows(win); + + /* Update DRAM init indication in bootROM register */ + reg = reg_read(REG_BOOTROM_ROUTINE_ADDR); + reg_write(REG_BOOTROM_ROUTINE_ADDR, + reg | (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS)); + + /* DLB config */ + ddr3_new_tip_dlb_config(); + +#if defined(ECC_SUPPORT) + if (ddr3_if_ecc_enabled()) + ddr3_new_tip_ecc_scrub(); +#endif + + printf("%s Training Sequence - Ended Successfully\n", ddr_type); + + return MV_OK; +} + +/* + * Name: ddr3_get_cpu_freq + * Desc: read S@R and return CPU frequency + * Args: + * Notes: + * Returns: required value + */ +u32 ddr3_get_cpu_freq(void) +{ + return ddr3_tip_get_init_freq(); +} + +/* + * Name: ddr3_get_fab_opt + * Desc: read S@R and return CPU frequency + * Args: + * Notes: + * Returns: required value + */ +u32 ddr3_get_fab_opt(void) +{ + return 0; /* No fabric */ +} + +/* + * Name: ddr3_get_static_m_cValue - Init Memory controller with + * static parameters + * Desc: Use this routine to init the controller without the HW training + * procedure. + * User must provide compatible header file with registers data. + * Args: None. + * Notes: + * Returns: None. + */ +u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, + u32 offset2, u32 mask2) +{ + u32 reg, temp; + + reg = reg_read(reg_addr); + + temp = (reg >> offset1) & mask1; + if (mask2) + temp |= (reg >> offset2) & mask2; + + return temp; +} + +/* + * Name: ddr3_get_static_ddr_mode - Init Memory controller with + * static parameters + * Desc: Use this routine to init the controller without the HW training + * procedure. + * User must provide compatible header file with registers data. + * Args: None. + * Notes: + * Returns: None. + */ +u32 ddr3_get_static_ddr_mode(void) +{ + u32 chip_board_rev, i; + u32 size; + + /* Valid only for A380 only, MSYS using dynamic controller config */ +#ifdef CONFIG_CUSTOMER_BOARD_SUPPORT + /* + * Customer boards select DDR mode according to + * board ID & Sample@Reset + */ + chip_board_rev = mv_board_id_get(); +#else + /* Marvell boards select DDR mode according to Sample@Reset only */ + chip_board_rev = MARVELL_BOARD; +#endif + + size = ARRAY_SIZE(ddr_modes); + for (i = 0; i < size; i++) { + if ((ddr3_get_cpu_freq() == ddr_modes[i].cpu_freq) && + (ddr3_get_fab_opt() == ddr_modes[i].fab_freq) && + (chip_board_rev == ddr_modes[i].chip_board_rev)) + return i; + } + + DEBUG_INIT_S("\n*** Error: ddr3_get_static_ddr_mode: No match for requested DDR mode. ***\n\n"); + + return 0; +} + +/****************************************************************************** + * Name: ddr3_get_cs_num_from_reg + * Desc: + * Args: + * Notes: + * Returns: + */ +u32 ddr3_get_cs_num_from_reg(void) +{ + u32 cs_ena = sys_env_get_cs_ena_from_reg(); + u32 cs_count = 0; + u32 cs; + + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) + cs_count++; + } + + return cs_count; +} + +/* + * Name: ddr3_load_topology_map + * Desc: + * Args: + * Notes: + * Returns: + */ +int ddr3_load_topology_map(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + +#if defined(MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI) + /* Update topology data */ + if (MV_OK != ddr3_update_topology_map(tm)) { + DEBUG_INIT_FULL_S("Failed update of DDR3 Topology map\n"); + } +#endif + + return MV_OK; +} + +void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps) +{ + u32 tmp, hclk = 200; + + switch (freq_mode) { + case 4: + tmp = 1; /* DDR_400; */ + hclk = 200; + break; + case 0x8: + tmp = 1; /* DDR_666; */ + hclk = 333; + break; + case 0xc: + tmp = 1; /* DDR_800; */ + hclk = 400; + break; + default: + *ddr_freq = 0; + *hclk_ps = 0; + break; + } + + *ddr_freq = tmp; /* DDR freq define */ + *hclk_ps = 1000000 / hclk; /* values are 1/HCLK in ps */ + + return; +} + +void ddr3_new_tip_dlb_config(void) +{ + u32 reg, i = 0; + struct dlb_config *config_table_ptr = sys_env_dlb_config_ptr_get(); + + /* Write the configuration */ + while (config_table_ptr[i].reg_addr != 0) { + reg_write(config_table_ptr[i].reg_addr, + config_table_ptr[i].reg_data); + i++; + } + + /* Enable DLB */ + reg = reg_read(REG_STATIC_DRAM_DLB_CONTROL); + reg |= DLB_ENABLE | DLB_WRITE_COALESING | DLB_AXI_PREFETCH_EN | + DLB_MBUS_PREFETCH_EN | PREFETCH_N_LN_SZ_TR; + reg_write(REG_STATIC_DRAM_DLB_CONTROL, reg); +} + +int ddr3_fast_path_dynamic_cs_size_config(u32 cs_ena) +{ + u32 reg, cs; + u32 mem_total_size = 0; + u32 cs_mem_size = 0; + u32 mem_total_size_c, cs_mem_size_c; + +#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE + u32 physical_mem_size; + u32 max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE; + struct hws_topology_map *tm = ddr3_get_topology_map(); +#endif + + /* Open fast path windows */ + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + /* get CS size */ + if (ddr3_calc_mem_cs_size(cs, &cs_mem_size) != MV_OK) + return MV_FAIL; + +#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE + /* + * if number of address pins doesn't allow to use max + * mem size that is defined in topology + * mem size is defined by DEVICE_MAX_DRAM_ADDRESS_SIZE + */ + physical_mem_size = mem_size + [tm->interface_params[0].memory_size]; + + if (ddr3_get_device_width(cs) == 16) { + /* + * 16bit mem device can be twice more - no need + * in less significant pin + */ + max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE * 2; + } + + if (physical_mem_size > max_mem_size) { + cs_mem_size = max_mem_size * + (ddr3_get_bus_width() / + ddr3_get_device_width(cs)); + printf("Updated Physical Mem size is from 0x%x to %x\n", + physical_mem_size, + DEVICE_MAX_DRAM_ADDRESS_SIZE); + } +#endif + + /* set fast path window control for the cs */ + reg = 0xffffe1; + reg |= (cs << 2); + reg |= (cs_mem_size - 1) & 0xffff0000; + /*Open fast path Window */ + reg_write(REG_FASTPATH_WIN_CTRL_ADDR(cs), reg); + + /* Set fast path window base address for the cs */ + reg = ((cs_mem_size) * cs) & 0xffff0000; + /* Set base address */ + reg_write(REG_FASTPATH_WIN_BASE_ADDR(cs), reg); + + /* + * Since memory size may be bigger than 4G the summ may + * be more than 32 bit word, + * so to estimate the result divide mem_total_size and + * cs_mem_size by 0x10000 (it is equal to >> 16) + */ + mem_total_size_c = mem_total_size >> 16; + cs_mem_size_c = cs_mem_size >> 16; + /* if the sum less than 2 G - calculate the value */ + if (mem_total_size_c + cs_mem_size_c < 0x10000) + mem_total_size += cs_mem_size; + else /* put max possible size */ + mem_total_size = L2_FILTER_FOR_MAX_MEMORY_SIZE; + } + } + + /* Set L2 filtering to Max Memory size */ + reg_write(ADDRESS_FILTERING_END_REGISTER, mem_total_size); + + return MV_OK; +} + +u32 ddr3_get_bus_width(void) +{ + u32 bus_width; + + bus_width = (reg_read(REG_SDRAM_CONFIG_ADDR) & 0x8000) >> + REG_SDRAM_CONFIG_WIDTH_OFFS; + + return (bus_width == 0) ? 16 : 32; +} + +u32 ddr3_get_device_width(u32 cs) +{ + u32 device_width; + + device_width = (reg_read(REG_SDRAM_ADDRESS_CTRL_ADDR) & + (0x3 << (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs))) >> + (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs); + + return (device_width == 0) ? 8 : 16; +} + +float ddr3_get_device_size(u32 cs) +{ + u32 device_size_low, device_size_high, device_size; + u32 data, cs_low_offset, cs_high_offset; + + cs_low_offset = REG_SDRAM_ADDRESS_SIZE_OFFS + cs * 4; + cs_high_offset = REG_SDRAM_ADDRESS_SIZE_OFFS + + REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS + cs; + + data = reg_read(REG_SDRAM_ADDRESS_CTRL_ADDR); + device_size_low = (data >> cs_low_offset) & 0x3; + device_size_high = (data >> cs_high_offset) & 0x1; + + device_size = device_size_low | (device_size_high << 2); + + switch (device_size) { + case 0: + return 2; + case 2: + return 0.5; + case 3: + return 1; + case 4: + return 4; + case 5: + return 8; + case 1: + default: + DEBUG_INIT_C("Error: Wrong device size of Cs: ", cs, 1); + /* + * Small value will give wrong emem size in + * ddr3_calc_mem_cs_size + */ + return 0.01; + } +} + +int ddr3_calc_mem_cs_size(u32 cs, u32 *cs_size) +{ + float cs_mem_size; + + /* Calculate in GiB */ + cs_mem_size = ((ddr3_get_bus_width() / ddr3_get_device_width(cs)) * + ddr3_get_device_size(cs)) / 8; + + /* + * Multiple controller bus width, 2x for 64 bit + * (SoC controller may be 32 or 64 bit, + * so bit 15 in 0x1400, that means if whole bus used or only half, + * have a differnt meaning + */ + cs_mem_size *= DDR_CONTROLLER_BUS_WIDTH_MULTIPLIER; + + if (cs_mem_size == 0.125) { + *cs_size = 128 << 20; + } else if (cs_mem_size == 0.25) { + *cs_size = 256 << 20; + } else if (cs_mem_size == 0.5) { + *cs_size = 512 << 20; + } else if (cs_mem_size == 1) { + *cs_size = 1 << 30; + } else if (cs_mem_size == 2) { + *cs_size = 2 << 30; + } else { + DEBUG_INIT_C("Error: Wrong Memory size of Cs: ", cs, 1); + return MV_BAD_VALUE; + } + + return MV_OK; +} + +#if defined(MV_DDR_TOPOLOGY_UPDATE_FROM_TWSI) +/* + * Name: ddr3_update_topology_map + * Desc: + * Args: + * Notes: Update topology map by Sat_r values + * Returns: + */ +static int ddr3_update_topology_map(struct hws_topology_map *tm) +{ + struct topology_update_info topology_update_info; + + topology_update_info.update_width = 0; + topology_update_info.update_ecc = 0; + topology_update_info.update_ecc_pup3_mode = 0; + sys_env_get_topology_update_info(&topology_update_info); + if (topology_update_info.update_width) { + tm->bus_act_mask &= + ~(TOPOLOGY_UPDATE_WIDTH_32BIT_MASK); + if (topology_update_info.width == TOPOLOGY_UPDATE_WIDTH_16BIT) + tm->bus_act_mask = + TOPOLOGY_UPDATE_WIDTH_16BIT_MASK; + else + tm->bus_act_mask = + TOPOLOGY_UPDATE_WIDTH_32BIT_MASK; + } + + if (topology_update_info.update_ecc) { + if (topology_update_info.ecc == TOPOLOGY_UPDATE_ECC_OFF) { + tm->bus_act_mask &= + ~(1 << topology_update_info.ecc_pup_mode_offset); + } else { + tm->bus_act_mask |= + topology_update_info. + ecc << topology_update_info.ecc_pup_mode_offset; + } + } + + return MV_OK; +} +#endif + +/* + * Name: ddr3_hws_tune_training_params + * Desc: + * Args: + * Notes: Tune internal training params + * Returns: + */ +static int ddr3_hws_tune_training_params(u8 dev_num) +{ + struct tune_train_params params; + int status; + + /* NOTE: do not remove any field initilization */ + params.ck_delay = TUNE_TRAINING_PARAMS_CK_DELAY; + params.ck_delay_16 = TUNE_TRAINING_PARAMS_CK_DELAY_16; + params.p_finger = TUNE_TRAINING_PARAMS_PFINGER; + params.n_finger = TUNE_TRAINING_PARAMS_NFINGER; + params.phy_reg3_val = TUNE_TRAINING_PARAMS_PHYREG3VAL; + + status = ddr3_tip_tune_training_params(dev_num, ¶ms); + if (MV_OK != status) { + printf("%s Training Sequence - FAILED\n", ddr_type); + return status; + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_init.h b/drivers/ddr/marvell/a38x/ddr3_init.h new file mode 100644 index 0000000..e2ff040 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_init.h @@ -0,0 +1,395 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_INIT_H +#define _DDR3_INIT_H + +#if defined(CONFIG_ARMADA_38X) +#include "ddr3_a38x.h" +#include "ddr3_a38x_mc_static.h" +#include "ddr3_a38x_topology.h" +#endif +#include "ddr3_hws_hw_training.h" +#include "ddr3_hws_sil_training.h" +#include "ddr3_logging_def.h" +#include "ddr3_training_hw_algo.h" +#include "ddr3_training_ip.h" +#include "ddr3_training_ip_centralization.h" +#include "ddr3_training_ip_engine.h" +#include "ddr3_training_ip_flow.h" +#include "ddr3_training_ip_pbs.h" +#include "ddr3_training_ip_prv_if.h" +#include "ddr3_training_ip_static.h" +#include "ddr3_training_leveling.h" +#include "xor.h" + +/* + * MV_DEBUG_INIT need to be defines, otherwise the output of the + * DDR2 training code is not complete and misleading + */ +#define MV_DEBUG_INIT + +#define BIT(x) (1 << (x)) + +#ifdef MV_DEBUG_INIT +#define DEBUG_INIT_S(s) puts(s) +#define DEBUG_INIT_D(d, l) printf("%x", d) +#define DEBUG_INIT_D_10(d, l) printf("%d", d) +#else +#define DEBUG_INIT_S(s) +#define DEBUG_INIT_D(d, l) +#define DEBUG_INIT_D_10(d, l) +#endif + +#ifdef MV_DEBUG_INIT_FULL +#define DEBUG_INIT_FULL_S(s) puts(s) +#define DEBUG_INIT_FULL_D(d, l) printf("%x", d) +#define DEBUG_INIT_FULL_D_10(d, l) printf("%d", d) +#define DEBUG_WR_REG(reg, val) \ + { DEBUG_INIT_S("Write Reg: 0x"); DEBUG_INIT_D((reg), 8); \ + DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); } +#define DEBUG_RD_REG(reg, val) \ + { DEBUG_INIT_S("Read Reg: 0x"); DEBUG_INIT_D((reg), 8); \ + DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); } +#else +#define DEBUG_INIT_FULL_S(s) +#define DEBUG_INIT_FULL_D(d, l) +#define DEBUG_INIT_FULL_D_10(d, l) +#define DEBUG_WR_REG(reg, val) +#define DEBUG_RD_REG(reg, val) +#endif + +#define DEBUG_INIT_FULL_C(s, d, l) \ + { DEBUG_INIT_FULL_S(s); \ + DEBUG_INIT_FULL_D(d, l); \ + DEBUG_INIT_FULL_S("\n"); } +#define DEBUG_INIT_C(s, d, l) \ + { DEBUG_INIT_S(s); DEBUG_INIT_D(d, l); DEBUG_INIT_S("\n"); } + +/* + * Debug (Enable/Disable modules) and Error report + */ + +#ifdef BASIC_DEBUG +#define MV_DEBUG_WL +#define MV_DEBUG_RL +#define MV_DEBUG_DQS_RESULTS +#endif + +#ifdef FULL_DEBUG +#define MV_DEBUG_WL +#define MV_DEBUG_RL +#define MV_DEBUG_DQS + +#define MV_DEBUG_PBS +#define MV_DEBUG_DFS +#define MV_DEBUG_MAIN_FULL +#define MV_DEBUG_DFS_FULL +#define MV_DEBUG_DQS_FULL +#define MV_DEBUG_RL_FULL +#define MV_DEBUG_WL_FULL +#endif + +#if defined(CONFIG_ARMADA_38X) +#include "ddr3_a38x.h" +#include "ddr3_a38x_topology.h" +#endif + +/* The following is a list of Marvell status */ +#define MV_ERROR (-1) +#define MV_OK (0x00) /* Operation succeeded */ +#define MV_FAIL (0x01) /* Operation failed */ +#define MV_BAD_VALUE (0x02) /* Illegal value (general) */ +#define MV_OUT_OF_RANGE (0x03) /* The value is out of range */ +#define MV_BAD_PARAM (0x04) /* Illegal parameter in function called */ +#define MV_BAD_PTR (0x05) /* Illegal pointer value */ +#define MV_BAD_SIZE (0x06) /* Illegal size */ +#define MV_BAD_STATE (0x07) /* Illegal state of state machine */ +#define MV_SET_ERROR (0x08) /* Set operation failed */ +#define MV_GET_ERROR (0x09) /* Get operation failed */ +#define MV_CREATE_ERROR (0x0a) /* Fail while creating an item */ +#define MV_NOT_FOUND (0x0b) /* Item not found */ +#define MV_NO_MORE (0x0c) /* No more items found */ +#define MV_NO_SUCH (0x0d) /* No such item */ +#define MV_TIMEOUT (0x0e) /* Time Out */ +#define MV_NO_CHANGE (0x0f) /* Parameter(s) is already in this value */ +#define MV_NOT_SUPPORTED (0x10) /* This request is not support */ +#define MV_NOT_IMPLEMENTED (0x11) /* Request supported but not implemented*/ +#define MV_NOT_INITIALIZED (0x12) /* The item is not initialized */ +#define MV_NO_RESOURCE (0x13) /* Resource not available (memory ...) */ +#define MV_FULL (0x14) /* Item is full (Queue or table etc...) */ +#define MV_EMPTY (0x15) /* Item is empty (Queue or table etc...) */ +#define MV_INIT_ERROR (0x16) /* Error occured while INIT process */ +#define MV_HW_ERROR (0x17) /* Hardware error */ +#define MV_TX_ERROR (0x18) /* Transmit operation not succeeded */ +#define MV_RX_ERROR (0x19) /* Recieve operation not succeeded */ +#define MV_NOT_READY (0x1a) /* The other side is not ready yet */ +#define MV_ALREADY_EXIST (0x1b) /* Tried to create existing item */ +#define MV_OUT_OF_CPU_MEM (0x1c) /* Cpu memory allocation failed. */ +#define MV_NOT_STARTED (0x1d) /* Not started yet */ +#define MV_BUSY (0x1e) /* Item is busy. */ +#define MV_TERMINATE (0x1f) /* Item terminates it's work. */ +#define MV_NOT_ALIGNED (0x20) /* Wrong alignment */ +#define MV_NOT_ALLOWED (0x21) /* Operation NOT allowed */ +#define MV_WRITE_PROTECT (0x22) /* Write protected */ +#define MV_INVALID (int)(-1) + +/* For checking function return values */ +#define CHECK_STATUS(orig_func) \ + { \ + int status; \ + status = orig_func; \ + if (MV_OK != status) \ + return status; \ + } + +enum log_level { + MV_LOG_LEVEL_0, + MV_LOG_LEVEL_1, + MV_LOG_LEVEL_2, + MV_LOG_LEVEL_3 +}; + +/* Globals */ +extern u8 debug_training; +extern u8 is_reg_dump; +extern u8 generic_init_controller; +extern u32 freq_val[]; +extern u32 is_pll_old; +extern struct cl_val_per_freq cas_latency_table[]; +extern struct pattern_info pattern_table[]; +extern struct cl_val_per_freq cas_write_latency_table[]; +extern u8 debug_training; +extern u8 debug_centralization, debug_training_ip, debug_training_bist, + debug_pbs, debug_training_static, debug_leveling; +extern u32 pipe_multicast_mask; +extern struct hws_tip_config_func_db config_func_info[]; +extern u8 cs_mask_reg[]; +extern u8 twr_mask_table[]; +extern u8 cl_mask_table[]; +extern u8 cwl_mask_table[]; +extern u16 rfc_table[]; +extern u32 speed_bin_table_t_rc[]; +extern u32 speed_bin_table_t_rcd_t_rp[]; +extern u32 ck_delay, ck_delay_16; + +extern u32 g_zpri_data; +extern u32 g_znri_data; +extern u32 g_zpri_ctrl; +extern u32 g_znri_ctrl; +extern u32 g_zpodt_data; +extern u32 g_znodt_data; +extern u32 g_zpodt_ctrl; +extern u32 g_znodt_ctrl; +extern u32 g_dic; +extern u32 g_odt_config; +extern u32 g_rtt_nom; + +extern u8 debug_training_access; +extern u8 debug_training_a38x; +extern u32 first_active_if; +extern enum hws_ddr_freq init_freq; +extern u32 delay_enable, ck_delay, ck_delay_16, ca_delay; +extern u32 mask_tune_func; +extern u32 rl_version; +extern int rl_mid_freq_wa; +extern u8 calibration_update_control; /* 2 external only, 1 is internal only */ +extern enum hws_ddr_freq medium_freq; + +extern u32 ck_delay, ck_delay_16; +extern enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]; +extern u32 first_active_if; +extern u32 mask_tune_func; +extern u32 freq_val[]; +extern enum hws_ddr_freq init_freq; +extern enum hws_ddr_freq low_freq; +extern enum hws_ddr_freq medium_freq; +extern u8 generic_init_controller; +extern enum auto_tune_stage training_stage; +extern u32 is_pll_before_init; +extern u32 is_adll_calib_before_init; +extern u32 is_dfs_in_init; +extern int wl_debug_delay; +extern u32 silicon_delay[HWS_MAX_DEVICE_NUM]; +extern u32 p_finger; +extern u32 n_finger; +extern u32 freq_val[DDR_FREQ_LIMIT]; +extern u32 start_pattern, end_pattern; +extern u32 phy_reg0_val; +extern u32 phy_reg1_val; +extern u32 phy_reg2_val; +extern u32 phy_reg3_val; +extern enum hws_pattern sweep_pattern; +extern enum hws_pattern pbs_pattern; +extern u8 is_rzq6; +extern u32 znri_data_phy_val; +extern u32 zpri_data_phy_val; +extern u32 znri_ctrl_phy_val; +extern u32 zpri_ctrl_phy_val; +extern u8 debug_training_access; +extern u32 finger_test, p_finger_start, p_finger_end, n_finger_start, + n_finger_end, p_finger_step, n_finger_step; +extern u32 mode2_t; +extern u32 xsb_validate_type; +extern u32 xsb_validation_base_address; +extern u32 odt_additional; +extern u32 debug_mode; +extern u32 delay_enable; +extern u32 ca_delay; +extern u32 debug_dunit; +extern u32 clamp_tbl[]; +extern u32 freq_mask[HWS_MAX_DEVICE_NUM][DDR_FREQ_LIMIT]; +extern u32 start_pattern, end_pattern; + +extern u32 maxt_poll_tries; +extern u32 is_bist_reset_bit; +extern u8 debug_training_bist; + +extern u8 vref_window_size[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +extern u32 debug_mode; +extern u32 effective_cs; +extern int ddr3_tip_centr_skip_min_win_check; +extern u32 *dq_map_table; +extern enum auto_tune_stage training_stage; +extern u8 debug_centralization; + +extern u32 delay_enable; +extern u32 start_pattern, end_pattern; +extern u32 freq_val[DDR_FREQ_LIMIT]; +extern u8 debug_training_hw_alg; +extern enum auto_tune_stage training_stage; + +extern u8 debug_training_ip; +extern enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]; +extern enum auto_tune_stage training_stage; +extern u32 effective_cs; + +extern u8 debug_leveling; +extern enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]; +extern enum auto_tune_stage training_stage; +extern u32 rl_version; +extern struct cl_val_per_freq cas_latency_table[]; +extern u32 start_xsb_offset; +extern u32 debug_mode; +extern u32 odt_config; +extern u32 effective_cs; +extern u32 phy_reg1_val; + +extern u8 debug_pbs; +extern u32 effective_cs; +extern u16 mask_results_dq_reg_map[]; +extern enum hws_ddr_freq medium_freq; +extern u32 freq_val[]; +extern enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]; +extern enum auto_tune_stage training_stage; +extern u32 debug_mode; +extern u32 *dq_map_table; + +extern u32 vref; +extern struct cl_val_per_freq cas_latency_table[]; +extern u32 target_freq; +extern struct hws_tip_config_func_db config_func_info[HWS_MAX_DEVICE_NUM]; +extern u32 clamp_tbl[]; +extern u32 init_freq; +/* list of allowed frequency listed in order of enum hws_ddr_freq */ +extern u32 freq_val[]; +extern u8 debug_training_static; +extern u32 first_active_if; + +/* Prototypes */ +int ddr3_tip_enable_init_sequence(u32 dev_num); + +int ddr3_tip_init_a38x(u32 dev_num, u32 board_id); + +int ddr3_hws_hw_training(void); +int ddr3_silicon_pre_init(void); +int ddr3_silicon_post_init(void); +int ddr3_post_run_alg(void); +int ddr3_if_ecc_enabled(void); +void ddr3_new_tip_ecc_scrub(void); + +void ddr3_print_version(void); +void ddr3_new_tip_dlb_config(void); +struct hws_topology_map *ddr3_get_topology_map(void); + +int ddr3_if_ecc_enabled(void); +int ddr3_tip_reg_write(u32 dev_num, u32 reg_addr, u32 data); +int ddr3_tip_reg_read(u32 dev_num, u32 reg_addr, u32 *data, u32 reg_mask); +int ddr3_silicon_get_ddr_target_freq(u32 *ddr_freq); +int ddr3_tip_a38x_get_freq_config(u8 dev_num, enum hws_ddr_freq freq, + struct hws_tip_freq_config_info + *freq_config_info); +int ddr3_a38x_update_topology_map(u32 dev_num, + struct hws_topology_map *topology_map); +int ddr3_tip_a38x_get_init_freq(int dev_num, enum hws_ddr_freq *freq); +int ddr3_tip_a38x_get_medium_freq(int dev_num, enum hws_ddr_freq *freq); +int ddr3_tip_a38x_if_read(u8 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 *data, u32 mask); +int ddr3_tip_a38x_if_write(u8 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 data, u32 mask); +int ddr3_tip_a38x_get_device_info(u8 dev_num, + struct ddr3_device_info *info_ptr); + +int ddr3_tip_init_a38x(u32 dev_num, u32 board_id); + +int print_adll(u32 dev_num, u32 adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]); +int ddr3_tip_restore_dunit_regs(u32 dev_num); +void print_topology(struct hws_topology_map *topology_db); + +u32 mv_board_id_get(void); + +int ddr3_load_topology_map(void); +int ddr3_tip_init_specific_reg_config(u32 dev_num, + struct reg_data *reg_config_arr); +u32 ddr3_tip_get_init_freq(void); +void ddr3_hws_set_log_level(enum ddr_lib_debug_block block, u8 level); +int ddr3_tip_tune_training_params(u32 dev_num, + struct tune_train_params *params); +void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps); +int ddr3_fast_path_dynamic_cs_size_config(u32 cs_ena); +void ddr3_fast_path_static_cs_size_config(u32 cs_ena); +u32 ddr3_get_device_width(u32 cs); +u32 mv_board_id_index_get(u32 board_id); +u32 mv_board_id_get(void); +u32 ddr3_get_bus_width(void); +void ddr3_set_log_level(u32 n_log_level); +int ddr3_calc_mem_cs_size(u32 cs, u32 *cs_size); + +int hws_ddr3_cs_base_adr_calc(u32 if_id, u32 cs, u32 *cs_base_addr); + +int ddr3_tip_print_pbs_result(u32 dev_num, u32 cs_num, enum pbs_dir pbs_mode); +int ddr3_tip_clean_pbs_result(u32 dev_num, enum pbs_dir pbs_mode); + +int ddr3_tip_static_round_trip_arr_build(u32 dev_num, + struct trip_delay_element *table_ptr, + int is_wl, u32 *round_trip_delay_arr); + +u32 hws_ddr3_tip_max_cs_get(void); + +/* + * Accessor functions for the registers + */ +static inline void reg_write(u32 addr, u32 val) +{ + writel(val, INTER_REGS_BASE + addr); +} + +static inline u32 reg_read(u32 addr) +{ + return readl(INTER_REGS_BASE + addr); +} + +static inline void reg_bit_set(u32 addr, u32 mask) +{ + setbits_le32(INTER_REGS_BASE + addr, mask); +} + +static inline void reg_bit_clr(u32 addr, u32 mask) +{ + clrbits_le32(INTER_REGS_BASE + addr, mask); +} + +#endif /* _DDR3_INIT_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_logging_def.h b/drivers/ddr/marvell/a38x/ddr3_logging_def.h new file mode 100644 index 0000000..2de7c4f --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_logging_def.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_LOGGING_CONFIG_H +#define _DDR3_LOGGING_CONFIG_H + +#ifdef SILENT_LIB +#define DEBUG_TRAINING_BIST_ENGINE(level, s) +#define DEBUG_TRAINING_IP(level, s) +#define DEBUG_CENTRALIZATION_ENGINE(level, s) +#define DEBUG_TRAINING_HW_ALG(level, s) +#define DEBUG_TRAINING_IP_ENGINE(level, s) +#define DEBUG_LEVELING(level, s) +#define DEBUG_PBS_ENGINE(level, s) +#define DEBUG_TRAINING_STATIC_IP(level, s) +#define DEBUG_TRAINING_ACCESS(level, s) +#else +#ifdef LIB_FUNCTIONAL_DEBUG_ONLY +#define DEBUG_TRAINING_BIST_ENGINE(level, s) +#define DEBUG_TRAINING_IP_ENGINE(level, s) +#define DEBUG_TRAINING_IP(level, s) \ + if (level >= debug_training) \ + printf s +#define DEBUG_CENTRALIZATION_ENGINE(level, s) \ + if (level >= debug_centralization) \ + printf s +#define DEBUG_TRAINING_HW_ALG(level, s) \ + if (level >= debug_training_hw_alg) \ + printf s +#define DEBUG_LEVELING(level, s) \ + if (level >= debug_leveling) \ + printf s +#define DEBUG_PBS_ENGINE(level, s) \ + if (level >= debug_pbs) \ + printf s +#define DEBUG_TRAINING_STATIC_IP(level, s) \ + if (level >= debug_training_static) \ + printf s +#define DEBUG_TRAINING_ACCESS(level, s) \ + if (level >= debug_training_access) \ + printf s +#else +#define DEBUG_TRAINING_BIST_ENGINE(level, s) \ + if (level >= debug_training_bist) \ + printf s + +#define DEBUG_TRAINING_IP_ENGINE(level, s) \ + if (level >= debug_training_ip) \ + printf s +#define DEBUG_TRAINING_IP(level, s) \ + if (level >= debug_training) \ + printf s +#define DEBUG_CENTRALIZATION_ENGINE(level, s) \ + if (level >= debug_centralization) \ + printf s +#define DEBUG_TRAINING_HW_ALG(level, s) \ + if (level >= debug_training_hw_alg) \ + printf s +#define DEBUG_LEVELING(level, s) \ + if (level >= debug_leveling) \ + printf s +#define DEBUG_PBS_ENGINE(level, s) \ + if (level >= debug_pbs) \ + printf s +#define DEBUG_TRAINING_STATIC_IP(level, s) \ + if (level >= debug_training_static) \ + printf s +#define DEBUG_TRAINING_ACCESS(level, s) \ + if (level >= debug_training_access) \ + printf s +#endif +#endif + +/* Logging defines */ +#define DEBUG_LEVEL_TRACE 1 +#define DEBUG_LEVEL_INFO 2 +#define DEBUG_LEVEL_ERROR 3 + +enum ddr_lib_debug_block { + DEBUG_BLOCK_STATIC, + DEBUG_BLOCK_TRAINING_MAIN, + DEBUG_BLOCK_LEVELING, + DEBUG_BLOCK_CENTRALIZATION, + DEBUG_BLOCK_PBS, + DEBUG_BLOCK_IP, + DEBUG_BLOCK_BIST, + DEBUG_BLOCK_ALG, + DEBUG_BLOCK_DEVICE, + DEBUG_BLOCK_ACCESS, + DEBUG_STAGES_REG_DUMP, + /* All excluding IP and REG_DUMP, should be enabled separatelly */ + DEBUG_BLOCK_ALL +}; + +int ddr3_tip_print_log(u32 dev_num, u32 mem_addr); +int ddr3_tip_print_stability_log(u32 dev_num); + +#endif /* _DDR3_LOGGING_CONFIG_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h b/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h new file mode 100644 index 0000000..0ce0479 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_patterns_64bit.h @@ -0,0 +1,924 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_PATTERNS_64_H +#define __DDR3_PATTERNS_64_H + +/* + * Patterns Declerations + */ + +u32 wl_sup_pattern[LEN_WL_SUP_PATTERN] __aligned(32) = { + 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, + 0x14131211, 0x18171615, 0x1c1b1a19, 0x201f1e1d, + 0x24232221, 0x28272625, 0x2c2b2a29, 0x302f2e2d, + 0x34333231, 0x38373635, 0x3c3b3a39, 0x403f3e3d, + 0x44434241, 0x48474645, 0x4c4b4a49, 0x504f4e4d, + 0x54535251, 0x58575655, 0x5c5b5a59, 0x605f5e5d, + 0x64636261, 0x68676665, 0x6c6b6a69, 0x706f6e6d, + 0x74737271, 0x78777675, 0x7c7b7a79, 0x807f7e7d +}; + +u32 pbs_pattern_32b[2][LEN_PBS_PATTERN] __aligned(32) = { + { + 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555, + 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, 0x55555555 + }, + { + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa, + 0x55555555, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa + } +}; + +u32 pbs_pattern_64b[2][LEN_PBS_PATTERN] __aligned(32) = { + { + 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, + 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, + 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555, + 0xaaaaaaaa, 0xaaaaaaaa, 0x55555555, 0x55555555 + }, + { + 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, + 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, + 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa, + 0x55555555, 0x55555555, 0xaaaaaaaa, 0xaaaaaaaa + } +}; + +u32 rl_pattern[LEN_STD_PATTERN] __aligned(32) = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0x01010101, 0x01010101 +}; + +u32 killer_pattern_32b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = { + { + 0x01010101, 0x00000000, 0x01010101, 0xffffffff, + 0x01010101, 0x00000000, 0x01010101, 0xffffffff, + 0xfefefefe, 0xfefefefe, 0x01010101, 0xfefefefe, + 0xfefefefe, 0xfefefefe, 0x01010101, 0xfefefefe, + 0x01010101, 0xfefefefe, 0x01010101, 0x01010101, + 0x01010101, 0xfefefefe, 0x01010101, 0x01010101, + 0xfefefefe, 0x01010101, 0xfefefefe, 0x00000000, + 0xfefefefe, 0x01010101, 0xfefefefe, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x01010101, + 0xffffffff, 0x00000000, 0xffffffff, 0x01010101, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xfefefefe, + 0x00000000, 0x00000000, 0x00000000, 0xfefefefe, + 0xfefefefe, 0xffffffff, 0x00000000, 0x00000000, + 0xfefefefe, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xfefefefe, 0x00000000, 0xfefefefe, 0x00000000, + 0xfefefefe, 0x00000000, 0xfefefefe, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x01010101, + 0x00000000, 0xffffffff, 0xffffffff, 0x01010101, + 0xffffffff, 0xffffffff, 0x01010101, 0x00000000, + 0xffffffff, 0xffffffff, 0x01010101, 0x00000000, + 0x01010101, 0xffffffff, 0xfefefefe, 0xfefefefe, + 0x01010101, 0xffffffff, 0xfefefefe, 0xfefefefe + }, + { + 0x02020202, 0x00000000, 0x02020202, 0xffffffff, + 0x02020202, 0x00000000, 0x02020202, 0xffffffff, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, + 0x02020202, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, 0x00000000, + 0xfdfdfdfd, 0x02020202, 0xfdfdfdfd, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x02020202, + 0xffffffff, 0x00000000, 0xffffffff, 0x02020202, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xfdfdfdfd, + 0x00000000, 0x00000000, 0x00000000, 0xfdfdfdfd, + 0xfdfdfdfd, 0xffffffff, 0x00000000, 0x00000000, + 0xfdfdfdfd, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xfdfdfdfd, 0x00000000, 0xfdfdfdfd, 0x00000000, + 0xfdfdfdfd, 0x00000000, 0xfdfdfdfd, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x02020202, + 0x00000000, 0xffffffff, 0xffffffff, 0x02020202, + 0xffffffff, 0xffffffff, 0x02020202, 0x00000000, + 0xffffffff, 0xffffffff, 0x02020202, 0x00000000, + 0x02020202, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd, + 0x02020202, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd + }, + { + 0x04040404, 0x00000000, 0x04040404, 0xffffffff, + 0x04040404, 0x00000000, 0x04040404, 0xffffffff, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, + 0x04040404, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0x04040404, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, 0x00000000, + 0xfbfbfbfb, 0x04040404, 0xfbfbfbfb, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x04040404, + 0xffffffff, 0x00000000, 0xffffffff, 0x04040404, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xfbfbfbfb, + 0x00000000, 0x00000000, 0x00000000, 0xfbfbfbfb, + 0xfbfbfbfb, 0xffffffff, 0x00000000, 0x00000000, + 0xfbfbfbfb, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xfbfbfbfb, 0x00000000, 0xfbfbfbfb, 0x00000000, + 0xfbfbfbfb, 0x00000000, 0xfbfbfbfb, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x04040404, + 0x00000000, 0xffffffff, 0xffffffff, 0x04040404, + 0xffffffff, 0xffffffff, 0x04040404, 0x00000000, + 0xffffffff, 0xffffffff, 0x04040404, 0x00000000, + 0x04040404, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb, + 0x04040404, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb + }, + { + 0x08080808, 0x00000000, 0x08080808, 0xffffffff, + 0x08080808, 0x00000000, 0x08080808, 0xffffffff, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, + 0x08080808, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0x08080808, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, 0x00000000, + 0xf7f7f7f7, 0x08080808, 0xf7f7f7f7, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x08080808, + 0xffffffff, 0x00000000, 0xffffffff, 0x08080808, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xf7f7f7f7, + 0x00000000, 0x00000000, 0x00000000, 0xf7f7f7f7, + 0xf7f7f7f7, 0xffffffff, 0x00000000, 0x00000000, + 0xf7f7f7f7, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xf7f7f7f7, 0x00000000, 0xf7f7f7f7, 0x00000000, + 0xf7f7f7f7, 0x00000000, 0xf7f7f7f7, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x08080808, + 0x00000000, 0xffffffff, 0xffffffff, 0x08080808, + 0xffffffff, 0xffffffff, 0x08080808, 0x00000000, + 0xffffffff, 0xffffffff, 0x08080808, 0x00000000, + 0x08080808, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7, + 0x08080808, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7 + }, + { + 0x10101010, 0x00000000, 0x10101010, 0xffffffff, + 0x10101010, 0x00000000, 0x10101010, 0xffffffff, + 0xefefefef, 0xefefefef, 0x10101010, 0xefefefef, + 0xefefefef, 0xefefefef, 0x10101010, 0xefefefef, + 0x10101010, 0xefefefef, 0x10101010, 0x10101010, + 0x10101010, 0xefefefef, 0x10101010, 0x10101010, + 0xefefefef, 0x10101010, 0xefefefef, 0x00000000, + 0xefefefef, 0x10101010, 0xefefefef, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x10101010, + 0xffffffff, 0x00000000, 0xffffffff, 0x10101010, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xefefefef, + 0x00000000, 0x00000000, 0x00000000, 0xefefefef, + 0xefefefef, 0xffffffff, 0x00000000, 0x00000000, + 0xefefefef, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xefefefef, 0x00000000, 0xefefefef, 0x00000000, + 0xefefefef, 0x00000000, 0xefefefef, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x10101010, + 0x00000000, 0xffffffff, 0xffffffff, 0x10101010, + 0xffffffff, 0xffffffff, 0x10101010, 0x00000000, + 0xffffffff, 0xffffffff, 0x10101010, 0x00000000, + 0x10101010, 0xffffffff, 0xefefefef, 0xefefefef, + 0x10101010, 0xffffffff, 0xefefefef, 0xefefefef + }, + { + 0x20202020, 0x00000000, 0x20202020, 0xffffffff, + 0x20202020, 0x00000000, 0x20202020, 0xffffffff, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, + 0x20202020, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0x20202020, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, 0x00000000, + 0xdfdfdfdf, 0x20202020, 0xdfdfdfdf, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x20202020, + 0xffffffff, 0x00000000, 0xffffffff, 0x20202020, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xdfdfdfdf, + 0x00000000, 0x00000000, 0x00000000, 0xdfdfdfdf, + 0xdfdfdfdf, 0xffffffff, 0x00000000, 0x00000000, + 0xdfdfdfdf, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xdfdfdfdf, 0x00000000, 0xdfdfdfdf, 0x00000000, + 0xdfdfdfdf, 0x00000000, 0xdfdfdfdf, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x20202020, + 0x00000000, 0xffffffff, 0xffffffff, 0x20202020, + 0xffffffff, 0xffffffff, 0x20202020, 0x00000000, + 0xffffffff, 0xffffffff, 0x20202020, 0x00000000, + 0x20202020, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf, + 0x20202020, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf + }, + { + 0x40404040, 0x00000000, 0x40404040, 0xffffffff, + 0x40404040, 0x00000000, 0x40404040, 0xffffffff, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, + 0x40404040, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0x40404040, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, 0x00000000, + 0xbfbfbfbf, 0x40404040, 0xbfbfbfbf, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x40404040, + 0xffffffff, 0x00000000, 0xffffffff, 0x40404040, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0xbfbfbfbf, + 0x00000000, 0x00000000, 0x00000000, 0xbfbfbfbf, + 0xbfbfbfbf, 0xffffffff, 0x00000000, 0x00000000, + 0xbfbfbfbf, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0xbfbfbfbf, 0x00000000, 0xbfbfbfbf, 0x00000000, + 0xbfbfbfbf, 0x00000000, 0xbfbfbfbf, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x40404040, + 0x00000000, 0xffffffff, 0xffffffff, 0x40404040, + 0xffffffff, 0xffffffff, 0x40404040, 0x00000000, + 0xffffffff, 0xffffffff, 0x40404040, 0x00000000, + 0x40404040, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf, + 0x40404040, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf + }, + { + 0x80808080, 0x00000000, 0x80808080, 0xffffffff, + 0x80808080, 0x00000000, 0x80808080, 0xffffffff, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, + 0x80808080, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x80808080, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, 0x00000000, + 0x7f7f7f7f, 0x80808080, 0x7f7f7f7f, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x80808080, + 0xffffffff, 0x00000000, 0xffffffff, 0x80808080, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x7f7f7f7f, + 0x00000000, 0x00000000, 0x00000000, 0x7f7f7f7f, + 0x7f7f7f7f, 0xffffffff, 0x00000000, 0x00000000, + 0x7f7f7f7f, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, + 0x7f7f7f7f, 0x00000000, 0x7f7f7f7f, 0x00000000, + 0x7f7f7f7f, 0x00000000, 0x7f7f7f7f, 0x00000000, + 0x00000000, 0xffffffff, 0xffffffff, 0x80808080, + 0x00000000, 0xffffffff, 0xffffffff, 0x80808080, + 0xffffffff, 0xffffffff, 0x80808080, 0x00000000, + 0xffffffff, 0xffffffff, 0x80808080, 0x00000000, + 0x80808080, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f, + 0x80808080, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f + } +}; + +u32 killer_pattern_64b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = { + { + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0xffffffff, 0xffffffff, + 0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe, + 0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe, + 0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe, + 0x01010101, 0x01010101, 0x01010101, 0x01010101, + 0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101, + 0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x01010101, 0x01010101, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xfefefefe, 0xfefefefe, + 0xfefefefe, 0xfefefefe, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000, + 0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x01010101, 0x01010101, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0xffffffff, 0xffffffff, + 0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe + }, + { + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0x02020202, 0x02020202, 0xffffffff, 0xffffffff, + 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, + 0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd, + 0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x02020202, 0x02020202, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xfdfdfdfd, 0xfdfdfdfd, + 0xfdfdfdfd, 0xfdfdfdfd, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000, + 0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x02020202, 0x02020202, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0x02020202, 0x02020202, 0xffffffff, 0xffffffff, + 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd + }, + { + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0x04040404, 0x04040404, 0xffffffff, 0xffffffff, + 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, + 0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb, + 0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x04040404, 0x04040404, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xfbfbfbfb, 0xfbfbfbfb, + 0xfbfbfbfb, 0xfbfbfbfb, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000, + 0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x04040404, 0x04040404, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0x04040404, 0x04040404, 0xffffffff, 0xffffffff, + 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb + }, + { + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0x08080808, 0x08080808, 0xffffffff, 0xffffffff, + 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, + 0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7, + 0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7, + 0x08080808, 0x08080808, 0x08080808, 0x08080808, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x08080808, 0x08080808, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xf7f7f7f7, 0xf7f7f7f7, + 0xf7f7f7f7, 0xf7f7f7f7, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000, + 0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x08080808, 0x08080808, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0x08080808, 0x08080808, 0xffffffff, 0xffffffff, + 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7 + }, + { + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0x10101010, 0x10101010, 0xffffffff, 0xffffffff, + 0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef, + 0x10101010, 0x10101010, 0xefefefef, 0xefefefef, + 0x10101010, 0x10101010, 0xefefefef, 0xefefefef, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0xefefefef, 0xefefefef, 0x10101010, 0x10101010, + 0xefefefef, 0xefefefef, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x10101010, 0x10101010, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xefefefef, 0xefefefef, + 0xefefefef, 0xefefefef, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xefefefef, 0xefefefef, 0x00000000, 0x00000000, + 0xefefefef, 0xefefefef, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x10101010, 0x10101010, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0x10101010, 0x10101010, 0xffffffff, 0xffffffff, + 0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef + }, + { + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0x20202020, 0x20202020, 0xffffffff, 0xffffffff, + 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, + 0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf, + 0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x20202020, 0x20202020, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xdfdfdfdf, 0xdfdfdfdf, + 0xdfdfdfdf, 0xdfdfdfdf, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000, + 0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x20202020, 0x20202020, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0x20202020, 0x20202020, 0xffffffff, 0xffffffff, + 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf + }, + { + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0x40404040, 0x40404040, 0xffffffff, 0xffffffff, + 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, + 0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf, + 0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf, + 0x40404040, 0x40404040, 0x40404040, 0x40404040, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x40404040, 0x40404040, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xbfbfbfbf, 0xbfbfbfbf, + 0xbfbfbfbf, 0xbfbfbfbf, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000, + 0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x40404040, 0x40404040, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0x40404040, 0x40404040, 0xffffffff, 0xffffffff, + 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf + }, + { + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0x80808080, 0x80808080, 0xffffffff, 0xffffffff, + 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, + 0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f, + 0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f, + 0x80808080, 0x80808080, 0x80808080, 0x80808080, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x80808080, 0x80808080, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x7f7f7f7f, 0x7f7f7f7f, + 0x7f7f7f7f, 0x7f7f7f7f, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000, + 0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x80808080, 0x80808080, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0x80808080, 0x80808080, 0xffffffff, 0xffffffff, + 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f + } +}; + +u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN] __aligned(32) = { + { + 0x00000000, 0x00000000, 0x01010101, 0x01010101, + 0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe, + 0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101, + 0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101, + 0xfefefefe, 0xfefefefe, 0x01010101, 0x01010101, + 0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe, + 0x01010101, 0x01010101, 0xfefefefe, 0xfefefefe, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xfefefefe, 0xfefefefe, 0xfefefefe, 0xfefefefe, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe, + 0x00000000, 0x00000000, 0xfefefefe, 0xfefefefe, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x01010101, 0x01010101, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x01010101, 0x01010101, + 0x00000000, 0x00000000, 0x01010101, 0x01010101, + 0xffffffff, 0xffffffff, 0xfefefefe, 0xfefefefe, + 0xfefefefe, 0xfefefefe, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x02020202, 0x02020202, + 0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0xfdfdfdfd, 0xfdfdfdfd, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd, + 0x02020202, 0x02020202, 0xfdfdfdfd, 0xfdfdfdfd, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, 0xfdfdfdfd, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd, + 0x00000000, 0x00000000, 0xfdfdfdfd, 0xfdfdfdfd, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x02020202, 0x02020202, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x02020202, 0x02020202, + 0x00000000, 0x00000000, 0x02020202, 0x02020202, + 0xffffffff, 0xffffffff, 0xfdfdfdfd, 0xfdfdfdfd, + 0xfdfdfdfd, 0xfdfdfdfd, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x04040404, 0x04040404, + 0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0xfbfbfbfb, 0xfbfbfbfb, 0x04040404, 0x04040404, + 0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb, + 0x04040404, 0x04040404, 0xfbfbfbfb, 0xfbfbfbfb, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, 0xfbfbfbfb, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb, + 0x00000000, 0x00000000, 0xfbfbfbfb, 0xfbfbfbfb, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x04040404, 0x04040404, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x04040404, 0x04040404, + 0x00000000, 0x00000000, 0x04040404, 0x04040404, + 0xffffffff, 0xffffffff, 0xfbfbfbfb, 0xfbfbfbfb, + 0xfbfbfbfb, 0xfbfbfbfb, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x08080808, 0x08080808, + 0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0xf7f7f7f7, 0xf7f7f7f7, 0x08080808, 0x08080808, + 0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7, + 0x08080808, 0x08080808, 0xf7f7f7f7, 0xf7f7f7f7, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, 0xf7f7f7f7, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7, + 0x00000000, 0x00000000, 0xf7f7f7f7, 0xf7f7f7f7, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x08080808, 0x08080808, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x08080808, 0x08080808, + 0x00000000, 0x00000000, 0x08080808, 0x08080808, + 0xffffffff, 0xffffffff, 0xf7f7f7f7, 0xf7f7f7f7, + 0xf7f7f7f7, 0xf7f7f7f7, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x10101010, 0x10101010, + 0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef, + 0xefefefef, 0xefefefef, 0x10101010, 0x10101010, + 0xefefefef, 0xefefefef, 0x10101010, 0x10101010, + 0xefefefef, 0xefefefef, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0xefefefef, 0xefefefef, + 0x10101010, 0x10101010, 0xefefefef, 0xefefefef, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xefefefef, 0xefefefef, 0xefefefef, 0xefefefef, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef, + 0x00000000, 0x00000000, 0xefefefef, 0xefefefef, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x10101010, 0x10101010, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x10101010, 0x10101010, + 0x00000000, 0x00000000, 0x10101010, 0x10101010, + 0xffffffff, 0xffffffff, 0xefefefef, 0xefefefef, + 0xefefefef, 0xefefefef, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x20202020, 0x20202020, + 0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0xdfdfdfdf, 0xdfdfdfdf, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf, + 0x20202020, 0x20202020, 0xdfdfdfdf, 0xdfdfdfdf, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, 0xdfdfdfdf, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf, + 0x00000000, 0x00000000, 0xdfdfdfdf, 0xdfdfdfdf, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x20202020, 0x20202020, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x20202020, 0x20202020, + 0x00000000, 0x00000000, 0x20202020, 0x20202020, + 0xffffffff, 0xffffffff, 0xdfdfdfdf, 0xdfdfdfdf, + 0xdfdfdfdf, 0xdfdfdfdf, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x40404040, 0x40404040, + 0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0xbfbfbfbf, 0xbfbfbfbf, 0x40404040, 0x40404040, + 0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf, + 0x40404040, 0x40404040, 0xbfbfbfbf, 0xbfbfbfbf, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, 0xbfbfbfbf, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf, + 0x00000000, 0x00000000, 0xbfbfbfbf, 0xbfbfbfbf, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x40404040, 0x40404040, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x40404040, 0x40404040, + 0x00000000, 0x00000000, 0x40404040, 0x40404040, + 0xffffffff, 0xffffffff, 0xbfbfbfbf, 0xbfbfbfbf, + 0xbfbfbfbf, 0xbfbfbfbf, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x80808080, 0x80808080, + 0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x7f7f7f7f, 0x7f7f7f7f, 0x80808080, 0x80808080, + 0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f, + 0x80808080, 0x80808080, 0x7f7f7f7f, 0x7f7f7f7f, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f, + 0x00000000, 0x00000000, 0x7f7f7f7f, 0x7f7f7f7f, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x80808080, 0x80808080, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0x80808080, 0x80808080, + 0x00000000, 0x00000000, 0x80808080, 0x80808080, + 0xffffffff, 0xffffffff, 0x7f7f7f7f, 0x7f7f7f7f, + 0x7f7f7f7f, 0x7f7f7f7f, 0x00000000, 0x00000000 + } +}; + +/* Fabric ratios table */ +u32 fabric_ratio[FAB_OPT] = { + 0x04010204, + 0x04020202, + 0x08020306, + 0x08020303, + 0x04020303, + 0x04020204, + 0x04010202, + 0x08030606, + 0x08030505, + 0x04020306, + 0x0804050a, + 0x04030606, + 0x04020404, + 0x04030306, + 0x04020505, + 0x08020505, + 0x04010303, + 0x08050a0a, + 0x04030408, + 0x04010102, + 0x08030306 +}; + +u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM] = { + {3, 2, 5, 7, 1, 0, 6, 4}, + {2, 3, 6, 7, 1, 0, 4, 5}, + {1, 3, 5, 6, 0, 2, 4, 7}, + {0, 2, 4, 7, 1, 3, 5, 6}, + {3, 0, 4, 6, 1, 2, 5, 7}, + {0, 3, 5, 7, 1, 2, 4, 6}, + {2, 3, 5, 7, 1, 0, 4, 6}, + {0, 2, 5, 4, 1, 3, 6, 7}, + {2, 3, 4, 7, 0, 1, 5, 6} +}; + +#endif /* __DDR3_PATTERNS_64_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_topology_def.h b/drivers/ddr/marvell/a38x/ddr3_topology_def.h new file mode 100644 index 0000000..64a0447 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_topology_def.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TOPOLOGY_DEF_H +#define _DDR3_TOPOLOGY_DEF_H + +/* TOPOLOGY */ + +enum hws_speed_bin { + SPEED_BIN_DDR_800D, + SPEED_BIN_DDR_800E, + SPEED_BIN_DDR_1066E, + SPEED_BIN_DDR_1066F, + SPEED_BIN_DDR_1066G, + SPEED_BIN_DDR_1333F, + SPEED_BIN_DDR_1333G, + SPEED_BIN_DDR_1333H, + SPEED_BIN_DDR_1333J, + SPEED_BIN_DDR_1600G, + SPEED_BIN_DDR_1600H, + SPEED_BIN_DDR_1600J, + SPEED_BIN_DDR_1600K, + SPEED_BIN_DDR_1866J, + SPEED_BIN_DDR_1866K, + SPEED_BIN_DDR_1866L, + SPEED_BIN_DDR_1866M, + SPEED_BIN_DDR_2133K, + SPEED_BIN_DDR_2133L, + SPEED_BIN_DDR_2133M, + SPEED_BIN_DDR_2133N, + + SPEED_BIN_DDR_1333H_EXT, + SPEED_BIN_DDR_1600K_EXT, + SPEED_BIN_DDR_1866M_EXT +}; + +enum hws_ddr_freq { + DDR_FREQ_LOW_FREQ, + DDR_FREQ_400, + DDR_FREQ_533, + DDR_FREQ_667, + DDR_FREQ_800, + DDR_FREQ_933, + DDR_FREQ_1066, + DDR_FREQ_311, + DDR_FREQ_333, + DDR_FREQ_467, + DDR_FREQ_850, + DDR_FREQ_600, + DDR_FREQ_300, + DDR_FREQ_900, + DDR_FREQ_360, + DDR_FREQ_1000, + DDR_FREQ_LIMIT +}; + +enum speed_bin_table_elements { + SPEED_BIN_TRCD, + SPEED_BIN_TRP, + SPEED_BIN_TRAS, + SPEED_BIN_TRC, + SPEED_BIN_TRRD1K, + SPEED_BIN_TRRD2K, + SPEED_BIN_TPD, + SPEED_BIN_TFAW1K, + SPEED_BIN_TFAW2K, + SPEED_BIN_TWTR, + SPEED_BIN_TRTP, + SPEED_BIN_TWR, + SPEED_BIN_TMOD +}; + +#endif /* _DDR3_TOPOLOGY_DEF_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training.c b/drivers/ddr/marvell/a38x/ddr3_training.c new file mode 100644 index 0000000..80ef050 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training.c @@ -0,0 +1,2644 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define GET_MAX_VALUE(x, y) \ + ((x) > (y)) ? (x) : (y) +#define CEIL_DIVIDE(x, y) \ + ((x - (x / y) * y) == 0) ? ((x / y) - 1) : (x / y) + +#define TIME_2_CLOCK_CYCLES CEIL_DIVIDE + +#define GET_CS_FROM_MASK(mask) (cs_mask2_num[mask]) +#define CS_CBE_VALUE(cs_num) (cs_cbe_reg[cs_num]) + +u32 window_mem_addr = 0; +u32 phy_reg0_val = 0; +u32 phy_reg1_val = 8; +u32 phy_reg2_val = 0; +u32 phy_reg3_val = 0xa; +enum hws_ddr_freq init_freq = DDR_FREQ_667; +enum hws_ddr_freq low_freq = DDR_FREQ_LOW_FREQ; +enum hws_ddr_freq medium_freq; +u32 debug_dunit = 0; +u32 odt_additional = 1; +u32 *dq_map_table = NULL; +u32 odt_config = 1; + +#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ALLEYCAT3) || \ + defined(CONFIG_ARMADA_39X) +u32 is_pll_before_init = 0, is_adll_calib_before_init = 0, is_dfs_in_init = 0; +u32 dfs_low_freq = 130; +#else +u32 is_pll_before_init = 0, is_adll_calib_before_init = 1, is_dfs_in_init = 0; +u32 dfs_low_freq = 100; +#endif +u32 g_rtt_nom_c_s0, g_rtt_nom_c_s1; +u8 calibration_update_control; /* 2 external only, 1 is internal only */ + +enum hws_result training_result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]; +enum auto_tune_stage training_stage = INIT_CONTROLLER; +u32 finger_test = 0, p_finger_start = 11, p_finger_end = 64, + n_finger_start = 11, n_finger_end = 64, + p_finger_step = 3, n_finger_step = 3; +u32 clamp_tbl[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + +/* Initiate to 0xff, this variable is define by user in debug mode */ +u32 mode2_t = 0xff; +u32 xsb_validate_type = 0; +u32 xsb_validation_base_address = 0xf000; +u32 first_active_if = 0; +u32 dfs_low_phy1 = 0x1f; +u32 multicast_id = 0; +int use_broadcast = 0; +struct hws_tip_freq_config_info *freq_info_table = NULL; +u8 is_cbe_required = 0; +u32 debug_mode = 0; +u32 delay_enable = 0; +int rl_mid_freq_wa = 0; + +u32 effective_cs = 0; + +u32 mask_tune_func = (SET_MEDIUM_FREQ_MASK_BIT | + WRITE_LEVELING_MASK_BIT | + LOAD_PATTERN_2_MASK_BIT | + READ_LEVELING_MASK_BIT | + SET_TARGET_FREQ_MASK_BIT | WRITE_LEVELING_TF_MASK_BIT | + READ_LEVELING_TF_MASK_BIT | + CENTRALIZATION_RX_MASK_BIT | CENTRALIZATION_TX_MASK_BIT); + +void ddr3_print_version(void) +{ + printf(DDR3_TIP_VERSION_STRING); +} + +static int ddr3_tip_ddr3_training_main_flow(u32 dev_num); +static int ddr3_tip_write_odt(u32 dev_num, enum hws_access_type access_type, + u32 if_id, u32 cl_value, u32 cwl_value); +static int ddr3_tip_ddr3_auto_tune(u32 dev_num); +static int is_bus_access_done(u32 dev_num, u32 if_id, + u32 dunit_reg_adrr, u32 bit); +#ifdef ODT_TEST_SUPPORT +static int odt_test(u32 dev_num, enum hws_algo_type algo_type); +#endif + +int adll_calibration(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_ddr_freq frequency); +static int ddr3_tip_set_timing(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_ddr_freq frequency); + +static struct page_element page_param[] = { + /* + * 8bits 16 bits + * page-size(K) page-size(K) mask + */ + { 1, 2, 2}, + /* 512M */ + { 1, 2, 3}, + /* 1G */ + { 1, 2, 0}, + /* 2G */ + { 1, 2, 4}, + /* 4G */ + { 2, 2, 5} + /* 8G */ +}; + +static u8 mem_size_config[MEM_SIZE_LAST] = { + 0x2, /* 512Mbit */ + 0x3, /* 1Gbit */ + 0x0, /* 2Gbit */ + 0x4, /* 4Gbit */ + 0x5 /* 8Gbit */ +}; + +static u8 cs_mask2_num[] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }; + +static struct reg_data odpg_default_value[] = { + {0x1034, 0x38000, MASK_ALL_BITS}, + {0x1038, 0x0, MASK_ALL_BITS}, + {0x10b0, 0x0, MASK_ALL_BITS}, + {0x10b8, 0x0, MASK_ALL_BITS}, + {0x10c0, 0x0, MASK_ALL_BITS}, + {0x10f0, 0x0, MASK_ALL_BITS}, + {0x10f4, 0x0, MASK_ALL_BITS}, + {0x10f8, 0xff, MASK_ALL_BITS}, + {0x10fc, 0xffff, MASK_ALL_BITS}, + {0x1130, 0x0, MASK_ALL_BITS}, + {0x1830, 0x2000000, MASK_ALL_BITS}, + {0x14d0, 0x0, MASK_ALL_BITS}, + {0x14d4, 0x0, MASK_ALL_BITS}, + {0x14d8, 0x0, MASK_ALL_BITS}, + {0x14dc, 0x0, MASK_ALL_BITS}, + {0x1454, 0x0, MASK_ALL_BITS}, + {0x1594, 0x0, MASK_ALL_BITS}, + {0x1598, 0x0, MASK_ALL_BITS}, + {0x159c, 0x0, MASK_ALL_BITS}, + {0x15a0, 0x0, MASK_ALL_BITS}, + {0x15a4, 0x0, MASK_ALL_BITS}, + {0x15a8, 0x0, MASK_ALL_BITS}, + {0x15ac, 0x0, MASK_ALL_BITS}, + {0x1604, 0x0, MASK_ALL_BITS}, + {0x1608, 0x0, MASK_ALL_BITS}, + {0x160c, 0x0, MASK_ALL_BITS}, + {0x1610, 0x0, MASK_ALL_BITS}, + {0x1614, 0x0, MASK_ALL_BITS}, + {0x1618, 0x0, MASK_ALL_BITS}, + {0x1624, 0x0, MASK_ALL_BITS}, + {0x1690, 0x0, MASK_ALL_BITS}, + {0x1694, 0x0, MASK_ALL_BITS}, + {0x1698, 0x0, MASK_ALL_BITS}, + {0x169c, 0x0, MASK_ALL_BITS}, + {0x14b8, 0x6f67, MASK_ALL_BITS}, + {0x1630, 0x0, MASK_ALL_BITS}, + {0x1634, 0x0, MASK_ALL_BITS}, + {0x1638, 0x0, MASK_ALL_BITS}, + {0x163c, 0x0, MASK_ALL_BITS}, + {0x16b0, 0x0, MASK_ALL_BITS}, + {0x16b4, 0x0, MASK_ALL_BITS}, + {0x16b8, 0x0, MASK_ALL_BITS}, + {0x16bc, 0x0, MASK_ALL_BITS}, + {0x16c0, 0x0, MASK_ALL_BITS}, + {0x16c4, 0x0, MASK_ALL_BITS}, + {0x16c8, 0x0, MASK_ALL_BITS}, + {0x16cc, 0x1, MASK_ALL_BITS}, + {0x16f0, 0x1, MASK_ALL_BITS}, + {0x16f4, 0x0, MASK_ALL_BITS}, + {0x16f8, 0x0, MASK_ALL_BITS}, + {0x16fc, 0x0, MASK_ALL_BITS} +}; + +static int ddr3_tip_bus_access(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, enum hws_access_type phy_access, + u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, + u32 data_value, enum hws_operation oper_type); +static int ddr3_tip_pad_inv(u32 dev_num, u32 if_id); +static int ddr3_tip_rank_control(u32 dev_num, u32 if_id); + +/* + * Update global training parameters by data from user + */ +int ddr3_tip_tune_training_params(u32 dev_num, + struct tune_train_params *params) +{ + if (params->ck_delay != -1) + ck_delay = params->ck_delay; + if (params->ck_delay_16 != -1) + ck_delay_16 = params->ck_delay_16; + if (params->phy_reg3_val != -1) + phy_reg3_val = params->phy_reg3_val; + + return MV_OK; +} + +/* + * Configure CS + */ +int ddr3_tip_configure_cs(u32 dev_num, u32 if_id, u32 cs_num, u32 enable) +{ + u32 data, addr_hi, data_high; + u32 mem_index; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (enable == 1) { + data = (tm->interface_params[if_id].bus_width == + BUS_WIDTH_8) ? 0 : 1; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + SDRAM_ACCESS_CONTROL_REG, (data << (cs_num * 4)), + 0x3 << (cs_num * 4))); + mem_index = tm->interface_params[if_id].memory_size; + + addr_hi = mem_size_config[mem_index] & 0x3; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + SDRAM_ACCESS_CONTROL_REG, + (addr_hi << (2 + cs_num * 4)), + 0x3 << (2 + cs_num * 4))); + + data_high = (mem_size_config[mem_index] & 0x4) >> 2; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + SDRAM_ACCESS_CONTROL_REG, + data_high << (20 + cs_num), 1 << (20 + cs_num))); + + /* Enable Address Select Mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + SDRAM_ACCESS_CONTROL_REG, 1 << (16 + cs_num), + 1 << (16 + cs_num))); + } + switch (cs_num) { + case 0: + case 1: + case 2: + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + DDR_CONTROL_LOW_REG, (enable << (cs_num + 11)), + 1 << (cs_num + 11))); + break; + case 3: + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + DDR_CONTROL_LOW_REG, (enable << 15), 1 << 15)); + break; + } + + return MV_OK; +} + +/* + * Calculate number of CS + */ +static int calc_cs_num(u32 dev_num, u32 if_id, u32 *cs_num) +{ + u32 cs; + u32 bus_cnt; + u32 cs_count; + u32 cs_bitmask; + u32 curr_cs_num = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + cs_count = 0; + cs_bitmask = tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask; + for (cs = 0; cs < MAX_CS_NUM; cs++) { + if ((cs_bitmask >> cs) & 1) + cs_count++; + } + + if (curr_cs_num == 0) { + curr_cs_num = cs_count; + } else if (cs_count != curr_cs_num) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("CS number is different per bus (IF %d BUS %d cs_num %d curr_cs_num %d)\n", + if_id, bus_cnt, cs_count, + curr_cs_num)); + return MV_NOT_SUPPORTED; + } + } + *cs_num = curr_cs_num; + + return MV_OK; +} + +/* + * Init Controller Flow + */ +int hws_ddr3_tip_init_controller(u32 dev_num, struct init_cntr_param *init_cntr_prm) +{ + u32 if_id; + u32 cs_num; + u32 t_refi = 0, t_hclk = 0, t_ckclk = 0, t_faw = 0, t_pd = 0, + t_wr = 0, t2t = 0, txpdll = 0; + u32 data_value = 0, bus_width = 0, page_size = 0, cs_cnt = 0, + mem_mask = 0, bus_index = 0; + enum hws_speed_bin speed_bin_index = SPEED_BIN_DDR_2133N; + enum hws_mem_size memory_size = MEM_2G; + enum hws_ddr_freq freq = init_freq; + u32 cs_mask = 0; + u32 cl_value = 0, cwl_val = 0; + u32 refresh_interval_cnt = 0, bus_cnt = 0, adll_tap = 0; + enum hws_access_type access_type = ACCESS_TYPE_UNICAST; + u32 data_read[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("Init_controller, do_mrs_phy=%d, is_ctrl64_bit=%d\n", + init_cntr_prm->do_mrs_phy, + init_cntr_prm->is_ctrl64_bit)); + + if (init_cntr_prm->init_phy == 1) { + CHECK_STATUS(ddr3_tip_configure_phy(dev_num)); + } + + if (generic_init_controller == 1) { + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("active IF %d\n", if_id)); + mem_mask = 0; + for (bus_index = 0; + bus_index < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + mem_mask |= + tm->interface_params[if_id]. + as_bus_params[bus_index].mirror_enable_bitmask; + } + + if (mem_mask != 0) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, + if_id, CS_ENABLE_REG, 0, + 0x8)); + } + + memory_size = + tm->interface_params[if_id]. + memory_size; + speed_bin_index = + tm->interface_params[if_id]. + speed_bin_index; + freq = init_freq; + t_refi = + (tm->interface_params[if_id]. + interface_temp == + HWS_TEMP_HIGH) ? TREFI_HIGH : TREFI_LOW; + t_refi *= 1000; /* psec */ + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("memy_size %d speed_bin_ind %d freq %d t_refi %d\n", + memory_size, speed_bin_index, freq, + t_refi)); + /* HCLK & CK CLK in 2:1[ps] */ + /* t_ckclk is external clock */ + t_ckclk = (MEGA / freq_val[freq]); + /* t_hclk is internal clock */ + t_hclk = 2 * t_ckclk; + refresh_interval_cnt = t_refi / t_hclk; /* no units */ + bus_width = + (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) + == 1) ? (16) : (32); + + if (init_cntr_prm->is_ctrl64_bit) + bus_width = 64; + + data_value = + (refresh_interval_cnt | 0x4000 | + ((bus_width == + 32) ? 0x8000 : 0) | 0x1000000) & ~(1 << 26); + + /* Interface Bus Width */ + /* SRMode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, data_value, + 0x100ffff)); + + /* Interleave first command pre-charge enable (TBD) */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_OPEN_PAGE_CONTROL_REG, (1 << 10), + (1 << 10))); + + /* PHY configuration */ + /* + * Postamble Length = 1.5cc, Addresscntl to clk skew + * \BD, Preamble length normal, parralal ADLL enable + */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DRAM_PHY_CONFIGURATION, 0x28, 0x3e)); + if (init_cntr_prm->is_ctrl64_bit) { + /* positive edge */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DRAM_PHY_CONFIGURATION, 0x0, + 0xff80)); + } + + /* calibration block disable */ + /* Xbar Read buffer select (for Internal access) */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + CALIB_MACHINE_CTRL_REG, 0x1200c, + 0x7dffe01c)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + CALIB_MACHINE_CTRL_REG, + calibration_update_control << 3, 0x3 << 3)); + + /* Pad calibration control - enable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + CALIB_MACHINE_CTRL_REG, 0x1, 0x1)); + + cs_mask = 0; + data_value = 0x7; + /* + * Address ctrl \96 Part of the Generic code + * The next configuration is done: + * 1) Memory Size + * 2) Bus_width + * 3) CS# + * 4) Page Number + * 5) t_faw + * Per Dunit get from the Map_topology the parameters: + * Bus_width + * t_faw is per Dunit not per CS + */ + page_size = + (tm->interface_params[if_id]. + bus_width == + BUS_WIDTH_8) ? page_param[memory_size]. + page_size_8bit : page_param[memory_size]. + page_size_16bit; + + t_faw = + (page_size == 1) ? speed_bin_table(speed_bin_index, + SPEED_BIN_TFAW1K) + : speed_bin_table(speed_bin_index, + SPEED_BIN_TFAW2K); + + data_value = TIME_2_CLOCK_CYCLES(t_faw, t_ckclk); + data_value = data_value << 24; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_ACCESS_CONTROL_REG, data_value, + 0x7f000000)); + + data_value = + (tm->interface_params[if_id]. + bus_width == BUS_WIDTH_8) ? 0 : 1; + + /* create merge cs mask for all cs available in dunit */ + for (bus_cnt = 0; + bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + cs_mask |= + tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask; + } + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("Init_controller IF %d cs_mask %d\n", + if_id, cs_mask)); + /* + * Configure the next upon the Map Topology \96 If the + * Dunit is CS0 Configure CS0 if it is multi CS + * configure them both: The Bust_width it\92s the + * Memory Bus width \96 x8 or x16 + */ + for (cs_cnt = 0; cs_cnt < NUM_OF_CS; cs_cnt++) { + ddr3_tip_configure_cs(dev_num, if_id, cs_cnt, + ((cs_mask & (1 << cs_cnt)) ? 1 + : 0)); + } + + if (init_cntr_prm->do_mrs_phy) { + /* + * MR0 \96 Part of the Generic code + * The next configuration is done: + * 1) Burst Length + * 2) CAS Latency + * get for each dunit what is it Speed_bin & + * Target Frequency. From those both parameters + * get the appropriate Cas_l from the CL table + */ + cl_value = + tm->interface_params[if_id]. + cas_l; + cwl_val = + tm->interface_params[if_id]. + cas_wl; + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("cl_value 0x%x cwl_val 0x%x\n", + cl_value, cwl_val)); + + data_value = + ((cl_mask_table[cl_value] & 0x1) << 2) | + ((cl_mask_table[cl_value] & 0xe) << 3); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + MR0_REG, data_value, + (0x7 << 4) | (1 << 2))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + MR0_REG, twr_mask_table[t_wr + 1], + 0xe00)); + + /* + * MR1: Set RTT and DIC Design GL values + * configured by user + */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, MR1_REG, + g_dic | g_rtt_nom, 0x266)); + + /* MR2 - Part of the Generic code */ + /* + * The next configuration is done: + * 1) SRT + * 2) CAS Write Latency + */ + data_value = (cwl_mask_table[cwl_val] << 3); + data_value |= + ((tm->interface_params[if_id]. + interface_temp == + HWS_TEMP_HIGH) ? (1 << 7) : 0); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + MR2_REG, data_value, + (0x7 << 3) | (0x1 << 7) | (0x3 << + 9))); + } + + ddr3_tip_write_odt(dev_num, access_type, if_id, + cl_value, cwl_val); + ddr3_tip_set_timing(dev_num, access_type, if_id, freq); + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DUNIT_CONTROL_HIGH_REG, 0x177, + 0x1000177)); + + if (init_cntr_prm->is_ctrl64_bit) { + /* disable 0.25 cc delay */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DUNIT_CONTROL_HIGH_REG, 0x0, + 0x800)); + } + + /* reset bit 7 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DUNIT_CONTROL_HIGH_REG, + (init_cntr_prm->msys_init << 7), (1 << 7))); + + if (mode2_t != 0xff) { + t2t = mode2_t; + } else { + /* calculate number of CS (per interface) */ + CHECK_STATUS(calc_cs_num + (dev_num, if_id, &cs_num)); + t2t = (cs_num == 1) ? 0 : 1; + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DDR_CONTROL_LOW_REG, t2t << 3, + 0x3 << 3)); + /* move the block to ddr3_tip_set_timing - start */ + t_pd = GET_MAX_VALUE(t_ckclk * 3, + speed_bin_table(speed_bin_index, + SPEED_BIN_TPD)); + t_pd = TIME_2_CLOCK_CYCLES(t_pd, t_ckclk); + txpdll = GET_MAX_VALUE(t_ckclk * 10, 24); + txpdll = CEIL_DIVIDE((txpdll - 1), t_ckclk); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DDR_TIMING_REG, txpdll << 4, + 0x1f << 4)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DDR_TIMING_REG, 0x28 << 9, 0x3f << 9)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DDR_TIMING_REG, 0xa << 21, 0xff << 21)); + + /* move the block to ddr3_tip_set_timing - end */ + /* AUTO_ZQC_TIMING */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + TIMING_REG, (AUTO_ZQC_TIMING | (2 << 20)), + 0x3fffff)); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, access_type, if_id, + DRAM_PHY_CONFIGURATION, data_read, 0x30)); + data_value = + (data_read[if_id] == 0) ? (1 << 11) : 0; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DUNIT_CONTROL_HIGH_REG, data_value, + (1 << 11))); + + /* Set Active control for ODT write transactions */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, 0x1494, g_odt_config, + MASK_ALL_BITS)); + } + } else { +#ifdef STATIC_ALGO_SUPPORT + CHECK_STATUS(ddr3_tip_static_init_controller(dev_num)); +#if defined(CONFIG_ARMADA_38X) || defined(CONFIG_ARMADA_39X) + CHECK_STATUS(ddr3_tip_static_phy_init_controller(dev_num)); +#endif +#endif /* STATIC_ALGO_SUPPORT */ + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_rank_control(dev_num, if_id)); + + if (init_cntr_prm->do_mrs_phy) { + CHECK_STATUS(ddr3_tip_pad_inv(dev_num, if_id)); + } + + /* Pad calibration control - disable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + CALIB_MACHINE_CTRL_REG, 0x0, 0x1)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + CALIB_MACHINE_CTRL_REG, + calibration_update_control << 3, 0x3 << 3)); + } + + CHECK_STATUS(ddr3_tip_enable_init_sequence(dev_num)); + + if (delay_enable != 0) { + adll_tap = MEGA / (freq_val[freq] * 64); + ddr3_tip_cmd_addr_init_delay(dev_num, adll_tap); + } + + return MV_OK; +} + +/* + * Load Topology map + */ +int hws_ddr3_tip_load_topology_map(u32 dev_num, struct hws_topology_map *tm) +{ + enum hws_speed_bin speed_bin_index; + enum hws_ddr_freq freq = DDR_FREQ_LIMIT; + u32 if_id; + + freq_val[DDR_FREQ_LOW_FREQ] = dfs_low_freq; + tm = ddr3_get_topology_map(); + CHECK_STATUS(ddr3_tip_get_first_active_if + ((u8)dev_num, tm->if_act_mask, + &first_active_if)); + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("board IF_Mask=0x%x num_of_bus_per_interface=0x%x\n", + tm->if_act_mask, + tm->num_of_bus_per_interface)); + + /* + * if CL, CWL values are missing in topology map, then fill them + * according to speedbin tables + */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + speed_bin_index = + tm->interface_params[if_id].speed_bin_index; + /* TBD memory frequency of interface 0 only is used ! */ + freq = tm->interface_params[first_active_if].memory_freq; + + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("speed_bin_index =%d freq=%d cl=%d cwl=%d\n", + speed_bin_index, freq_val[freq], + tm->interface_params[if_id]. + cas_l, + tm->interface_params[if_id]. + cas_wl)); + + if (tm->interface_params[if_id].cas_l == 0) { + tm->interface_params[if_id].cas_l = + cas_latency_table[speed_bin_index].cl_val[freq]; + } + + if (tm->interface_params[if_id].cas_wl == 0) { + tm->interface_params[if_id].cas_wl = + cas_write_latency_table[speed_bin_index].cl_val[freq]; + } + } + + return MV_OK; +} + +/* + * RANK Control Flow + */ +static int ddr3_tip_rank_control(u32 dev_num, u32 if_id) +{ + u32 data_value = 0, bus_cnt; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (bus_cnt = 1; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + if ((tm->interface_params[if_id]. + as_bus_params[0].cs_bitmask != + tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask) || + (tm->interface_params[if_id]. + as_bus_params[0].mirror_enable_bitmask != + tm->interface_params[if_id]. + as_bus_params[bus_cnt].mirror_enable_bitmask)) + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("WARNING:Wrong configuration for pup #%d CS mask and CS mirroring for all pups should be the same\n", + bus_cnt)); + } + + data_value |= tm->interface_params[if_id]. + as_bus_params[0].cs_bitmask; + data_value |= tm->interface_params[if_id]. + as_bus_params[0].mirror_enable_bitmask << 4; + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, RANK_CTRL_REG, + data_value, 0xff)); + + return MV_OK; +} + +/* + * PAD Inverse Flow + */ +static int ddr3_tip_pad_inv(u32 dev_num, u32 if_id) +{ + u32 bus_cnt, data_value, ck_swap_pup_ctrl; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + if (tm->interface_params[if_id]. + as_bus_params[bus_cnt].is_dqs_swap == 1) { + /* dqs swap */ + ddr3_tip_bus_read_modify_write(dev_num, ACCESS_TYPE_UNICAST, + if_id, bus_cnt, + DDR_PHY_DATA, + PHY_CONTROL_PHY_REG, 0xc0, + 0xc0); + } + + if (tm->interface_params[if_id]. + as_bus_params[bus_cnt].is_ck_swap == 1) { + if (bus_cnt <= 1) + data_value = 0x5 << 2; + else + data_value = 0xa << 2; + + /* mask equals data */ + /* ck swap pup is only control pup #0 ! */ + ck_swap_pup_ctrl = 0; + ddr3_tip_bus_read_modify_write(dev_num, ACCESS_TYPE_UNICAST, + if_id, ck_swap_pup_ctrl, + DDR_PHY_CONTROL, + PHY_CONTROL_PHY_REG, + data_value, data_value); + } + } + + return MV_OK; +} + +/* + * Run Training Flow + */ +int hws_ddr3_tip_run_alg(u32 dev_num, enum hws_algo_type algo_type) +{ + int ret = MV_OK, ret_tune = MV_OK; + +#ifdef ODT_TEST_SUPPORT + if (finger_test == 1) + return odt_test(dev_num, algo_type); +#endif + + if (algo_type == ALGO_TYPE_DYNAMIC) { + ret = ddr3_tip_ddr3_auto_tune(dev_num); + } else { +#ifdef STATIC_ALGO_SUPPORT + { + enum hws_ddr_freq freq; + freq = init_freq; + + /* add to mask */ + if (is_adll_calib_before_init != 0) { + printf("with adll calib before init\n"); + adll_calibration(dev_num, ACCESS_TYPE_MULTICAST, + 0, freq); + } + /* + * Frequency per interface is not relevant, + * only interface 0 + */ + ret = ddr3_tip_run_static_alg(dev_num, + freq); + } +#endif + } + + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Run_alg: tuning failed %d\n", ret_tune)); + } + + return ret; +} + +#ifdef ODT_TEST_SUPPORT +/* + * ODT Test + */ +static int odt_test(u32 dev_num, enum hws_algo_type algo_type) +{ + int ret = MV_OK, ret_tune = MV_OK; + int pfinger_val = 0, nfinger_val; + + for (pfinger_val = p_finger_start; pfinger_val <= p_finger_end; + pfinger_val += p_finger_step) { + for (nfinger_val = n_finger_start; nfinger_val <= n_finger_end; + nfinger_val += n_finger_step) { + if (finger_test != 0) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("pfinger_val %d nfinger_val %d\n", + pfinger_val, nfinger_val)); + p_finger = pfinger_val; + n_finger = nfinger_val; + } + + if (algo_type == ALGO_TYPE_DYNAMIC) { + ret = ddr3_tip_ddr3_auto_tune(dev_num); + } else { + /* + * Frequency per interface is not relevant, + * only interface 0 + */ + ret = ddr3_tip_run_static_alg(dev_num, + init_freq); + } + } + } + + if (ret_tune != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Run_alg: tuning failed %d\n", ret_tune)); + ret = (ret == MV_OK) ? ret_tune : ret; + } + + return ret; +} +#endif + +/* + * Select Controller + */ +int hws_ddr3_tip_select_ddr_controller(u32 dev_num, int enable) +{ + if (config_func_info[dev_num].tip_dunit_mux_select_func != NULL) { + return config_func_info[dev_num]. + tip_dunit_mux_select_func((u8)dev_num, enable); + } + + return MV_FAIL; +} + +/* + * Dunit Register Write + */ +int ddr3_tip_if_write(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 data_value, u32 mask) +{ + if (config_func_info[dev_num].tip_dunit_write_func != NULL) { + return config_func_info[dev_num]. + tip_dunit_write_func((u8)dev_num, interface_access, + if_id, reg_addr, + data_value, mask); + } + + return MV_FAIL; +} + +/* + * Dunit Register Read + */ +int ddr3_tip_if_read(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 *data, u32 mask) +{ + if (config_func_info[dev_num].tip_dunit_read_func != NULL) { + return config_func_info[dev_num]. + tip_dunit_read_func((u8)dev_num, interface_access, + if_id, reg_addr, + data, mask); + } + + return MV_FAIL; +} + +/* + * Dunit Register Polling + */ +int ddr3_tip_if_polling(u32 dev_num, enum hws_access_type access_type, + u32 if_id, u32 exp_value, u32 mask, u32 offset, + u32 poll_tries) +{ + u32 poll_cnt = 0, interface_num = 0, start_if, end_if; + u32 read_data[MAX_INTERFACE_NUM]; + int ret; + int is_fail = 0, is_if_fail; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = if_id; + end_if = if_id; + } + + for (interface_num = start_if; interface_num <= end_if; interface_num++) { + /* polling bit 3 for n times */ + VALIDATE_ACTIVE(tm->if_act_mask, interface_num); + + is_if_fail = 0; + for (poll_cnt = 0; poll_cnt < poll_tries; poll_cnt++) { + ret = + ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, + interface_num, offset, read_data, + mask); + if (ret != MV_OK) + return ret; + + if (read_data[interface_num] == exp_value) + break; + } + + if (poll_cnt >= poll_tries) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("max poll IF #%d\n", interface_num)); + is_fail = 1; + is_if_fail = 1; + } + + training_result[training_stage][interface_num] = + (is_if_fail == 1) ? TEST_FAILED : TEST_SUCCESS; + } + + return (is_fail == 0) ? MV_OK : MV_FAIL; +} + +/* + * Bus read access + */ +int ddr3_tip_bus_read(u32 dev_num, u32 if_id, + enum hws_access_type phy_access, u32 phy_id, + enum hws_ddr_phy phy_type, u32 reg_addr, u32 *data) +{ + u32 bus_index = 0; + u32 data_read[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (phy_access == ACCESS_TYPE_MULTICAST) { + for (bus_index = 0; bus_index < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + CHECK_STATUS(ddr3_tip_bus_access + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_index, phy_type, reg_addr, 0, + OPERATION_READ)); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + PHY_REG_FILE_ACCESS, data_read, + MASK_ALL_BITS)); + data[bus_index] = (data_read[if_id] & 0xffff); + } + } else { + CHECK_STATUS(ddr3_tip_bus_access + (dev_num, ACCESS_TYPE_UNICAST, if_id, + phy_access, phy_id, phy_type, reg_addr, 0, + OPERATION_READ)); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + PHY_REG_FILE_ACCESS, data_read, MASK_ALL_BITS)); + + /* + * only 16 lsb bit are valid in Phy (each register is different, + * some can actually be less than 16 bits) + */ + *data = (data_read[if_id] & 0xffff); + } + + return MV_OK; +} + +/* + * Bus write access + */ +int ddr3_tip_bus_write(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, enum hws_access_type phy_access, + u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, + u32 data_value) +{ + CHECK_STATUS(ddr3_tip_bus_access + (dev_num, interface_access, if_id, phy_access, + phy_id, phy_type, reg_addr, data_value, OPERATION_WRITE)); + + return MV_OK; +} + +/* + * Bus access routine (relevant for both read & write) + */ +static int ddr3_tip_bus_access(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, enum hws_access_type phy_access, + u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, + u32 data_value, enum hws_operation oper_type) +{ + u32 addr_low = 0x3f & reg_addr; + u32 addr_hi = ((0xc0 & reg_addr) >> 6); + u32 data_p1 = + (oper_type << 30) + (addr_hi << 28) + (phy_access << 27) + + (phy_type << 26) + (phy_id << 22) + (addr_low << 16) + + (data_value & 0xffff); + u32 data_p2 = data_p1 + (1 << 31); + u32 start_if, end_if; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, interface_access, if_id, PHY_REG_FILE_ACCESS, + data_p1, MASK_ALL_BITS)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, interface_access, if_id, PHY_REG_FILE_ACCESS, + data_p2, MASK_ALL_BITS)); + + if (interface_access == ACCESS_TYPE_UNICAST) { + start_if = if_id; + end_if = if_id; + } else { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } + + /* polling for read/write execution done */ + for (if_id = start_if; if_id <= end_if; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(is_bus_access_done + (dev_num, if_id, PHY_REG_FILE_ACCESS, 31)); + } + + return MV_OK; +} + +/* + * Check bus access done + */ +static int is_bus_access_done(u32 dev_num, u32 if_id, u32 dunit_reg_adrr, + u32 bit) +{ + u32 rd_data = 1; + u32 cnt = 0; + u32 data_read[MAX_INTERFACE_NUM]; + + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, dunit_reg_adrr, + data_read, MASK_ALL_BITS)); + rd_data = data_read[if_id]; + rd_data &= (1 << bit); + + while (rd_data != 0) { + if (cnt++ >= MAX_POLLING_ITERATIONS) + break; + + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + dunit_reg_adrr, data_read, MASK_ALL_BITS)); + rd_data = data_read[if_id]; + rd_data &= (1 << bit); + } + + if (cnt < MAX_POLLING_ITERATIONS) + return MV_OK; + else + return MV_FAIL; +} + +/* + * Phy read-modify-write + */ +int ddr3_tip_bus_read_modify_write(u32 dev_num, enum hws_access_type access_type, + u32 interface_id, u32 phy_id, + enum hws_ddr_phy phy_type, u32 reg_addr, + u32 data_value, u32 reg_mask) +{ + u32 data_val = 0, if_id, start_if, end_if; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = interface_id; + end_if = interface_id; + } + + for (if_id = start_if; if_id <= end_if; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, phy_id, + phy_type, reg_addr, &data_val)); + data_value = (data_val & (~reg_mask)) | (data_value & reg_mask); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, phy_id, phy_type, reg_addr, + data_value)); + } + + return MV_OK; +} + +/* + * ADLL Calibration + */ +int adll_calibration(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_ddr_freq frequency) +{ + struct hws_tip_freq_config_info freq_config_info; + u32 bus_cnt = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* Reset Diver_b assert -> de-assert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, SDRAM_CONFIGURATION_REG, + 0, 0x10000000)); + mdelay(10); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, SDRAM_CONFIGURATION_REG, + 0x10000000, 0x10000000)); + + if (config_func_info[dev_num].tip_get_freq_config_info_func != NULL) { + CHECK_STATUS(config_func_info[dev_num]. + tip_get_freq_config_info_func((u8)dev_num, frequency, + &freq_config_info)); + } else { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("tip_get_freq_config_info_func is NULL")); + return MV_NOT_INITIALIZED; + } + + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, access_type, if_id, bus_cnt, + DDR_PHY_DATA, BW_PHY_REG, + freq_config_info.bw_per_freq << 8, 0x700)); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, access_type, if_id, bus_cnt, + DDR_PHY_DATA, RATE_PHY_REG, + freq_config_info.rate_per_freq, 0x7)); + } + + /* DUnit to Phy drive post edge, ADLL reset assert de-assert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DRAM_PHY_CONFIGURATION, + 0, (0x80000000 | 0x40000000))); + mdelay(100 / (freq_val[frequency] / freq_val[DDR_FREQ_LOW_FREQ])); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DRAM_PHY_CONFIGURATION, + (0x80000000 | 0x40000000), (0x80000000 | 0x40000000))); + + /* polling for ADLL Done */ + if (ddr3_tip_if_polling(dev_num, access_type, if_id, + 0x3ff03ff, 0x3ff03ff, PHY_LOCK_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Freq_set: DDR3 poll failed(1)")); + } + + /* pup data_pup reset assert-> deassert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, SDRAM_CONFIGURATION_REG, + 0, 0x60000000)); + mdelay(10); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, SDRAM_CONFIGURATION_REG, + 0x60000000, 0x60000000)); + + return MV_OK; +} + +int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_ddr_freq frequency) +{ + u32 cl_value = 0, cwl_value = 0, mem_mask = 0, val = 0, + bus_cnt = 0, t_hclk = 0, t_wr = 0, + refresh_interval_cnt = 0, cnt_id; + u32 t_refi = 0, end_if, start_if; + u32 bus_index = 0; + int is_dll_off = 0; + enum hws_speed_bin speed_bin_index = 0; + struct hws_tip_freq_config_info freq_config_info; + enum hws_result *flow_result = training_result[training_stage]; + u32 adll_tap = 0; + u32 cs_mask[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("dev %d access %d IF %d freq %d\n", dev_num, + access_type, if_id, frequency)); + + if (frequency == DDR_FREQ_LOW_FREQ) + is_dll_off = 1; + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = if_id; + end_if = if_id; + } + + /* calculate interface cs mask - Oferb 4/11 */ + /* speed bin can be different for each interface */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + /* cs enable is active low */ + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + cs_mask[if_id] = CS_BIT_MASK; + training_result[training_stage][if_id] = TEST_SUCCESS; + ddr3_tip_calc_cs_mask(dev_num, if_id, effective_cs, + &cs_mask[if_id]); + } + + /* speed bin can be different for each interface */ + /* + * moti b - need to remove the loop for multicas access functions + * and loop the unicast access functions + */ + for (if_id = start_if; if_id <= end_if; if_id++) { + if (IS_ACTIVE(tm->if_act_mask, if_id) == 0) + continue; + + flow_result[if_id] = TEST_SUCCESS; + speed_bin_index = + tm->interface_params[if_id].speed_bin_index; + if (tm->interface_params[if_id].memory_freq == + frequency) { + cl_value = + tm->interface_params[if_id].cas_l; + cwl_value = + tm->interface_params[if_id].cas_wl; + } else { + cl_value = + cas_latency_table[speed_bin_index].cl_val[frequency]; + cwl_value = + cas_write_latency_table[speed_bin_index]. + cl_val[frequency]; + } + + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("Freq_set dev 0x%x access 0x%x if 0x%x freq 0x%x speed %d:\n\t", + dev_num, access_type, if_id, + frequency, speed_bin_index)); + + for (cnt_id = 0; cnt_id < DDR_FREQ_LIMIT; cnt_id++) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, + ("%d ", + cas_latency_table[speed_bin_index]. + cl_val[cnt_id])); + } + + DEBUG_TRAINING_IP(DEBUG_LEVEL_TRACE, ("\n")); + mem_mask = 0; + for (bus_index = 0; bus_index < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + mem_mask |= + tm->interface_params[if_id]. + as_bus_params[bus_index].mirror_enable_bitmask; + } + + if (mem_mask != 0) { + /* motib redundent in KW28 */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, + CS_ENABLE_REG, 0, 0x8)); + } + + /* dll state after exiting SR */ + if (is_dll_off == 1) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DFS_REG, 0x1, 0x1)); + } else { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DFS_REG, 0, 0x1)); + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DUNIT_MMASK_REG, 0, 0x1)); + /* DFS - block transactions */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DFS_REG, 0x2, 0x2)); + + /* disable ODT in case of dll off */ + if (is_dll_off == 1) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + 0x1874, 0, 0x244)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + 0x1884, 0, 0x244)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + 0x1894, 0, 0x244)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + 0x18a4, 0, 0x244)); + } + + /* DFS - Enter Self-Refresh */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, 0x4, + 0x4)); + /* polling on self refresh entry */ + if (ddr3_tip_if_polling(dev_num, ACCESS_TYPE_UNICAST, + if_id, 0x8, 0x8, DFS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Freq_set: DDR3 poll failed on SR entry\n")); + } + + /* PLL configuration */ + if (config_func_info[dev_num].tip_set_freq_divider_func != NULL) { + config_func_info[dev_num]. + tip_set_freq_divider_func(dev_num, if_id, + frequency); + } + + /* PLL configuration End */ + + /* adjust t_refi to new frequency */ + t_refi = (tm->interface_params[if_id].interface_temp == + HWS_TEMP_HIGH) ? TREFI_LOW : TREFI_HIGH; + t_refi *= 1000; /*psec */ + + /* HCLK in[ps] */ + t_hclk = MEGA / (freq_val[frequency] / 2); + refresh_interval_cnt = t_refi / t_hclk; /* no units */ + val = 0x4000 | refresh_interval_cnt; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, val, 0x7fff)); + + /* DFS - CL/CWL/WR parameters after exiting SR */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, + (cl_mask_table[cl_value] << 8), 0xf00)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, + (cwl_mask_table[cwl_value] << 12), 0x7000)); + t_wr = speed_bin_table(speed_bin_index, SPEED_BIN_TWR); + t_wr = (t_wr / 1000); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, + (twr_mask_table[t_wr + 1] << 16), 0x70000)); + + /* Restore original RTT values if returning from DLL OFF mode */ + if (is_dll_off == 1) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, 0x1874, + g_dic | g_rtt_nom, 0x266)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, 0x1884, + g_dic | g_rtt_nom, 0x266)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, 0x1894, + g_dic | g_rtt_nom, 0x266)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, 0x18a4, + g_dic | g_rtt_nom, 0x266)); + } + + /* Reset Diver_b assert -> de-assert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, 0, 0x10000000)); + mdelay(10); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, 0x10000000, 0x10000000)); + + /* Adll configuration function of process and Frequency */ + if (config_func_info[dev_num].tip_get_freq_config_info_func != NULL) { + CHECK_STATUS(config_func_info[dev_num]. + tip_get_freq_config_info_func(dev_num, frequency, + &freq_config_info)); + } + /* TBD check milo5 using device ID ? */ + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, bus_cnt, DDR_PHY_DATA, + 0x92, + freq_config_info. + bw_per_freq << 8 + /*freq_mask[dev_num][frequency] << 8 */ + , 0x700)); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + bus_cnt, DDR_PHY_DATA, 0x94, + freq_config_info.rate_per_freq, 0x7)); + } + + /* DUnit to Phy drive post edge, ADLL reset assert de-assert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DRAM_PHY_CONFIGURATION, 0, + (0x80000000 | 0x40000000))); + mdelay(100 / (freq_val[frequency] / freq_val[DDR_FREQ_LOW_FREQ])); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + DRAM_PHY_CONFIGURATION, (0x80000000 | 0x40000000), + (0x80000000 | 0x40000000))); + + /* polling for ADLL Done */ + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x3ff03ff, + 0x3ff03ff, PHY_LOCK_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Freq_set: DDR3 poll failed(1)\n")); + } + + /* pup data_pup reset assert-> deassert */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, 0, 0x60000000)); + mdelay(10); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_CONFIGURATION_REG, 0x60000000, 0x60000000)); + + /* Set proper timing params before existing Self-Refresh */ + ddr3_tip_set_timing(dev_num, access_type, if_id, frequency); + if (delay_enable != 0) { + adll_tap = MEGA / (freq_val[frequency] * 64); + ddr3_tip_cmd_addr_init_delay(dev_num, adll_tap); + } + + /* Exit SR */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, 0, + 0x4)); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x8, DFS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Freq_set: DDR3 poll failed(2)")); + } + + /* Refresh Command */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + SDRAM_OPERATION_REG, 0x2, 0xf1f)); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1f, + SDRAM_OPERATION_REG, MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Freq_set: DDR3 poll failed(3)")); + } + + /* Release DFS Block */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DFS_REG, 0, + 0x2)); + /* Controller to MBUS Retry - normal */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, DUNIT_MMASK_REG, + 0x1, 0x1)); + + /* MRO: Burst Length 8, CL , Auto_precharge 0x16cc */ + val = + ((cl_mask_table[cl_value] & 0x1) << 2) | + ((cl_mask_table[cl_value] & 0xe) << 3); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, MR0_REG, + val, (0x7 << 4) | (1 << 2))); + /* MR2: CWL = 10 , Auto Self-Refresh - disable */ + val = (cwl_mask_table[cwl_value] << 3); + /* + * nklein 24.10.13 - should not be here - leave value as set in + * the init configuration val |= (1 << 9); + * val |= ((tm->interface_params[if_id]. + * interface_temp == HWS_TEMP_HIGH) ? (1 << 7) : 0); + */ + /* nklein 24.10.13 - see above comment */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, MR2_REG, + val, (0x7 << 3))); + + /* ODT TIMING */ + val = ((cl_value - cwl_value + 1) << 4) | + ((cl_value - cwl_value + 6) << 8) | + ((cl_value - 1) << 12) | ((cl_value + 6) << 16); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, ODT_TIMING_LOW, + val, 0xffff0)); + val = 0x71 | ((cwl_value - 1) << 8) | ((cwl_value + 5) << 12); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, ODT_TIMING_HI_REG, + val, 0xffff)); + + /* ODT Active */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, + DUNIT_ODT_CONTROL_REG, + 0xf, 0xf)); + + /* re-write CL */ + val = ((cl_mask_table[cl_value] & 0x1) << 2) | + ((cl_mask_table[cl_value] & 0xe) << 3); + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + 0, MR0_REG, val, + (0x7 << 4) | (1 << 2))); + + /* re-write CWL */ + val = (cwl_mask_table[cwl_value] << 3); + CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask, MRS2_CMD, + val, (0x7 << 3))); + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + 0, MR2_REG, val, (0x7 << 3))); + + if (mem_mask != 0) { + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, + CS_ENABLE_REG, + 1 << 3, 0x8)); + } + } + + return MV_OK; +} + +/* + * Set ODT values + */ +static int ddr3_tip_write_odt(u32 dev_num, enum hws_access_type access_type, + u32 if_id, u32 cl_value, u32 cwl_value) +{ + /* ODT TIMING */ + u32 val = (cl_value - cwl_value + 6); + + val = ((cl_value - cwl_value + 1) << 4) | ((val & 0xf) << 8) | + (((cl_value - 1) & 0xf) << 12) | + (((cl_value + 6) & 0xf) << 16) | (((val & 0x10) >> 4) << 21); + val |= (((cl_value - 1) >> 4) << 22) | (((cl_value + 6) >> 4) << 23); + + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODT_TIMING_LOW, val, 0xffff0)); + val = 0x71 | ((cwl_value - 1) << 8) | ((cwl_value + 5) << 12); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODT_TIMING_HI_REG, val, 0xffff)); + if (odt_additional == 1) { + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, + if_id, + SDRAM_ODT_CONTROL_HIGH_REG, + 0xf, 0xf)); + } + + /* ODT Active */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + DUNIT_ODT_CONTROL_REG, 0xf, 0xf)); + + return MV_OK; +} + +/* + * Set Timing values for training + */ +static int ddr3_tip_set_timing(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_ddr_freq frequency) +{ + u32 t_ckclk = 0, t_ras = 0; + u32 t_rcd = 0, t_rp = 0, t_wr = 0, t_wtr = 0, t_rrd = 0, t_rtp = 0, + t_rfc = 0, t_mod = 0; + u32 val = 0, page_size = 0; + enum hws_speed_bin speed_bin_index; + enum hws_mem_size memory_size = MEM_2G; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + speed_bin_index = tm->interface_params[if_id].speed_bin_index; + memory_size = tm->interface_params[if_id].memory_size; + page_size = + (tm->interface_params[if_id].bus_width == + BUS_WIDTH_8) ? page_param[memory_size]. + page_size_8bit : page_param[memory_size].page_size_16bit; + t_ckclk = (MEGA / freq_val[frequency]); + t_rrd = (page_size == 1) ? speed_bin_table(speed_bin_index, + SPEED_BIN_TRRD1K) : + speed_bin_table(speed_bin_index, SPEED_BIN_TRRD2K); + t_rrd = GET_MAX_VALUE(t_ckclk * 4, t_rrd); + t_rtp = GET_MAX_VALUE(t_ckclk * 4, speed_bin_table(speed_bin_index, + SPEED_BIN_TRTP)); + t_wtr = GET_MAX_VALUE(t_ckclk * 4, speed_bin_table(speed_bin_index, + SPEED_BIN_TWTR)); + t_ras = TIME_2_CLOCK_CYCLES(speed_bin_table(speed_bin_index, + SPEED_BIN_TRAS), + t_ckclk); + t_rcd = TIME_2_CLOCK_CYCLES(speed_bin_table(speed_bin_index, + SPEED_BIN_TRCD), + t_ckclk); + t_rp = TIME_2_CLOCK_CYCLES(speed_bin_table(speed_bin_index, + SPEED_BIN_TRP), + t_ckclk); + t_wr = TIME_2_CLOCK_CYCLES(speed_bin_table(speed_bin_index, + SPEED_BIN_TWR), + t_ckclk); + t_wtr = TIME_2_CLOCK_CYCLES(t_wtr, t_ckclk); + t_rrd = TIME_2_CLOCK_CYCLES(t_rrd, t_ckclk); + t_rtp = TIME_2_CLOCK_CYCLES(t_rtp, t_ckclk); + t_rfc = TIME_2_CLOCK_CYCLES(rfc_table[memory_size] * 1000, t_ckclk); + t_mod = GET_MAX_VALUE(t_ckclk * 24, 15000); + t_mod = TIME_2_CLOCK_CYCLES(t_mod, t_ckclk); + + /* SDRAM Timing Low */ + val = (t_ras & 0xf) | (t_rcd << 4) | (t_rp << 8) | (t_wr << 12) | + (t_wtr << 16) | (((t_ras & 0x30) >> 4) << 20) | (t_rrd << 24) | + (t_rtp << 28); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_LOW_REG, val, 0xff3fffff)); + + /* SDRAM Timing High */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + t_rfc & 0x7f, 0x7f)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + 0x180, 0x180)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + 0x600, 0x600)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + 0x1800, 0xf800)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + ((t_rfc & 0x380) >> 7) << 16, 0x70000)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, 0, + 0x380000)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + (t_mod & 0xf) << 25, 0x1e00000)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + (t_mod >> 4) << 30, 0xc0000000)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + 0x16000000, 0x1e000000)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + SDRAM_TIMING_HIGH_REG, + 0x40000000, 0xc0000000)); + + return MV_OK; +} + +/* + * Mode Read + */ +int hws_ddr3_tip_mode_read(u32 dev_num, struct mode_info *mode_info) +{ + u32 ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + MR0_REG, mode_info->reg_mr0, MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + MR1_REG, mode_info->reg_mr1, MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + MR2_REG, mode_info->reg_mr2, MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + MR3_REG, mode_info->reg_mr2, MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + READ_DATA_SAMPLE_DELAY, mode_info->read_data_sample, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + READ_DATA_READY_DELAY, mode_info->read_data_ready, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + + return MV_OK; +} + +/* + * Get first active IF + */ +int ddr3_tip_get_first_active_if(u8 dev_num, u32 interface_mask, + u32 *interface_id) +{ + u32 if_id; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (interface_mask & (1 << if_id)) { + *interface_id = if_id; + break; + } + } + + return MV_OK; +} + +/* + * Write CS Result + */ +int ddr3_tip_write_cs_result(u32 dev_num, u32 offset) +{ + u32 if_id, bus_num, cs_bitmask, data_val, cs_num; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_num = 0; bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + cs_bitmask = + tm->interface_params[if_id]. + as_bus_params[bus_num].cs_bitmask; + if (cs_bitmask != effective_cs) { + cs_num = GET_CS_FROM_MASK(cs_bitmask); + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_num, + DDR_PHY_DATA, + offset + + CS_REG_VALUE(effective_cs), + &data_val); + ddr3_tip_bus_write(dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_num, DDR_PHY_DATA, + offset + + CS_REG_VALUE(cs_num), + data_val); + } + } + } + + return MV_OK; +} + +/* + * Write MRS + */ +int ddr3_tip_write_mrs_cmd(u32 dev_num, u32 *cs_mask_arr, u32 cmd, + u32 data, u32 mask) +{ + u32 if_id, reg; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + reg = (cmd == MRS1_CMD) ? MR1_REG : MR2_REG; + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, reg, data, mask)); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + SDRAM_OPERATION_REG, + (cs_mask_arr[if_id] << 8) | cmd, 0xf1f)); + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (ddr3_tip_if_polling(dev_num, ACCESS_TYPE_UNICAST, if_id, 0, + 0x1f, SDRAM_OPERATION_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("write_mrs_cmd: Poll cmd fail")); + } + } + + return MV_OK; +} + +/* + * Reset XSB Read FIFO + */ +int ddr3_tip_reset_fifo_ptr(u32 dev_num) +{ + u32 if_id = 0; + + /* Configure PHY reset value to 0 in order to "clean" the FIFO */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, 0x15c8, 0, 0xff000000)); + /* + * Move PHY to RL mode (only in RL mode the PHY overrides FIFO values + * during FIFO reset) + */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, TRAINING_SW_2_REG, + 0x1, 0x9)); + /* In order that above configuration will influence the PHY */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, 0x15b0, + 0x80000000, 0x80000000)); + /* Reset read fifo assertion */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, 0x1400, 0, 0x40000000)); + /* Reset read fifo deassertion */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, 0x1400, + 0x40000000, 0x40000000)); + /* Move PHY back to functional mode */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, TRAINING_SW_2_REG, + 0x8, 0x9)); + /* Stop training machine */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + if_id, 0x15b4, 0x10000, 0x10000)); + + return MV_OK; +} + +/* + * Reset Phy registers + */ +int ddr3_tip_ddr3_reset_phy_regs(u32 dev_num) +{ + u32 if_id, phy_id, cs; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (phy_id = 0; phy_id < tm->num_of_bus_per_interface; + phy_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, phy_id); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + phy_id, DDR_PHY_DATA, + WL_PHY_REG + + CS_REG_VALUE(effective_cs), + phy_reg0_val)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA, + RL_PHY_REG + CS_REG_VALUE(effective_cs), + phy_reg2_val)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG + + CS_REG_VALUE(effective_cs), phy_reg3_val)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, phy_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + CS_REG_VALUE(effective_cs), phy_reg3_val)); + } + } + + /* Set Receiver Calibration value */ + for (cs = 0; cs < MAX_CS_NUM; cs++) { + /* PHY register 0xdb bits[5:0] - configure to 63 */ + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + DDR_PHY_DATA, CSN_IOB_VREF_REG(cs), 63)); + } + + return MV_OK; +} + +/* + * Restore Dunit registers + */ +int ddr3_tip_restore_dunit_regs(u32 dev_num) +{ + u32 index_cnt; + + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, CALIB_MACHINE_CTRL_REG, + 0x1, 0x1)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, CALIB_MACHINE_CTRL_REG, + calibration_update_control << 3, + 0x3 << 3)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + ODPG_WRITE_READ_MODE_ENABLE_REG, + 0xffff, MASK_ALL_BITS)); + + for (index_cnt = 0; index_cnt < ARRAY_SIZE(odpg_default_value); + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + odpg_default_value[index_cnt].reg_addr, + odpg_default_value[index_cnt].reg_data, + odpg_default_value[index_cnt].reg_mask)); + } + + return MV_OK; +} + +/* + * Auto tune main flow + */ +static int ddr3_tip_ddr3_training_main_flow(u32 dev_num) +{ + enum hws_ddr_freq freq = init_freq; + struct init_cntr_param init_cntr_prm; + int ret = MV_OK; + u32 if_id; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + +#ifndef EXCLUDE_SWITCH_DEBUG + if (debug_training == DEBUG_LEVEL_TRACE) { + CHECK_STATUS(print_device_info((u8)dev_num)); + } +#endif + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + CHECK_STATUS(ddr3_tip_ddr3_reset_phy_regs(dev_num)); + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + freq = init_freq; + if (is_pll_before_init != 0) { + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + config_func_info[dev_num].tip_set_freq_divider_func( + (u8)dev_num, if_id, freq); + } + } + + if (is_adll_calib_before_init != 0) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("with adll calib before init\n")); + adll_calibration(dev_num, ACCESS_TYPE_MULTICAST, 0, freq); + } + + if (is_reg_dump != 0) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("Dump before init controller\n")); + ddr3_tip_reg_dump(dev_num); + } + + if (mask_tune_func & INIT_CONTROLLER_MASK_BIT) { + training_stage = INIT_CONTROLLER; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("INIT_CONTROLLER_MASK_BIT\n")); + init_cntr_prm.do_mrs_phy = 1; + init_cntr_prm.is_ctrl64_bit = 0; + init_cntr_prm.init_phy = 1; + init_cntr_prm.msys_init = 0; + ret = hws_ddr3_tip_init_controller(dev_num, &init_cntr_prm); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("hws_ddr3_tip_init_controller failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + +#ifdef STATIC_ALGO_SUPPORT + if (mask_tune_func & STATIC_LEVELING_MASK_BIT) { + training_stage = STATIC_LEVELING; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("STATIC_LEVELING_MASK_BIT\n")); + ret = ddr3_tip_run_static_alg(dev_num, freq); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_run_static_alg failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } +#endif + + if (mask_tune_func & SET_LOW_FREQ_MASK_BIT) { + training_stage = SET_LOW_FREQ; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("SET_LOW_FREQ_MASK_BIT %d\n", + freq_val[low_freq])); + ret = ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, low_freq); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_freq_set failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & LOAD_PATTERN_MASK_BIT) { + training_stage = LOAD_PATTERN; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("LOAD_PATTERN_MASK_BIT #%d\n", + effective_cs)); + ret = ddr3_tip_load_all_pattern_to_mem(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_load_all_pattern_to_mem failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + if (mask_tune_func & SET_MEDIUM_FREQ_MASK_BIT) { + training_stage = SET_MEDIUM_FREQ; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("SET_MEDIUM_FREQ_MASK_BIT %d\n", + freq_val[medium_freq])); + ret = + ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, medium_freq); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_freq_set failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & WRITE_LEVELING_MASK_BIT) { + training_stage = WRITE_LEVELING; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("WRITE_LEVELING_MASK_BIT\n")); + if ((rl_mid_freq_wa == 0) || (freq_val[medium_freq] == 533)) { + ret = ddr3_tip_dynamic_write_leveling(dev_num); + } else { + /* Use old WL */ + ret = ddr3_tip_legacy_dynamic_write_leveling(dev_num); + } + + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_write_leveling failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & LOAD_PATTERN_2_MASK_BIT) { + training_stage = LOAD_PATTERN_2; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("LOAD_PATTERN_2_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_load_all_pattern_to_mem(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_load_all_pattern_to_mem failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + if (mask_tune_func & READ_LEVELING_MASK_BIT) { + training_stage = READ_LEVELING; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("READ_LEVELING_MASK_BIT\n")); + if ((rl_mid_freq_wa == 0) || (freq_val[medium_freq] == 533)) { + ret = ddr3_tip_dynamic_read_leveling(dev_num, medium_freq); + } else { + /* Use old RL */ + ret = ddr3_tip_legacy_dynamic_read_leveling(dev_num); + } + + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_read_leveling failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & WRITE_LEVELING_SUPP_MASK_BIT) { + training_stage = WRITE_LEVELING_SUPP; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("WRITE_LEVELING_SUPP_MASK_BIT\n")); + ret = ddr3_tip_dynamic_write_leveling_supp(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_write_leveling_supp failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & PBS_RX_MASK_BIT) { + training_stage = PBS_RX; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("PBS_RX_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_pbs_rx(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_pbs_rx failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & PBS_TX_MASK_BIT) { + training_stage = PBS_TX; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("PBS_TX_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_pbs_tx(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_pbs_tx failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + if (mask_tune_func & SET_TARGET_FREQ_MASK_BIT) { + training_stage = SET_TARGET_FREQ; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("SET_TARGET_FREQ_MASK_BIT %d\n", + freq_val[tm-> + interface_params[first_active_if]. + memory_freq])); + ret = ddr3_tip_freq_set(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + tm->interface_params[first_active_if]. + memory_freq); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_freq_set failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & WRITE_LEVELING_TF_MASK_BIT) { + training_stage = WRITE_LEVELING_TF; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("WRITE_LEVELING_TF_MASK_BIT\n")); + ret = ddr3_tip_dynamic_write_leveling(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_write_leveling TF failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & LOAD_PATTERN_HIGH_MASK_BIT) { + training_stage = LOAD_PATTERN_HIGH; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("LOAD_PATTERN_HIGH\n")); + ret = ddr3_tip_load_all_pattern_to_mem(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_load_all_pattern_to_mem failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & READ_LEVELING_TF_MASK_BIT) { + training_stage = READ_LEVELING_TF; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("READ_LEVELING_TF_MASK_BIT\n")); + ret = ddr3_tip_dynamic_read_leveling(dev_num, tm-> + interface_params[first_active_if]. + memory_freq); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_read_leveling TF failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + + if (mask_tune_func & DM_PBS_TX_MASK_BIT) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("DM_PBS_TX_MASK_BIT\n")); + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & VREF_CALIBRATION_MASK_BIT) { + training_stage = VREF_CALIBRATION; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("VREF\n")); + ret = ddr3_tip_vref(dev_num); + if (is_reg_dump != 0) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("VREF Dump\n")); + ddr3_tip_reg_dump(dev_num); + } + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_vref failure\n")); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & CENTRALIZATION_RX_MASK_BIT) { + training_stage = CENTRALIZATION_RX; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("CENTRALIZATION_RX_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_centralization_rx(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_centralization_rx failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & WRITE_LEVELING_SUPP_TF_MASK_BIT) { + training_stage = WRITE_LEVELING_SUPP_TF; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("WRITE_LEVELING_SUPP_TF_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_dynamic_write_leveling_supp(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_dynamic_write_leveling_supp TF failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + if (mask_tune_func & CENTRALIZATION_TX_MASK_BIT) { + training_stage = CENTRALIZATION_TX; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("CENTRALIZATION_TX_MASK_BIT CS #%d\n", + effective_cs)); + ret = ddr3_tip_centralization_tx(dev_num); + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + if (ret != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("ddr3_tip_centralization_tx failure CS #%d\n", + effective_cs)); + if (debug_mode == 0) + return MV_FAIL; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, ("restore registers to default\n")); + /* restore register values */ + CHECK_STATUS(ddr3_tip_restore_dunit_regs(dev_num)); + + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + + return MV_OK; +} + +/* + * DDR3 Dynamic training flow + */ +static int ddr3_tip_ddr3_auto_tune(u32 dev_num) +{ + u32 if_id, stage, ret; + int is_if_fail = 0, is_auto_tune_fail = 0; + + training_stage = INIT_CONTROLLER; + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + for (stage = 0; stage < MAX_STAGE_LIMIT; stage++) + training_result[stage][if_id] = NO_TEST_DONE; + } + + ret = ddr3_tip_ddr3_training_main_flow(dev_num); + + /* activate XSB test */ + if (xsb_validate_type != 0) { + run_xsb_test(dev_num, xsb_validation_base_address, 1, 1, + 0x1024); + } + + if (is_reg_dump != 0) + ddr3_tip_reg_dump(dev_num); + + /* print log */ + CHECK_STATUS(ddr3_tip_print_log(dev_num, window_mem_addr)); + + if (ret != MV_OK) { + CHECK_STATUS(ddr3_tip_print_stability_log(dev_num)); + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + is_if_fail = 0; + for (stage = 0; stage < MAX_STAGE_LIMIT; stage++) { + if (training_result[stage][if_id] == TEST_FAILED) + is_if_fail = 1; + } + if (is_if_fail == 1) { + is_auto_tune_fail = 1; + DEBUG_TRAINING_IP(DEBUG_LEVEL_INFO, + ("Auto Tune failed for IF %d\n", + if_id)); + } + } + + if ((ret == MV_FAIL) || (is_auto_tune_fail == 1)) + return MV_FAIL; + else + return MV_OK; +} + +/* + * Enable init sequence + */ +int ddr3_tip_enable_init_sequence(u32 dev_num) +{ + int is_fail = 0; + u32 if_id = 0, mem_mask = 0, bus_index = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* Enable init sequence */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_MULTICAST, 0, + SDRAM_INIT_CONTROL_REG, 0x1, 0x1)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1, + SDRAM_INIT_CONTROL_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("polling failed IF %d\n", + if_id)); + is_fail = 1; + continue; + } + + mem_mask = 0; + for (bus_index = 0; bus_index < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + mem_mask |= + tm->interface_params[if_id]. + as_bus_params[bus_index].mirror_enable_bitmask; + } + + if (mem_mask != 0) { + /* Disable Multi CS */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, + if_id, CS_ENABLE_REG, 1 << 3, + 1 << 3)); + } + } + + return (is_fail == 0) ? MV_OK : MV_FAIL; +} + +int ddr3_tip_register_dq_table(u32 dev_num, u32 *table) +{ + dq_map_table = table; + + return MV_OK; +} + +/* + * Check if pup search is locked + */ +int ddr3_tip_is_pup_lock(u32 *pup_buf, enum hws_training_result read_mode) +{ + u32 bit_start = 0, bit_end = 0, bit_id; + + if (read_mode == RESULT_PER_BIT) { + bit_start = 0; + bit_end = BUS_WIDTH_IN_BITS - 1; + } else { + bit_start = 0; + bit_end = 0; + } + + for (bit_id = bit_start; bit_id <= bit_end; bit_id++) { + if (GET_LOCK_RESULT(pup_buf[bit_id]) == 0) + return 0; + } + + return 1; +} + +/* + * Get minimum buffer value + */ +u8 ddr3_tip_get_buf_min(u8 *buf_ptr) +{ + u8 min_val = 0xff; + u8 cnt = 0; + + for (cnt = 0; cnt < BUS_WIDTH_IN_BITS; cnt++) { + if (buf_ptr[cnt] < min_val) + min_val = buf_ptr[cnt]; + } + + return min_val; +} + +/* + * Get maximum buffer value + */ +u8 ddr3_tip_get_buf_max(u8 *buf_ptr) +{ + u8 max_val = 0; + u8 cnt = 0; + + for (cnt = 0; cnt < BUS_WIDTH_IN_BITS; cnt++) { + if (buf_ptr[cnt] > max_val) + max_val = buf_ptr[cnt]; + } + + return max_val; +} + +/* + * The following functions return memory parameters: + * bus and device width, device size + */ + +u32 hws_ddr3_get_bus_width(void) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + return (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == + 1) ? 16 : 32; +} + +u32 hws_ddr3_get_device_width(u32 if_id) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + return (tm->interface_params[if_id].bus_width == + BUS_WIDTH_8) ? 8 : 16; +} + +u32 hws_ddr3_get_device_size(u32 if_id) +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (tm->interface_params[if_id].memory_size >= + MEM_SIZE_LAST) { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Error: Wrong device size of Cs: %d", + tm->interface_params[if_id].memory_size)); + return 0; + } else { + return 1 << tm->interface_params[if_id].memory_size; + } +} + +int hws_ddr3_calc_mem_cs_size(u32 if_id, u32 cs, u32 *cs_size) +{ + u32 cs_mem_size, dev_size; + + dev_size = hws_ddr3_get_device_size(if_id); + if (dev_size != 0) { + cs_mem_size = ((hws_ddr3_get_bus_width() / + hws_ddr3_get_device_width(if_id)) * dev_size); + + /* the calculated result in Gbytex16 to avoid float using */ + + if (cs_mem_size == 2) { + *cs_size = _128M; + } else if (cs_mem_size == 4) { + *cs_size = _256M; + } else if (cs_mem_size == 8) { + *cs_size = _512M; + } else if (cs_mem_size == 16) { + *cs_size = _1G; + } else if (cs_mem_size == 32) { + *cs_size = _2G; + } else { + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Error: Wrong Memory size of Cs: %d", cs)); + return MV_FAIL; + } + return MV_OK; + } else { + return MV_FAIL; + } +} + +int hws_ddr3_cs_base_adr_calc(u32 if_id, u32 cs, u32 *cs_base_addr) +{ + u32 cs_mem_size = 0; +#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE + u32 physical_mem_size; + u32 max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE; +#endif + + if (hws_ddr3_calc_mem_cs_size(if_id, cs, &cs_mem_size) != MV_OK) + return MV_FAIL; + +#ifdef DEVICE_MAX_DRAM_ADDRESS_SIZE + struct hws_topology_map *tm = ddr3_get_topology_map(); + /* + * if number of address pins doesn't allow to use max mem size that + * is defined in topology mem size is defined by + * DEVICE_MAX_DRAM_ADDRESS_SIZE + */ + physical_mem_size = + mv_hwsmem_size[tm->interface_params[0].memory_size]; + + if (hws_ddr3_get_device_width(cs) == 16) { + /* + * 16bit mem device can be twice more - no need in less + * significant pin + */ + max_mem_size = DEVICE_MAX_DRAM_ADDRESS_SIZE * 2; + } + + if (physical_mem_size > max_mem_size) { + cs_mem_size = max_mem_size * + (hws_ddr3_get_bus_width() / + hws_ddr3_get_device_width(if_id)); + DEBUG_TRAINING_IP(DEBUG_LEVEL_ERROR, + ("Updated Physical Mem size is from 0x%x to %x\n", + physical_mem_size, + DEVICE_MAX_DRAM_ADDRESS_SIZE)); + } +#endif + + /* calculate CS base addr */ + *cs_base_addr = ((cs_mem_size) * cs) & 0xffff0000; + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_bist.c b/drivers/ddr/marvell/a38x/ddr3_training_bist.c new file mode 100644 index 0000000..bd0e260 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_bist.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +static u32 bist_offset = 32; +enum hws_pattern sweep_pattern = PATTERN_KILLER_DQ0; + +static int ddr3_tip_bist_operation(u32 dev_num, + enum hws_access_type access_type, + u32 if_id, + enum hws_bist_operation oper_type); + +/* + * BIST activate + */ +int ddr3_tip_bist_activate(u32 dev_num, enum hws_pattern pattern, + enum hws_access_type access_type, u32 if_num, + enum hws_dir direction, + enum hws_stress_jump addr_stress_jump, + enum hws_pattern_duration duration, + enum hws_bist_operation oper_type, + u32 offset, u32 cs_num, u32 pattern_addr_length) +{ + u32 tx_burst_size; + u32 delay_between_burst; + u32 rd_mode, val; + u32 poll_cnt = 0, max_poll = 1000, i, start_if, end_if; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + u32 read_data[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* ODPG Write enable from BIST */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_num, + ODPG_DATA_CONTROL_REG, 0x1, 0x1)); + /* ODPG Read enable/disable from BIST */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_num, + ODPG_DATA_CONTROL_REG, + (direction == OPER_READ) ? + 0x2 : 0, 0x2)); + CHECK_STATUS(ddr3_tip_load_pattern_to_odpg(dev_num, access_type, if_num, + pattern, offset)); + + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_num, + ODPG_DATA_BUF_SIZE_REG, + pattern_addr_length, MASK_ALL_BITS)); + tx_burst_size = (direction == OPER_WRITE) ? + pattern_table[pattern].tx_burst_size : 0; + delay_between_burst = (direction == OPER_WRITE) ? 2 : 0; + rd_mode = (direction == OPER_WRITE) ? 1 : 0; + CHECK_STATUS(ddr3_tip_configure_odpg + (dev_num, access_type, if_num, direction, + pattern_table[pattern].num_of_phases_tx, tx_burst_size, + pattern_table[pattern].num_of_phases_rx, + delay_between_burst, + rd_mode, cs_num, addr_stress_jump, duration)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_num, + ODPG_PATTERN_ADDR_OFFSET_REG, + offset, MASK_ALL_BITS)); + if (oper_type == BIST_STOP) { + CHECK_STATUS(ddr3_tip_bist_operation(dev_num, access_type, + if_num, BIST_STOP)); + } else { + CHECK_STATUS(ddr3_tip_bist_operation(dev_num, access_type, + if_num, BIST_START)); + if (duration != DURATION_CONT) { + /* + * This pdelay is a WA, becuase polling fives "done" + * also the odpg did nmot finish its task + */ + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = if_num; + end_if = if_num; + } + + for (i = start_if; i <= end_if; i++) { + VALIDATE_ACTIVE(tm-> + if_act_mask, i); + + for (poll_cnt = 0; poll_cnt < max_poll; + poll_cnt++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_num, ODPG_BIST_DONE, + read_data, + MASK_ALL_BITS)); + val = read_data[i]; + if ((val & 0x1) == 0x0) { + /* + * In SOC type devices this bit + * is self clear so, if it was + * cleared all good + */ + break; + } + } + + if (poll_cnt >= max_poll) { + DEBUG_TRAINING_BIST_ENGINE + (DEBUG_LEVEL_ERROR, + ("Bist poll failure 2\n")); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_num, + ODPG_DATA_CONTROL_REG, 0, + MASK_ALL_BITS)); + return MV_FAIL; + } + } + + CHECK_STATUS(ddr3_tip_bist_operation + (dev_num, access_type, if_num, BIST_STOP)); + } + } + + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_num, + ODPG_DATA_CONTROL_REG, 0, + MASK_ALL_BITS)); + + return MV_OK; +} + +/* + * BIST read result + */ +int ddr3_tip_bist_read_result(u32 dev_num, u32 if_id, + struct bist_result *pst_bist_result) +{ + int ret; + u32 read_data[MAX_INTERFACE_NUM]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (IS_ACTIVE(tm->if_act_mask, if_id) == 0) + return MV_NOT_SUPPORTED; + DEBUG_TRAINING_BIST_ENGINE(DEBUG_LEVEL_TRACE, + ("ddr3_tip_bist_read_result if_id %d\n", + if_id)); + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_BIST_FAILED_DATA_HI_REG, read_data, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + pst_bist_result->bist_fail_high = read_data[if_id]; + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_BIST_FAILED_DATA_LOW_REG, read_data, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + pst_bist_result->bist_fail_low = read_data[if_id]; + + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_BIST_LAST_FAIL_ADDR_REG, read_data, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + pst_bist_result->bist_last_fail_addr = read_data[if_id]; + ret = ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_BIST_DATA_ERROR_COUNTER_REG, read_data, + MASK_ALL_BITS); + if (ret != MV_OK) + return ret; + pst_bist_result->bist_error_cnt = read_data[if_id]; + + return MV_OK; +} + +/* + * BIST flow - Activate & read result + */ +int hws_ddr3_run_bist(u32 dev_num, enum hws_pattern pattern, u32 *result, + u32 cs_num) +{ + int ret; + u32 i = 0; + u32 win_base; + struct bist_result st_bist_result; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (i = 0; i < MAX_INTERFACE_NUM; i++) { + VALIDATE_ACTIVE(tm->if_act_mask, i); + hws_ddr3_cs_base_adr_calc(i, cs_num, &win_base); + ret = ddr3_tip_bist_activate(dev_num, pattern, + ACCESS_TYPE_UNICAST, + i, OPER_WRITE, STRESS_NONE, + DURATION_SINGLE, BIST_START, + bist_offset + win_base, + cs_num, 15); + if (ret != MV_OK) { + printf("ddr3_tip_bist_activate failed (0x%x)\n", ret); + return ret; + } + + ret = ddr3_tip_bist_activate(dev_num, pattern, + ACCESS_TYPE_UNICAST, + i, OPER_READ, STRESS_NONE, + DURATION_SINGLE, BIST_START, + bist_offset + win_base, + cs_num, 15); + if (ret != MV_OK) { + printf("ddr3_tip_bist_activate failed (0x%x)\n", ret); + return ret; + } + + ret = ddr3_tip_bist_read_result(dev_num, i, &st_bist_result); + if (ret != MV_OK) { + printf("ddr3_tip_bist_read_result failed\n"); + return ret; + } + result[i] = st_bist_result.bist_error_cnt; + } + + return MV_OK; +} + +/* + * Set BIST Operation + */ + +static int ddr3_tip_bist_operation(u32 dev_num, + enum hws_access_type access_type, + u32 if_id, enum hws_bist_operation oper_type) +{ + if (oper_type == BIST_STOP) { + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODPG_BIST_DONE, 1 << 8, 1 << 8)); + } else { + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODPG_BIST_DONE, 1, 1)); + } + + return MV_OK; +} + +/* + * Print BIST result + */ +void ddr3_tip_print_bist_res(void) +{ + u32 dev_num = 0; + u32 i; + struct bist_result st_bist_result[MAX_INTERFACE_NUM]; + int res; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (i = 0; i < MAX_INTERFACE_NUM; i++) { + if (IS_ACTIVE(tm->if_act_mask, i) == 0) + continue; + + res = ddr3_tip_bist_read_result(dev_num, i, &st_bist_result[i]); + if (res != MV_OK) { + DEBUG_TRAINING_BIST_ENGINE( + DEBUG_LEVEL_ERROR, + ("ddr3_tip_bist_read_result failed\n")); + return; + } + } + + DEBUG_TRAINING_BIST_ENGINE( + DEBUG_LEVEL_INFO, + ("interface | error_cnt | fail_low | fail_high | fail_addr\n")); + + for (i = 0; i < MAX_INTERFACE_NUM; i++) { + if (IS_ACTIVE(tm->if_act_mask, i) == + 0) + continue; + + DEBUG_TRAINING_BIST_ENGINE( + DEBUG_LEVEL_INFO, + ("%d | 0x%08x | 0x%08x | 0x%08x | 0x%08x\n", + i, st_bist_result[i].bist_error_cnt, + st_bist_result[i].bist_fail_low, + st_bist_result[i].bist_fail_high, + st_bist_result[i].bist_last_fail_addr)); + } +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_centralization.c b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c new file mode 100644 index 0000000..9d216da --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_centralization.c @@ -0,0 +1,714 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define VALIDATE_WIN_LENGTH(e1, e2, maxsize) \ + (((e2) + 1 > (e1) + (u8)MIN_WINDOW_SIZE) && \ + ((e2) + 1 < (e1) + (u8)maxsize)) +#define IS_WINDOW_OUT_BOUNDARY(e1, e2, maxsize) \ + (((e1) == 0 && (e2) != 0) || \ + ((e1) != (maxsize - 1) && (e2) == (maxsize - 1))) +#define CENTRAL_TX 0 +#define CENTRAL_RX 1 +#define NUM_OF_CENTRAL_TYPES 2 + +u32 start_pattern = PATTERN_KILLER_DQ0, end_pattern = PATTERN_KILLER_DQ7; +u32 start_if = 0, end_if = (MAX_INTERFACE_NUM - 1); +u8 bus_end_window[NUM_OF_CENTRAL_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 bus_start_window[NUM_OF_CENTRAL_TYPES][MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 centralization_state[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +static u8 ddr3_tip_special_rx_run_once_flag; + +static int ddr3_tip_centralization(u32 dev_num, u32 mode); + +/* + * Centralization RX Flow + */ +int ddr3_tip_centralization_rx(u32 dev_num) +{ + CHECK_STATUS(ddr3_tip_special_rx(dev_num)); + CHECK_STATUS(ddr3_tip_centralization(dev_num, CENTRAL_RX)); + + return MV_OK; +} + +/* + * Centralization TX Flow + */ +int ddr3_tip_centralization_tx(u32 dev_num) +{ + CHECK_STATUS(ddr3_tip_centralization(dev_num, CENTRAL_TX)); + + return MV_OK; +} + +/* + * Centralization Flow + */ +static int ddr3_tip_centralization(u32 dev_num, u32 mode) +{ + enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM]; + u32 if_id, pattern_id, bit_id; + u8 bus_id; + u8 cur_start_win[BUS_WIDTH_IN_BITS]; + u8 centralization_result[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS]; + u8 cur_end_win[BUS_WIDTH_IN_BITS]; + u8 current_window[BUS_WIDTH_IN_BITS]; + u8 opt_window, waste_window, start_window_skew, end_window_skew; + u8 final_pup_window[MAX_INTERFACE_NUM][BUS_WIDTH_IN_BITS]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + enum hws_training_result result_type = RESULT_PER_BIT; + enum hws_dir direction; + u32 *result[HWS_SEARCH_DIR_LIMIT]; + u32 reg_phy_off, reg; + u8 max_win_size; + int lock_success = 1; + u8 cur_end_win_min, cur_start_win_max; + u32 cs_enable_reg_val[MAX_INTERFACE_NUM]; + int is_if_fail = 0; + enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage); + u32 pup_win_length = 0; + enum hws_search_dir search_dir_id; + u8 cons_tap = (mode == CENTRAL_TX) ? (64) : (0); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* save current cs enable reg val */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val, MASK_ALL_BITS)); + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, (1 << 3), (1 << 3))); + } + + if (mode == CENTRAL_TX) { + max_win_size = MAX_WINDOW_SIZE_TX; + reg_phy_off = WRITE_CENTRALIZATION_PHY_REG + (effective_cs * 4); + direction = OPER_WRITE; + } else { + max_win_size = MAX_WINDOW_SIZE_RX; + reg_phy_off = READ_CENTRALIZATION_PHY_REG + (effective_cs * 4); + direction = OPER_READ; + } + + /* DB initialization */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; + bus_id < tm->num_of_bus_per_interface; bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + centralization_state[if_id][bus_id] = 0; + bus_end_window[mode][if_id][bus_id] = + (max_win_size - 1) + cons_tap; + bus_start_window[mode][if_id][bus_id] = 0; + centralization_result[if_id][bus_id] = 0; + } + } + + /* start flow */ + for (pattern_id = start_pattern; pattern_id <= end_pattern; + pattern_id++) { + ddr3_tip_ip_training_wrapper(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, + HWS_CONTROL_ELEMENT_ADLL, + PARAM_NOT_CARE, direction, + tm-> + if_act_mask, 0x0, + max_win_size - 1, + max_win_size - 1, + pattern_id, EDGE_FPF, CS_SINGLE, + PARAM_NOT_CARE, training_result); + + for (if_id = start_if; if_id <= end_if; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; + bus_id <= tm->num_of_bus_per_interface - 1; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + + for (search_dir_id = HWS_LOW2HIGH; + search_dir_id <= HWS_HIGH2LOW; + search_dir_id++) { + CHECK_STATUS + (ddr3_tip_read_training_result + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + ALL_BITS_PER_PUP, + search_dir_id, + direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, + &result[search_dir_id], + 1, 0, 0)); + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("%s pat %d IF %d pup %d Regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + ((mode == + CENTRAL_TX) ? "TX" : "RX"), + pattern_id, if_id, bus_id, + result[search_dir_id][0], + result[search_dir_id][1], + result[search_dir_id][2], + result[search_dir_id][3], + result[search_dir_id][4], + result[search_dir_id][5], + result[search_dir_id][6], + result[search_dir_id][7])); + } + + for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS; + bit_id++) { + /* check if this code is valid for 2 edge, probably not :( */ + cur_start_win[bit_id] = + GET_TAP_RESULT(result + [HWS_LOW2HIGH] + [bit_id], + EDGE_1); + cur_end_win[bit_id] = + GET_TAP_RESULT(result + [HWS_HIGH2LOW] + [bit_id], + EDGE_1); + /* window length */ + current_window[bit_id] = + cur_end_win[bit_id] - + cur_start_win[bit_id] + 1; + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_TRACE, + ("cs %x patern %d IF %d pup %d cur_start_win %d cur_end_win %d current_window %d\n", + effective_cs, pattern_id, + if_id, bus_id, + cur_start_win[bit_id], + cur_end_win[bit_id], + current_window[bit_id])); + } + + if ((ddr3_tip_is_pup_lock + (result[HWS_LOW2HIGH], result_type)) && + (ddr3_tip_is_pup_lock + (result[HWS_HIGH2LOW], result_type))) { + /* read result success */ + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("Pup locked, pat %d IF %d pup %d\n", + pattern_id, if_id, bus_id)); + } else { + /* read result failure */ + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("fail Lock, pat %d IF %d pup %d\n", + pattern_id, if_id, bus_id)); + if (centralization_state[if_id][bus_id] + == 1) { + /* continue with next pup */ + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_TRACE, + ("continue to next pup %d %d\n", + if_id, bus_id)); + continue; + } + + for (bit_id = 0; + bit_id < BUS_WIDTH_IN_BITS; + bit_id++) { + /* + * the next check is relevant + * only when using search + * machine 2 edges + */ + if (cur_start_win[bit_id] > 0 && + cur_end_win[bit_id] == 0) { + cur_end_win + [bit_id] = + max_win_size - 1; + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_TRACE, + ("fail, IF %d pup %d bit %d fail #1\n", + if_id, bus_id, + bit_id)); + /* the next bit */ + continue; + } else { + centralization_state + [if_id][bus_id] = 1; + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_TRACE, + ("fail, IF %d pup %d bit %d fail #2\n", + if_id, bus_id, + bit_id)); + } + } + + if (centralization_state[if_id][bus_id] + == 1) { + /* going to next pup */ + continue; + } + } /*bit */ + + opt_window = + ddr3_tip_get_buf_min(current_window); + /* final pup window length */ + final_pup_window[if_id][bus_id] = + ddr3_tip_get_buf_min(cur_end_win) - + ddr3_tip_get_buf_max(cur_start_win) + + 1; + waste_window = + opt_window - + final_pup_window[if_id][bus_id]; + start_window_skew = + ddr3_tip_get_buf_max(cur_start_win) - + ddr3_tip_get_buf_min( + cur_start_win); + end_window_skew = + ddr3_tip_get_buf_max( + cur_end_win) - + ddr3_tip_get_buf_min( + cur_end_win); + /* min/max updated with pattern change */ + cur_end_win_min = + ddr3_tip_get_buf_min( + cur_end_win); + cur_start_win_max = + ddr3_tip_get_buf_max( + cur_start_win); + bus_end_window[mode][if_id][bus_id] = + GET_MIN(bus_end_window[mode][if_id] + [bus_id], + cur_end_win_min); + bus_start_window[mode][if_id][bus_id] = + GET_MAX(bus_start_window[mode][if_id] + [bus_id], + cur_start_win_max); + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("pat %d IF %d pup %d opt_win %d final_win %d waste_win %d st_win_skew %d end_win_skew %d cur_st_win_max %d cur_end_win_min %d bus_st_win %d bus_end_win %d\n", + pattern_id, if_id, bus_id, opt_window, + final_pup_window[if_id][bus_id], + waste_window, start_window_skew, + end_window_skew, + cur_start_win_max, + cur_end_win_min, + bus_start_window[mode][if_id][bus_id], + bus_end_window[mode][if_id][bus_id])); + + /* check if window is valid */ + if (ddr3_tip_centr_skip_min_win_check == 0) { + if ((VALIDATE_WIN_LENGTH + (bus_start_window[mode][if_id] + [bus_id], + bus_end_window[mode][if_id] + [bus_id], + max_win_size) == 1) || + (IS_WINDOW_OUT_BOUNDARY + (bus_start_window[mode][if_id] + [bus_id], + bus_end_window[mode][if_id] + [bus_id], + max_win_size) == 1)) { + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("win valid, pat %d IF %d pup %d\n", + pattern_id, if_id, + bus_id)); + /* window is valid */ + } else { + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("fail win, pat %d IF %d pup %d bus_st_win %d bus_end_win %d\n", + pattern_id, if_id, bus_id, + bus_start_window[mode] + [if_id][bus_id], + bus_end_window[mode] + [if_id][bus_id])); + centralization_state[if_id] + [bus_id] = 1; + if (debug_mode == 0) + return MV_FAIL; + } + } /* ddr3_tip_centr_skip_min_win_check */ + } /* pup */ + } /* interface */ + } /* pattern */ + + for (if_id = start_if; if_id <= end_if; if_id++) { + if (IS_ACTIVE(tm->if_act_mask, if_id) == 0) + continue; + + is_if_fail = 0; + flow_result[if_id] = TEST_SUCCESS; + + for (bus_id = 0; + bus_id <= (tm->num_of_bus_per_interface - 1); bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + + /* continue only if lock */ + if (centralization_state[if_id][bus_id] != 1) { + if (ddr3_tip_centr_skip_min_win_check == 0) { + if ((bus_end_window + [mode][if_id][bus_id] == + (max_win_size - 1)) && + ((bus_end_window + [mode][if_id][bus_id] - + bus_start_window[mode][if_id] + [bus_id]) < MIN_WINDOW_SIZE) && + ((bus_end_window[mode][if_id] + [bus_id] - bus_start_window + [mode][if_id][bus_id]) > 2)) { + /* prevent false lock */ + /* TBD change to enum */ + centralization_state + [if_id][bus_id] = 2; + } + + if ((bus_end_window[mode][if_id][bus_id] + == 0) && + ((bus_end_window[mode][if_id] + [bus_id] - + bus_start_window[mode][if_id] + [bus_id]) < MIN_WINDOW_SIZE) && + ((bus_end_window[mode][if_id] + [bus_id] - + bus_start_window[mode][if_id] + [bus_id]) > 2)) + /*prevent false lock */ + centralization_state[if_id] + [bus_id] = 3; + } + + if ((bus_end_window[mode][if_id][bus_id] > + (max_win_size - 1)) && direction == + OPER_WRITE) { + DEBUG_CENTRALIZATION_ENGINE + (DEBUG_LEVEL_INFO, + ("Tx special pattern\n")); + cons_tap = 64; + } + } + + /* check states */ + if (centralization_state[if_id][bus_id] == 3) { + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("SSW - TBD IF %d pup %d\n", + if_id, bus_id)); + lock_success = 1; + } else if (centralization_state[if_id][bus_id] == 2) { + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("SEW - TBD IF %d pup %d\n", + if_id, bus_id)); + lock_success = 1; + } else if (centralization_state[if_id][bus_id] == 0) { + lock_success = 1; + } else { + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_ERROR, + ("fail, IF %d pup %d\n", + if_id, bus_id)); + lock_success = 0; + } + + if (lock_success == 1) { + centralization_result[if_id][bus_id] = + (bus_end_window[mode][if_id][bus_id] + + bus_start_window[mode][if_id][bus_id]) + / 2 - cons_tap; + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_TRACE, + (" bus_id %d Res= %d\n", bus_id, + centralization_result[if_id][bus_id])); + /* copy results to registers */ + pup_win_length = + bus_end_window[mode][if_id][bus_id] - + bus_start_window[mode][if_id][bus_id] + + 1; + + ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, + RESULT_DB_PHY_REG_ADDR + + effective_cs, ®); + reg = (reg & (~0x1f << + ((mode == CENTRAL_TX) ? + (RESULT_DB_PHY_REG_TX_OFFSET) : + (RESULT_DB_PHY_REG_RX_OFFSET)))) + | pup_win_length << + ((mode == CENTRAL_TX) ? + (RESULT_DB_PHY_REG_TX_OFFSET) : + (RESULT_DB_PHY_REG_RX_OFFSET)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + RESULT_DB_PHY_REG_ADDR + + effective_cs, reg)); + + /* offset per CS is calculated earlier */ + CHECK_STATUS( + ddr3_tip_bus_write(dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_id, + DDR_PHY_DATA, + reg_phy_off, + centralization_result + [if_id] + [bus_id])); + } else { + is_if_fail = 1; + } + } + + if (is_if_fail == 1) + flow_result[if_id] = TEST_FAILED; + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + /* restore cs enable value */ + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, + if_id, CS_ENABLE_REG, + cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + } + + return is_if_fail; +} + +/* + * Centralization Flow + */ +int ddr3_tip_special_rx(u32 dev_num) +{ + enum hws_training_ip_stat training_result[MAX_INTERFACE_NUM]; + u32 if_id, pup_id, pattern_id, bit_id; + u8 cur_start_win[BUS_WIDTH_IN_BITS]; + u8 cur_end_win[BUS_WIDTH_IN_BITS]; + enum hws_training_result result_type = RESULT_PER_BIT; + enum hws_dir direction; + enum hws_search_dir search_dir_id; + u32 *result[HWS_SEARCH_DIR_LIMIT]; + u32 max_win_size; + u8 cur_end_win_min, cur_start_win_max; + u32 cs_enable_reg_val[MAX_INTERFACE_NUM]; + u32 temp = 0; + int pad_num = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (ddr3_tip_special_rx_run_once_flag != 0) + return MV_OK; + + ddr3_tip_special_rx_run_once_flag = 1; + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* save current cs enable reg val */ + CHECK_STATUS(ddr3_tip_if_read(dev_num, ACCESS_TYPE_UNICAST, + if_id, CS_ENABLE_REG, + cs_enable_reg_val, + MASK_ALL_BITS)); + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, ACCESS_TYPE_UNICAST, + if_id, CS_ENABLE_REG, + (1 << 3), (1 << 3))); + } + + max_win_size = MAX_WINDOW_SIZE_RX; + direction = OPER_READ; + pattern_id = PATTERN_VREF; + + /* start flow */ + ddr3_tip_ip_training_wrapper(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, + HWS_CONTROL_ELEMENT_ADLL, + PARAM_NOT_CARE, direction, + tm->if_act_mask, 0x0, + max_win_size - 1, max_win_size - 1, + pattern_id, EDGE_FPF, CS_SINGLE, + PARAM_NOT_CARE, training_result); + + for (if_id = start_if; if_id <= end_if; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup_id = 0; + pup_id <= tm->num_of_bus_per_interface; pup_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup_id); + + for (search_dir_id = HWS_LOW2HIGH; + search_dir_id <= HWS_HIGH2LOW; + search_dir_id++) { + CHECK_STATUS(ddr3_tip_read_training_result + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup_id, + ALL_BITS_PER_PUP, search_dir_id, + direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &result[search_dir_id], + 1, 0, 0)); + DEBUG_CENTRALIZATION_ENGINE(DEBUG_LEVEL_INFO, + ("Special: pat %d IF %d pup %d Regs: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + pattern_id, if_id, + pup_id, + result + [search_dir_id][0], + result + [search_dir_id][1], + result + [search_dir_id][2], + result + [search_dir_id][3], + result + [search_dir_id][4], + result + [search_dir_id][5], + result + [search_dir_id][6], + result + [search_dir_id] + [7])); + } + + for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS; bit_id++) { + /* + * check if this code is valid for 2 edge, + * probably not :( + */ + cur_start_win[bit_id] = + GET_TAP_RESULT(result[HWS_LOW2HIGH] + [bit_id], EDGE_1); + cur_end_win[bit_id] = + GET_TAP_RESULT(result[HWS_HIGH2LOW] + [bit_id], EDGE_1); + } + if (!((ddr3_tip_is_pup_lock + (result[HWS_LOW2HIGH], result_type)) && + (ddr3_tip_is_pup_lock + (result[HWS_HIGH2LOW], result_type)))) { + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_ERROR, + ("Special: Pup lock fail, pat %d IF %d pup %d\n", + pattern_id, if_id, pup_id)); + return MV_FAIL; + } + + cur_end_win_min = + ddr3_tip_get_buf_min(cur_end_win); + cur_start_win_max = + ddr3_tip_get_buf_max(cur_start_win); + + if (cur_start_win_max <= 1) { /* Align left */ + for (bit_id = 0; bit_id < BUS_WIDTH_IN_BITS; + bit_id++) { + pad_num = + dq_map_table[bit_id + + pup_id * + BUS_WIDTH_IN_BITS + + if_id * + BUS_WIDTH_IN_BITS * + tm-> + num_of_bus_per_interface]; + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, + pup_id, DDR_PHY_DATA, + PBS_RX_PHY_REG + pad_num, + &temp)); + temp = (temp + 0xa > 31) ? + (31) : (temp + 0xa); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + pup_id, DDR_PHY_DATA, + PBS_RX_PHY_REG + pad_num, + temp)); + } + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("Special: PBS:: I/F# %d , Bus# %d fix align to the Left\n", + if_id, pup_id)); + } + + if (cur_end_win_min > 30) { /* Align right */ + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup_id, + DDR_PHY_DATA, PBS_RX_PHY_REG + 4, + &temp)); + temp += 0xa; + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + pup_id, DDR_PHY_DATA, + PBS_RX_PHY_REG + 4, temp)); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup_id, + DDR_PHY_DATA, PBS_RX_PHY_REG + 5, + &temp)); + temp += 0xa; + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + pup_id, DDR_PHY_DATA, + PBS_RX_PHY_REG + 5, temp)); + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("Special: PBS:: I/F# %d , Bus# %d fix align to the right\n", + if_id, pup_id)); + } + + vref_window_size[if_id][pup_id] = + cur_end_win_min - + cur_start_win_max + 1; + DEBUG_CENTRALIZATION_ENGINE( + DEBUG_LEVEL_INFO, + ("Special: Winsize I/F# %d , Bus# %d is %d\n", + if_id, pup_id, vref_window_size + [if_id][pup_id])); + } /* pup */ + } /* end of interface */ + + return MV_OK; +} + +/* + * Print Centralization Result + */ +int ddr3_tip_print_centralization_result(u32 dev_num) +{ + u32 if_id = 0, bus_id = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + dev_num = dev_num; + + printf("Centralization Results\n"); + printf("I/F0 Result[0 - success 1-fail 2 - state_2 3 - state_3] ...\n"); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + printf("%d ,\n", centralization_state[if_id][bus_id]); + } + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_db.c b/drivers/ddr/marvell/a38x/ddr3_training_db.c new file mode 100644 index 0000000..861dfb1 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_db.c @@ -0,0 +1,652 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +/* List of allowed frequency listed in order of enum hws_ddr_freq */ +u32 freq_val[DDR_FREQ_LIMIT] = { + 0, /*DDR_FREQ_LOW_FREQ */ + 400, /*DDR_FREQ_400, */ + 533, /*DDR_FREQ_533, */ + 666, /*DDR_FREQ_667, */ + 800, /*DDR_FREQ_800, */ + 933, /*DDR_FREQ_933, */ + 1066, /*DDR_FREQ_1066, */ + 311, /*DDR_FREQ_311, */ + 333, /*DDR_FREQ_333, */ + 467, /*DDR_FREQ_467, */ + 850, /*DDR_FREQ_850, */ + 600, /*DDR_FREQ_600 */ + 300, /*DDR_FREQ_300 */ + 900, /*DDR_FREQ_900 */ + 360, /*DDR_FREQ_360 */ + 1000 /*DDR_FREQ_1000 */ +}; + +/* Table for CL values per frequency for each speed bin index */ +struct cl_val_per_freq cas_latency_table[] = { + /* + * 400M 667M 933M 311M 467M 600M 360 + * 100M 533M 800M 1066M 333M 850M 900 + * 1000 (the order is 100, 400, 533 etc.) + */ + /* DDR3-800D */ + { {6, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} }, + /* DDR3-800E */ + { {6, 6, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 6, 0, 6, 0} }, + /* DDR3-1066E */ + { {6, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 0, 5, 0, 5, 0} }, + /* DDR3-1066F */ + { {6, 6, 7, 0, 0, 0, 0, 6, 6, 7, 0, 0, 6, 0, 6, 0} }, + /* DDR3-1066G */ + { {6, 6, 8, 0, 0, 0, 0, 6, 6, 8, 0, 0, 6, 0, 6, 0} }, + /* DDR3-1333F* */ + { {6, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1333G */ + { {6, 5, 7, 8, 0, 0, 0, 5, 5, 7, 0, 8, 5, 0, 5, 0} }, + /* DDR3-1333H */ + { {6, 6, 8, 9, 0, 0, 0, 6, 6, 8, 0, 9, 6, 0, 6, 0} }, + /* DDR3-1333J* */ + { {6, 6, 8, 10, 0, 0, 0, 6, 6, 8, 0, 10, 6, 0, 6, 0} + /* DDR3-1600G* */}, + { {6, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600H */ + { {6, 5, 6, 8, 9, 0, 0, 5, 5, 6, 0, 8, 5, 0, 5, 0} }, + /* DDR3-1600J */ + { {6, 5, 7, 9, 10, 0, 0, 5, 5, 7, 0, 9, 5, 0, 5, 0} }, + /* DDR3-1600K */ + { {6, 6, 8, 10, 11, 0, 0, 6, 6, 8, 0, 10, 6, 0, 6, 0 } }, + /* DDR3-1866J* */ + { {6, 5, 6, 8, 9, 11, 0, 5, 5, 6, 11, 8, 5, 0, 5, 0} }, + /* DDR3-1866K */ + { {6, 5, 7, 8, 10, 11, 0, 5, 5, 7, 11, 8, 5, 11, 5, 11} }, + /* DDR3-1866L */ + { {6, 6, 7, 9, 11, 12, 0, 6, 6, 7, 12, 9, 6, 12, 6, 12} }, + /* DDR3-1866M* */ + { {6, 6, 8, 10, 11, 13, 0, 6, 6, 8, 13, 10, 6, 13, 6, 13} }, + /* DDR3-2133K* */ + { {6, 5, 6, 7, 9, 10, 11, 5, 5, 6, 10, 7, 5, 11, 5, 11} }, + /* DDR3-2133L */ + { {6, 5, 6, 8, 9, 11, 12, 5, 5, 6, 11, 8, 5, 12, 5, 12} }, + /* DDR3-2133M */ + { {6, 5, 7, 9, 10, 12, 13, 5, 5, 7, 12, 9, 5, 13, 5, 13} }, + /* DDR3-2133N* */ + { {6, 6, 7, 9, 11, 13, 14, 6, 6, 7, 13, 9, 6, 14, 6, 14} }, + /* DDR3-1333H-ext */ + { {6, 6, 7, 9, 0, 0, 0, 6, 6, 7, 0, 9, 6, 0, 6, 0} }, + /* DDR3-1600K-ext */ + { {6, 6, 7, 9, 11, 0, 0, 6, 6, 7, 0, 9, 6, 0, 6, 0} }, + /* DDR3-1866M-ext */ + { {6, 6, 7, 9, 11, 13, 0, 6, 6, 7, 13, 9, 6, 13, 6, 13} }, +}; + +/* Table for CWL values per speedbin index */ +struct cl_val_per_freq cas_write_latency_table[] = { + /* + * 400M 667M 933M 311M 467M 600M 360 + * 100M 533M 800M 1066M 333M 850M 900 + * (the order is 100, 400, 533 etc.) + */ + /* DDR3-800D */ + { {5, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} }, + /* DDR3-800E */ + { {5, 5, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 5, 0, 5, 0} }, + /* DDR3-1066E */ + { {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1066F */ + { {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1066G */ + { {5, 5, 6, 0, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1333F* */ + { {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1333G */ + { {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1333H */ + { {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1333J* */ + { {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600G* */ + { {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600H */ + { {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600J */ + { {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600K */ + { {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1866J* */ + { {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 0, 5, 0} }, + /* DDR3-1866K */ + { {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 0, 5, 0} }, + /* DDR3-1866L */ + { {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} }, + /* DDR3-1866M* */ + { {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} }, + /* DDR3-2133K* */ + { {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} }, + /* DDR3-2133L */ + { {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} }, + /* DDR3-2133M */ + { {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} }, + /* DDR3-2133N* */ + { {5, 5, 6, 7, 8, 9, 10, 5, 5, 6, 9, 7, 5, 9, 5, 10} }, + /* DDR3-1333H-ext */ + { {5, 5, 6, 7, 0, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1600K-ext */ + { {5, 5, 6, 7, 8, 0, 0, 5, 5, 6, 0, 7, 5, 0, 5, 0} }, + /* DDR3-1866M-ext */ + { {5, 5, 6, 7, 8, 9, 0, 5, 5, 6, 9, 7, 5, 9, 5, 9} }, +}; + +u8 twr_mask_table[] = { + 10, + 10, + 10, + 10, + 10, + 1, /*5 */ + 2, /*6 */ + 3, /*7 */ + 10, + 10, + 5, /*10 */ + 10, + 6, /*12 */ + 10, + 7, /*14 */ + 10, + 0 /*16 */ +}; + +u8 cl_mask_table[] = { + 0, + 0, + 0, + 0, + 0, + 0x2, + 0x4, + 0x6, + 0x8, + 0xa, + 0xc, + 0xe, + 0x1, + 0x3, + 0x5, + 0x5 +}; + +u8 cwl_mask_table[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0x9 +}; + +/* RFC values (in ns) */ +u16 rfc_table[] = { + 90, /* 512M */ + 110, /* 1G */ + 160, /* 2G */ + 260, /* 4G */ + 350 /* 8G */ +}; + +u32 speed_bin_table_t_rc[] = { + 50000, + 52500, + 48750, + 50625, + 52500, + 46500, + 48000, + 49500, + 51000, + 45000, + 46250, + 47500, + 48750, + 44700, + 45770, + 46840, + 47910, + 43285, + 44220, + 45155, + 46900 +}; + +u32 speed_bin_table_t_rcd_t_rp[] = { + 12500, + 15000, + 11250, + 13125, + 15000, + 10500, + 12000, + 13500, + 15000, + 10000, + 11250, + 12500, + 13750, + 10700, + 11770, + 12840, + 13910, + 10285, + 11022, + 12155, + 13090, +}; + +enum { + PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR = 0, + PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM +}; + +static u8 pattern_killer_pattern_table_map[KILLER_PATTERN_LENGTH * 2][2] = { + /*Aggressor / Victim */ + {1, 0}, + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, + {0, 1}, + {1, 0}, + {0, 1}, + {1, 0}, + {0, 1}, + {1, 0}, + {1, 0}, + {0, 1}, + {1, 0}, + {0, 1}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {1, 0}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 1}, + {0, 1}, + {1, 1}, + {0, 0}, + {0, 0}, + {1, 1}, + {1, 1}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {1, 1}, + {0, 0}, + {0, 0}, + {1, 1}, + {0, 0}, + {1, 1}, + {0, 1}, + {0, 0}, + {0, 1}, + {0, 1}, + {0, 0}, + {1, 1}, + {1, 1}, + {1, 0}, + {1, 0}, + {1, 1}, + {1, 1}, + {1, 1}, + {1, 1}, + {1, 1}, + {1, 1}, + {1, 1} +}; + +static u8 pattern_vref_pattern_table_map[] = { + /* 1 means 0xffffffff, 0 is 0x0 */ + 0xb8, + 0x52, + 0x55, + 0x8a, + 0x33, + 0xa6, + 0x6d, + 0xfe +}; + +/* Return speed Bin value for selected index and t* element */ +u32 speed_bin_table(u8 index, enum speed_bin_table_elements element) +{ + u32 result = 0; + + switch (element) { + case SPEED_BIN_TRCD: + case SPEED_BIN_TRP: + result = speed_bin_table_t_rcd_t_rp[index]; + break; + case SPEED_BIN_TRAS: + if (index < 6) + result = 37500; + else if (index < 10) + result = 36000; + else if (index < 14) + result = 35000; + else if (index < 18) + result = 34000; + else + result = 33000; + break; + case SPEED_BIN_TRC: + result = speed_bin_table_t_rc[index]; + break; + case SPEED_BIN_TRRD1K: + if (index < 3) + result = 10000; + else if (index < 6) + result = 7005; + else if (index < 14) + result = 6000; + else + result = 5000; + break; + case SPEED_BIN_TRRD2K: + if (index < 6) + result = 10000; + else if (index < 14) + result = 7005; + else + result = 6000; + break; + case SPEED_BIN_TPD: + if (index < 3) + result = 7500; + else if (index < 10) + result = 5625; + else + result = 5000; + break; + case SPEED_BIN_TFAW1K: + if (index < 3) + result = 40000; + else if (index < 6) + result = 37500; + else if (index < 14) + result = 30000; + else if (index < 18) + result = 27000; + else + result = 25000; + break; + case SPEED_BIN_TFAW2K: + if (index < 6) + result = 50000; + else if (index < 10) + result = 45000; + else if (index < 14) + result = 40000; + else + result = 35000; + break; + case SPEED_BIN_TWTR: + result = 7500; + break; + case SPEED_BIN_TRTP: + result = 7500; + break; + case SPEED_BIN_TWR: + result = 15000; + break; + case SPEED_BIN_TMOD: + result = 15000; + break; + default: + break; + } + + return result; +} + +static inline u32 pattern_table_get_killer_word(u8 dqs, u8 index) +{ + u8 i, byte = 0; + u8 role; + + for (i = 0; i < 8; i++) { + role = (i == dqs) ? + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR) : + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM); + byte |= pattern_killer_pattern_table_map[index][role] << i; + } + + return byte | (byte << 8) | (byte << 16) | (byte << 24); +} + +static inline u32 pattern_table_get_killer_word16(u8 dqs, u8 index) +{ + u8 i, byte0 = 0, byte1 = 0; + u8 role; + + for (i = 0; i < 8; i++) { + role = (i == dqs) ? + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR) : + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM); + byte0 |= pattern_killer_pattern_table_map[index * 2][role] << i; + } + + for (i = 0; i < 8; i++) { + role = (i == dqs) ? + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_AGGRESSOR) : + (PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM); + byte1 |= pattern_killer_pattern_table_map + [index * 2 + 1][role] << i; + } + + return byte0 | (byte0 << 8) | (byte1 << 16) | (byte1 << 24); +} + +static inline u32 pattern_table_get_sso_word(u8 sso, u8 index) +{ + u8 step = sso + 1; + + if (0 == ((index / step) & 1)) + return 0x0; + else + return 0xffffffff; +} + +static inline u32 pattern_table_get_vref_word(u8 index) +{ + if (0 == ((pattern_vref_pattern_table_map[index / 8] >> + (index % 8)) & 1)) + return 0x0; + else + return 0xffffffff; +} + +static inline u32 pattern_table_get_vref_word16(u8 index) +{ + if (0 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] && + 0 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1]) + return 0x00000000; + else if (1 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] && + 0 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1]) + return 0xffff0000; + else if (0 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2] && + 1 == pattern_killer_pattern_table_map + [PATTERN_KILLER_PATTERN_TABLE_MAP_ROLE_VICTIM][index * 2 + 1]) + return 0x0000ffff; + else + return 0xffffffff; +} + +static inline u32 pattern_table_get_static_pbs_word(u8 index) +{ + u16 temp; + + temp = ((0x00ff << (index / 3)) & 0xff00) >> 8; + + return temp | (temp << 8) | (temp << 16) | (temp << 24); +} + +inline u32 pattern_table_get_word(u32 dev_num, enum hws_pattern type, u8 index) +{ + u32 pattern; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == 0) { + /* 32bit patterns */ + switch (type) { + case PATTERN_PBS1: + case PATTERN_PBS2: + if (index == 0 || index == 2 || index == 5 || + index == 7) + pattern = PATTERN_55; + else + pattern = PATTERN_AA; + break; + case PATTERN_PBS3: + if (0 == (index & 1)) + pattern = PATTERN_55; + else + pattern = PATTERN_AA; + break; + case PATTERN_RL: + if (index < 6) + pattern = PATTERN_00; + else + pattern = PATTERN_80; + break; + case PATTERN_STATIC_PBS: + pattern = pattern_table_get_static_pbs_word(index); + break; + case PATTERN_KILLER_DQ0: + case PATTERN_KILLER_DQ1: + case PATTERN_KILLER_DQ2: + case PATTERN_KILLER_DQ3: + case PATTERN_KILLER_DQ4: + case PATTERN_KILLER_DQ5: + case PATTERN_KILLER_DQ6: + case PATTERN_KILLER_DQ7: + pattern = pattern_table_get_killer_word( + (u8)(type - PATTERN_KILLER_DQ0), index); + break; + case PATTERN_RL2: + if (index < 6) + pattern = PATTERN_00; + else + pattern = PATTERN_01; + break; + case PATTERN_TEST: + if (index > 1 && index < 6) + pattern = PATTERN_20; + else + pattern = PATTERN_00; + break; + case PATTERN_FULL_SSO0: + case PATTERN_FULL_SSO1: + case PATTERN_FULL_SSO2: + case PATTERN_FULL_SSO3: + pattern = pattern_table_get_sso_word( + (u8)(type - PATTERN_FULL_SSO0), index); + break; + case PATTERN_VREF: + pattern = pattern_table_get_vref_word(index); + break; + default: + pattern = 0; + break; + } + } else { + /* 16bit patterns */ + switch (type) { + case PATTERN_PBS1: + case PATTERN_PBS2: + case PATTERN_PBS3: + pattern = PATTERN_55AA; + break; + case PATTERN_RL: + if (index < 3) + pattern = PATTERN_00; + else + pattern = PATTERN_80; + break; + case PATTERN_STATIC_PBS: + pattern = PATTERN_00FF; + break; + case PATTERN_KILLER_DQ0: + case PATTERN_KILLER_DQ1: + case PATTERN_KILLER_DQ2: + case PATTERN_KILLER_DQ3: + case PATTERN_KILLER_DQ4: + case PATTERN_KILLER_DQ5: + case PATTERN_KILLER_DQ6: + case PATTERN_KILLER_DQ7: + pattern = pattern_table_get_killer_word16( + (u8)(type - PATTERN_KILLER_DQ0), index); + break; + case PATTERN_RL2: + if (index < 3) + pattern = PATTERN_00; + else + pattern = PATTERN_01; + break; + case PATTERN_TEST: + pattern = PATTERN_0080; + break; + case PATTERN_FULL_SSO0: + pattern = 0x0000ffff; + break; + case PATTERN_FULL_SSO1: + case PATTERN_FULL_SSO2: + case PATTERN_FULL_SSO3: + pattern = pattern_table_get_sso_word( + (u8)(type - PATTERN_FULL_SSO1), index); + break; + case PATTERN_VREF: + pattern = pattern_table_get_vref_word16(index); + break; + default: + pattern = 0; + break; + } + } + + return pattern; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c new file mode 100644 index 0000000..56fce17 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.c @@ -0,0 +1,686 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define VREF_INITIAL_STEP 3 +#define VREF_SECOND_STEP 1 +#define VREF_MAX_INDEX 7 +#define MAX_VALUE (1024 - 1) +#define MIN_VALUE (-MAX_VALUE) +#define GET_RD_SAMPLE_DELAY(data, cs) ((data >> rd_sample_mask[cs]) & 0xf) + +u32 ck_delay = (u32)-1, ck_delay_16 = (u32)-1; +u32 ca_delay; +int ddr3_tip_centr_skip_min_win_check = 0; +u8 current_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM]; +u8 last_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM]; +u16 current_valid_window[MAX_BUS_NUM][MAX_INTERFACE_NUM]; +u16 last_valid_window[MAX_BUS_NUM][MAX_INTERFACE_NUM]; +u8 lim_vref[MAX_BUS_NUM][MAX_INTERFACE_NUM]; +u8 interface_state[MAX_INTERFACE_NUM]; +u8 vref_window_size[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 vref_window_size_th = 12; + +static u8 pup_st[MAX_BUS_NUM][MAX_INTERFACE_NUM]; + +static u32 rd_sample_mask[] = { + 0, + 8, + 16, + 24 +}; + +#define VREF_STEP_1 0 +#define VREF_STEP_2 1 +#define VREF_CONVERGE 2 + +/* + * ODT additional timing + */ +int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id) +{ + u32 cs_num = 0, max_read_sample = 0, min_read_sample = 0; + u32 data_read[MAX_INTERFACE_NUM] = { 0 }; + u32 read_sample[MAX_CS_NUM]; + u32 val; + u32 pup_index; + int max_phase = MIN_VALUE, current_phase; + enum hws_access_type access_type = ACCESS_TYPE_UNICAST; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + DUNIT_ODT_CONTROL_REG, + 0 << 8, 0x3 << 8)); + CHECK_STATUS(ddr3_tip_if_read(dev_num, access_type, if_id, + READ_DATA_SAMPLE_DELAY, + data_read, MASK_ALL_BITS)); + val = data_read[if_id]; + + for (cs_num = 0; cs_num < MAX_CS_NUM; cs_num++) { + read_sample[cs_num] = GET_RD_SAMPLE_DELAY(val, cs_num); + + /* find maximum of read_samples */ + if (read_sample[cs_num] >= max_read_sample) { + if (read_sample[cs_num] == max_read_sample) + max_phase = MIN_VALUE; + else + max_read_sample = read_sample[cs_num]; + + for (pup_index = 0; + pup_index < tm->num_of_bus_per_interface; + pup_index++) { + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup_index, + DDR_PHY_DATA, + RL_PHY_REG + CS_REG_VALUE(cs_num), + &val)); + + current_phase = ((int)val & 0xe0) >> 6; + if (current_phase >= max_phase) + max_phase = current_phase; + } + } + + /* find minimum */ + if (read_sample[cs_num] < min_read_sample) + min_read_sample = read_sample[cs_num]; + } + + min_read_sample = min_read_sample - 1; + max_read_sample = max_read_sample + 4 + (max_phase + 1) / 2 + 1; + if (min_read_sample >= 0xf) + min_read_sample = 0xf; + if (max_read_sample >= 0x1f) + max_read_sample = 0x1f; + + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODT_TIMING_LOW, + ((min_read_sample - 1) << 12), + 0xf << 12)); + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, if_id, + ODT_TIMING_LOW, + (max_read_sample << 16), + 0x1f << 16)); + + return MV_OK; +} + +int get_valid_win_rx(u32 dev_num, u32 if_id, u8 res[4]) +{ + u32 reg_pup = RESULT_DB_PHY_REG_ADDR; + u32 reg_data; + u32 cs_num; + int i; + + cs_num = 0; + + /* TBD */ + reg_pup += cs_num; + + for (i = 0; i < 4; i++) { + CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id, + ACCESS_TYPE_UNICAST, i, + DDR_PHY_DATA, reg_pup, + ®_data)); + res[i] = (reg_data >> RESULT_DB_PHY_REG_RX_OFFSET) & 0x1f; + } + + return 0; +} + +/* + * This algorithm deals with the vertical optimum from Voltage point of view + * of the sample signal. + * Voltage sample point can improve the Eye / window size of the bit and the + * pup. + * The problem is that it is tune for all DQ the same so there isn't any + * PBS like code. + * It is more like centralization. + * But because we don't have The training SM support we do it a bit more + * smart search to save time. + */ +int ddr3_tip_vref(u32 dev_num) +{ + /* + * The Vref register have non linear order. Need to check what will be + * in future projects. + */ + u32 vref_map[8] = { + 1, 2, 3, 4, 5, 6, 7, 0 + }; + /* State and parameter definitions */ + u32 initial_step = VREF_INITIAL_STEP; + /* need to be assign with minus ????? */ + u32 second_step = VREF_SECOND_STEP; + u32 algo_run_flag = 0, currrent_vref = 0; + u32 while_count = 0; + u32 pup = 0, if_id = 0, num_pup = 0, rep = 0; + u32 val = 0; + u32 reg_addr = 0xa8; + u32 copy_start_pattern, copy_end_pattern; + enum hws_result *flow_result = ddr3_tip_get_result_ptr(training_stage); + u8 res[4]; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_special_rx(dev_num)); + + /* save start/end pattern */ + copy_start_pattern = start_pattern; + copy_end_pattern = end_pattern; + + /* set vref as centralization pattern */ + start_pattern = PATTERN_VREF; + end_pattern = PATTERN_VREF; + + /* init params */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = 0; + pup < tm->num_of_bus_per_interface; pup++) { + current_vref[pup][if_id] = 0; + last_vref[pup][if_id] = 0; + lim_vref[pup][if_id] = 0; + current_valid_window[pup][if_id] = 0; + last_valid_window[pup][if_id] = 0; + if (vref_window_size[if_id][pup] > + vref_window_size_th) { + pup_st[pup][if_id] = VREF_CONVERGE; + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_INFO, + ("VREF config, IF[ %d ]pup[ %d ] - Vref tune not requered (%d)\n", + if_id, pup, __LINE__)); + } else { + pup_st[pup][if_id] = VREF_STEP_1; + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, &val)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + pup, DDR_PHY_DATA, reg_addr, + (val & (~0xf)) | vref_map[0])); + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_INFO, + ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, + (val & (~0xf)) | vref_map[0], + __LINE__)); + } + } + interface_state[if_id] = 0; + } + + /* TODO: Set number of active interfaces */ + num_pup = tm->num_of_bus_per_interface * MAX_INTERFACE_NUM; + + while ((algo_run_flag <= num_pup) & (while_count < 10)) { + while_count++; + for (rep = 1; rep < 4; rep++) { + ddr3_tip_centr_skip_min_win_check = 1; + ddr3_tip_centralization_rx(dev_num); + ddr3_tip_centr_skip_min_win_check = 0; + + /* Read Valid window results only for non converge pups */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (interface_state[if_id] != 4) { + get_valid_win_rx(dev_num, if_id, res); + for (pup = 0; + pup < tm->num_of_bus_per_interface; + pup++) { + VALIDATE_ACTIVE + (tm->bus_act_mask, pup); + if (pup_st[pup] + [if_id] == + VREF_CONVERGE) + continue; + + current_valid_window[pup] + [if_id] = + (current_valid_window[pup] + [if_id] * (rep - 1) + + 1000 * res[pup]) / rep; + } + } + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_TRACE, + ("current_valid_window: IF[ %d ] - ", if_id)); + + for (pup = 0; + pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE, + ("%d ", + current_valid_window + [pup][if_id])); + } + DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE, ("\n")); + } + + /* Compare results and respond as function of state */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = 0; + pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] STATE #%d (%d)\n", + if_id, pup, + pup_st[pup] + [if_id], __LINE__)); + + if (pup_st[pup][if_id] == VREF_CONVERGE) + continue; + + DEBUG_TRAINING_HW_ALG(DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] CHECK progress - Current %d Last %d, limit VREF %d (%d)\n", + if_id, pup, + current_valid_window[pup] + [if_id], + last_valid_window[pup] + [if_id], lim_vref[pup] + [if_id], __LINE__)); + + /* + * The -1 is for solution resolution +/- 1 tap + * of ADLL + */ + if (current_valid_window[pup][if_id] + 200 >= + (last_valid_window[pup][if_id])) { + if (pup_st[pup][if_id] == VREF_STEP_1) { + /* + * We stay in the same state and + * step just update the window + * size (take the max) and Vref + */ + if (current_vref[pup] + [if_id] == VREF_MAX_INDEX) { + /* + * If we step to the end + * and didn't converge + * to some particular + * better Vref value + * define the pup as + * converge and step + * back to nominal + * Vref. + */ + pup_st[pup] + [if_id] = + VREF_CONVERGE; + algo_run_flag++; + interface_state + [if_id]++; + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n", + if_id, pup, + current_vref[pup] + [if_id], + __LINE__)); + } else { + /* continue to update the Vref index */ + current_vref[pup] + [if_id] = + ((current_vref[pup] + [if_id] + + initial_step) > + VREF_MAX_INDEX) ? + VREF_MAX_INDEX + : (current_vref[pup] + [if_id] + + initial_step); + if (current_vref[pup] + [if_id] == + VREF_MAX_INDEX) { + pup_st[pup] + [if_id] + = + VREF_STEP_2; + } + lim_vref[pup] + [if_id] = + last_vref[pup] + [if_id] = + current_vref[pup] + [if_id]; + } + + last_valid_window[pup] + [if_id] = + GET_MAX(current_valid_window + [pup][if_id], + last_valid_window + [pup] + [if_id]); + + /* update the Vref for next stage */ + currrent_vref = + current_vref[pup] + [if_id]; + CHECK_STATUS + (ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + &val)); + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (val & (~0xf)) | + vref_map[currrent_vref])); + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, + (val & (~0xf)) | + vref_map[currrent_vref], + __LINE__)); + } else if (pup_st[pup][if_id] + == VREF_STEP_2) { + /* + * We keep on search back with + * the same step size. + */ + last_valid_window[pup] + [if_id] = + GET_MAX(current_valid_window + [pup][if_id], + last_valid_window + [pup] + [if_id]); + last_vref[pup][if_id] = + current_vref[pup] + [if_id]; + + /* we finish all search space */ + if ((current_vref[pup] + [if_id] - second_step) == lim_vref[pup][if_id]) { + /* + * If we step to the end + * and didn't converge + * to some particular + * better Vref value + * define the pup as + * converge and step + * back to nominal + * Vref. + */ + pup_st[pup] + [if_id] = + VREF_CONVERGE; + algo_run_flag++; + + interface_state + [if_id]++; + + current_vref[pup] + [if_id] = + (current_vref[pup] + [if_id] - + second_step); + + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n", + if_id, pup, + current_vref[pup] + [if_id], + __LINE__)); + } else + /* we finish all search space */ + if (current_vref[pup] + [if_id] == + lim_vref[pup] + [if_id]) { + /* + * If we step to the end + * and didn't converge + * to some particular + * better Vref value + * define the pup as + * converge and step + * back to nominal + * Vref. + */ + pup_st[pup] + [if_id] = + VREF_CONVERGE; + + algo_run_flag++; + interface_state + [if_id]++; + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n", + if_id, pup, + current_vref[pup] + [if_id], + __LINE__)); + } else { + current_vref[pup] + [if_id] = + current_vref[pup] + [if_id] - + second_step; + } + + /* Update the Vref for next stage */ + currrent_vref = + current_vref[pup] + [if_id]; + CHECK_STATUS + (ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + &val)); + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (val & (~0xf)) | + vref_map[currrent_vref])); + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, + (val & (~0xf)) | + vref_map[currrent_vref], + __LINE__)); + } + } else { + /* we change state and change step */ + if (pup_st[pup][if_id] == VREF_STEP_1) { + pup_st[pup][if_id] = + VREF_STEP_2; + lim_vref[pup][if_id] = + current_vref[pup] + [if_id] - initial_step; + last_valid_window[pup] + [if_id] = + current_valid_window[pup] + [if_id]; + last_vref[pup][if_id] = + current_vref[pup] + [if_id]; + current_vref[pup][if_id] = + last_vref[pup][if_id] - + second_step; + + /* Update the Vref for next stage */ + CHECK_STATUS + (ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + &val)); + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (val & (~0xf)) | + vref_map[current_vref[pup] + [if_id]])); + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, + (val & (~0xf)) | + vref_map[current_vref[pup] + [if_id]], + __LINE__)); + + } else if (pup_st[pup][if_id] == VREF_STEP_2) { + /* + * The last search was the max + * point set value and exit + */ + CHECK_STATUS + (ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + &val)); + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (val & (~0xf)) | + vref_map[last_vref[pup] + [if_id]])); + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("VREF config, IF[ %d ]pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, + (val & (~0xf)) | + vref_map[last_vref[pup] + [if_id]], + __LINE__)); + pup_st[pup][if_id] = + VREF_CONVERGE; + algo_run_flag++; + interface_state[if_id]++; + DEBUG_TRAINING_HW_ALG + (DEBUG_LEVEL_TRACE, + ("I/F[ %d ], pup[ %d ] VREF_CONVERGE - Vref = %X (%d)\n", + if_id, pup, + current_vref[pup] + [if_id], __LINE__)); + } + } + } + } + } + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = 0; + pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, &val)); + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_INFO, + ("FINAL values: I/F[ %d ], pup[ %d ] - Vref = %X (%d)\n", + if_id, pup, val, __LINE__)); + } + } + + flow_result[if_id] = TEST_SUCCESS; + + /* restore start/end pattern */ + start_pattern = copy_start_pattern; + end_pattern = copy_end_pattern; + + return 0; +} + +/* + * CK/CA Delay + */ +int ddr3_tip_cmd_addr_init_delay(u32 dev_num, u32 adll_tap) +{ + u32 if_id = 0; + u32 ck_num_adll_tap = 0, ca_num_adll_tap = 0, data = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * ck_delay_table is delaying the of the clock signal only. + * (to overcome timing issues between_c_k & command/address signals) + */ + /* + * ca_delay is delaying the of the entire command & Address signals + * (include Clock signal to overcome DGL error on the Clock versus + * the DQS). + */ + + /* Calc ADLL Tap */ + if ((ck_delay == -1) || (ck_delay_16 == -1)) { + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_ERROR, + ("ERROR: One of ck_delay values not initialized!!!\n")); + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* Calc delay ps in ADLL tap */ + if (tm->interface_params[if_id].bus_width == + BUS_WIDTH_16) + ck_num_adll_tap = ck_delay_16 / adll_tap; + else + ck_num_adll_tap = ck_delay / adll_tap; + + ca_num_adll_tap = ca_delay / adll_tap; + data = (ck_num_adll_tap & 0x3f) + + ((ca_num_adll_tap & 0x3f) << 10); + + /* + * Set the ADLL number to the CK ADLL for Interfaces for + * all Pup + */ + DEBUG_TRAINING_HW_ALG( + DEBUG_LEVEL_TRACE, + ("ck_num_adll_tap %d ca_num_adll_tap %d adll_tap %d\n", + ck_num_adll_tap, ca_num_adll_tap, adll_tap)); + + CHECK_STATUS(ddr3_tip_bus_write(dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, DDR_PHY_CONTROL, + 0x0, data)); + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h new file mode 100644 index 0000000..6e1bab2 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_hw_algo.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_HW_ALGO_H_ +#define _DDR3_TRAINING_HW_ALGO_H_ + +int ddr3_tip_vref(u32 dev_num); +int ddr3_tip_write_additional_odt_setting(u32 dev_num, u32 if_id); +int ddr3_tip_cmd_addr_init_delay(u32 dev_num, u32 adll_tap); + +#endif /* _DDR3_TRAINING_HW_ALGO_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip.h b/drivers/ddr/marvell/a38x/ddr3_training_ip.h new file mode 100644 index 0000000..76a1b6a0 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_H_ +#define _DDR3_TRAINING_IP_H_ + +#include "ddr3_training_ip_def.h" +#include "ddr_topology_def.h" +#include "ddr_training_ip_db.h" + +#define DDR3_TIP_VERSION_STRING "DDR3 Training Sequence - Ver TIP-1.29." + +#define MAX_CS_NUM 4 +#define MAX_TOTAL_BUS_NUM (MAX_INTERFACE_NUM * MAX_BUS_NUM) +#define MAX_DQ_NUM 40 + +#define GET_MIN(arg1, arg2) ((arg1) < (arg2)) ? (arg1) : (arg2) +#define GET_MAX(arg1, arg2) ((arg1) < (arg2)) ? (arg2) : (arg1) + +#define INIT_CONTROLLER_MASK_BIT 0x00000001 +#define STATIC_LEVELING_MASK_BIT 0x00000002 +#define SET_LOW_FREQ_MASK_BIT 0x00000004 +#define LOAD_PATTERN_MASK_BIT 0x00000008 +#define SET_MEDIUM_FREQ_MASK_BIT 0x00000010 +#define WRITE_LEVELING_MASK_BIT 0x00000020 +#define LOAD_PATTERN_2_MASK_BIT 0x00000040 +#define READ_LEVELING_MASK_BIT 0x00000080 +#define SW_READ_LEVELING_MASK_BIT 0x00000100 +#define WRITE_LEVELING_SUPP_MASK_BIT 0x00000200 +#define PBS_RX_MASK_BIT 0x00000400 +#define PBS_TX_MASK_BIT 0x00000800 +#define SET_TARGET_FREQ_MASK_BIT 0x00001000 +#define ADJUST_DQS_MASK_BIT 0x00002000 +#define WRITE_LEVELING_TF_MASK_BIT 0x00004000 +#define LOAD_PATTERN_HIGH_MASK_BIT 0x00008000 +#define READ_LEVELING_TF_MASK_BIT 0x00010000 +#define WRITE_LEVELING_SUPP_TF_MASK_BIT 0x00020000 +#define DM_PBS_TX_MASK_BIT 0x00040000 +#define CENTRALIZATION_RX_MASK_BIT 0x00100000 +#define CENTRALIZATION_TX_MASK_BIT 0x00200000 +#define TX_EMPHASIS_MASK_BIT 0x00400000 +#define PER_BIT_READ_LEVELING_TF_MASK_BIT 0x00800000 +#define VREF_CALIBRATION_MASK_BIT 0x01000000 + +enum hws_result { + TEST_FAILED = 0, + TEST_SUCCESS = 1, + NO_TEST_DONE = 2 +}; + +enum hws_training_result { + RESULT_PER_BIT, + RESULT_PER_BYTE +}; + +enum auto_tune_stage { + INIT_CONTROLLER, + STATIC_LEVELING, + SET_LOW_FREQ, + LOAD_PATTERN, + SET_MEDIUM_FREQ, + WRITE_LEVELING, + LOAD_PATTERN_2, + READ_LEVELING, + WRITE_LEVELING_SUPP, + PBS_RX, + PBS_TX, + SET_TARGET_FREQ, + ADJUST_DQS, + WRITE_LEVELING_TF, + READ_LEVELING_TF, + WRITE_LEVELING_SUPP_TF, + DM_PBS_TX, + VREF_CALIBRATION, + CENTRALIZATION_RX, + CENTRALIZATION_TX, + TX_EMPHASIS, + LOAD_PATTERN_HIGH, + PER_BIT_READ_LEVELING_TF, + MAX_STAGE_LIMIT +}; + +enum hws_access_type { + ACCESS_TYPE_UNICAST = 0, + ACCESS_TYPE_MULTICAST = 1 +}; + +enum hws_algo_type { + ALGO_TYPE_DYNAMIC, + ALGO_TYPE_STATIC +}; + +struct init_cntr_param { + int is_ctrl64_bit; + int do_mrs_phy; + int init_phy; + int msys_init; +}; + +struct pattern_info { + u8 num_of_phases_tx; + u8 tx_burst_size; + u8 delay_between_bursts; + u8 num_of_phases_rx; + u32 start_addr; + u8 pattern_len; +}; + +/* CL value for each frequency */ +struct cl_val_per_freq { + u8 cl_val[DDR_FREQ_LIMIT]; +}; + +struct cs_element { + u8 cs_num; + u8 num_of_cs; +}; + +struct mode_info { + /* 32 bits representing MRS bits */ + u32 reg_mr0[MAX_INTERFACE_NUM]; + u32 reg_mr1[MAX_INTERFACE_NUM]; + u32 reg_mr2[MAX_INTERFACE_NUM]; + u32 reg_m_r3[MAX_INTERFACE_NUM]; + /* + * Each element in array represent read_data_sample register delay for + * a specific interface. + * Each register, 4 bits[0+CS*8 to 4+CS*8] represent Number of DDR + * cycles from read command until data is ready to be fetched from + * the PHY, when accessing CS. + */ + u32 read_data_sample[MAX_INTERFACE_NUM]; + /* + * Each element in array represent read_data_sample register delay for + * a specific interface. + * Each register, 4 bits[0+CS*8 to 4+CS*8] represent the total delay + * from read command until opening the read mask, when accessing CS. + * This field defines the delay in DDR cycles granularity. + */ + u32 read_data_ready[MAX_INTERFACE_NUM]; +}; + +struct hws_tip_freq_config_info { + u8 is_supported; + u8 bw_per_freq; + u8 rate_per_freq; +}; + +struct hws_cs_config_info { + u32 cs_reg_value; + u32 cs_cbe_value; +}; + +struct dfx_access { + u8 pipe; + u8 client; +}; + +struct hws_xsb_info { + struct dfx_access *dfx_table; +}; + +int ddr3_tip_register_dq_table(u32 dev_num, u32 *table); +int hws_ddr3_tip_select_ddr_controller(u32 dev_num, int enable); +int hws_ddr3_tip_init_controller(u32 dev_num, + struct init_cntr_param *init_cntr_prm); +int hws_ddr3_tip_load_topology_map(u32 dev_num, + struct hws_topology_map *topology); +int hws_ddr3_tip_run_alg(u32 dev_num, enum hws_algo_type algo_type); +int hws_ddr3_tip_mode_read(u32 dev_num, struct mode_info *mode_info); +int hws_ddr3_tip_read_training_result(u32 dev_num, + enum hws_result result[MAX_STAGE_LIMIT][MAX_INTERFACE_NUM]); +int ddr3_tip_is_pup_lock(u32 *pup_buf, enum hws_training_result read_mode); +u8 ddr3_tip_get_buf_min(u8 *buf_ptr); +u8 ddr3_tip_get_buf_max(u8 *buf_ptr); + +#endif /* _DDR3_TRAINING_IP_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h new file mode 100644 index 0000000..5c9bfe9 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_bist.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_BIST_H_ +#define _DDR3_TRAINING_IP_BIST_H_ + +#include "ddr3_training_ip.h" + +enum hws_bist_operation { + BIST_STOP = 0, + BIST_START = 1 +}; + +enum hws_stress_jump { + STRESS_NONE = 0, + STRESS_ENABLE = 1 +}; + +enum hws_pattern_duration { + DURATION_SINGLE = 0, + DURATION_STOP_AT_FAIL = 1, + DURATION_ADDRESS = 2, + DURATION_CONT = 4 +}; + +struct bist_result { + u32 bist_error_cnt; + u32 bist_fail_low; + u32 bist_fail_high; + u32 bist_last_fail_addr; +}; + +int ddr3_tip_bist_read_result(u32 dev_num, u32 if_id, + struct bist_result *pst_bist_result); +int ddr3_tip_bist_activate(u32 dev_num, enum hws_pattern pattern, + enum hws_access_type access_type, + u32 if_num, enum hws_dir direction, + enum hws_stress_jump addr_stress_jump, + enum hws_pattern_duration duration, + enum hws_bist_operation oper_type, + u32 offset, u32 cs_num, u32 pattern_addr_length); +int hws_ddr3_run_bist(u32 dev_num, enum hws_pattern pattern, u32 *result, + u32 cs_num); +int ddr3_tip_run_sweep_test(int dev_num, u32 repeat_num, u32 direction, + u32 mode); +int ddr3_tip_print_regs(u32 dev_num); +int ddr3_tip_reg_dump(u32 dev_num); +int run_xsb_test(u32 dev_num, u32 mem_addr, u32 write_type, u32 read_type, + u32 burst_length); + +#endif /* _DDR3_TRAINING_IP_BIST_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h new file mode 100644 index 0000000..7c57603 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_centralization.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_CENTRALIZATION_H +#define _DDR3_TRAINING_IP_CENTRALIZATION_H + +int ddr3_tip_centralization_tx(u32 dev_num); +int ddr3_tip_centralization_rx(u32 dev_num); +int ddr3_tip_print_centralization_result(u32 dev_num); +int ddr3_tip_special_rx(u32 dev_num); + +#endif /* _DDR3_TRAINING_IP_CENTRALIZATION_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h new file mode 100644 index 0000000..c0afa77 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_db.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_DB_H_ +#define _DDR3_TRAINING_IP_DB_H_ + +enum hws_pattern { + PATTERN_PBS1, + PATTERN_PBS2, + PATTERN_RL, + PATTERN_STATIC_PBS, + PATTERN_KILLER_DQ0, + PATTERN_KILLER_DQ1, + PATTERN_KILLER_DQ2, + PATTERN_KILLER_DQ3, + PATTERN_KILLER_DQ4, + PATTERN_KILLER_DQ5, + PATTERN_KILLER_DQ6, + PATTERN_KILLER_DQ7, + PATTERN_PBS3, + PATTERN_RL2, + PATTERN_TEST, + PATTERN_FULL_SSO0, + PATTERN_FULL_SSO1, + PATTERN_FULL_SSO2, + PATTERN_FULL_SSO3, + PATTERN_VREF, + PATTERN_LIMIT +}; + +#endif /* _DDR3_TRAINING_IP_DB_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h new file mode 100644 index 0000000..51a66d8 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_def.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_DEF_H +#define _DDR3_TRAINING_IP_DEF_H + +#include "silicon_if.h" + +#define PATTERN_55 0x55555555 +#define PATTERN_AA 0xaaaaaaaa +#define PATTERN_80 0x80808080 +#define PATTERN_20 0x20202020 +#define PATTERN_01 0x01010101 +#define PATTERN_FF 0xffffffff +#define PATTERN_00 0x00000000 + +/* 16bit bus width patterns */ +#define PATTERN_55AA 0x5555aaaa +#define PATTERN_00FF 0x0000ffff +#define PATTERN_0080 0x00008080 + +#define INVALID_VALUE 0xffffffff +#define MAX_NUM_OF_DUNITS 32 +/* + * length *2 = length in words of pattern, first low address, + * second high address + */ +#define TEST_PATTERN_LENGTH 4 +#define KILLER_PATTERN_DQ_NUMBER 8 +#define SSO_DQ_NUMBER 4 +#define PATTERN_MAXIMUM_LENGTH 64 +#define ADLL_TX_LENGTH 64 +#define ADLL_RX_LENGTH 32 + +#define PARAM_NOT_CARE 0 + +#define READ_LEVELING_PHY_OFFSET 2 +#define WRITE_LEVELING_PHY_OFFSET 0 + +#define MASK_ALL_BITS 0xffffffff + +#define CS_BIT_MASK 0xf + +/* DFX access */ +#define BROADCAST_ID 28 +#define MULTICAST_ID 29 + +#define XSB_BASE_ADDR 0x00004000 +#define XSB_CTRL_0_REG 0x00000000 +#define XSB_CTRL_1_REG 0x00000004 +#define XSB_CMD_REG 0x00000008 +#define XSB_ADDRESS_REG 0x0000000c +#define XSB_DATA_REG 0x00000010 +#define PIPE_ENABLE_ADDR 0x000f8000 +#define ENABLE_DDR_TUNING_ADDR 0x000f829c + +#define CLIENT_BASE_ADDR 0x00002000 +#define CLIENT_CTRL_REG 0x00000000 + +#define TARGET_INT 0x1801 +#define TARGET_EXT 0x180e +#define BYTE_EN 0 +#define CMD_READ 0 +#define CMD_WRITE 1 + +#define INTERNAL_ACCESS_PORT 1 +#define EXECUTING 1 +#define ACCESS_EXT 1 +#define CS2_EXIST_BIT 2 +#define TRAINING_ID 0xf +#define EXT_TRAINING_ID 1 +#define EXT_MODE 0x4 + +#define GET_RESULT_STATE(res) (res) +#define SET_RESULT_STATE(res, state) (res = state) + +#define _1K 0x00000400 +#define _4K 0x00001000 +#define _8K 0x00002000 +#define _16K 0x00004000 +#define _32K 0x00008000 +#define _64K 0x00010000 +#define _128K 0x00020000 +#define _256K 0x00040000 +#define _512K 0x00080000 + +#define _1M 0x00100000 +#define _2M 0x00200000 +#define _4M 0x00400000 +#define _8M 0x00800000 +#define _16M 0x01000000 +#define _32M 0x02000000 +#define _64M 0x04000000 +#define _128M 0x08000000 +#define _256M 0x10000000 +#define _512M 0x20000000 + +#define _1G 0x40000000 +#define _2G 0x80000000 + +#define ADDR_SIZE_512MB 0x04000000 +#define ADDR_SIZE_1GB 0x08000000 +#define ADDR_SIZE_2GB 0x10000000 +#define ADDR_SIZE_4GB 0x20000000 +#define ADDR_SIZE_8GB 0x40000000 + +enum hws_edge_compare { + EDGE_PF, + EDGE_FP, + EDGE_FPF, + EDGE_PFP +}; + +enum hws_control_element { + HWS_CONTROL_ELEMENT_ADLL, /* per bit 1 edge */ + HWS_CONTROL_ELEMENT_DQ_SKEW, + HWS_CONTROL_ELEMENT_DQS_SKEW +}; + +enum hws_search_dir { + HWS_LOW2HIGH, + HWS_HIGH2LOW, + HWS_SEARCH_DIR_LIMIT +}; + +enum hws_page_size { + PAGE_SIZE_1K, + PAGE_SIZE_2K +}; + +enum hws_operation { + OPERATION_READ = 0, + OPERATION_WRITE = 1 +}; + +enum hws_training_ip_stat { + HWS_TRAINING_IP_STATUS_FAIL, + HWS_TRAINING_IP_STATUS_SUCCESS, + HWS_TRAINING_IP_STATUS_TIMEOUT +}; + +enum hws_ddr_cs { + CS_SINGLE, + CS_NON_SINGLE +}; + +enum hws_ddr_phy { + DDR_PHY_DATA = 0, + DDR_PHY_CONTROL = 1 +}; + +enum hws_dir { + OPER_WRITE, + OPER_READ, + OPER_WRITE_AND_READ +}; + +enum hws_wl_supp { + PHASE_SHIFT, + CLOCK_SHIFT, + ALIGN_SHIFT +}; + +struct reg_data { + u32 reg_addr; + u32 reg_data; + u32 reg_mask; +}; + +#endif /* _DDR3_TRAINING_IP_DEF_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c new file mode 100644 index 0000000..011824a --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.c @@ -0,0 +1,1354 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define PATTERN_1 0x55555555 +#define PATTERN_2 0xaaaaaaaa + +#define VALIDATE_TRAINING_LIMIT(e1, e2) \ + ((((e2) - (e1) + 1) > 33) && ((e1) < 67)) + +u32 phy_reg_bk[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; + +u32 training_res[MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS * + HWS_SEARCH_DIR_LIMIT]; + +u16 mask_results_dq_reg_map[] = { + RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG, + RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG, + RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG, + RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG, + RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG, + RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG, + RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG, + RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG, + RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG, + RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG, + RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG, + RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG, + RESULT_CONTROL_PUP_3_BIT_0_REG, RESULT_CONTROL_PUP_3_BIT_1_REG, + RESULT_CONTROL_PUP_3_BIT_2_REG, RESULT_CONTROL_PUP_3_BIT_3_REG, + RESULT_CONTROL_PUP_3_BIT_4_REG, RESULT_CONTROL_PUP_3_BIT_5_REG, + RESULT_CONTROL_PUP_3_BIT_6_REG, RESULT_CONTROL_PUP_3_BIT_7_REG, + RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG, + RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG, + RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG, + RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG, +}; + +u16 mask_results_pup_reg_map[] = { + RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG, + RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_3_REG, + RESULT_CONTROL_BYTE_PUP_4_REG +}; + +u16 mask_results_dq_reg_map_pup3_ecc[] = { + RESULT_CONTROL_PUP_0_BIT_0_REG, RESULT_CONTROL_PUP_0_BIT_1_REG, + RESULT_CONTROL_PUP_0_BIT_2_REG, RESULT_CONTROL_PUP_0_BIT_3_REG, + RESULT_CONTROL_PUP_0_BIT_4_REG, RESULT_CONTROL_PUP_0_BIT_5_REG, + RESULT_CONTROL_PUP_0_BIT_6_REG, RESULT_CONTROL_PUP_0_BIT_7_REG, + RESULT_CONTROL_PUP_1_BIT_0_REG, RESULT_CONTROL_PUP_1_BIT_1_REG, + RESULT_CONTROL_PUP_1_BIT_2_REG, RESULT_CONTROL_PUP_1_BIT_3_REG, + RESULT_CONTROL_PUP_1_BIT_4_REG, RESULT_CONTROL_PUP_1_BIT_5_REG, + RESULT_CONTROL_PUP_1_BIT_6_REG, RESULT_CONTROL_PUP_1_BIT_7_REG, + RESULT_CONTROL_PUP_2_BIT_0_REG, RESULT_CONTROL_PUP_2_BIT_1_REG, + RESULT_CONTROL_PUP_2_BIT_2_REG, RESULT_CONTROL_PUP_2_BIT_3_REG, + RESULT_CONTROL_PUP_2_BIT_4_REG, RESULT_CONTROL_PUP_2_BIT_5_REG, + RESULT_CONTROL_PUP_2_BIT_6_REG, RESULT_CONTROL_PUP_2_BIT_7_REG, + RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG, + RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG, + RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG, + RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG, + RESULT_CONTROL_PUP_4_BIT_0_REG, RESULT_CONTROL_PUP_4_BIT_1_REG, + RESULT_CONTROL_PUP_4_BIT_2_REG, RESULT_CONTROL_PUP_4_BIT_3_REG, + RESULT_CONTROL_PUP_4_BIT_4_REG, RESULT_CONTROL_PUP_4_BIT_5_REG, + RESULT_CONTROL_PUP_4_BIT_6_REG, RESULT_CONTROL_PUP_4_BIT_7_REG, +}; + +u16 mask_results_pup_reg_map_pup3_ecc[] = { + RESULT_CONTROL_BYTE_PUP_0_REG, RESULT_CONTROL_BYTE_PUP_1_REG, + RESULT_CONTROL_BYTE_PUP_2_REG, RESULT_CONTROL_BYTE_PUP_4_REG, + RESULT_CONTROL_BYTE_PUP_4_REG +}; + +struct pattern_info pattern_table_16[] = { + /* + * num tx phases, tx burst, delay between, rx pattern, + * start_address, pattern_len + */ + {1, 1, 2, 1, 0x0080, 2}, /* PATTERN_PBS1 */ + {1, 1, 2, 1, 0x00c0, 2}, /* PATTERN_PBS2 */ + {1, 1, 2, 1, 0x0100, 2}, /* PATTERN_RL */ + {0xf, 0x7, 2, 0x7, 0x0140, 16}, /* PATTERN_STATIC_PBS */ + {0xf, 0x7, 2, 0x7, 0x0190, 16}, /* PATTERN_KILLER_DQ0 */ + {0xf, 0x7, 2, 0x7, 0x01d0, 16}, /* PATTERN_KILLER_DQ1 */ + {0xf, 0x7, 2, 0x7, 0x0210, 16}, /* PATTERN_KILLER_DQ2 */ + {0xf, 0x7, 2, 0x7, 0x0250, 16}, /* PATTERN_KILLER_DQ3 */ + {0xf, 0x7, 2, 0x7, 0x0290, 16}, /* PATTERN_KILLER_DQ4 */ + {0xf, 0x7, 2, 0x7, 0x02d0, 16}, /* PATTERN_KILLER_DQ5 */ + {0xf, 0x7, 2, 0x7, 0x0310, 16}, /* PATTERN_KILLER_DQ6 */ + {0xf, 0x7, 2, 0x7, 0x0350, 16}, /* PATTERN_KILLER_DQ7 */ + {1, 1, 2, 1, 0x0380, 2}, /* PATTERN_PBS3 */ + {1, 1, 2, 1, 0x0000, 2}, /* PATTERN_RL2 */ + {1, 1, 2, 1, 0x0040, 2}, /* PATTERN_TEST */ + {0xf, 0x7, 2, 0x7, 0x03c0, 16}, /* PATTERN_FULL_SSO_1T */ + {0xf, 0x7, 2, 0x7, 0x0400, 16}, /* PATTERN_FULL_SSO_2T */ + {0xf, 0x7, 2, 0x7, 0x0440, 16}, /* PATTERN_FULL_SSO_3T */ + {0xf, 0x7, 2, 0x7, 0x0480, 16}, /* PATTERN_FULL_SSO_4T */ + {0xf, 0x7, 2, 0x7, 0x04c0, 16} /* PATTERN_VREF */ + /*Note: actual start_address is <<3 of defined addess */ +}; + +struct pattern_info pattern_table_32[] = { + /* + * num tx phases, tx burst, delay between, rx pattern, + * start_address, pattern_len + */ + {3, 3, 2, 3, 0x0080, 4}, /* PATTERN_PBS1 */ + {3, 3, 2, 3, 0x00c0, 4}, /* PATTERN_PBS2 */ + {3, 3, 2, 3, 0x0100, 4}, /* PATTERN_RL */ + {0x1f, 0xf, 2, 0xf, 0x0140, 32}, /* PATTERN_STATIC_PBS */ + {0x1f, 0xf, 2, 0xf, 0x0190, 32}, /* PATTERN_KILLER_DQ0 */ + {0x1f, 0xf, 2, 0xf, 0x01d0, 32}, /* PATTERN_KILLER_DQ1 */ + {0x1f, 0xf, 2, 0xf, 0x0210, 32}, /* PATTERN_KILLER_DQ2 */ + {0x1f, 0xf, 2, 0xf, 0x0250, 32}, /* PATTERN_KILLER_DQ3 */ + {0x1f, 0xf, 2, 0xf, 0x0290, 32}, /* PATTERN_KILLER_DQ4 */ + {0x1f, 0xf, 2, 0xf, 0x02d0, 32}, /* PATTERN_KILLER_DQ5 */ + {0x1f, 0xf, 2, 0xf, 0x0310, 32}, /* PATTERN_KILLER_DQ6 */ + {0x1f, 0xf, 2, 0xf, 0x0350, 32}, /* PATTERN_KILLER_DQ7 */ + {3, 3, 2, 3, 0x0380, 4}, /* PATTERN_PBS3 */ + {3, 3, 2, 3, 0x0000, 4}, /* PATTERN_RL2 */ + {3, 3, 2, 3, 0x0040, 4}, /* PATTERN_TEST */ + {0x1f, 0xf, 2, 0xf, 0x03c0, 32}, /* PATTERN_FULL_SSO_1T */ + {0x1f, 0xf, 2, 0xf, 0x0400, 32}, /* PATTERN_FULL_SSO_2T */ + {0x1f, 0xf, 2, 0xf, 0x0440, 32}, /* PATTERN_FULL_SSO_3T */ + {0x1f, 0xf, 2, 0xf, 0x0480, 32}, /* PATTERN_FULL_SSO_4T */ + {0x1f, 0xf, 2, 0xf, 0x04c0, 32} /* PATTERN_VREF */ + /*Note: actual start_address is <<3 of defined addess */ +}; + +u32 train_dev_num; +enum hws_ddr_cs traintrain_cs_type; +u32 train_pup_num; +enum hws_training_result train_result_type; +enum hws_control_element train_control_element; +enum hws_search_dir traine_search_dir; +enum hws_dir train_direction; +u32 train_if_select; +u32 train_init_value; +u32 train_number_iterations; +enum hws_pattern train_pattern; +enum hws_edge_compare train_edge_compare; +u32 train_cs_num; +u32 train_if_acess, train_if_id, train_pup_access; +u32 max_polling_for_done = 1000000; + +u32 *ddr3_tip_get_buf_ptr(u32 dev_num, enum hws_search_dir search, + enum hws_training_result result_type, + u32 interface_num) +{ + u32 *buf_ptr = NULL; + + buf_ptr = &training_res + [MAX_INTERFACE_NUM * MAX_BUS_NUM * BUS_WIDTH_IN_BITS * search + + interface_num * MAX_BUS_NUM * BUS_WIDTH_IN_BITS]; + + return buf_ptr; +} + +/* + * IP Training search + * Note: for one edge search only from fail to pass, else jitter can + * be be entered into solution. + */ +int ddr3_tip_ip_training(u32 dev_num, enum hws_access_type access_type, + u32 interface_num, + enum hws_access_type pup_access_type, + u32 pup_num, enum hws_training_result result_type, + enum hws_control_element control_element, + enum hws_search_dir search_dir, enum hws_dir direction, + u32 interface_mask, u32 init_value, u32 num_iter, + enum hws_pattern pattern, + enum hws_edge_compare edge_comp, + enum hws_ddr_cs cs_type, u32 cs_num, + enum hws_training_ip_stat *train_status) +{ + u32 mask_dq_num_of_regs, mask_pup_num_of_regs, index_cnt, poll_cnt, + reg_data, pup_id; + u32 tx_burst_size; + u32 delay_between_burst; + u32 rd_mode; + u32 read_data[MAX_INTERFACE_NUM]; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (pup_num >= tm->num_of_bus_per_interface) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("pup_num %d not valid\n", pup_num)); + } + if (interface_num >= MAX_INTERFACE_NUM) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("if_id %d not valid\n", + interface_num)); + } + if (train_status == NULL) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("error param 4\n")); + return MV_BAD_PARAM; + } + + /* load pattern */ + if (cs_type == CS_SINGLE) { + /* All CSs to CS0 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + CS_ENABLE_REG, 1 << 3, 1 << 3)); + /* All CSs to CS0 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + ODPG_DATA_CONTROL_REG, + (0x3 | (effective_cs << 26)), 0xc000003)); + } else { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + CS_ENABLE_REG, 0, 1 << 3)); + /* CS select */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + ODPG_DATA_CONTROL_REG, 0x3 | cs_num << 26, + 0x3 | 3 << 26)); + } + + /* load pattern to ODPG */ + ddr3_tip_load_pattern_to_odpg(dev_num, access_type, interface_num, + pattern, + pattern_table[pattern].start_addr); + tx_burst_size = (direction == OPER_WRITE) ? + pattern_table[pattern].tx_burst_size : 0; + delay_between_burst = (direction == OPER_WRITE) ? 2 : 0; + rd_mode = (direction == OPER_WRITE) ? 1 : 0; + CHECK_STATUS(ddr3_tip_configure_odpg + (dev_num, access_type, interface_num, direction, + pattern_table[pattern].num_of_phases_tx, tx_burst_size, + pattern_table[pattern].num_of_phases_rx, + delay_between_burst, rd_mode, effective_cs, STRESS_NONE, + DURATION_SINGLE)); + reg_data = (direction == OPER_READ) ? 0 : (0x3 << 30); + reg_data |= (direction == OPER_READ) ? 0x60 : 0xfa; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + ODPG_WRITE_READ_MODE_ENABLE_REG, reg_data, + MASK_ALL_BITS)); + reg_data = (edge_comp == EDGE_PF || edge_comp == EDGE_FP) ? 0 : 1 << 6; + reg_data |= (edge_comp == EDGE_PF || edge_comp == EDGE_PFP) ? + (1 << 7) : 0; + + /* change from Pass to Fail will lock the result */ + if (pup_access_type == ACCESS_TYPE_MULTICAST) + reg_data |= 0xe << 14; + else + reg_data |= pup_num << 14; + + if (edge_comp == EDGE_FP) { + /* don't search for readl edge change, only the state */ + reg_data |= (0 << 20); + } else if (edge_comp == EDGE_FPF) { + reg_data |= (0 << 20); + } else { + reg_data |= (3 << 20); + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + ODPG_TRAINING_CONTROL_REG, + reg_data | (0x7 << 8) | (0x7 << 11), + (0x3 | (0x3 << 2) | (0x3 << 6) | (1 << 5) | (0x7 << 8) | + (0x7 << 11) | (0xf << 14) | (0x3 << 18) | (3 << 20)))); + reg_data = (search_dir == HWS_LOW2HIGH) ? 0 : (1 << 8); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, ODPG_OBJ1_OPCODE_REG, + 1 | reg_data | init_value << 9 | (1 << 25) | (1 << 26), + 0xff | (1 << 8) | (0xffff << 9) | (1 << 25) | (1 << 26))); + + /* + * Write2_dunit(0x10b4, Number_iteration , [15:0]) + * Max number of iterations + */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, interface_num, + ODPG_OBJ1_ITER_CNT_REG, num_iter, + 0xffff)); + if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW && + direction == OPER_READ) { + /* + * Write2_dunit(0x10c0, 0x5f , [7:0]) + * MC PBS Reg Address at DDR PHY + */ + reg_data = 0x5f + + effective_cs * CALIBRATED_OBJECTS_REG_ADDR_OFFSET; + } else if (control_element == HWS_CONTROL_ELEMENT_DQ_SKEW && + direction == OPER_WRITE) { + reg_data = 0x1f + + effective_cs * CALIBRATED_OBJECTS_REG_ADDR_OFFSET; + } else if (control_element == HWS_CONTROL_ELEMENT_ADLL && + direction == OPER_WRITE) { + /* + * LOOP 0x00000001 + 4*n: + * where n (0-3) represents M_CS number + */ + /* + * Write2_dunit(0x10c0, 0x1 , [7:0]) + * ADLL WR Reg Address at DDR PHY + */ + reg_data = 1 + effective_cs * CS_REGISTER_ADDR_OFFSET; + } else if (control_element == HWS_CONTROL_ELEMENT_ADLL && + direction == OPER_READ) { + /* ADLL RD Reg Address at DDR PHY */ + reg_data = 3 + effective_cs * CS_REGISTER_ADDR_OFFSET; + } else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW && + direction == OPER_WRITE) { + /* TBD not defined in 0.5.0 requirement */ + } else if (control_element == HWS_CONTROL_ELEMENT_DQS_SKEW && + direction == OPER_READ) { + /* TBD not defined in 0.5.0 requirement */ + } + + reg_data |= (0x6 << 28); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, CALIB_OBJ_PRFA_REG, + reg_data | (init_value << 8), + 0xff | (0xffff << 8) | (0xf << 24) | (u32) (0xf << 28))); + + mask_dq_num_of_regs = tm->num_of_bus_per_interface * BUS_WIDTH_IN_BITS; + mask_pup_num_of_regs = tm->num_of_bus_per_interface; + + if (result_type == RESULT_PER_BIT) { + for (index_cnt = 0; index_cnt < mask_dq_num_of_regs; + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + mask_results_dq_reg_map[index_cnt], 0, + 1 << 24)); + } + + /* Mask disabled buses */ + for (pup_id = 0; pup_id < tm->num_of_bus_per_interface; + pup_id++) { + if (IS_ACTIVE(tm->bus_act_mask, pup_id) == 1) + continue; + + for (index_cnt = (mask_dq_num_of_regs - pup_id * 8); + index_cnt < + (mask_dq_num_of_regs - (pup_id + 1) * 8); + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, + interface_num, + mask_results_dq_reg_map + [index_cnt], (1 << 24), 1 << 24)); + } + } + + for (index_cnt = 0; index_cnt < mask_pup_num_of_regs; + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + mask_results_pup_reg_map[index_cnt], + (1 << 24), 1 << 24)); + } + } else if (result_type == RESULT_PER_BYTE) { + /* write to adll */ + for (index_cnt = 0; index_cnt < mask_pup_num_of_regs; + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + mask_results_pup_reg_map[index_cnt], 0, + 1 << 24)); + } + for (index_cnt = 0; index_cnt < mask_dq_num_of_regs; + index_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, interface_num, + mask_results_dq_reg_map[index_cnt], + (1 << 24), (1 << 24))); + } + } + + /* Start Training Trigger */ + CHECK_STATUS(ddr3_tip_if_write(dev_num, access_type, interface_num, + ODPG_TRAINING_TRIGGER_REG, 1, 1)); + /* wait for all RFU tests to finish (or timeout) */ + /* WA for 16 bit mode, more investigation needed */ + mdelay(1); + + /* Training "Done ?" */ + for (index_cnt = 0; index_cnt < MAX_INTERFACE_NUM; index_cnt++) { + if (IS_ACTIVE(tm->if_act_mask, index_cnt) == 0) + continue; + + if (interface_mask & (1 << index_cnt)) { + /* need to check results for this Dunit */ + for (poll_cnt = 0; poll_cnt < max_polling_for_done; + poll_cnt++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + index_cnt, + ODPG_TRAINING_STATUS_REG, + ®_data, MASK_ALL_BITS)); + if ((reg_data & 0x2) != 0) { + /*done */ + train_status[index_cnt] = + HWS_TRAINING_IP_STATUS_SUCCESS; + break; + } + } + + if (poll_cnt == max_polling_for_done) { + train_status[index_cnt] = + HWS_TRAINING_IP_STATUS_TIMEOUT; + } + } + /* Be sure that ODPG done */ + CHECK_STATUS(is_odpg_access_done(dev_num, index_cnt)); + } + + /* Write ODPG done in Dunit */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_STATUS_DONE_REG, 0, 0x1)); + + /* wait for all Dunit tests to finish (or timeout) */ + /* Training "Done ?" */ + /* Training "Pass ?" */ + for (index_cnt = 0; index_cnt < MAX_INTERFACE_NUM; index_cnt++) { + if (IS_ACTIVE(tm->if_act_mask, index_cnt) == 0) + continue; + + if (interface_mask & (1 << index_cnt)) { + /* need to check results for this Dunit */ + for (poll_cnt = 0; poll_cnt < max_polling_for_done; + poll_cnt++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + index_cnt, + ODPG_TRAINING_TRIGGER_REG, + read_data, MASK_ALL_BITS)); + reg_data = read_data[index_cnt]; + if ((reg_data & 0x2) != 0) { + /* done */ + if ((reg_data & 0x4) == 0) { + train_status[index_cnt] = + HWS_TRAINING_IP_STATUS_SUCCESS; + } else { + train_status[index_cnt] = + HWS_TRAINING_IP_STATUS_FAIL; + } + break; + } + } + + if (poll_cnt == max_polling_for_done) { + train_status[index_cnt] = + HWS_TRAINING_IP_STATUS_TIMEOUT; + } + } + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS)); + + return MV_OK; +} + +/* + * Load expected Pattern to ODPG + */ +int ddr3_tip_load_pattern_to_odpg(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_pattern pattern, + u32 load_addr) +{ + u32 pattern_length_cnt = 0; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + + for (pattern_length_cnt = 0; + pattern_length_cnt < pattern_table[pattern].pattern_len; + pattern_length_cnt++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + ODPG_PATTERN_DATA_LOW_REG, + pattern_table_get_word(dev_num, pattern, + (u8) (pattern_length_cnt * + 2)), MASK_ALL_BITS)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + ODPG_PATTERN_DATA_HI_REG, + pattern_table_get_word(dev_num, pattern, + (u8) (pattern_length_cnt * + 2 + 1)), + MASK_ALL_BITS)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + ODPG_PATTERN_ADDR_REG, pattern_length_cnt, + MASK_ALL_BITS)); + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, access_type, if_id, + ODPG_PATTERN_ADDR_OFFSET_REG, load_addr, MASK_ALL_BITS)); + + return MV_OK; +} + +/* + * Configure ODPG + */ +int ddr3_tip_configure_odpg(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_dir direction, u32 tx_phases, + u32 tx_burst_size, u32 rx_phases, + u32 delay_between_burst, u32 rd_mode, u32 cs_num, + u32 addr_stress_jump, u32 single_pattern) +{ + u32 data_value = 0; + int ret; + + data_value = ((single_pattern << 2) | (tx_phases << 5) | + (tx_burst_size << 11) | (delay_between_burst << 15) | + (rx_phases << 21) | (rd_mode << 25) | (cs_num << 26) | + (addr_stress_jump << 29)); + ret = ddr3_tip_if_write(dev_num, access_type, if_id, + ODPG_DATA_CONTROL_REG, data_value, 0xaffffffc); + if (ret != MV_OK) + return ret; + + return MV_OK; +} + +int ddr3_tip_process_result(u32 *ar_result, enum hws_edge e_edge, + enum hws_edge_search e_edge_search, + u32 *edge_result) +{ + u32 i, res; + int tap_val, max_val = -10000, min_val = 10000; + int lock_success = 1; + + for (i = 0; i < BUS_WIDTH_IN_BITS; i++) { + res = GET_LOCK_RESULT(ar_result[i]); + if (res == 0) { + lock_success = 0; + break; + } + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("lock failed for bit %d\n", i)); + } + + if (lock_success == 1) { + for (i = 0; i < BUS_WIDTH_IN_BITS; i++) { + tap_val = GET_TAP_RESULT(ar_result[i], e_edge); + if (tap_val > max_val) + max_val = tap_val; + if (tap_val < min_val) + min_val = tap_val; + if (e_edge_search == TRAINING_EDGE_MAX) + *edge_result = (u32) max_val; + else + *edge_result = (u32) min_val; + + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("i %d ar_result[i] 0x%x tap_val %d max_val %d min_val %d Edge_result %d\n", + i, ar_result[i], tap_val, + max_val, min_val, + *edge_result)); + } + } else { + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Read training search result + */ +int ddr3_tip_read_training_result(u32 dev_num, u32 if_id, + enum hws_access_type pup_access_type, + u32 pup_num, u32 bit_num, + enum hws_search_dir search, + enum hws_dir direction, + enum hws_training_result result_type, + enum hws_training_load_op operation, + u32 cs_num_type, u32 **load_res, + int is_read_from_db, u8 cons_tap, + int is_check_result_validity) +{ + u32 reg_offset, pup_cnt, start_pup, end_pup, start_reg, end_reg; + u32 *interface_train_res = NULL; + u16 *reg_addr = NULL; + u32 read_data[MAX_INTERFACE_NUM]; + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * Agreed assumption: all CS mask contain same number of bits, + * i.e. in multi CS, the number of CS per memory is the same for + * all pups + */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, CS_ENABLE_REG, + (cs_num_type == 0) ? 1 << 3 : 0, (1 << 3))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_DATA_CONTROL_REG, (cs_num_type << 26), (3 << 26))); + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE, + ("Read_from_d_b %d cs_type %d oper %d result_type %d direction %d search %d pup_num %d if_id %d pup_access_type %d\n", + is_read_from_db, cs_num_type, operation, + result_type, direction, search, pup_num, + if_id, pup_access_type)); + + if ((load_res == NULL) && (is_read_from_db == 1)) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("ddr3_tip_read_training_result load_res = NULL")); + return MV_FAIL; + } + if (pup_num >= tm->num_of_bus_per_interface) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("pup_num %d not valid\n", pup_num)); + } + if (if_id >= MAX_INTERFACE_NUM) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("if_id %d not valid\n", if_id)); + } + if (result_type == RESULT_PER_BIT) + reg_addr = mask_results_dq_reg_map; + else + reg_addr = mask_results_pup_reg_map; + if (pup_access_type == ACCESS_TYPE_UNICAST) { + start_pup = pup_num; + end_pup = pup_num; + } else { /*pup_access_type == ACCESS_TYPE_MULTICAST) */ + + start_pup = 0; + end_pup = tm->num_of_bus_per_interface - 1; + } + + for (pup_cnt = start_pup; pup_cnt <= end_pup; pup_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup_cnt); + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_TRACE, + ("if_id %d start_pup %d end_pup %d pup_cnt %d\n", + if_id, start_pup, end_pup, pup_cnt)); + if (result_type == RESULT_PER_BIT) { + if (bit_num == ALL_BITS_PER_PUP) { + start_reg = pup_cnt * BUS_WIDTH_IN_BITS; + end_reg = (pup_cnt + 1) * BUS_WIDTH_IN_BITS - 1; + } else { + start_reg = + pup_cnt * BUS_WIDTH_IN_BITS + bit_num; + end_reg = pup_cnt * BUS_WIDTH_IN_BITS + bit_num; + } + } else { + start_reg = pup_cnt; + end_reg = pup_cnt; + } + + interface_train_res = + ddr3_tip_get_buf_ptr(dev_num, search, result_type, + if_id); + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_TRACE, + ("start_reg %d end_reg %d interface %p\n", + start_reg, end_reg, interface_train_res)); + if (interface_train_res == NULL) { + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_ERROR, + ("interface_train_res is NULL\n")); + return MV_FAIL; + } + + for (reg_offset = start_reg; reg_offset <= end_reg; + reg_offset++) { + if (operation == TRAINING_LOAD_OPERATION_UNLOAD) { + if (is_read_from_db == 0) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + reg_addr[reg_offset], + read_data, + MASK_ALL_BITS)); + if (is_check_result_validity == 1) { + if ((read_data[if_id] & + 0x02000000) == 0) { + interface_train_res + [reg_offset] = + 0x02000000 + + 64 + cons_tap; + } else { + interface_train_res + [reg_offset] = + read_data + [if_id] + + cons_tap; + } + } else { + interface_train_res[reg_offset] + = read_data[if_id] + + cons_tap; + } + DEBUG_TRAINING_IP_ENGINE + (DEBUG_LEVEL_TRACE, + ("reg_offset %d value 0x%x addr %p\n", + reg_offset, + interface_train_res + [reg_offset], + &interface_train_res + [reg_offset])); + } else { + *load_res = + &interface_train_res[start_reg]; + DEBUG_TRAINING_IP_ENGINE + (DEBUG_LEVEL_TRACE, + ("*load_res %p\n", *load_res)); + } + } else { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_TRACE, + ("not supported\n")); + } + } + } + + return MV_OK; +} + +/* + * Load all pattern to memory using ODPG + */ +int ddr3_tip_load_all_pattern_to_mem(u32 dev_num) +{ + u32 pattern = 0, if_id; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + training_result[training_stage][if_id] = TEST_SUCCESS; + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, (1 << 3), (1 << 3))); + } + + for (pattern = 0; pattern < PATTERN_LIMIT; pattern++) + ddr3_tip_load_pattern_to_mem(dev_num, pattern); + + return MV_OK; +} + +/* + * Wait till ODPG access is ready + */ +int is_odpg_access_done(u32 dev_num, u32 if_id) +{ + u32 poll_cnt = 0, data_value; + u32 read_data[MAX_INTERFACE_NUM]; + + for (poll_cnt = 0; poll_cnt < MAX_POLLING_ITERATIONS; poll_cnt++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_BIST_DONE, read_data, MASK_ALL_BITS)); + data_value = read_data[if_id]; + if (((data_value >> ODPG_BIST_DONE_BIT_OFFS) & 0x1) == + ODPG_BIST_DONE_BIT_VALUE) { + data_value = data_value & 0xfffffffe; + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ODPG_BIST_DONE, data_value, + MASK_ALL_BITS)); + break; + } + } + + if (poll_cnt >= MAX_POLLING_ITERATIONS) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("Bist Activate: poll failure 2\n")); + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Load specific pattern to memory using ODPG + */ +int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern pattern) +{ + u32 reg_data, if_id; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* load pattern to memory */ + /* + * Write Tx mode, CS0, phases, Tx burst size, delay between burst, + * rx pattern phases + */ + reg_data = + 0x1 | (pattern_table[pattern].num_of_phases_tx << 5) | + (pattern_table[pattern].tx_burst_size << 11) | + (pattern_table[pattern].delay_between_bursts << 15) | + (pattern_table[pattern].num_of_phases_rx << 21) | (0x1 << 25) | + (effective_cs << 26); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, reg_data, MASK_ALL_BITS)); + /* ODPG Write enable from BIST */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, (0x1 | (effective_cs << 26)), + 0xc000003)); + /* disable error injection */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_WRITE_DATA_ERROR_REG, 0, 0x1)); + /* load pattern to ODPG */ + ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, pattern, + pattern_table[pattern].start_addr); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + if (IS_ACTIVE(tm->if_act_mask, if_id) == 0) + continue; + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0x1498, + 0x3, 0xf)); + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_ENABLE_REG, 0x1 << ODPG_ENABLE_OFFS, + (0x1 << ODPG_ENABLE_OFFS))); + + mdelay(1); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(is_odpg_access_done(dev_num, if_id)); + } + + /* Disable ODPG and stop write to memory */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, (0x1 << 30), (u32) (0x3 << 30))); + + /* return to default */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS)); + + /* Disable odt0 for CS0 training - need to adjust for multy CS */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x1498, + 0x0, 0xf)); + + /* temporary added */ + mdelay(1); + + return MV_OK; +} + +/* + * Load specific pattern to memory using CPU + */ +int ddr3_tip_load_pattern_to_mem_by_cpu(u32 dev_num, enum hws_pattern pattern, + u32 offset) +{ + /* eranba - TBD */ + return MV_OK; +} + +/* + * Training search routine + */ +int ddr3_tip_ip_training_wrapper_int(u32 dev_num, + enum hws_access_type access_type, + u32 if_id, + enum hws_access_type pup_access_type, + u32 pup_num, u32 bit_num, + enum hws_training_result result_type, + enum hws_control_element control_element, + enum hws_search_dir search_dir, + enum hws_dir direction, + u32 interface_mask, u32 init_value_l2h, + u32 init_value_h2l, u32 num_iter, + enum hws_pattern pattern, + enum hws_edge_compare edge_comp, + enum hws_ddr_cs train_cs_type, u32 cs_num, + enum hws_training_ip_stat *train_status) +{ + u32 interface_num = 0, start_if, end_if, init_value_used; + enum hws_search_dir search_dir_id, start_search, end_search; + enum hws_edge_compare edge_comp_used; + u8 cons_tap = (direction == OPER_WRITE) ? (64) : (0); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (train_status == NULL) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("train_status is NULL\n")); + return MV_FAIL; + } + + if ((train_cs_type > CS_NON_SINGLE) || + (edge_comp >= EDGE_PFP) || + (pattern >= PATTERN_LIMIT) || + (direction > OPER_WRITE_AND_READ) || + (search_dir > HWS_HIGH2LOW) || + (control_element > HWS_CONTROL_ELEMENT_DQS_SKEW) || + (result_type > RESULT_PER_BYTE) || + (pup_num >= tm->num_of_bus_per_interface) || + (pup_access_type > ACCESS_TYPE_MULTICAST) || + (if_id > 11) || (access_type > ACCESS_TYPE_MULTICAST)) { + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_ERROR, + ("wrong parameter train_cs_type %d edge_comp %d pattern %d direction %d search_dir %d control_element %d result_type %d pup_num %d pup_access_type %d if_id %d access_type %d\n", + train_cs_type, edge_comp, pattern, direction, + search_dir, control_element, result_type, pup_num, + pup_access_type, if_id, access_type)); + return MV_FAIL; + } + + if (edge_comp == EDGE_FPF) { + start_search = HWS_LOW2HIGH; + end_search = HWS_HIGH2LOW; + edge_comp_used = EDGE_FP; + } else { + start_search = search_dir; + end_search = search_dir; + edge_comp_used = edge_comp; + } + + for (search_dir_id = start_search; search_dir_id <= end_search; + search_dir_id++) { + init_value_used = (search_dir_id == HWS_LOW2HIGH) ? + init_value_l2h : init_value_h2l; + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_TRACE, + ("dev_num %d, access_type %d, if_id %d, pup_access_type %d,pup_num %d, result_type %d, control_element %d search_dir_id %d, direction %d, interface_mask %d,init_value_used %d, num_iter %d, pattern %d, edge_comp_used %d, train_cs_type %d, cs_num %d\n", + dev_num, access_type, if_id, pup_access_type, pup_num, + result_type, control_element, search_dir_id, + direction, interface_mask, init_value_used, num_iter, + pattern, edge_comp_used, train_cs_type, cs_num)); + + ddr3_tip_ip_training(dev_num, access_type, if_id, + pup_access_type, pup_num, result_type, + control_element, search_dir_id, direction, + interface_mask, init_value_used, num_iter, + pattern, edge_comp_used, train_cs_type, + cs_num, train_status); + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = if_id; + end_if = if_id; + } + + for (interface_num = start_if; interface_num <= end_if; + interface_num++) { + VALIDATE_ACTIVE(tm->if_act_mask, interface_num); + cs_num = 0; + CHECK_STATUS(ddr3_tip_read_training_result + (dev_num, interface_num, pup_access_type, + pup_num, bit_num, search_dir_id, + direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + train_cs_type, NULL, 0, cons_tap, + 0)); + } + } + + return MV_OK; +} + +/* + * Training search & read result routine + */ +int ddr3_tip_ip_training_wrapper(u32 dev_num, enum hws_access_type access_type, + u32 if_id, + enum hws_access_type pup_access_type, + u32 pup_num, + enum hws_training_result result_type, + enum hws_control_element control_element, + enum hws_search_dir search_dir, + enum hws_dir direction, u32 interface_mask, + u32 init_value_l2h, u32 init_value_h2l, + u32 num_iter, enum hws_pattern pattern, + enum hws_edge_compare edge_comp, + enum hws_ddr_cs train_cs_type, u32 cs_num, + enum hws_training_ip_stat *train_status) +{ + u8 e1, e2; + u32 interface_cnt, bit_id, start_if, end_if, bit_end = 0; + u32 *result[HWS_SEARCH_DIR_LIMIT] = { 0 }; + u8 cons_tap = (direction == OPER_WRITE) ? (64) : (0); + u8 bit_bit_mask[MAX_BUS_NUM] = { 0 }, bit_bit_mask_active = 0; + u8 pup_id; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (pup_num >= tm->num_of_bus_per_interface) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("pup_num %d not valid\n", pup_num)); + } + + if (if_id >= MAX_INTERFACE_NUM) { + DEBUG_TRAINING_IP_ENGINE(DEBUG_LEVEL_ERROR, + ("if_id %d not valid\n", if_id)); + } + + CHECK_STATUS(ddr3_tip_ip_training_wrapper_int + (dev_num, access_type, if_id, pup_access_type, pup_num, + ALL_BITS_PER_PUP, result_type, control_element, + search_dir, direction, interface_mask, init_value_l2h, + init_value_h2l, num_iter, pattern, edge_comp, + train_cs_type, cs_num, train_status)); + + if (access_type == ACCESS_TYPE_MULTICAST) { + start_if = 0; + end_if = MAX_INTERFACE_NUM - 1; + } else { + start_if = if_id; + end_if = if_id; + } + + for (interface_cnt = start_if; interface_cnt <= end_if; + interface_cnt++) { + VALIDATE_ACTIVE(tm->if_act_mask, interface_cnt); + for (pup_id = 0; + pup_id <= (tm->num_of_bus_per_interface - 1); pup_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup_id); + if (result_type == RESULT_PER_BIT) + bit_end = BUS_WIDTH_IN_BITS - 1; + else + bit_end = 0; + + bit_bit_mask[pup_id] = 0; + for (bit_id = 0; bit_id <= bit_end; bit_id++) { + enum hws_search_dir search_dir_id; + for (search_dir_id = HWS_LOW2HIGH; + search_dir_id <= HWS_HIGH2LOW; + search_dir_id++) { + CHECK_STATUS + (ddr3_tip_read_training_result + (dev_num, interface_cnt, + ACCESS_TYPE_UNICAST, pup_id, + bit_id, search_dir_id, + direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, + &result[search_dir_id], + 1, 0, 0)); + } + e1 = GET_TAP_RESULT(result[HWS_LOW2HIGH][0], + EDGE_1); + e2 = GET_TAP_RESULT(result[HWS_HIGH2LOW][0], + EDGE_1); + DEBUG_TRAINING_IP_ENGINE( + DEBUG_LEVEL_INFO, + ("wrapper if_id %d pup_id %d bit %d l2h 0x%x (e1 0x%x) h2l 0x%x (e2 0x%x)\n", + interface_cnt, pup_id, bit_id, + result[HWS_LOW2HIGH][0], e1, + result[HWS_HIGH2LOW][0], e2)); + /* TBD validate is valid only for tx */ + if (VALIDATE_TRAINING_LIMIT(e1, e2) == 1 && + GET_LOCK_RESULT(result[HWS_LOW2HIGH][0]) && + GET_LOCK_RESULT(result[HWS_LOW2HIGH][0])) { + /* Mark problem bits */ + bit_bit_mask[pup_id] |= 1 << bit_id; + bit_bit_mask_active = 1; + } + } /* For all bits */ + } /* For all PUPs */ + + /* Fix problem bits */ + if (bit_bit_mask_active != 0) { + u32 *l2h_if_train_res = NULL; + u32 *h2l_if_train_res = NULL; + l2h_if_train_res = + ddr3_tip_get_buf_ptr(dev_num, HWS_LOW2HIGH, + result_type, + interface_cnt); + h2l_if_train_res = + ddr3_tip_get_buf_ptr(dev_num, HWS_HIGH2LOW, + result_type, + interface_cnt); + + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_UNICAST, + interface_cnt, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, + control_element, HWS_LOW2HIGH, + direction, interface_mask, + num_iter / 2, num_iter / 2, + pattern, EDGE_FP, train_cs_type, + cs_num, train_status); + + for (pup_id = 0; + pup_id <= (tm->num_of_bus_per_interface - 1); + pup_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup_id); + + if (bit_bit_mask[pup_id] == 0) + continue; + + for (bit_id = 0; bit_id <= bit_end; bit_id++) { + if ((bit_bit_mask[pup_id] & + (1 << bit_id)) == 0) + continue; + CHECK_STATUS + (ddr3_tip_read_training_result + (dev_num, interface_cnt, + ACCESS_TYPE_UNICAST, pup_id, + bit_id, HWS_LOW2HIGH, + direction, + result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &l2h_if_train_res, + 0, 0, 1)); + } + } + + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_UNICAST, + interface_cnt, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, result_type, + control_element, HWS_HIGH2LOW, + direction, interface_mask, + num_iter / 2, num_iter / 2, + pattern, EDGE_FP, train_cs_type, + cs_num, train_status); + + for (pup_id = 0; + pup_id <= (tm->num_of_bus_per_interface - 1); + pup_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup_id); + + if (bit_bit_mask[pup_id] == 0) + continue; + + for (bit_id = 0; bit_id <= bit_end; bit_id++) { + if ((bit_bit_mask[pup_id] & + (1 << bit_id)) == 0) + continue; + CHECK_STATUS + (ddr3_tip_read_training_result + (dev_num, interface_cnt, + ACCESS_TYPE_UNICAST, pup_id, + bit_id, HWS_HIGH2LOW, direction, + result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &h2l_if_train_res, + 0, cons_tap, 1)); + } + } + } /* if bit_bit_mask_active */ + } /* For all Interfacess */ + + return MV_OK; +} + +/* + * Load phy values + */ +int ddr3_tip_load_phy_values(int b_load) +{ + u32 bus_cnt = 0, if_id, dev_num = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_cnt = 0; bus_cnt < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + if (b_load == 1) { + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_cnt, + DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + &phy_reg_bk[if_id][bus_cnt] + [0])); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_cnt, + DDR_PHY_DATA, + RL_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + &phy_reg_bk[if_id][bus_cnt] + [1])); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, bus_cnt, + DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + &phy_reg_bk[if_id][bus_cnt] + [2])); + } else { + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + phy_reg_bk[if_id][bus_cnt] + [0])); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, + RL_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + phy_reg_bk[if_id][bus_cnt] + [1])); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, + bus_cnt, DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG + + (effective_cs * + CS_REGISTER_ADDR_OFFSET), + phy_reg_bk[if_id][bus_cnt] + [2])); + } + } + } + + return MV_OK; +} + +int ddr3_tip_training_ip_test(u32 dev_num, enum hws_training_result result_type, + enum hws_search_dir search_dir, + enum hws_dir direction, + enum hws_edge_compare edge, + u32 init_val1, u32 init_val2, + u32 num_of_iterations, + u32 start_pattern, u32 end_pattern) +{ + u32 pattern, if_id, pup_id; + enum hws_training_ip_stat train_status[MAX_INTERFACE_NUM]; + u32 *res = NULL; + u32 search_state = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + ddr3_tip_load_phy_values(1); + + for (pattern = start_pattern; pattern <= end_pattern; pattern++) { + for (search_state = 0; search_state < HWS_SEARCH_DIR_LIMIT; + search_state++) { + ddr3_tip_ip_training_wrapper(dev_num, + ACCESS_TYPE_MULTICAST, 0, + ACCESS_TYPE_MULTICAST, 0, + result_type, + HWS_CONTROL_ELEMENT_ADLL, + search_dir, direction, + 0xfff, init_val1, + init_val2, + num_of_iterations, pattern, + edge, CS_SINGLE, + PARAM_NOT_CARE, + train_status); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup_id = 0; pup_id < + tm->num_of_bus_per_interface; + pup_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, + pup_id); + CHECK_STATUS + (ddr3_tip_read_training_result + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup_id, + ALL_BITS_PER_PUP, + search_state, + direction, result_type, + TRAINING_LOAD_OPERATION_UNLOAD, + CS_SINGLE, &res, 1, 0, + 0)); + if (result_type == RESULT_PER_BYTE) { + DEBUG_TRAINING_IP_ENGINE + (DEBUG_LEVEL_INFO, + ("search_state %d if_id %d pup_id %d 0x%x\n", + search_state, if_id, + pup_id, res[0])); + } else { + DEBUG_TRAINING_IP_ENGINE + (DEBUG_LEVEL_INFO, + ("search_state %d if_id %d pup_id %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + search_state, if_id, + pup_id, res[0], + res[1], res[2], + res[3], res[4], + res[5], res[6], + res[7])); + } + } + } /* interface */ + } /* search */ + } /* pattern */ + + ddr3_tip_load_phy_values(0); + + return MV_OK; +} + +struct pattern_info *ddr3_tip_get_pattern_table() +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (DDR3_IS_16BIT_DRAM_MODE(tm->bus_act_mask) == 0) + return pattern_table_32; + else + return pattern_table_16; +} + +u16 *ddr3_tip_get_mask_results_dq_reg() +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) + return mask_results_dq_reg_map_pup3_ecc; + else + return mask_results_dq_reg_map; +} + +u16 *ddr3_tip_get_mask_results_pup_reg_map() +{ + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (DDR3_IS_ECC_PUP3_MODE(tm->bus_act_mask)) + return mask_results_pup_reg_map_pup3_ecc; + else + return mask_results_pup_reg_map; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h new file mode 100644 index 0000000..25b1462 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_engine.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_ENGINE_H_ +#define _DDR3_TRAINING_IP_ENGINE_H_ + +#include "ddr3_training_ip_def.h" +#include "ddr3_training_ip_flow.h" + +#define EDGE_1 0 +#define EDGE_2 1 +#define ALL_PUP_TRAINING 0xe +#define PUP_RESULT_EDGE_1_MASK 0xff +#define PUP_RESULT_EDGE_2_MASK (0xff << 8) +#define PUP_LOCK_RESULT_BIT 25 + +#define GET_TAP_RESULT(reg, edge) \ + (((edge) == EDGE_1) ? ((reg) & PUP_RESULT_EDGE_1_MASK) : \ + (((reg) & PUP_RESULT_EDGE_2_MASK) >> 8)); +#define GET_LOCK_RESULT(reg) \ + (((reg) & (1<<PUP_LOCK_RESULT_BIT)) >> PUP_LOCK_RESULT_BIT) + +#define EDGE_FAILURE 128 +#define ALL_BITS_PER_PUP 128 + +#define MIN_WINDOW_SIZE 6 +#define MAX_WINDOW_SIZE_RX 32 +#define MAX_WINDOW_SIZE_TX 64 + +int ddr3_tip_training_ip_test(u32 dev_num, enum hws_training_result result_type, + enum hws_search_dir search_dir, + enum hws_dir direction, + enum hws_edge_compare edge, + u32 init_val1, u32 init_val2, + u32 num_of_iterations, u32 start_pattern, + u32 end_pattern); +int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern pattern); +int ddr3_tip_load_pattern_to_mem_by_cpu(u32 dev_num, enum hws_pattern pattern, + u32 offset); +int ddr3_tip_load_all_pattern_to_mem(u32 dev_num); +int ddr3_tip_read_training_result(u32 dev_num, u32 if_id, + enum hws_access_type pup_access_type, + u32 pup_num, u32 bit_num, + enum hws_search_dir search, + enum hws_dir direction, + enum hws_training_result result_type, + enum hws_training_load_op operation, + u32 cs_num_type, u32 **load_res, + int is_read_from_db, u8 cons_tap, + int is_check_result_validity); +int ddr3_tip_ip_training(u32 dev_num, enum hws_access_type access_type, + u32 interface_num, + enum hws_access_type pup_access_type, + u32 pup_num, enum hws_training_result result_type, + enum hws_control_element control_element, + enum hws_search_dir search_dir, enum hws_dir direction, + u32 interface_mask, u32 init_value, u32 num_iter, + enum hws_pattern pattern, + enum hws_edge_compare edge_comp, + enum hws_ddr_cs cs_type, u32 cs_num, + enum hws_training_ip_stat *train_status); +int ddr3_tip_ip_training_wrapper(u32 dev_num, enum hws_access_type access_type, + u32 if_id, + enum hws_access_type pup_access_type, + u32 pup_num, + enum hws_training_result result_type, + enum hws_control_element control_element, + enum hws_search_dir search_dir, + enum hws_dir direction, + u32 interface_mask, u32 init_value1, + u32 init_value2, u32 num_iter, + enum hws_pattern pattern, + enum hws_edge_compare edge_comp, + enum hws_ddr_cs train_cs_type, u32 cs_num, + enum hws_training_ip_stat *train_status); +int is_odpg_access_done(u32 dev_num, u32 if_id); +void ddr3_tip_print_bist_res(void); +struct pattern_info *ddr3_tip_get_pattern_table(void); +u16 *ddr3_tip_get_mask_results_dq_reg(void); +u16 *ddr3_tip_get_mask_results_pup_reg_map(void); + +#endif /* _DDR3_TRAINING_IP_ENGINE_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h new file mode 100644 index 0000000..22d7ce2 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_flow.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_FLOW_H_ +#define _DDR3_TRAINING_IP_FLOW_H_ + +#include "ddr3_training_ip.h" +#include "ddr3_training_ip_pbs.h" + +#define MRS0_CMD 0x3 +#define MRS1_CMD 0x4 +#define MRS2_CMD 0x8 +#define MRS3_CMD 0x9 + +/* + * Definitions of INTERFACE registers + */ + +#define READ_BUFFER_SELECT 0x14a4 + +/* + * Definitions of PHY registers + */ + +#define KILLER_PATTERN_LENGTH 32 +#define EXT_ACCESS_BURST_LENGTH 8 + +#define IS_ACTIVE(if_mask , if_id) \ + ((if_mask) & (1 << (if_id))) +#define VALIDATE_ACTIVE(mask, id) \ + { \ + if (IS_ACTIVE(mask, id) == 0) \ + continue; \ + } + +#define GET_TOPOLOGY_NUM_OF_BUSES() \ + (ddr3_get_topology_map()->num_of_bus_per_interface) + +#define DDR3_IS_ECC_PUP3_MODE(if_mask) \ + (((if_mask) == 0xb) ? 1 : 0) +#define DDR3_IS_ECC_PUP4_MODE(if_mask) \ + (((((if_mask) & 0x10) == 0)) ? 0 : 1) +#define DDR3_IS_16BIT_DRAM_MODE(mask) \ + (((((mask) & 0x4) == 0)) ? 1 : 0) + +#define MEGA 1000000 +#define BUS_WIDTH_IN_BITS 8 + +/* + * DFX address Space + * Table 2: DFX address space + * Address Bits Value Description + * [31 : 20] 0x? DFX base address bases PCIe mapping + * [19 : 15] 0...Number_of_client-1 Client Index inside pipe. + * See also Table 1 Multi_cast = 29 Broadcast = 28 + * [14 : 13] 2'b01 Access to Client Internal Register + * [12 : 0] Client Internal Register offset See related Client Registers + * [14 : 13] 2'b00 Access to Ram Wrappers Internal Register + * [12 : 6] 0 Number_of_rams-1 Ram Index inside Client + * [5 : 0] Ram Wrapper Internal Register offset See related Ram Wrappers + * Registers + */ + +/* nsec */ +#define TREFI_LOW 7800 +#define TREFI_HIGH 3900 + +#define TR2R_VALUE_REG 0x180 +#define TR2R_MASK_REG 0x180 +#define TRFC_MASK_REG 0x7f +#define TR2W_MASK_REG 0x600 +#define TW2W_HIGH_VALUE_REG 0x1800 +#define TW2W_HIGH_MASK_REG 0xf800 +#define TRFC_HIGH_VALUE_REG 0x20000 +#define TRFC_HIGH_MASK_REG 0x70000 +#define TR2R_HIGH_VALUE_REG 0x0 +#define TR2R_HIGH_MASK_REG 0x380000 +#define TMOD_VALUE_REG 0x16000000 +#define TMOD_MASK_REG 0x1e000000 +#define T_VALUE_REG 0x40000000 +#define T_MASK_REG 0xc0000000 +#define AUTO_ZQC_TIMING 15384 +#define WRITE_XBAR_PORT1 0xc03f8077 +#define READ_XBAR_PORT1 0xc03f8073 +#define DISABLE_DDR_TUNING_DATA 0x02294285 +#define ENABLE_DDR_TUNING_DATA 0x12294285 + +#define ODPG_TRAINING_STATUS_REG 0x18488 +#define ODPG_TRAINING_TRIGGER_REG 0x1030 +#define ODPG_STATUS_DONE_REG 0x16fc +#define ODPG_ENABLE_REG 0x186d4 +#define ODPG_ENABLE_OFFS 0 +#define ODPG_DISABLE_OFFS 8 + +#define ODPG_TRAINING_CONTROL_REG 0x1034 +#define ODPG_OBJ1_OPCODE_REG 0x103c +#define ODPG_OBJ1_ITER_CNT_REG 0x10b4 +#define CALIB_OBJ_PRFA_REG 0x10c4 +#define ODPG_WRITE_LEVELING_DONE_CNTR_REG 0x10f8 +#define ODPG_WRITE_READ_MODE_ENABLE_REG 0x10fc +#define TRAINING_OPCODE_1_REG 0x10b4 +#define SDRAM_CONFIGURATION_REG 0x1400 +#define DDR_CONTROL_LOW_REG 0x1404 +#define SDRAM_TIMING_LOW_REG 0x1408 +#define SDRAM_TIMING_HIGH_REG 0x140c +#define SDRAM_ACCESS_CONTROL_REG 0x1410 +#define SDRAM_OPEN_PAGE_CONTROL_REG 0x1414 +#define SDRAM_OPERATION_REG 0x1418 +#define DUNIT_CONTROL_HIGH_REG 0x1424 +#define ODT_TIMING_LOW 0x1428 +#define DDR_TIMING_REG 0x142c +#define ODT_TIMING_HI_REG 0x147c +#define SDRAM_INIT_CONTROL_REG 0x1480 +#define SDRAM_ODT_CONTROL_HIGH_REG 0x1498 +#define DUNIT_ODT_CONTROL_REG 0x149c +#define READ_BUFFER_SELECT_REG 0x14a4 +#define DUNIT_MMASK_REG 0x14b0 +#define CALIB_MACHINE_CTRL_REG 0x14cc +#define DRAM_DLL_TIMING_REG 0x14e0 +#define DRAM_ZQ_INIT_TIMIMG_REG 0x14e4 +#define DRAM_ZQ_TIMING_REG 0x14e8 +#define DFS_REG 0x1528 +#define READ_DATA_SAMPLE_DELAY 0x1538 +#define READ_DATA_READY_DELAY 0x153c +#define TRAINING_REG 0x15b0 +#define TRAINING_SW_1_REG 0x15b4 +#define TRAINING_SW_2_REG 0x15b8 +#define TRAINING_PATTERN_BASE_ADDRESS_REG 0x15bc +#define TRAINING_DBG_1_REG 0x15c0 +#define TRAINING_DBG_2_REG 0x15c4 +#define TRAINING_DBG_3_REG 0x15c8 +#define RANK_CTRL_REG 0x15e0 +#define TIMING_REG 0x15e4 +#define DRAM_PHY_CONFIGURATION 0x15ec +#define MR0_REG 0x15d0 +#define MR1_REG 0x15d4 +#define MR2_REG 0x15d8 +#define MR3_REG 0x15dc +#define TIMING_REG 0x15e4 +#define ODPG_CTRL_CONTROL_REG 0x1600 +#define ODPG_DATA_CONTROL_REG 0x1630 +#define ODPG_PATTERN_ADDR_OFFSET_REG 0x1638 +#define ODPG_DATA_BUF_SIZE_REG 0x163c +#define PHY_LOCK_STATUS_REG 0x1674 +#define PHY_REG_FILE_ACCESS 0x16a0 +#define TRAINING_WRITE_LEVELING_REG 0x16ac +#define ODPG_PATTERN_ADDR_REG 0x16b0 +#define ODPG_PATTERN_DATA_HI_REG 0x16b4 +#define ODPG_PATTERN_DATA_LOW_REG 0x16b8 +#define ODPG_BIST_LAST_FAIL_ADDR_REG 0x16bc +#define ODPG_BIST_DATA_ERROR_COUNTER_REG 0x16c0 +#define ODPG_BIST_FAILED_DATA_HI_REG 0x16c4 +#define ODPG_BIST_FAILED_DATA_LOW_REG 0x16c8 +#define ODPG_WRITE_DATA_ERROR_REG 0x16cc +#define CS_ENABLE_REG 0x16d8 +#define WR_LEVELING_DQS_PATTERN_REG 0x16dc + +#define ODPG_BIST_DONE 0x186d4 +#define ODPG_BIST_DONE_BIT_OFFS 0 +#define ODPG_BIST_DONE_BIT_VALUE 0 + +#define RESULT_CONTROL_BYTE_PUP_0_REG 0x1830 +#define RESULT_CONTROL_BYTE_PUP_1_REG 0x1834 +#define RESULT_CONTROL_BYTE_PUP_2_REG 0x1838 +#define RESULT_CONTROL_BYTE_PUP_3_REG 0x183c +#define RESULT_CONTROL_BYTE_PUP_4_REG 0x18b0 + +#define RESULT_CONTROL_PUP_0_BIT_0_REG 0x18b4 +#define RESULT_CONTROL_PUP_0_BIT_1_REG 0x18b8 +#define RESULT_CONTROL_PUP_0_BIT_2_REG 0x18bc +#define RESULT_CONTROL_PUP_0_BIT_3_REG 0x18c0 +#define RESULT_CONTROL_PUP_0_BIT_4_REG 0x18c4 +#define RESULT_CONTROL_PUP_0_BIT_5_REG 0x18c8 +#define RESULT_CONTROL_PUP_0_BIT_6_REG 0x18cc +#define RESULT_CONTROL_PUP_0_BIT_7_REG 0x18f0 +#define RESULT_CONTROL_PUP_1_BIT_0_REG 0x18f4 +#define RESULT_CONTROL_PUP_1_BIT_1_REG 0x18f8 +#define RESULT_CONTROL_PUP_1_BIT_2_REG 0x18fc +#define RESULT_CONTROL_PUP_1_BIT_3_REG 0x1930 +#define RESULT_CONTROL_PUP_1_BIT_4_REG 0x1934 +#define RESULT_CONTROL_PUP_1_BIT_5_REG 0x1938 +#define RESULT_CONTROL_PUP_1_BIT_6_REG 0x193c +#define RESULT_CONTROL_PUP_1_BIT_7_REG 0x19b0 +#define RESULT_CONTROL_PUP_2_BIT_0_REG 0x19b4 +#define RESULT_CONTROL_PUP_2_BIT_1_REG 0x19b8 +#define RESULT_CONTROL_PUP_2_BIT_2_REG 0x19bc +#define RESULT_CONTROL_PUP_2_BIT_3_REG 0x19c0 +#define RESULT_CONTROL_PUP_2_BIT_4_REG 0x19c4 +#define RESULT_CONTROL_PUP_2_BIT_5_REG 0x19c8 +#define RESULT_CONTROL_PUP_2_BIT_6_REG 0x19cc +#define RESULT_CONTROL_PUP_2_BIT_7_REG 0x19f0 +#define RESULT_CONTROL_PUP_3_BIT_0_REG 0x19f4 +#define RESULT_CONTROL_PUP_3_BIT_1_REG 0x19f8 +#define RESULT_CONTROL_PUP_3_BIT_2_REG 0x19fc +#define RESULT_CONTROL_PUP_3_BIT_3_REG 0x1a30 +#define RESULT_CONTROL_PUP_3_BIT_4_REG 0x1a34 +#define RESULT_CONTROL_PUP_3_BIT_5_REG 0x1a38 +#define RESULT_CONTROL_PUP_3_BIT_6_REG 0x1a3c +#define RESULT_CONTROL_PUP_3_BIT_7_REG 0x1ab0 +#define RESULT_CONTROL_PUP_4_BIT_0_REG 0x1ab4 +#define RESULT_CONTROL_PUP_4_BIT_1_REG 0x1ab8 +#define RESULT_CONTROL_PUP_4_BIT_2_REG 0x1abc +#define RESULT_CONTROL_PUP_4_BIT_3_REG 0x1ac0 +#define RESULT_CONTROL_PUP_4_BIT_4_REG 0x1ac4 +#define RESULT_CONTROL_PUP_4_BIT_5_REG 0x1ac8 +#define RESULT_CONTROL_PUP_4_BIT_6_REG 0x1acc +#define RESULT_CONTROL_PUP_4_BIT_7_REG 0x1af0 + +#define WL_PHY_REG 0x0 +#define WRITE_CENTRALIZATION_PHY_REG 0x1 +#define RL_PHY_REG 0x2 +#define READ_CENTRALIZATION_PHY_REG 0x3 +#define PBS_RX_PHY_REG 0x50 +#define PBS_TX_PHY_REG 0x10 +#define PHY_CONTROL_PHY_REG 0x90 +#define BW_PHY_REG 0x92 +#define RATE_PHY_REG 0x94 +#define CMOS_CONFIG_PHY_REG 0xa2 +#define PAD_ZRI_CALIB_PHY_REG 0xa4 +#define PAD_ODT_CALIB_PHY_REG 0xa6 +#define PAD_CONFIG_PHY_REG 0xa8 +#define PAD_PRE_DISABLE_PHY_REG 0xa9 +#define TEST_ADLL_REG 0xbf +#define CSN_IOB_VREF_REG(cs) (0xdb + (cs * 12)) +#define CSN_IO_BASE_VREF_REG(cs) (0xd0 + (cs * 12)) + +#define RESULT_DB_PHY_REG_ADDR 0xc0 +#define RESULT_DB_PHY_REG_RX_OFFSET 5 +#define RESULT_DB_PHY_REG_TX_OFFSET 0 + +/* TBD - for NP5 use only CS 0 */ +#define PHY_WRITE_DELAY(cs) WL_PHY_REG +/*( ( _cs_ == 0 ) ? 0x0 : 0x4 )*/ +/* TBD - for NP5 use only CS 0 */ +#define PHY_READ_DELAY(cs) RL_PHY_REG + +#define DDR0_ADDR_1 0xf8258 +#define DDR0_ADDR_2 0xf8254 +#define DDR1_ADDR_1 0xf8270 +#define DDR1_ADDR_2 0xf8270 +#define DDR2_ADDR_1 0xf825c +#define DDR2_ADDR_2 0xf825c +#define DDR3_ADDR_1 0xf8264 +#define DDR3_ADDR_2 0xf8260 +#define DDR4_ADDR_1 0xf8274 +#define DDR4_ADDR_2 0xf8274 + +#define GENERAL_PURPOSE_RESERVED0_REG 0x182e0 + +#define GET_BLOCK_ID_MAX_FREQ(dev_num, block_id) 800000 +#define CS0_RD_LVL_REF_DLY_OFFS 0 +#define CS0_RD_LVL_REF_DLY_LEN 0 +#define CS0_RD_LVL_PH_SEL_OFFS 0 +#define CS0_RD_LVL_PH_SEL_LEN 0 + +#define CS_REGISTER_ADDR_OFFSET 4 +#define CALIBRATED_OBJECTS_REG_ADDR_OFFSET 0x10 + +#define MAX_POLLING_ITERATIONS 100000 + +#define PHASE_REG_OFFSET 32 +#define NUM_BYTES_IN_BURST 31 +#define NUM_OF_CS 4 +#define CS_REG_VALUE(cs_num) (cs_mask_reg[cs_num]) +#define ADLL_LENGTH 32 + +struct write_supp_result { + enum hws_wl_supp stage; + int is_pup_fail; +}; + +struct page_element { + enum hws_page_size page_size_8bit; + /* page size in 8 bits bus width */ + enum hws_page_size page_size_16bit; + /* page size in 16 bits bus width */ + u32 ui_page_mask; + /* Mask used in register */ +}; + +int ddr3_tip_write_leveling_static_config(u32 dev_num, u32 if_id, + enum hws_ddr_freq frequency, + u32 *round_trip_delay_arr); +int ddr3_tip_read_leveling_static_config(u32 dev_num, u32 if_id, + enum hws_ddr_freq frequency, + u32 *total_round_trip_delay_arr); +int ddr3_tip_if_write(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 data_value, u32 mask); +int ddr3_tip_if_polling(u32 dev_num, enum hws_access_type access_type, + u32 if_id, u32 exp_value, u32 mask, u32 offset, + u32 poll_tries); +int ddr3_tip_if_read(u32 dev_num, enum hws_access_type interface_access, + u32 if_id, u32 reg_addr, u32 *data, u32 mask); +int ddr3_tip_bus_read_modify_write(u32 dev_num, + enum hws_access_type access_type, + u32 if_id, u32 phy_id, + enum hws_ddr_phy phy_type, + u32 reg_addr, u32 data_value, u32 reg_mask); +int ddr3_tip_bus_read(u32 dev_num, u32 if_id, enum hws_access_type phy_access, + u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, + u32 *data); +int ddr3_tip_bus_write(u32 dev_num, enum hws_access_type e_interface_access, + u32 if_id, enum hws_access_type e_phy_access, u32 phy_id, + enum hws_ddr_phy e_phy_type, u32 reg_addr, + u32 data_value); +int ddr3_tip_freq_set(u32 dev_num, enum hws_access_type e_access, u32 if_id, + enum hws_ddr_freq memory_freq); +int ddr3_tip_adjust_dqs(u32 dev_num); +int ddr3_tip_init_controller(u32 dev_num); +int ddr3_tip_ext_read(u32 dev_num, u32 if_id, u32 reg_addr, + u32 num_of_bursts, u32 *addr); +int ddr3_tip_ext_write(u32 dev_num, u32 if_id, u32 reg_addr, + u32 num_of_bursts, u32 *addr); +int ddr3_tip_dynamic_read_leveling(u32 dev_num, u32 ui_freq); +int ddr3_tip_legacy_dynamic_read_leveling(u32 dev_num); +int ddr3_tip_dynamic_per_bit_read_leveling(u32 dev_num, u32 ui_freq); +int ddr3_tip_legacy_dynamic_write_leveling(u32 dev_num); +int ddr3_tip_dynamic_write_leveling(u32 dev_num); +int ddr3_tip_dynamic_write_leveling_supp(u32 dev_num); +int ddr3_tip_static_init_controller(u32 dev_num); +int ddr3_tip_configure_phy(u32 dev_num); +int ddr3_tip_load_pattern_to_odpg(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_pattern pattern, + u32 load_addr); +int ddr3_tip_load_pattern_to_mem(u32 dev_num, enum hws_pattern e_pattern); +int ddr3_tip_configure_odpg(u32 dev_num, enum hws_access_type access_type, + u32 if_id, enum hws_dir direction, u32 tx_phases, + u32 tx_burst_size, u32 rx_phases, + u32 delay_between_burst, u32 rd_mode, u32 cs_num, + u32 addr_stress_jump, u32 single_pattern); +int ddr3_tip_set_atr(u32 dev_num, u32 flag_id, u32 value); +int ddr3_tip_write_mrs_cmd(u32 dev_num, u32 *cs_mask_arr, u32 cmd, u32 data, + u32 mask); +int ddr3_tip_write_cs_result(u32 dev_num, u32 offset); +int ddr3_tip_get_first_active_if(u8 dev_num, u32 interface_mask, u32 *if_id); +int ddr3_tip_reset_fifo_ptr(u32 dev_num); +int read_pup_value(int pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], + int reg_addr, u32 mask); +int read_adll_value(u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], + int reg_addr, u32 mask); +int write_adll_value(u32 pup_values[MAX_INTERFACE_NUM * MAX_BUS_NUM], + int reg_addr); +int ddr3_tip_tune_training_params(u32 dev_num, + struct tune_train_params *params); + +#endif /* _DDR3_TRAINING_IP_FLOW_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h new file mode 100644 index 0000000..c6be67c --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_pbs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_PBS_H_ +#define _DDR3_TRAINING_IP_PBS_H_ + +enum { + EBA_CONFIG, + EEBA_CONFIG, + SBA_CONFIG +}; + +enum hws_training_load_op { + TRAINING_LOAD_OPERATION_UNLOAD, + TRAINING_LOAD_OPERATION_LOAD +}; + +enum hws_edge { + TRAINING_EDGE_1, + TRAINING_EDGE_2 +}; + +enum hws_edge_search { + TRAINING_EDGE_MAX, + TRAINING_EDGE_MIN +}; + +enum pbs_dir { + PBS_TX_MODE = 0, + PBS_RX_MODE, + NUM_OF_PBS_MODES +}; + +int ddr3_tip_pbs_rx(u32 dev_num); +int ddr3_tip_print_all_pbs_result(u32 dev_num); +int ddr3_tip_pbs_tx(u32 dev_num); + +#endif /* _DDR3_TRAINING_IP_PBS_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h new file mode 100644 index 0000000..724b106 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_prv_if.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_PRV_IF_H +#define _DDR3_TRAINING_IP_PRV_IF_H + +#include "ddr3_training_ip.h" +#include "ddr3_training_ip_flow.h" +#include "ddr3_training_ip_bist.h" + +enum hws_static_config_type { + WRITE_LEVELING_STATIC, + READ_LEVELING_STATIC +}; + +struct ddr3_device_info { + u32 device_id; + u32 ck_delay; +}; + +typedef int (*HWS_TIP_DUNIT_MUX_SELECT_FUNC_PTR)(u8 dev_num, int enable); +typedef int (*HWS_TIP_DUNIT_REG_READ_FUNC_PTR)( + u8 dev_num, enum hws_access_type interface_access, u32 if_id, + u32 offset, u32 *data, u32 mask); +typedef int (*HWS_TIP_DUNIT_REG_WRITE_FUNC_PTR)( + u8 dev_num, enum hws_access_type interface_access, u32 if_id, + u32 offset, u32 data, u32 mask); +typedef int (*HWS_TIP_GET_FREQ_CONFIG_INFO)( + u8 dev_num, enum hws_ddr_freq freq, + struct hws_tip_freq_config_info *freq_config_info); +typedef int (*HWS_TIP_GET_DEVICE_INFO)( + u8 dev_num, struct ddr3_device_info *info_ptr); +typedef int (*HWS_GET_CS_CONFIG_FUNC_PTR)( + u8 dev_num, u32 cs_mask, struct hws_cs_config_info *cs_info); +typedef int (*HWS_SET_FREQ_DIVIDER_FUNC_PTR)( + u8 dev_num, u32 if_id, enum hws_ddr_freq freq); +typedef int (*HWS_GET_INIT_FREQ)(u8 dev_num, enum hws_ddr_freq *freq); +typedef int (*HWS_TRAINING_IP_IF_WRITE_FUNC_PTR)( + u32 dev_num, enum hws_access_type access_type, u32 dunit_id, + u32 reg_addr, u32 data, u32 mask); +typedef int (*HWS_TRAINING_IP_IF_READ_FUNC_PTR)( + u32 dev_num, enum hws_access_type access_type, u32 dunit_id, + u32 reg_addr, u32 *data, u32 mask); +typedef int (*HWS_TRAINING_IP_BUS_WRITE_FUNC_PTR)( + u32 dev_num, enum hws_access_type dunit_access_type, u32 if_id, + enum hws_access_type phy_access_type, u32 phy_id, + enum hws_ddr_phy phy_type, u32 reg_addr, u32 data); +typedef int (*HWS_TRAINING_IP_BUS_READ_FUNC_PTR)( + u32 dev_num, u32 if_id, enum hws_access_type phy_access_type, + u32 phy_id, enum hws_ddr_phy phy_type, u32 reg_addr, u32 *data); +typedef int (*HWS_TRAINING_IP_ALGO_RUN_FUNC_PTR)( + u32 dev_num, enum hws_algo_type algo_type); +typedef int (*HWS_TRAINING_IP_SET_FREQ_FUNC_PTR)( + u32 dev_num, enum hws_access_type access_type, u32 if_id, + enum hws_ddr_freq frequency); +typedef int (*HWS_TRAINING_IP_INIT_CONTROLLER_FUNC_PTR)( + u32 dev_num, struct init_cntr_param *init_cntr_prm); +typedef int (*HWS_TRAINING_IP_PBS_RX_FUNC_PTR)(u32 dev_num); +typedef int (*HWS_TRAINING_IP_PBS_TX_FUNC_PTR)(u32 dev_num); +typedef int (*HWS_TRAINING_IP_SELECT_CONTROLLER_FUNC_PTR)( + u32 dev_num, int enable); +typedef int (*HWS_TRAINING_IP_TOPOLOGY_MAP_LOAD_FUNC_PTR)( + u32 dev_num, struct hws_topology_map *topology_map); +typedef int (*HWS_TRAINING_IP_STATIC_CONFIG_FUNC_PTR)( + u32 dev_num, enum hws_ddr_freq frequency, + enum hws_static_config_type static_config_type, u32 if_id); +typedef int (*HWS_TRAINING_IP_EXTERNAL_READ_PTR)( + u32 dev_num, u32 if_id, u32 ddr_addr, u32 num_bursts, u32 *data); +typedef int (*HWS_TRAINING_IP_EXTERNAL_WRITE_PTR)( + u32 dev_num, u32 if_id, u32 ddr_addr, u32 num_bursts, u32 *data); +typedef int (*HWS_TRAINING_IP_BIST_ACTIVATE)( + u32 dev_num, enum hws_pattern pattern, enum hws_access_type access_type, + u32 if_num, enum hws_dir direction, + enum hws_stress_jump addr_stress_jump, + enum hws_pattern_duration duration, + enum hws_bist_operation oper_type, u32 offset, u32 cs_num, + u32 pattern_addr_length); +typedef int (*HWS_TRAINING_IP_BIST_READ_RESULT)( + u32 dev_num, u32 if_id, struct bist_result *pst_bist_result); +typedef int (*HWS_TRAINING_IP_LOAD_TOPOLOGY)(u32 dev_num, u32 config_num); +typedef int (*HWS_TRAINING_IP_READ_LEVELING)(u32 dev_num, u32 config_num); +typedef int (*HWS_TRAINING_IP_WRITE_LEVELING)(u32 dev_num, u32 config_num); +typedef u32 (*HWS_TRAINING_IP_GET_TEMP)(u8 dev_num); + +struct hws_tip_config_func_db { + HWS_TIP_DUNIT_MUX_SELECT_FUNC_PTR tip_dunit_mux_select_func; + HWS_TIP_DUNIT_REG_READ_FUNC_PTR tip_dunit_read_func; + HWS_TIP_DUNIT_REG_WRITE_FUNC_PTR tip_dunit_write_func; + HWS_TIP_GET_FREQ_CONFIG_INFO tip_get_freq_config_info_func; + HWS_TIP_GET_DEVICE_INFO tip_get_device_info_func; + HWS_SET_FREQ_DIVIDER_FUNC_PTR tip_set_freq_divider_func; + HWS_GET_CS_CONFIG_FUNC_PTR tip_get_cs_config_info; + HWS_TRAINING_IP_GET_TEMP tip_get_temperature; +}; + +int ddr3_tip_init_config_func(u32 dev_num, + struct hws_tip_config_func_db *config_func); +int ddr3_tip_register_xsb_info(u32 dev_num, + struct hws_xsb_info *xsb_info_table); +enum hws_result *ddr3_tip_get_result_ptr(u32 stage); +int ddr3_set_freq_config_info(struct hws_tip_freq_config_info *table); +int print_device_info(u8 dev_num); + +#endif /* _DDR3_TRAINING_IP_PRV_IF_H */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_ip_static.h b/drivers/ddr/marvell/a38x/ddr3_training_ip_static.h new file mode 100644 index 0000000..878068b --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_ip_static.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_IP_STATIC_H_ +#define _DDR3_TRAINING_IP_STATIC_H_ + +#include "ddr3_training_ip_def.h" +#include "ddr3_training_ip.h" + +struct trip_delay_element { + u32 dqs_delay; /* DQS delay (m_sec) */ + u32 ck_delay; /* CK Delay (m_sec) */ +}; + +struct hws_tip_static_config_info { + u32 silicon_delay; + struct trip_delay_element *package_trace_arr; + struct trip_delay_element *board_trace_arr; +}; + +int ddr3_tip_run_static_alg(u32 dev_num, enum hws_ddr_freq freq); +int ddr3_tip_init_static_config_db( + u32 dev_num, struct hws_tip_static_config_info *static_config_info); +int ddr3_tip_init_specific_reg_config(u32 dev_num, + struct reg_data *reg_config_arr); +int ddr3_tip_static_phy_init_controller(u32 dev_num); + +#endif /* _DDR3_TRAINING_IP_STATIC_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_leveling.c b/drivers/ddr/marvell/a38x/ddr3_training_leveling.c new file mode 100644 index 0000000..3c40f19 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_leveling.c @@ -0,0 +1,1836 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define WL_ITERATION_NUM 10 +#define ONE_CLOCK_ERROR_SHIFT 2 +#define ALIGN_ERROR_SHIFT -2 + +static u32 pup_mask_table[] = { + 0x000000ff, + 0x0000ff00, + 0x00ff0000, + 0xff000000 +}; + +static struct write_supp_result wr_supp_res[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + +static int ddr3_tip_dynamic_write_leveling_seq(u32 dev_num); +static int ddr3_tip_dynamic_read_leveling_seq(u32 dev_num); +static int ddr3_tip_dynamic_per_bit_read_leveling_seq(u32 dev_num); +static int ddr3_tip_wl_supp_align_err_shift(u32 dev_num, u32 if_id, u32 bus_id, + u32 bus_id_delta); +static int ddr3_tip_wl_supp_align_phase_shift(u32 dev_num, u32 if_id, + u32 bus_id, u32 offset, + u32 bus_id_delta); +static int ddr3_tip_xsb_compare_test(u32 dev_num, u32 if_id, u32 bus_id, + u32 edge_offset, u32 bus_id_delta); +static int ddr3_tip_wl_supp_one_clk_err_shift(u32 dev_num, u32 if_id, + u32 bus_id, u32 bus_id_delta); + +u32 hws_ddr3_tip_max_cs_get(void) +{ + u32 c_cs; + static u32 max_cs; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (!max_cs) { + for (c_cs = 0; c_cs < NUM_OF_CS; c_cs++) { + VALIDATE_ACTIVE(tm-> + interface_params[0].as_bus_params[0]. + cs_bitmask, c_cs); + max_cs++; + } + } + + return max_cs; +} + +/***************************************************************************** +Dynamic read leveling +******************************************************************************/ +int ddr3_tip_dynamic_read_leveling(u32 dev_num, u32 freq) +{ + u32 data, mask; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + u32 bus_num, if_id, cl_val; + enum hws_speed_bin speed_bin_index; + /* save current CS value */ + u32 cs_enable_reg_val[MAX_INTERFACE_NUM] = { 0 }; + int is_any_pup_fail = 0; + u32 data_read[MAX_INTERFACE_NUM + 1] = { 0 }; + u8 rl_values[NUM_OF_CS][MAX_BUS_NUM][MAX_INTERFACE_NUM]; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + if (rl_version == 0) { + /* OLD RL machine */ + data = 0x40; + data |= (1 << 20); + + /* TBD multi CS */ + CHECK_STATUS(ddr3_tip_if_write( + dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, TRAINING_REG, + data, 0x11ffff)); + CHECK_STATUS(ddr3_tip_if_write( + dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + TRAINING_PATTERN_BASE_ADDRESS_REG, + 0, 0xfffffff8)); + CHECK_STATUS(ddr3_tip_if_write( + dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, TRAINING_REG, + (u32)(1 << 31), (u32)(1 << 31))); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + training_result[training_stage][if_id] = TEST_SUCCESS; + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, + (u32)(1 << 31), TRAINING_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("RL: DDR3 poll failed(1) IF %d\n", + if_id)); + training_result[training_stage][if_id] = + TEST_FAILED; + + if (debug_mode == 0) + return MV_FAIL; + } + } + + /* read read-leveling result */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, data_read, 1 << 30)); + /* exit read leveling mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x8, 0x9)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_1_REG, 1 << 16, 1 << 16)); + + /* disable RL machine all Trn_CS[3:0] , [16:0] */ + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, 0, 0xf1ffff)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if ((data_read[if_id] & (1 << 30)) == 0) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("\n_read Leveling failed for IF %d\n", + if_id)); + training_result[training_stage][if_id] = + TEST_FAILED; + if (debug_mode == 0) + return MV_FAIL; + } + } + return MV_OK; + } + + /* NEW RL machine */ + for (effective_cs = 0; effective_cs < NUM_OF_CS; effective_cs++) + for (bus_num = 0; bus_num < MAX_BUS_NUM; bus_num++) + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) + rl_values[effective_cs][bus_num][if_id] = 0; + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + training_result[training_stage][if_id] = TEST_SUCCESS; + + /* save current cs enable reg val */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val, + MASK_ALL_BITS)); + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, (1 << 3), (1 << 3))); + } + + ddr3_tip_reset_fifo_ptr(dev_num); + + /* + * Phase 1: Load pattern (using ODPG) + * + * enter Read Leveling mode + * only 27 bits are masked + * assuming non multi-CS configuration + * write to CS = 0 for the non multi CS configuration, note + * that the results shall be read back to the required CS !!! + */ + + /* BUS count is 0 shifted 26 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x3, 0x3)); + CHECK_STATUS(ddr3_tip_configure_odpg + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0, + pattern_table[PATTERN_RL].num_of_phases_tx, 0, + pattern_table[PATTERN_RL].num_of_phases_rx, 0, 0, + effective_cs, STRESS_NONE, DURATION_SINGLE)); + + /* load pattern to ODPG */ + ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, PATTERN_RL, + pattern_table[PATTERN_RL]. + start_addr); + + /* + * Phase 2: ODPG to Read Leveling mode + */ + + /* General Training Opcode register */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_WRITE_READ_MODE_ENABLE_REG, 0, + MASK_ALL_BITS)); + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_CONTROL_REG, + (0x301b01 | effective_cs << 2), 0x3c3fef)); + + /* Object1 opcode register 0 & 1 */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + speed_bin_index = + tm->interface_params[if_id].speed_bin_index; + cl_val = + cas_latency_table[speed_bin_index].cl_val[freq]; + data = (cl_val << 17) | (0x3 << 25); + mask = (0xff << 9) | (0x1f << 17) | (0x3 << 25); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_OBJ1_OPCODE_REG, data, mask)); + } + + /* Set iteration count to max value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_OPCODE_1_REG, 0xd00, 0xd00)); + + /* + * Phase 2: Mask config + */ + + ddr3_tip_dynamic_read_leveling_seq(dev_num); + + /* + * Phase 3: Read Leveling execution + */ + + /* temporary jira dunit=14751 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_DBG_1_REG, 0, (u32)(1 << 31))); + /* configure phy reset value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_DBG_3_REG, (0x7f << 24), + (u32)(0xff << 24))); + /* data pup rd reset enable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + SDRAM_CONFIGURATION_REG, 0, (1 << 30))); + /* data pup rd reset disable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + SDRAM_CONFIGURATION_REG, (1 << 30), (1 << 30))); + /* training SW override & training RL mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x1, 0x9)); + /* training enable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, (1 << 24) | (1 << 20), + (1 << 24) | (1 << 20))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, (u32)(1 << 31), (u32)(1 << 31))); + + /********* trigger training *******************/ + /* Trigger, poll on status and disable ODPG */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_TRIGGER_REG, 0x1, 0x1)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_STATUS_REG, 0x1, 0x1)); + + /* check for training done + results pass */ + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x2, 0x2, + ODPG_TRAINING_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("Training Done Failed\n")); + return MV_FAIL; + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, + ODPG_TRAINING_TRIGGER_REG, data_read, + 0x4)); + data = data_read[if_id]; + if (data != 0x0) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("Training Result Failed\n")); + } + } + + /*disable ODPG - Back to functional mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_ENABLE_REG, 0x1 << ODPG_DISABLE_OFFS, + (0x1 << ODPG_DISABLE_OFFS))); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x0, 0x1, + ODPG_ENABLE_REG, MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("ODPG disable failed ")); + return MV_FAIL; + } + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS)); + + /* double loop on bus, pup */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* check training done */ + is_any_pup_fail = 0; + for (bus_num = 0; + bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, + if_id, (1 << 25), (1 << 25), + mask_results_pup_reg_map[bus_num], + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("\n_r_l: DDR3 poll failed(2) for bus %d", + bus_num)); + is_any_pup_fail = 1; + } else { + /* read result per pup */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + mask_results_pup_reg_map + [bus_num], data_read, + 0xff)); + rl_values[effective_cs][bus_num] + [if_id] = (u8)data_read[if_id]; + } + } + + if (is_any_pup_fail == 1) { + training_result[training_stage][if_id] = + TEST_FAILED; + if (debug_mode == 0) + return MV_FAIL; + } + } + + DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("RL exit read leveling\n")); + + /* + * Phase 3: Exit Read Leveling + */ + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, (1 << 3), (1 << 3))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_1_REG, (1 << 16), (1 << 16))); + /* set ODPG to functional */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x0, MASK_ALL_BITS)); + + /* + * Copy the result from the effective CS search to the + * real Functional CS + */ + /*ddr3_tip_write_cs_result(dev_num, RL_PHY_REG); */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x0, MASK_ALL_BITS)); + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + /* double loop on bus, pup */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_num = 0; + bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + /* read result per pup from arry */ + data = rl_values[effective_cs][bus_num][if_id]; + data = (data & 0x1f) | + (((data & 0xe0) >> 5) << 6); + ddr3_tip_bus_write(dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_num, DDR_PHY_DATA, + RL_PHY_REG + + ((effective_cs == + 0) ? 0x0 : 0x4), data); + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* restore cs enable value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + if (odt_config != 0) { + CHECK_STATUS(ddr3_tip_write_additional_odt_setting + (dev_num, if_id)); + } + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (training_result[training_stage][if_id] == TEST_FAILED) + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Legacy Dynamic write leveling + */ +int ddr3_tip_legacy_dynamic_write_leveling(u32 dev_num) +{ + u32 c_cs, if_id, cs_mask = 0; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * In TRAINIUNG reg (0x15b0) write 0x80000008 | cs_mask: + * Trn_start + * cs_mask = 0x1 <<20 Trn_CS0 - CS0 is included in the DDR3 training + * cs_mask = 0x1 <<21 Trn_CS1 - CS1 is included in the DDR3 training + * cs_mask = 0x1 <<22 Trn_CS2 - CS2 is included in the DDR3 training + * cs_mask = 0x1 <<23 Trn_CS3 - CS3 is included in the DDR3 training + * Trn_auto_seq = write leveling + */ + for (c_cs = 0; c_cs < max_cs; c_cs++) + cs_mask = cs_mask | 1 << (20 + c_cs); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, 0, + TRAINING_REG, (0x80000008 | cs_mask), + 0xffffffff)); + mdelay(20); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, + (u32)0x80000000, TRAINING_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("polling failed for Old WL result\n")); + return MV_FAIL; + } + } + + return MV_OK; +} + +/* + * Legacy Dynamic read leveling + */ +int ddr3_tip_legacy_dynamic_read_leveling(u32 dev_num) +{ + u32 c_cs, if_id, cs_mask = 0; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * In TRAINIUNG reg (0x15b0) write 0x80000040 | cs_mask: + * Trn_start + * cs_mask = 0x1 <<20 Trn_CS0 - CS0 is included in the DDR3 training + * cs_mask = 0x1 <<21 Trn_CS1 - CS1 is included in the DDR3 training + * cs_mask = 0x1 <<22 Trn_CS2 - CS2 is included in the DDR3 training + * cs_mask = 0x1 <<23 Trn_CS3 - CS3 is included in the DDR3 training + * Trn_auto_seq = Read Leveling using training pattern + */ + for (c_cs = 0; c_cs < max_cs; c_cs++) + cs_mask = cs_mask | 1 << (20 + c_cs); + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, 0, TRAINING_REG, + (0x80000040 | cs_mask), 0xffffffff)); + mdelay(100); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, + (u32)0x80000000, TRAINING_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("polling failed for Old RL result\n")); + return MV_FAIL; + } + } + + return MV_OK; +} + +/* + * Dynamic per bit read leveling + */ +int ddr3_tip_dynamic_per_bit_read_leveling(u32 dev_num, u32 freq) +{ + u32 data, mask; + u32 bus_num, if_id, cl_val, bit_num; + u32 curr_numb, curr_min_delay; + int adll_array[3] = { 0, -0xa, 0x14 }; + u32 phyreg3_arr[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + enum hws_speed_bin speed_bin_index; + int is_any_pup_fail = 0; + int break_loop = 0; + u32 cs_enable_reg_val[MAX_INTERFACE_NUM]; /* save current CS value */ + u32 data_read[MAX_INTERFACE_NUM]; + int per_bit_rl_pup_status[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + u32 data2_write[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_num = 0; + bus_num <= tm->num_of_bus_per_interface; bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + per_bit_rl_pup_status[if_id][bus_num] = 0; + data2_write[if_id][bus_num] = 0; + /* read current value of phy register 0x3 */ + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_num, DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG, + &phyreg3_arr[if_id][bus_num])); + } + } + + /* NEW RL machine */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + training_result[training_stage][if_id] = TEST_SUCCESS; + + /* save current cs enable reg val */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, &cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, (1 << 3), (1 << 3))); + } + + ddr3_tip_reset_fifo_ptr(dev_num); + for (curr_numb = 0; curr_numb < 3; curr_numb++) { + /* + * Phase 1: Load pattern (using ODPG) + * + * enter Read Leveling mode + * only 27 bits are masked + * assuming non multi-CS configuration + * write to CS = 0 for the non multi CS configuration, note that + * the results shall be read back to the required CS !!! + */ + + /* BUS count is 0 shifted 26 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x3, 0x3)); + CHECK_STATUS(ddr3_tip_configure_odpg + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0, + pattern_table[PATTERN_TEST].num_of_phases_tx, 0, + pattern_table[PATTERN_TEST].num_of_phases_rx, 0, + 0, 0, STRESS_NONE, DURATION_SINGLE)); + + /* load pattern to ODPG */ + ddr3_tip_load_pattern_to_odpg(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, PATTERN_TEST, + pattern_table[PATTERN_TEST]. + start_addr); + + /* + * Phase 2: ODPG to Read Leveling mode + */ + + /* General Training Opcode register */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_WRITE_READ_MODE_ENABLE_REG, 0, + MASK_ALL_BITS)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_CONTROL_REG, 0x301b01, 0x3c3fef)); + + /* Object1 opcode register 0 & 1 */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + speed_bin_index = + tm->interface_params[if_id].speed_bin_index; + cl_val = + cas_latency_table[speed_bin_index].cl_val[freq]; + data = (cl_val << 17) | (0x3 << 25); + mask = (0xff << 9) | (0x1f << 17) | (0x3 << 25); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ODPG_OBJ1_OPCODE_REG, data, mask)); + } + + /* Set iteration count to max value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_OPCODE_1_REG, 0xd00, 0xd00)); + + /* + * Phase 2: Mask config + */ + + ddr3_tip_dynamic_per_bit_read_leveling_seq(dev_num); + + /* + * Phase 3: Read Leveling execution + */ + + /* temporary jira dunit=14751 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_DBG_1_REG, 0, (u32)(1 << 31))); + /* configure phy reset value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_DBG_3_REG, (0x7f << 24), + (u32)(0xff << 24))); + /* data pup rd reset enable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + SDRAM_CONFIGURATION_REG, 0, (1 << 30))); + /* data pup rd reset disable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + SDRAM_CONFIGURATION_REG, (1 << 30), (1 << 30))); + /* training SW override & training RL mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x1, 0x9)); + /* training enable */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, (1 << 24) | (1 << 20), + (1 << 24) | (1 << 20))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_REG, (u32)(1 << 31), (u32)(1 << 31))); + + /********* trigger training *******************/ + /* Trigger, poll on status and disable ODPG */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_TRIGGER_REG, 0x1, 0x1)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_STATUS_REG, 0x1, 0x1)); + + /*check for training done + results pass */ + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x2, 0x2, + ODPG_TRAINING_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("Training Done Failed\n")); + return MV_FAIL; + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, + ODPG_TRAINING_TRIGGER_REG, data_read, + 0x4)); + data = data_read[if_id]; + if (data != 0x0) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("Training Result Failed\n")); + } + } + + /*disable ODPG - Back to functional mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_ENABLE_REG, 0x1 << ODPG_DISABLE_OFFS, + (0x1 << ODPG_DISABLE_OFFS))); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x0, 0x1, + ODPG_ENABLE_REG, MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("ODPG disable failed ")); + return MV_FAIL; + } + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0, MASK_ALL_BITS)); + + /* double loop on bus, pup */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* check training done */ + for (bus_num = 0; + bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + + if (per_bit_rl_pup_status[if_id][bus_num] + == 0) { + curr_min_delay = 0; + for (bit_num = 0; bit_num < 8; + bit_num++) { + if (ddr3_tip_if_polling + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, (1 << 25), + (1 << 25), + mask_results_dq_reg_map + [bus_num * 8 + bit_num], + MAX_POLLING_ITERATIONS) != + MV_OK) { + DEBUG_LEVELING + (DEBUG_LEVEL_ERROR, + ("\n_r_l: DDR3 poll failed(2) for bus %d bit %d\n", + bus_num, + bit_num)); + } else { + /* read result per pup */ + CHECK_STATUS + (ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + mask_results_dq_reg_map + [bus_num * 8 + + bit_num], + data_read, + MASK_ALL_BITS)); + data = + (data_read + [if_id] & + 0x1f) | + ((data_read + [if_id] & + 0xe0) << 1); + if (curr_min_delay == 0) + curr_min_delay = + data; + else if (data < + curr_min_delay) + curr_min_delay = + data; + if (data > data2_write[if_id][bus_num]) + data2_write + [if_id] + [bus_num] = + data; + } + } + + if (data2_write[if_id][bus_num] <= + (curr_min_delay + + MAX_DQ_READ_LEVELING_DELAY)) { + per_bit_rl_pup_status[if_id] + [bus_num] = 1; + } + } + } + } + + /* check if there is need to search new phyreg3 value */ + if (curr_numb < 2) { + /* if there is DLL that is not checked yet */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_num = 0; + bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, + bus_num); + if (per_bit_rl_pup_status[if_id] + [bus_num] != 1) { + /* go to next ADLL value */ + CHECK_STATUS + (ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_num, DDR_PHY_DATA, + READ_CENTRALIZATION_PHY_REG, + (phyreg3_arr[if_id] + [bus_num] + + adll_array[curr_numb]))); + break_loop = 1; + break; + } + } + if (break_loop) + break; + } + } /* if (curr_numb < 2) */ + if (!break_loop) + break; + } /* for ( curr_numb = 0; curr_numb <3; curr_numb++) */ + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_num = 0; bus_num < tm->num_of_bus_per_interface; + bus_num++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_num); + if (per_bit_rl_pup_status[if_id][bus_num] == 1) + ddr3_tip_bus_write(dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_num, DDR_PHY_DATA, + RL_PHY_REG + + CS_REG_VALUE(effective_cs), + data2_write[if_id] + [bus_num]); + else + is_any_pup_fail = 1; + } + + /* TBD flow does not support multi CS */ + /* + * cs_bitmask = tm->interface_params[if_id]. + * as_bus_params[bus_num].cs_bitmask; + */ + /* divide by 4 is used for retrieving the CS number */ + /* + * TBD BC2 - what is the PHY address for other + * CS ddr3_tip_write_cs_result() ??? + */ + /* + * find what should be written to PHY + * - max delay that is less than threshold + */ + if (is_any_pup_fail == 1) { + training_result[training_stage][if_id] = TEST_FAILED; + if (debug_mode == 0) + return MV_FAIL; + } + } + DEBUG_LEVELING(DEBUG_LEVEL_INFO, ("RL exit read leveling\n")); + + /* + * Phase 3: Exit Read Leveling + */ + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, (1 << 3), (1 << 3))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_1_REG, (1 << 16), (1 << 16))); + /* set ODPG to functional */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x0, MASK_ALL_BITS)); + /* + * Copy the result from the effective CS search to the real + * Functional CS + */ + ddr3_tip_write_cs_result(dev_num, RL_PHY_REG); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_DATA_CONTROL_REG, 0x0, MASK_ALL_BITS)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* restore cs enable value */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + if (odt_config != 0) { + CHECK_STATUS(ddr3_tip_write_additional_odt_setting + (dev_num, if_id)); + } + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (training_result[training_stage][if_id] == TEST_FAILED) + return MV_FAIL; + } + + return MV_OK; +} + +int ddr3_tip_calc_cs_mask(u32 dev_num, u32 if_id, u32 effective_cs, + u32 *cs_mask) +{ + u32 all_bus_cs = 0, same_bus_cs; + u32 bus_cnt; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + *cs_mask = same_bus_cs = CS_BIT_MASK; + + /* + * In some of the devices (such as BC2), the CS is per pup and there + * for mixed mode is valid on like other devices where CS configuration + * is per interface. + * In order to know that, we do 'Or' and 'And' operation between all + * CS (of the pups). + * If they are they are not the same then it's mixed mode so all CS + * should be configured (when configuring the MRS) + */ + for (bus_cnt = 0; bus_cnt < tm->num_of_bus_per_interface; bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + + all_bus_cs |= tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask; + same_bus_cs &= tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask; + + /* cs enable is active low */ + *cs_mask &= ~tm->interface_params[if_id]. + as_bus_params[bus_cnt].cs_bitmask; + } + + if (all_bus_cs == same_bus_cs) + *cs_mask = (*cs_mask | (~(1 << effective_cs))) & CS_BIT_MASK; + + return MV_OK; +} + +/* + * Dynamic write leveling + */ +int ddr3_tip_dynamic_write_leveling(u32 dev_num) +{ + u32 reg_data = 0, iter, if_id, bus_cnt; + u32 cs_enable_reg_val[MAX_INTERFACE_NUM] = { 0 }; + u32 cs_mask[MAX_INTERFACE_NUM]; + u32 read_data_sample_delay_vals[MAX_INTERFACE_NUM] = { 0 }; + u32 read_data_ready_delay_vals[MAX_INTERFACE_NUM] = { 0 }; + /* 0 for failure */ + u32 res_values[MAX_INTERFACE_NUM * MAX_BUS_NUM] = { 0 }; + u32 test_res = 0; /* 0 - success for all pup */ + u32 data_read[MAX_INTERFACE_NUM]; + u8 wl_values[NUM_OF_CS][MAX_BUS_NUM][MAX_INTERFACE_NUM]; + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u32 cs_mask0[MAX_INTERFACE_NUM] = { 0 }; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + training_result[training_stage][if_id] = TEST_SUCCESS; + + /* save Read Data Sample Delay */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_SAMPLE_DELAY, + read_data_sample_delay_vals, MASK_ALL_BITS)); + /* save Read Data Ready Delay */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_READY_DELAY, read_data_ready_delay_vals, + MASK_ALL_BITS)); + /* save current cs reg val */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val, MASK_ALL_BITS)); + } + + /* + * Phase 1: DRAM 2 Write Leveling mode + */ + + /*Assert 10 refresh commands to DRAM to all CS */ + for (iter = 0; iter < WL_ITERATION_NUM; iter++) { + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, SDRAM_OPERATION_REG, + (u32)((~(0xf) << 8) | 0x2), 0xf1f)); + } + } + /* check controller back to normal */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, 0, 0x1f, + SDRAM_OPERATION_REG, MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("WL: DDR3 poll failed(3)")); + } + } + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + /*enable write leveling to all cs - Q off , WL n */ + /* calculate interface cs mask */ + CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask0, MRS1_CMD, + 0x1000, 0x1080)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* cs enable is active low */ + ddr3_tip_calc_cs_mask(dev_num, if_id, effective_cs, + &cs_mask[if_id]); + } + + /* Enable Output buffer to relevant CS - Q on , WL on */ + CHECK_STATUS(ddr3_tip_write_mrs_cmd + (dev_num, cs_mask, MRS1_CMD, 0x80, 0x1080)); + + /*enable odt for relevant CS */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + 0x1498, (0x3 << (effective_cs * 2)), 0xf)); + + /* + * Phase 2: Set training IP to write leveling mode + */ + + CHECK_STATUS(ddr3_tip_dynamic_write_leveling_seq(dev_num)); + + /* + * Phase 3: Trigger training + */ + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_TRIGGER_REG, 0x1, 0x1)); + + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + /* training done */ + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, + (1 << 1), (1 << 1), ODPG_TRAINING_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL: DDR3 poll (4) failed (Data: 0x%x)\n", + reg_data)); + } +#if !defined(CONFIG_ARMADA_38X) /*Disabled. JIRA #1498 */ + else { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, + ODPG_TRAINING_TRIGGER_REG, + ®_data, (1 << 2))); + if (reg_data != 0) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL: WL failed IF %d reg_data=0x%x\n", + if_id, reg_data)); + } + } +#endif + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* training done */ + if (ddr3_tip_if_polling + (dev_num, ACCESS_TYPE_UNICAST, if_id, + (1 << 1), (1 << 1), ODPG_TRAINING_STATUS_REG, + MAX_POLLING_ITERATIONS) != MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL: DDR3 poll (4) failed (Data: 0x%x)\n", + reg_data)); + } else { +#if !defined(CONFIG_ARMADA_38X) /*Disabled. JIRA #1498 */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, + if_id, + ODPG_TRAINING_STATUS_REG, + data_read, (1 << 2))); + reg_data = data_read[if_id]; + if (reg_data != 0) { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL: WL failed IF %d reg_data=0x%x\n", + if_id, reg_data)); + } +#endif + + /* check for training completion per bus */ + for (bus_cnt = 0; + bus_cnt < tm->num_of_bus_per_interface; + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, + bus_cnt); + /* training status */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + mask_results_pup_reg_map + [bus_cnt], data_read, + (1 << 25))); + reg_data = data_read[if_id]; + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL: IF %d BUS %d reg 0x%x\n", + if_id, bus_cnt, reg_data)); + if (reg_data == 0) { + res_values[ + (if_id * + tm->num_of_bus_per_interface) + + bus_cnt] = 1; + } + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + mask_results_pup_reg_map + [bus_cnt], data_read, + 0xff)); + /* + * Save the read value that should be + * write to PHY register + */ + wl_values[effective_cs] + [bus_cnt][if_id] = + (u8)data_read[if_id]; + } + } + } + + /* + * Phase 4: Exit write leveling mode + */ + + /* disable DQs toggling */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + WR_LEVELING_DQS_PATTERN_REG, 0x0, 0x1)); + + /* Update MRS 1 (WL off) */ + CHECK_STATUS(ddr3_tip_write_mrs_cmd(dev_num, cs_mask0, MRS1_CMD, + 0x1000, 0x1080)); + + /* Update MRS 1 (return to functional mode - Q on , WL off) */ + CHECK_STATUS(ddr3_tip_write_mrs_cmd + (dev_num, cs_mask0, MRS1_CMD, 0x0, 0x1080)); + + /* set phy to normal mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x5, 0x7)); + + /* exit sw override mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x4, 0x7)); + } + + /* + * Phase 5: Load WL values to each PHY + */ + + for (effective_cs = 0; effective_cs < max_cs; effective_cs++) { + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + test_res = 0; + for (bus_cnt = 0; + bus_cnt < tm->num_of_bus_per_interface; + bus_cnt++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_cnt); + /* check if result == pass */ + if (res_values + [(if_id * + tm->num_of_bus_per_interface) + + bus_cnt] == 0) { + /* + * read result control register + * according to pup + */ + reg_data = + wl_values[effective_cs][bus_cnt] + [if_id]; + /* + * Write into write leveling register + * ([4:0] ADLL, [8:6] Phase, [15:10] + * (centralization) ADLL + 0x10) + */ + reg_data = + (reg_data & 0x1f) | + (((reg_data & 0xe0) >> 5) << 6) | + (((reg_data & 0x1f) + + phy_reg1_val) << 10); + ddr3_tip_bus_write( + dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, + bus_cnt, + DDR_PHY_DATA, + WL_PHY_REG + + effective_cs * + CS_REGISTER_ADDR_OFFSET, + reg_data); + } else { + test_res = 1; + /* + * read result control register + * according to pup + */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + mask_results_pup_reg_map + [bus_cnt], data_read, + 0xff)); + reg_data = data_read[if_id]; + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL: IF %d BUS %d failed, reg 0x%x\n", + if_id, bus_cnt, reg_data)); + } + } + + if (test_res != 0) { + training_result[training_stage][if_id] = + TEST_FAILED; + } + } + } + /* Set to 0 after each loop to avoid illegal value may be used */ + effective_cs = 0; + + /* + * Copy the result from the effective CS search to the real + * Functional CS + */ + /* ddr3_tip_write_cs_result(dev_num, WL_PHY_REG); */ + /* restore saved values */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* restore Read Data Sample Delay */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_SAMPLE_DELAY, + read_data_sample_delay_vals[if_id], + MASK_ALL_BITS)); + + /* restore Read Data Ready Delay */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_READY_DELAY, + read_data_ready_delay_vals[if_id], + MASK_ALL_BITS)); + + /* enable multi cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + } + + /* Disable modt0 for CS0 training - need to adjust for multy CS */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, 0x1498, + 0x0, 0xf)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (training_result[training_stage][if_id] == TEST_FAILED) + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Dynamic write leveling supplementary + */ +int ddr3_tip_dynamic_write_leveling_supp(u32 dev_num) +{ + int adll_offset; + u32 if_id, bus_id, data, data_tmp; + int is_if_fail = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + is_if_fail = 0; + + for (bus_id = 0; bus_id < GET_TOPOLOGY_NUM_OF_BUSES(); + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + wr_supp_res[if_id][bus_id].is_pup_fail = 1; + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + effective_cs * CS_REGISTER_ADDR_OFFSET, + &data)); + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: adll_offset=0 data delay = %d\n", + data)); + if (ddr3_tip_wl_supp_align_phase_shift + (dev_num, if_id, bus_id, 0, 0) == MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: IF %d bus_id %d adll_offset=0 Success !\n", + if_id, bus_id)); + continue; + } + + /* change adll */ + adll_offset = 5; + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + effective_cs * CS_REGISTER_ADDR_OFFSET, + data + adll_offset)); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + effective_cs * CS_REGISTER_ADDR_OFFSET, + &data_tmp)); + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: adll_offset= %d data delay = %d\n", + adll_offset, data_tmp)); + + if (ddr3_tip_wl_supp_align_phase_shift + (dev_num, if_id, bus_id, adll_offset, 0) == MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: IF %d bus_id %d adll_offset= %d Success !\n", + if_id, bus_id, adll_offset)); + continue; + } + + /* change adll */ + adll_offset = -5; + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, bus_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + effective_cs * CS_REGISTER_ADDR_OFFSET, + data + adll_offset)); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, + WRITE_CENTRALIZATION_PHY_REG + + effective_cs * CS_REGISTER_ADDR_OFFSET, + &data_tmp)); + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: adll_offset= %d data delay = %d\n", + adll_offset, data_tmp)); + if (ddr3_tip_wl_supp_align_phase_shift + (dev_num, if_id, bus_id, adll_offset, 0) == MV_OK) { + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("WL Supp: IF %d bus_id %d adll_offset= %d Success !\n", + if_id, bus_id, adll_offset)); + continue; + } else { + DEBUG_LEVELING( + DEBUG_LEVEL_ERROR, + ("WL Supp: IF %d bus_id %d Failed !\n", + if_id, bus_id)); + is_if_fail = 1; + } + } + DEBUG_LEVELING(DEBUG_LEVEL_TRACE, + ("WL Supp: IF %d bus_id %d is_pup_fail %d\n", + if_id, bus_id, is_if_fail)); + + if (is_if_fail == 1) { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("WL Supp: IF %d failed\n", if_id)); + training_result[training_stage][if_id] = TEST_FAILED; + } else { + training_result[training_stage][if_id] = TEST_SUCCESS; + } + } + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (training_result[training_stage][if_id] == TEST_FAILED) + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Phase Shift + */ +static int ddr3_tip_wl_supp_align_phase_shift(u32 dev_num, u32 if_id, + u32 bus_id, u32 offset, + u32 bus_id_delta) +{ + wr_supp_res[if_id][bus_id].stage = PHASE_SHIFT; + if (ddr3_tip_xsb_compare_test(dev_num, if_id, bus_id, + 0, bus_id_delta) == MV_OK) { + wr_supp_res[if_id][bus_id].is_pup_fail = 0; + return MV_OK; + } else if (ddr3_tip_xsb_compare_test(dev_num, if_id, bus_id, + ONE_CLOCK_ERROR_SHIFT, + bus_id_delta) == MV_OK) { + /* 1 clock error */ + wr_supp_res[if_id][bus_id].stage = CLOCK_SHIFT; + DEBUG_LEVELING(DEBUG_LEVEL_TRACE, + ("Supp: 1 error clock for if %d pup %d with ofsset %d success\n", + if_id, bus_id, offset)); + ddr3_tip_wl_supp_one_clk_err_shift(dev_num, if_id, bus_id, 0); + wr_supp_res[if_id][bus_id].is_pup_fail = 0; + return MV_OK; + } else if (ddr3_tip_xsb_compare_test(dev_num, if_id, bus_id, + ALIGN_ERROR_SHIFT, + bus_id_delta) == MV_OK) { + /* align error */ + DEBUG_LEVELING(DEBUG_LEVEL_TRACE, + ("Supp: align error for if %d pup %d with ofsset %d success\n", + if_id, bus_id, offset)); + wr_supp_res[if_id][bus_id].stage = ALIGN_SHIFT; + ddr3_tip_wl_supp_align_err_shift(dev_num, if_id, bus_id, 0); + wr_supp_res[if_id][bus_id].is_pup_fail = 0; + return MV_OK; + } else { + wr_supp_res[if_id][bus_id].is_pup_fail = 1; + return MV_FAIL; + } +} + +/* + * Compare Test + */ +static int ddr3_tip_xsb_compare_test(u32 dev_num, u32 if_id, u32 bus_id, + u32 edge_offset, u32 bus_id_delta) +{ + u32 num_of_succ_byte_compare, word_in_pattern, abs_offset; + u32 word_offset, i; + u32 read_pattern[TEST_PATTERN_LENGTH * 2]; + struct pattern_info *pattern_table = ddr3_tip_get_pattern_table(); + u32 pattern_test_pattern_table[8]; + + for (i = 0; i < 8; i++) { + pattern_test_pattern_table[i] = + pattern_table_get_word(dev_num, PATTERN_TEST, (u8)i); + } + + /* extern write, than read and compare */ + CHECK_STATUS(ddr3_tip_ext_write + (dev_num, if_id, + (pattern_table[PATTERN_TEST].start_addr + + ((SDRAM_CS_SIZE + 1) * effective_cs)), 1, + pattern_test_pattern_table)); + + CHECK_STATUS(ddr3_tip_reset_fifo_ptr(dev_num)); + + CHECK_STATUS(ddr3_tip_ext_read + (dev_num, if_id, + (pattern_table[PATTERN_TEST].start_addr + + ((SDRAM_CS_SIZE + 1) * effective_cs)), 1, read_pattern)); + + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: IF %d bus_id %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + if_id, bus_id, read_pattern[0], read_pattern[1], + read_pattern[2], read_pattern[3], read_pattern[4], + read_pattern[5], read_pattern[6], read_pattern[7])); + + /* compare byte per pup */ + num_of_succ_byte_compare = 0; + for (word_in_pattern = start_xsb_offset; + word_in_pattern < (TEST_PATTERN_LENGTH * 2); word_in_pattern++) { + word_offset = word_in_pattern + edge_offset; + if ((word_offset > (TEST_PATTERN_LENGTH * 2 - 1)) || + (word_offset < 0)) + continue; + + if ((read_pattern[word_in_pattern] & pup_mask_table[bus_id]) == + (pattern_test_pattern_table[word_offset] & + pup_mask_table[bus_id])) + num_of_succ_byte_compare++; + } + + abs_offset = (edge_offset > 0) ? edge_offset : -edge_offset; + if (num_of_succ_byte_compare == ((TEST_PATTERN_LENGTH * 2) - + abs_offset - start_xsb_offset)) { + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: IF %d bus_id %d num_of_succ_byte_compare %d - Success\n", + if_id, bus_id, num_of_succ_byte_compare)); + return MV_OK; + } else { + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: IF %d bus_id %d num_of_succ_byte_compare %d - Fail !\n", + if_id, bus_id, num_of_succ_byte_compare)); + + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: expected 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + pattern_test_pattern_table[0], + pattern_test_pattern_table[1], + pattern_test_pattern_table[2], + pattern_test_pattern_table[3], + pattern_test_pattern_table[4], + pattern_test_pattern_table[5], + pattern_test_pattern_table[6], + pattern_test_pattern_table[7])); + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: recieved 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + read_pattern[0], read_pattern[1], + read_pattern[2], read_pattern[3], + read_pattern[4], read_pattern[5], + read_pattern[6], read_pattern[7])); + + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("XSB-compt: IF %d bus_id %d num_of_succ_byte_compare %d - Fail !\n", + if_id, bus_id, num_of_succ_byte_compare)); + + return MV_FAIL; + } +} + +/* + * Clock error shift - function moves the write leveling delay 1cc forward + */ +static int ddr3_tip_wl_supp_one_clk_err_shift(u32 dev_num, u32 if_id, + u32 bus_id, u32 bus_id_delta) +{ + int phase, adll; + u32 data; + DEBUG_LEVELING(DEBUG_LEVEL_TRACE, ("One_clk_err_shift\n")); + + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, ACCESS_TYPE_UNICAST, bus_id, + DDR_PHY_DATA, WL_PHY_REG, &data)); + phase = ((data >> 6) & 0x7); + adll = data & 0x1f; + DEBUG_LEVELING(DEBUG_LEVEL_TRACE, + ("One_clk_err_shift: IF %d bus_id %d phase %d adll %d\n", + if_id, bus_id, phase, adll)); + + if ((phase == 0) || (phase == 1)) { + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, bus_id, + DDR_PHY_DATA, 0, (phase + 2), 0x1f)); + } else if (phase == 2) { + if (adll < 6) { + data = (3 << 6) + (0x1f); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + bus_id, DDR_PHY_DATA, 0, data, + (0x7 << 6 | 0x1f))); + data = 0x2f; + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + bus_id, DDR_PHY_DATA, 1, data, 0x3f)); + } + } else { + /* phase 3 */ + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Align error shift + */ +static int ddr3_tip_wl_supp_align_err_shift(u32 dev_num, u32 if_id, + u32 bus_id, u32 bus_id_delta) +{ + int phase, adll; + u32 data; + + /* Shift WL result 1 phase back */ + CHECK_STATUS(ddr3_tip_bus_read(dev_num, if_id, ACCESS_TYPE_UNICAST, + bus_id, DDR_PHY_DATA, WL_PHY_REG, + &data)); + phase = ((data >> 6) & 0x7); + adll = data & 0x1f; + DEBUG_LEVELING( + DEBUG_LEVEL_TRACE, + ("Wl_supp_align_err_shift: IF %d bus_id %d phase %d adll %d\n", + if_id, bus_id, phase, adll)); + + if (phase < 2) { + if (adll > 0x1a) { + if (phase == 0) + return MV_FAIL; + + if (phase == 1) { + data = 0; + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, bus_id, DDR_PHY_DATA, + 0, data, (0x7 << 6 | 0x1f))); + data = 0xf; + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, bus_id, DDR_PHY_DATA, + 1, data, 0x1f)); + return MV_OK; + } + } else { + return MV_FAIL; + } + } else if ((phase == 2) || (phase == 3)) { + phase = phase - 2; + data = (phase << 6) + (adll & 0x1f); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, bus_id, + DDR_PHY_DATA, 0, data, (0x7 << 6 | 0x1f))); + return MV_OK; + } else { + DEBUG_LEVELING(DEBUG_LEVEL_ERROR, + ("Wl_supp_align_err_shift: unexpected phase\n")); + + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Dynamic write leveling sequence + */ +static int ddr3_tip_dynamic_write_leveling_seq(u32 dev_num) +{ + u32 bus_id, dq_id; + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_SW_2_REG, 0x1, 0x5)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_WRITE_LEVELING_REG, 0x50, 0xff)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_WRITE_LEVELING_REG, 0x5c, 0xff)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_TRAINING_CONTROL_REG, 0x381b82, 0x3c3faf)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_OBJ1_OPCODE_REG, (0x3 << 25), (0x3ffff << 9))); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_OBJ1_ITER_CNT_REG, 0x80, 0xffff)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_WRITE_LEVELING_DONE_CNTR_REG, 0x14, 0xff)); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + TRAINING_WRITE_LEVELING_REG, 0xff5c, 0xffff)); + + /* mask PBS */ + for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_dq_reg_map[dq_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Mask all results */ + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; bus_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_pup_reg_map[bus_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Unmask only wanted */ + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_pup_reg_map[bus_id], 0, 0x1 << 24)); + } + + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + WR_LEVELING_DQS_PATTERN_REG, 0x1, 0x1)); + + return MV_OK; +} + +/* + * Dynamic read leveling sequence + */ +static int ddr3_tip_dynamic_read_leveling_seq(u32 dev_num) +{ + u32 bus_id, dq_id; + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* mask PBS */ + for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_dq_reg_map[dq_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Mask all results */ + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; bus_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_pup_reg_map[bus_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Unmask only wanted */ + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_pup_reg_map[bus_id], 0, 0x1 << 24)); + } + + return MV_OK; +} + +/* + * Dynamic read leveling sequence + */ +static int ddr3_tip_dynamic_per_bit_read_leveling_seq(u32 dev_num) +{ + u32 bus_id, dq_id; + u16 *mask_results_pup_reg_map = ddr3_tip_get_mask_results_pup_reg_map(); + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* mask PBS */ + for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_dq_reg_map[dq_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Mask all results */ + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; bus_id++) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_pup_reg_map[bus_id], 0x1 << 24, + 0x1 << 24)); + } + + /* Unmask only wanted */ + for (dq_id = 0; dq_id < MAX_DQ_NUM; dq_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, dq_id / 8); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + mask_results_dq_reg_map[dq_id], 0x0 << 24, + 0x1 << 24)); + } + + return MV_OK; +} + +/* + * Print write leveling supplementary results + */ +int ddr3_tip_print_wl_supp_result(u32 dev_num) +{ + u32 bus_id = 0, if_id = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + DEBUG_LEVELING(DEBUG_LEVEL_INFO, + ("I/F0 PUP0 Result[0 - success, 1-fail] ...\n")); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + DEBUG_LEVELING(DEBUG_LEVEL_INFO, + ("%d ,", wr_supp_res[if_id] + [bus_id].is_pup_fail)); + } + } + DEBUG_LEVELING( + DEBUG_LEVEL_INFO, + ("I/F0 PUP0 Stage[0-phase_shift, 1-clock_shift, 2-align_shift] ...\n")); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_id = 0; bus_id < tm->num_of_bus_per_interface; + bus_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_id); + DEBUG_LEVELING(DEBUG_LEVEL_INFO, + ("%d ,", wr_supp_res[if_id] + [bus_id].stage)); + } + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_leveling.h b/drivers/ddr/marvell/a38x/ddr3_training_leveling.h new file mode 100644 index 0000000..f2b4177 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_leveling.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR3_TRAINING_LEVELING_H_ +#define _DDR3_TRAINING_LEVELING_H_ + +#define MAX_DQ_READ_LEVELING_DELAY 15 + +int ddr3_tip_print_wl_supp_result(u32 dev_num); +int ddr3_tip_calc_cs_mask(u32 dev_num, u32 if_id, u32 effective_cs, + u32 *cs_mask); +u32 hws_ddr3_tip_max_cs_get(void); + +#endif /* _DDR3_TRAINING_LEVELING_H_ */ diff --git a/drivers/ddr/marvell/a38x/ddr3_training_pbs.c b/drivers/ddr/marvell/a38x/ddr3_training_pbs.c new file mode 100644 index 0000000..2b4a58f --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_pbs.c @@ -0,0 +1,995 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#define TYPICAL_PBS_VALUE 12 + +u32 nominal_adll[MAX_INTERFACE_NUM * MAX_BUS_NUM]; +enum hws_training_ip_stat train_status[MAX_INTERFACE_NUM]; +u8 result_mat[MAX_INTERFACE_NUM][MAX_BUS_NUM][BUS_WIDTH_IN_BITS]; +u8 result_mat_rx_dqs[MAX_INTERFACE_NUM][MAX_BUS_NUM][MAX_CS_NUM]; +/* 4-EEWA, 3-EWA, 2-SWA, 1-Fail, 0-Pass */ +u8 result_all_bit[MAX_BUS_NUM * BUS_WIDTH_IN_BITS * MAX_INTERFACE_NUM]; +u8 max_pbs_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 min_pbs_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 max_adll_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 min_adll_per_pup[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u32 pbsdelay_per_pup[NUM_OF_PBS_MODES][MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 adll_shift_lock[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +u8 adll_shift_val[MAX_INTERFACE_NUM][MAX_BUS_NUM]; +enum hws_pattern pbs_pattern = PATTERN_VREF; +static u8 pup_state[MAX_INTERFACE_NUM][MAX_BUS_NUM]; + +/* + * Name: ddr3_tip_pbs + * Desc: PBS + * Args: TBD + * Notes: + * Returns: OK if success, other error code if fail. + */ +int ddr3_tip_pbs(u32 dev_num, enum pbs_dir pbs_mode) +{ + u32 res0[MAX_INTERFACE_NUM]; + int adll_tap = MEGA / freq_val[medium_freq] / 64; + int pad_num = 0; + enum hws_search_dir search_dir = + (pbs_mode == PBS_RX_MODE) ? HWS_HIGH2LOW : HWS_LOW2HIGH; + enum hws_dir dir = (pbs_mode == PBS_RX_MODE) ? OPER_READ : OPER_WRITE; + int iterations = (pbs_mode == PBS_RX_MODE) ? 31 : 63; + u32 res_valid_mask = (pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f; + int init_val = (search_dir == HWS_LOW2HIGH) ? 0 : iterations; + enum hws_edge_compare search_edge = EDGE_FP; + u32 pup = 0, bit = 0, if_id = 0, all_lock = 0, cs_num = 0; + int reg_addr = 0; + u32 validation_val = 0; + u32 cs_enable_reg_val[MAX_INTERFACE_NUM]; + u16 *mask_results_dq_reg_map = ddr3_tip_get_mask_results_dq_reg(); + u8 temp = 0; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* save current cs enable reg val */ + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + /* save current cs enable reg val */ + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val, MASK_ALL_BITS)); + + /* enable single cs */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, (1 << 3), (1 << 3))); + } + + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (READ_CENTRALIZATION_PHY_REG + + (effective_cs * CS_REGISTER_ADDR_OFFSET)) : + (WRITE_CENTRALIZATION_PHY_REG + + (effective_cs * CS_REGISTER_ADDR_OFFSET)); + read_adll_value(nominal_adll, reg_addr, MASK_ALL_BITS); + + /* stage 1 shift ADLL */ + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, RESULT_PER_BIT, + HWS_CONTROL_ELEMENT_ADLL, search_dir, dir, + tm->if_act_mask, init_val, iterations, + pbs_pattern, search_edge, CS_SINGLE, cs_num, + train_status); + validation_val = (pbs_mode == PBS_RX_MODE) ? 0x1f : 0; + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + min_adll_per_pup[if_id][pup] = + (pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f; + pup_state[if_id][pup] = 0x3; + adll_shift_lock[if_id][pup] = 1; + max_adll_per_pup[if_id][pup] = 0x0; + } + } + + /* EBA */ + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + mask_results_dq_reg_map[ + bit + pup * BUS_WIDTH_IN_BITS], + res0, MASK_ALL_BITS)); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("FP I/F %d, bit:%d, pup:%d res0 0x%x\n", + if_id, bit, pup, + res0[if_id])); + if (pup_state[if_id][pup] != 3) + continue; + /* if not EBA state than move to next pup */ + + if ((res0[if_id] & 0x2000000) == 0) { + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("-- Fail Training IP\n")); + /* training machine failed */ + pup_state[if_id][pup] = 1; + adll_shift_lock[if_id][pup] = 0; + continue; + } + + else if ((res0[if_id] & res_valid_mask) == + validation_val) { + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("-- FAIL EBA %d %d %d %d\n", + if_id, bit, pup, + res0[if_id])); + pup_state[if_id][pup] = 4; + /* this pup move to EEBA */ + adll_shift_lock[if_id][pup] = 0; + continue; + } else { + /* + * The search ended in Pass we need + * Fail + */ + res0[if_id] = + (pbs_mode == PBS_RX_MODE) ? + ((res0[if_id] & + res_valid_mask) + 1) : + ((res0[if_id] & + res_valid_mask) - 1); + max_adll_per_pup[if_id][pup] = + (max_adll_per_pup[if_id][pup] < + res0[if_id]) ? + (u8)res0[if_id] : + max_adll_per_pup[if_id][pup]; + min_adll_per_pup[if_id][pup] = + (res0[if_id] > + min_adll_per_pup[if_id][pup]) ? + min_adll_per_pup[if_id][pup] : + (u8) + res0[if_id]; + /* + * vs the Rx we are searching for the + * smallest value of DQ shift so all + * Bus would fail + */ + adll_shift_val[if_id][pup] = + (pbs_mode == PBS_RX_MODE) ? + max_adll_per_pup[if_id][pup] : + min_adll_per_pup[if_id][pup]; + } + } + } + } + + /* EEBA */ + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + if (pup_state[if_id][pup] != 4) + continue; + /* + * if pup state different from EEBA than move to + * next pup + */ + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x54 + effective_cs * 0x10) : + (0x14 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA, + reg_addr, 0x1f)); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x55 + effective_cs * 0x10) : + (0x15 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA, + reg_addr, 0x1f)); + /* initialize the Edge2 Max. */ + adll_shift_val[if_id][pup] = 0; + min_adll_per_pup[if_id][pup] = + (pbs_mode == PBS_RX_MODE) ? 0x1f : 0x3f; + max_adll_per_pup[if_id][pup] = 0x0; + + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, RESULT_PER_BIT, + HWS_CONTROL_ELEMENT_ADLL, + search_dir, dir, + tm->if_act_mask, init_val, + iterations, pbs_pattern, + search_edge, CS_SINGLE, cs_num, + train_status); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("ADLL shift results:\n")); + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + mask_results_dq_reg_map[ + bit + pup * + BUS_WIDTH_IN_BITS], + res0, MASK_ALL_BITS)); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("FP I/F %d, bit:%d, pup:%d res0 0x%x\n", + if_id, bit, pup, + res0[if_id])); + + if ((res0[if_id] & 0x2000000) == 0) { + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + (" -- EEBA Fail\n")); + bit = BUS_WIDTH_IN_BITS; + /* exit bit loop */ + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("-- EEBA Fail Training IP\n")); + /* + * training machine failed but pass + * before in the EBA so maybe the DQS + * shift change env. + */ + pup_state[if_id][pup] = 2; + adll_shift_lock[if_id][pup] = 0; + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x54 + effective_cs * 0x10) : + (0x14 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + 0x0)); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x55 + effective_cs * 0x10) : + (0x15 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + 0x0)); + continue; + } else if ((res0[if_id] & res_valid_mask) == + validation_val) { + /* exit bit loop */ + bit = BUS_WIDTH_IN_BITS; + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("-- FAIL EEBA\n")); + /* this pup move to SBA */ + pup_state[if_id][pup] = 2; + adll_shift_lock[if_id][pup] = 0; + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x54 + effective_cs * 0x10) : + (0x14 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + 0x0)); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x55 + effective_cs * 0x10) : + (0x15 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, + ACCESS_TYPE_UNICAST, + if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + 0x0)); + continue; + } else { + adll_shift_lock[if_id][pup] = 1; + /* + * The search ended in Pass we need + * Fail + */ + res0[if_id] = + (pbs_mode == PBS_RX_MODE) ? + ((res0[if_id] & + res_valid_mask) + 1) : + ((res0[if_id] & + res_valid_mask) - 1); + max_adll_per_pup[if_id][pup] = + (max_adll_per_pup[if_id][pup] < + res0[if_id]) ? + (u8)res0[if_id] : + max_adll_per_pup[if_id][pup]; + min_adll_per_pup[if_id][pup] = + (res0[if_id] > + min_adll_per_pup[if_id][pup]) ? + min_adll_per_pup[if_id][pup] : + (u8)res0[if_id]; + /* + * vs the Rx we are searching for the + * smallest value of DQ shift so all Bus + * would fail + */ + adll_shift_val[if_id][pup] = + (pbs_mode == PBS_RX_MODE) ? + max_adll_per_pup[if_id][pup] : + min_adll_per_pup[if_id][pup]; + } + } + } + } + + /* Print Stage result */ + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("FP I/F %d, ADLL Shift for EBA: pup[%d] Lock status = %d Lock Val = %d,%d\n", + if_id, pup, + adll_shift_lock[if_id][pup], + max_adll_per_pup[if_id][pup], + min_adll_per_pup[if_id][pup])); + } + } + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("Update ADLL Shift of all pups:\n")); + + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (adll_shift_lock[if_id][pup] != 1) + continue; + /* if pup not locked continue to next pup */ + + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x3 + effective_cs * 4) : + (0x1 + effective_cs * 4); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA, + reg_addr, adll_shift_val[if_id][pup])); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_TRACE, + ("FP I/F %d, Pup[%d] = %d\n", if_id, + pup, adll_shift_val[if_id][pup])); + } + } + + /* PBS EEBA&EBA */ + /* Start the Per Bit Skew search */ + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + max_pbs_per_pup[if_id][pup] = 0x0; + min_pbs_per_pup[if_id][pup] = 0x1f; + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + /* reset result for PBS */ + result_all_bit[bit + pup * BUS_WIDTH_IN_BITS + + if_id * MAX_BUS_NUM * + BUS_WIDTH_IN_BITS] = 0; + } + } + } + + iterations = 31; + search_dir = HWS_LOW2HIGH; + /* !!!!! ran sh (search_dir == HWS_LOW2HIGH)?0:iterations; */ + init_val = 0; + + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + RESULT_PER_BIT, HWS_CONTROL_ELEMENT_DQ_SKEW, + search_dir, dir, tm->if_act_mask, init_val, + iterations, pbs_pattern, search_edge, + CS_SINGLE, cs_num, train_status); + + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (adll_shift_lock[if_id][pup] != 1) { + /* if pup not lock continue to next pup */ + continue; + } + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + mask_results_dq_reg_map[ + bit + + pup * BUS_WIDTH_IN_BITS], + res0, MASK_ALL_BITS)); + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("Per Bit Skew search, FP I/F %d, bit:%d, pup:%d res0 0x%x\n", + if_id, bit, pup, + res0[if_id])); + if ((res0[if_id] & 0x2000000) == 0) { + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("--EBA PBS Fail - Training IP machine\n")); + /* exit the bit loop */ + bit = BUS_WIDTH_IN_BITS; + /* + * ADLL is no long in lock need new + * search + */ + adll_shift_lock[if_id][pup] = 0; + /* Move to SBA */ + pup_state[if_id][pup] = 2; + max_pbs_per_pup[if_id][pup] = 0x0; + min_pbs_per_pup[if_id][pup] = 0x1f; + continue; + } else { + temp = (u8)(res0[if_id] & + res_valid_mask); + max_pbs_per_pup[if_id][pup] = + (temp > + max_pbs_per_pup[if_id][pup]) ? + temp : + max_pbs_per_pup[if_id][pup]; + min_pbs_per_pup[if_id][pup] = + (temp < + min_pbs_per_pup[if_id][pup]) ? + temp : + min_pbs_per_pup[if_id][pup]; + result_all_bit[bit + + pup * BUS_WIDTH_IN_BITS + + if_id * MAX_BUS_NUM * + BUS_WIDTH_IN_BITS] = + temp; + } + } + } + } + + /* Check all Pup lock */ + all_lock = 1; + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + all_lock = all_lock * adll_shift_lock[if_id][pup]; + } + } + + /* Only if not all Pups Lock */ + if (all_lock == 0) { + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("##########ADLL shift for SBA###########\n")); + + /* ADLL shift for SBA */ + search_dir = (pbs_mode == PBS_RX_MODE) ? HWS_LOW2HIGH : + HWS_HIGH2LOW; + init_val = (search_dir == HWS_LOW2HIGH) ? 0 : iterations; + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + if (adll_shift_lock[if_id][pup] == 1) { + /*if pup lock continue to next pup */ + continue; + } + /*init the var altogth init before */ + adll_shift_lock[if_id][pup] = 0; + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x54 + effective_cs * 0x10) : + (0x14 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, 0)); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x55 + effective_cs * 0x10) : + (0x15 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, 0)); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x5f + effective_cs * 0x10) : + (0x1f + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, 0)); + /* initilaze the Edge2 Max. */ + adll_shift_val[if_id][pup] = 0; + min_adll_per_pup[if_id][pup] = 0x1f; + max_adll_per_pup[if_id][pup] = 0x0; + + ddr3_tip_ip_training(dev_num, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + RESULT_PER_BIT, + HWS_CONTROL_ELEMENT_ADLL, + search_dir, dir, + tm->if_act_mask, + init_val, iterations, + pbs_pattern, + search_edge, CS_SINGLE, + cs_num, train_status); + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + mask_results_dq_reg_map + [bit + + pup * + BUS_WIDTH_IN_BITS], + res0, MASK_ALL_BITS)); + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + ("FP I/F %d, bit:%d, pup:%d res0 0x%x\n", + if_id, bit, pup, res0[if_id])); + if ((res0[if_id] & 0x2000000) == 0) { + /* exit the bit loop */ + bit = BUS_WIDTH_IN_BITS; + /* Fail SBA --> Fail PBS */ + pup_state[if_id][pup] = 1; + DEBUG_PBS_ENGINE + (DEBUG_LEVEL_INFO, + (" SBA Fail\n")); + continue; + } else { + /* + * - increment to get all + * 8 bit lock. + */ + adll_shift_lock[if_id][pup]++; + /* + * The search ended in Pass + * we need Fail + */ + res0[if_id] = + (pbs_mode == PBS_RX_MODE) ? + ((res0[if_id] & res_valid_mask) + 1) : + ((res0[if_id] & res_valid_mask) - 1); + max_adll_per_pup[if_id][pup] = + (max_adll_per_pup[if_id] + [pup] < res0[if_id]) ? + (u8)res0[if_id] : + max_adll_per_pup[if_id][pup]; + min_adll_per_pup[if_id][pup] = + (res0[if_id] > + min_adll_per_pup[if_id] + [pup]) ? + min_adll_per_pup[if_id][pup] : + (u8)res0[if_id]; + /* + * vs the Rx we are searching for + * the smallest value of DQ shift + * so all Bus would fail + */ + adll_shift_val[if_id][pup] = + (pbs_mode == PBS_RX_MODE) ? + max_adll_per_pup[if_id][pup] : + min_adll_per_pup[if_id][pup]; + } + } + /* 1 is lock */ + adll_shift_lock[if_id][pup] = + (adll_shift_lock[if_id][pup] == 8) ? + 1 : 0; + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x3 + effective_cs * 4) : + (0x1 + effective_cs * 4); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + adll_shift_val[if_id][pup])); + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + ("adll_shift_lock[%x][%x] = %x\n", + if_id, pup, + adll_shift_lock[if_id][pup])); + } + } + + /* End ADLL Shift for SBA */ + /* Start the Per Bit Skew search */ + /* The ADLL shift finished with a Pass */ + search_edge = (pbs_mode == PBS_RX_MODE) ? EDGE_PF : EDGE_FP; + search_dir = (pbs_mode == PBS_RX_MODE) ? + HWS_LOW2HIGH : HWS_HIGH2LOW; + iterations = 0x1f; + /* - The initial value is different in Rx and Tx mode */ + init_val = (pbs_mode == PBS_RX_MODE) ? 0 : iterations; + + ddr3_tip_ip_training(dev_num, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, RESULT_PER_BIT, + HWS_CONTROL_ELEMENT_DQ_SKEW, + search_dir, dir, tm->if_act_mask, + init_val, iterations, pbs_pattern, + search_edge, CS_SINGLE, cs_num, + train_status); + + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + CHECK_STATUS(ddr3_tip_if_read + (dev_num, + ACCESS_TYPE_MULTICAST, + PARAM_NOT_CARE, + mask_results_dq_reg_map + [bit + + pup * + BUS_WIDTH_IN_BITS], + res0, MASK_ALL_BITS)); + if (pup_state[if_id][pup] != 2) { + /* + * if pup is not SBA continue + * to next pup + */ + bit = BUS_WIDTH_IN_BITS; + continue; + } + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + ("Per Bit Skew search, PF I/F %d, bit:%d, pup:%d res0 0x%x\n", + if_id, bit, pup, res0[if_id])); + if ((res0[if_id] & 0x2000000) == 0) { + DEBUG_PBS_ENGINE + (DEBUG_LEVEL_INFO, + ("SBA Fail\n")); + + max_pbs_per_pup[if_id][pup] = + 0x1f; + result_all_bit[ + bit + pup * + BUS_WIDTH_IN_BITS + + if_id * MAX_BUS_NUM * + BUS_WIDTH_IN_BITS] = + 0x1f; + } else { + temp = (u8)(res0[if_id] & + res_valid_mask); + max_pbs_per_pup[if_id][pup] = + (temp > + max_pbs_per_pup[if_id] + [pup]) ? temp : + max_pbs_per_pup + [if_id][pup]; + min_pbs_per_pup[if_id][pup] = + (temp < + min_pbs_per_pup[if_id] + [pup]) ? temp : + min_pbs_per_pup + [if_id][pup]; + result_all_bit[ + bit + pup * + BUS_WIDTH_IN_BITS + + if_id * MAX_BUS_NUM * + BUS_WIDTH_IN_BITS] = + temp; + adll_shift_lock[if_id][pup] = 1; + } + } + } + } + + /* Check all Pup state */ + all_lock = 1; + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + /* + * DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + * ("pup_state[%d][%d] = %d\n",if_id,pup,pup_state + * [if_id][pup])); + */ + } + } + + /* END OF SBA */ + /* Norm */ + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; + if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* if pup not lock continue to next pup */ + if (adll_shift_lock[if_id][pup] != 1) { + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_ERROR, + ("PBS failed for IF #%d\n", + if_id)); + training_result[training_stage][if_id] + = TEST_FAILED; + + result_mat[if_id][pup][bit] = 0; + max_pbs_per_pup[if_id][pup] = 0; + min_pbs_per_pup[if_id][pup] = 0; + } else { + training_result[ + training_stage][if_id] = + (training_result[training_stage] + [if_id] == TEST_FAILED) ? + TEST_FAILED : TEST_SUCCESS; + result_mat[if_id][pup][bit] = + result_all_bit[ + bit + pup * + BUS_WIDTH_IN_BITS + + if_id * MAX_BUS_NUM * + BUS_WIDTH_IN_BITS] - + min_pbs_per_pup[if_id][pup]; + } + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + ("The abs min_pbs[%d][%d] = %d\n", + if_id, pup, + min_pbs_per_pup[if_id][pup])); + } + } + } + + /* Clean all results */ + ddr3_tip_clean_pbs_result(dev_num, pbs_mode); + + /* DQ PBS register update with the final result */ + for (if_id = 0; if_id < MAX_INTERFACE_NUM; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = 0; pup < tm->num_of_bus_per_interface; pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + ("Final Results: if_id %d, pup %d, Pup State: %d\n", + if_id, pup, pup_state[if_id][pup])); + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + if (dq_map_table == NULL) { + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_ERROR, + ("dq_map_table not initialized\n")); + return MV_FAIL; + } + pad_num = dq_map_table[ + bit + pup * BUS_WIDTH_IN_BITS + + if_id * BUS_WIDTH_IN_BITS * + tm->num_of_bus_per_interface]; + DEBUG_PBS_ENGINE(DEBUG_LEVEL_INFO, + ("result_mat: %d ", + result_mat[if_id][pup] + [bit])); + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (PBS_RX_PHY_REG + effective_cs * 0x10) : + (PBS_TX_PHY_REG + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr + pad_num, + result_mat[if_id][pup][bit])); + } + pbsdelay_per_pup[pbs_mode][if_id][pup] = + (max_pbs_per_pup[if_id][pup] == + min_pbs_per_pup[if_id][pup]) ? + TYPICAL_PBS_VALUE : + ((max_adll_per_pup[if_id][pup] - + min_adll_per_pup[if_id][pup]) * adll_tap / + (max_pbs_per_pup[if_id][pup] - + min_pbs_per_pup[if_id][pup])); + + /* RX results ready, write RX also */ + if (pbs_mode == PBS_TX_MODE) { + /* Write TX results */ + reg_addr = (0x14 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (max_pbs_per_pup[if_id][pup] - + min_pbs_per_pup[if_id][pup]) / + 2)); + reg_addr = (0x15 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + (max_pbs_per_pup[if_id][pup] - + min_pbs_per_pup[if_id][pup]) / + 2)); + + /* Write previously stored RX results */ + reg_addr = (0x54 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + result_mat_rx_dqs[if_id][pup] + [effective_cs])); + reg_addr = (0x55 + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr, + result_mat_rx_dqs[if_id][pup] + [effective_cs])); + } else { + /* + * RX results may affect RL results correctess, + * so just store the results that will written + * in TX stage + */ + result_mat_rx_dqs[if_id][pup][effective_cs] = + (max_pbs_per_pup[if_id][pup] - + min_pbs_per_pup[if_id][pup]) / 2; + } + DEBUG_PBS_ENGINE( + DEBUG_LEVEL_INFO, + (", PBS tap=%d [psec] ==> skew observed = %d\n", + pbsdelay_per_pup[pbs_mode][if_id][pup], + ((max_pbs_per_pup[if_id][pup] - + min_pbs_per_pup[if_id][pup]) * + pbsdelay_per_pup[pbs_mode][if_id][pup]))); + } + } + + /* Write back to the phy the default values */ + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (READ_CENTRALIZATION_PHY_REG + effective_cs * 4) : + (WRITE_CENTRALIZATION_PHY_REG + effective_cs * 4); + write_adll_value(nominal_adll, reg_addr); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + reg_addr = (pbs_mode == PBS_RX_MODE) ? + (0x5a + effective_cs * 0x10) : + (0x1a + effective_cs * 0x10); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, pup, DDR_PHY_DATA, reg_addr, + 0)); + + /* restore cs enable value */ + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + CS_ENABLE_REG, cs_enable_reg_val[if_id], + MASK_ALL_BITS)); + } + + /* exit test mode */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ODPG_WRITE_READ_MODE_ENABLE_REG, 0xffff, MASK_ALL_BITS)); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + /* + * meaning that there is no VW exist at all (No lock at + * the EBA ADLL shift at EBS) + */ + if (pup_state[if_id][pup] == 1) + return MV_FAIL; + } + + return MV_OK; +} + +/* + * Name: ddr3_tip_pbs_rx. + * Desc: PBS TX + * Args: TBD + * Notes: + * Returns: OK if success, other error code if fail. + */ +int ddr3_tip_pbs_rx(u32 uidev_num) +{ + return ddr3_tip_pbs(uidev_num, PBS_RX_MODE); +} + +/* + * Name: ddr3_tip_pbs_tx. + * Desc: PBS TX + * Args: TBD + * Notes: + * Returns: OK if success, other error code if fail. + */ +int ddr3_tip_pbs_tx(u32 uidev_num) +{ + return ddr3_tip_pbs(uidev_num, PBS_TX_MODE); +} + +#ifndef EXCLUDE_SWITCH_DEBUG +/* + * Print PBS Result + */ +int ddr3_tip_print_all_pbs_result(u32 dev_num) +{ + u32 curr_cs; + u32 max_cs = hws_ddr3_tip_max_cs_get(); + + for (curr_cs = 0; curr_cs < max_cs; curr_cs++) { + ddr3_tip_print_pbs_result(dev_num, curr_cs, PBS_RX_MODE); + ddr3_tip_print_pbs_result(dev_num, curr_cs, PBS_TX_MODE); + } + + return MV_OK; +} + +/* + * Print PBS Result + */ +int ddr3_tip_print_pbs_result(u32 dev_num, u32 cs_num, enum pbs_dir pbs_mode) +{ + u32 data_value = 0, bit = 0, if_id = 0, pup = 0; + u32 reg_addr = (pbs_mode == PBS_RX_MODE) ? + (PBS_RX_PHY_REG + cs_num * 0x10) : + (PBS_TX_PHY_REG + cs_num * 0x10); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + printf("CS%d, %s ,PBS\n", cs_num, + (pbs_mode == PBS_RX_MODE) ? "Rx" : "Tx"); + + for (bit = 0; bit < BUS_WIDTH_IN_BITS; bit++) { + printf("%s, DQ", (pbs_mode == PBS_RX_MODE) ? "Rx" : "Tx"); + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + printf("%d ,PBS,,, ", bit); + for (pup = 0; pup <= tm->num_of_bus_per_interface; + pup++) { + VALIDATE_ACTIVE(tm->bus_act_mask, pup); + CHECK_STATUS(ddr3_tip_bus_read + (dev_num, if_id, + ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr + bit, + &data_value)); + printf("%d , ", data_value); + } + } + printf("\n"); + } + printf("\n"); + + return MV_OK; +} +#endif + +/* + * Fixup PBS Result + */ +int ddr3_tip_clean_pbs_result(u32 dev_num, enum pbs_dir pbs_mode) +{ + u32 if_id, pup, bit; + u32 reg_addr = (pbs_mode == PBS_RX_MODE) ? + (PBS_RX_PHY_REG + effective_cs * 0x10) : + (PBS_TX_PHY_REG + effective_cs * 0x10); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (pup = 0; pup <= tm->num_of_bus_per_interface; pup++) { + for (bit = 0; bit <= BUS_WIDTH_IN_BITS + 3; bit++) { + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, ACCESS_TYPE_UNICAST, pup, + DDR_PHY_DATA, reg_addr + bit, 0)); + } + } + } + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr3_training_static.c b/drivers/ddr/marvell/a38x/ddr3_training_static.c new file mode 100644 index 0000000..5101f3f --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr3_training_static.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +/* Design Guidelines parameters */ +u32 g_zpri_data = 123; /* controller data - P drive strength */ +u32 g_znri_data = 123; /* controller data - N drive strength */ +u32 g_zpri_ctrl = 74; /* controller C/A - P drive strength */ +u32 g_znri_ctrl = 74; /* controller C/A - N drive strength */ +u32 g_zpodt_data = 45; /* controller data - P ODT */ +u32 g_znodt_data = 45; /* controller data - N ODT */ +u32 g_zpodt_ctrl = 45; /* controller data - P ODT */ +u32 g_znodt_ctrl = 45; /* controller data - N ODT */ +u32 g_odt_config = 0x120012; +u32 g_rtt_nom = 0x44; +u32 g_dic = 0x2; + +#ifdef STATIC_ALGO_SUPPORT + +#define PARAM_NOT_CARE 0 +#define MAX_STATIC_SEQ 48 + +u32 silicon_delay[HWS_MAX_DEVICE_NUM]; +struct hws_tip_static_config_info static_config[HWS_MAX_DEVICE_NUM]; +static reg_data *static_init_controller_config[HWS_MAX_DEVICE_NUM]; + +/* debug delay in write leveling */ +int wl_debug_delay = 0; +/* pup register #3 for functional board */ +int function_reg_value = 8; +u32 silicon; + +u32 read_ready_delay_phase_offset[] = { 4, 4, 4, 4, 6, 6, 6, 6 }; + +static struct cs_element chip_select_map[] = { + /* CS Value (single only) Num_CS */ + {0, 0}, + {0, 1}, + {1, 1}, + {0, 2}, + {2, 1}, + {0, 2}, + {0, 2}, + {0, 3}, + {3, 1}, + {0, 2}, + {0, 2}, + {0, 3}, + {0, 2}, + {0, 3}, + {0, 3}, + {0, 4} +}; + +/* + * Register static init controller DB + */ +int ddr3_tip_init_specific_reg_config(u32 dev_num, reg_data *reg_config_arr) +{ + static_init_controller_config[dev_num] = reg_config_arr; + return MV_OK; +} + +/* + * Register static info DB + */ +int ddr3_tip_init_static_config_db( + u32 dev_num, struct hws_tip_static_config_info *static_config_info) +{ + static_config[dev_num].board_trace_arr = + static_config_info->board_trace_arr; + static_config[dev_num].package_trace_arr = + static_config_info->package_trace_arr; + silicon_delay[dev_num] = static_config_info->silicon_delay; + + return MV_OK; +} + +/* + * Static round trip flow - Calculates the total round trip delay. + */ +int ddr3_tip_static_round_trip_arr_build(u32 dev_num, + struct trip_delay_element *table_ptr, + int is_wl, u32 *round_trip_delay_arr) +{ + u32 bus_index, global_bus; + u32 if_id; + u32 bus_per_interface; + int sign; + u32 temp; + u32 board_trace; + struct trip_delay_element *pkg_delay_ptr; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + /* + * In WL we calc the diff between Clock to DQs in RL we sum the round + * trip of Clock and DQs + */ + sign = (is_wl) ? -1 : 1; + + bus_per_interface = GET_TOPOLOGY_NUM_OF_BUSES(); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + for (bus_index = 0; bus_index < bus_per_interface; + bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + global_bus = (if_id * bus_per_interface) + bus_index; + + /* calculate total trip delay (package and board) */ + board_trace = (table_ptr[global_bus].dqs_delay * sign) + + table_ptr[global_bus].ck_delay; + temp = (board_trace * 163) / 1000; + + /* Convert the length to delay in psec units */ + pkg_delay_ptr = + static_config[dev_num].package_trace_arr; + round_trip_delay_arr[global_bus] = temp + + (int)(pkg_delay_ptr[global_bus].dqs_delay * + sign) + + (int)pkg_delay_ptr[global_bus].ck_delay + + (int)((is_wl == 1) ? wl_debug_delay : + (int)silicon_delay[dev_num]); + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("Round Trip Build round_trip_delay_arr[0x%x]: 0x%x temp 0x%x\n", + global_bus, round_trip_delay_arr[global_bus], + temp)); + } + } + + return MV_OK; +} + +/* + * Write leveling for static flow - calculating the round trip delay of the + * DQS signal. + */ +int ddr3_tip_write_leveling_static_config(u32 dev_num, u32 if_id, + enum hws_ddr_freq frequency, + u32 *round_trip_delay_arr) +{ + u32 bus_index; /* index to the bus loop */ + u32 bus_start_index; + u32 bus_per_interface; + u32 phase = 0; + u32 adll = 0, adll_cen, adll_inv, adll_final; + u32 adll_period = MEGA / freq_val[frequency] / 64; + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("ddr3_tip_write_leveling_static_config\n")); + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("dev_num 0x%x IF 0x%x freq %d (adll_period 0x%x)\n", + dev_num, if_id, frequency, adll_period)); + + bus_per_interface = GET_TOPOLOGY_NUM_OF_BUSES(); + bus_start_index = if_id * bus_per_interface; + for (bus_index = bus_start_index; + bus_index < (bus_start_index + bus_per_interface); bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + phase = round_trip_delay_arr[bus_index] / (32 * adll_period); + adll = (round_trip_delay_arr[bus_index] - + (phase * 32 * adll_period)) / adll_period; + adll = (adll > 31) ? 31 : adll; + adll_cen = 16 + adll; + adll_inv = adll_cen / 32; + adll_final = adll_cen - (adll_inv * 32); + adll_final = (adll_final > 31) ? 31 : adll_final; + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("\t%d - phase 0x%x adll 0x%x\n", + bus_index, phase, adll)); + /* + * Writing to all 4 phy of Interface number, + * bit 0 \96 4 \96 ADLL, bit 6-8 phase + */ + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + (bus_index % 4), DDR_PHY_DATA, + PHY_WRITE_DELAY(cs), + ((phase << 6) + (adll & 0x1f)), 0x1df)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ACCESS_TYPE_UNICAST, (bus_index % 4), + DDR_PHY_DATA, WRITE_CENTRALIZATION_PHY_REG, + ((adll_inv & 0x1) << 5) + adll_final)); + } + + return MV_OK; +} + +/* + * Read leveling for static flow + */ +int ddr3_tip_read_leveling_static_config(u32 dev_num, + u32 if_id, + enum hws_ddr_freq frequency, + u32 *total_round_trip_delay_arr) +{ + u32 cs, data0, data1, data3 = 0; + u32 bus_index; /* index to the bus loop */ + u32 bus_start_index; + u32 phase0, phase1, max_phase; + u32 adll0, adll1; + u32 cl_value; + u32 min_delay; + u32 sdr_period = MEGA / freq_val[frequency]; + u32 ddr_period = MEGA / freq_val[frequency] / 2; + u32 adll_period = MEGA / freq_val[frequency] / 64; + enum hws_speed_bin speed_bin_index; + u32 rd_sample_dly[MAX_CS_NUM] = { 0 }; + u32 rd_ready_del[MAX_CS_NUM] = { 0 }; + u32 bus_per_interface = GET_TOPOLOGY_NUM_OF_BUSES(); + struct hws_topology_map *tm = ddr3_get_topology_map(); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("ddr3_tip_read_leveling_static_config\n")); + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("dev_num 0x%x ifc 0x%x freq %d\n", dev_num, + if_id, frequency)); + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("Sdr_period 0x%x Ddr_period 0x%x adll_period 0x%x\n", + sdr_period, ddr_period, adll_period)); + + if (tm->interface_params[first_active_if].memory_freq == + frequency) { + cl_value = tm->interface_params[first_active_if].cas_l; + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("cl_value 0x%x\n", cl_value)); + } else { + speed_bin_index = tm->interface_params[if_id].speed_bin_index; + cl_value = cas_latency_table[speed_bin_index].cl_val[frequency]; + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("cl_value 0x%x speed_bin_index %d\n", + cl_value, speed_bin_index)); + } + + bus_start_index = if_id * bus_per_interface; + + for (bus_index = bus_start_index; + bus_index < (bus_start_index + bus_per_interface); + bus_index += 2) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + cs = chip_select_map[ + tm->interface_params[if_id].as_bus_params[ + (bus_index % 4)].cs_bitmask].cs_num; + + /* read sample delay calculation */ + min_delay = (total_round_trip_delay_arr[bus_index] < + total_round_trip_delay_arr[bus_index + 1]) ? + total_round_trip_delay_arr[bus_index] : + total_round_trip_delay_arr[bus_index + 1]; + /* round down */ + rd_sample_dly[cs] = 2 * (min_delay / (sdr_period * 2)); + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("\t%d - min_delay 0x%x cs 0x%x rd_sample_dly[cs] 0x%x\n", + bus_index, min_delay, cs, rd_sample_dly[cs])); + + /* phase calculation */ + phase0 = (total_round_trip_delay_arr[bus_index] - + (sdr_period * rd_sample_dly[cs])) / (ddr_period); + phase1 = (total_round_trip_delay_arr[bus_index + 1] - + (sdr_period * rd_sample_dly[cs])) / (ddr_period); + max_phase = (phase0 > phase1) ? phase0 : phase1; + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("\tphase0 0x%x phase1 0x%x max_phase 0x%x\n", + phase0, phase1, max_phase)); + + /* ADLL calculation */ + adll0 = (u32)((total_round_trip_delay_arr[bus_index] - + (sdr_period * rd_sample_dly[cs]) - + (ddr_period * phase0)) / adll_period); + adll0 = (adll0 > 31) ? 31 : adll0; + adll1 = (u32)((total_round_trip_delay_arr[bus_index + 1] - + (sdr_period * rd_sample_dly[cs]) - + (ddr_period * phase1)) / adll_period); + adll1 = (adll1 > 31) ? 31 : adll1; + + /* The Read delay close the Read FIFO */ + rd_ready_del[cs] = rd_sample_dly[cs] + + read_ready_delay_phase_offset[max_phase]; + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_TRACE, + ("\tadll0 0x%x adll1 0x%x rd_ready_del[cs] 0x%x\n", + adll0, adll1, rd_ready_del[cs])); + + /* + * Write to the phy of Interface (bit 0 \96 4 \96 ADLL, + * bit 6-8 phase) + */ + data0 = ((phase0 << 6) + (adll0 & 0x1f)); + data1 = ((phase1 << 6) + (adll1 & 0x1f)); + + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + (bus_index % 4), DDR_PHY_DATA, PHY_READ_DELAY(cs), + data0, 0x1df)); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + ((bus_index + 1) % 4), DDR_PHY_DATA, + PHY_READ_DELAY(cs), data1, 0x1df)); + } + + for (bus_index = 0; bus_index < bus_per_interface; bus_index++) { + VALIDATE_ACTIVE(tm->bus_act_mask, bus_index); + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + bus_index, DDR_PHY_DATA, 0x3, data3, 0x1f)); + } + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_SAMPLE_DELAY, + (rd_sample_dly[0] + cl_value) + (rd_sample_dly[1] << 8), + MASK_ALL_BITS)); + + /* Read_ready_del0 bit 0-4 , CS bits 8-12 */ + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_UNICAST, if_id, + READ_DATA_READY_DELAY, + rd_ready_del[0] + (rd_ready_del[1] << 8) + cl_value, + MASK_ALL_BITS)); + + return MV_OK; +} + +/* + * DDR3 Static flow + */ +int ddr3_tip_run_static_alg(u32 dev_num, enum hws_ddr_freq freq) +{ + u32 if_id = 0; + struct trip_delay_element *table_ptr; + u32 wl_total_round_trip_delay_arr[MAX_TOTAL_BUS_NUM]; + u32 rl_total_round_trip_delay_arr[MAX_TOTAL_BUS_NUM]; + struct init_cntr_param init_cntr_prm; + int ret; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("ddr3_tip_run_static_alg")); + + init_cntr_prm.do_mrs_phy = 1; + init_cntr_prm.is_ctrl64_bit = 0; + init_cntr_prm.init_phy = 1; + ret = hws_ddr3_tip_init_controller(dev_num, &init_cntr_prm); + if (ret != MV_OK) { + DEBUG_TRAINING_STATIC_IP( + DEBUG_LEVEL_ERROR, + ("hws_ddr3_tip_init_controller failure\n")); + } + + /* calculate the round trip delay for Write Leveling */ + table_ptr = static_config[dev_num].board_trace_arr; + CHECK_STATUS(ddr3_tip_static_round_trip_arr_build + (dev_num, table_ptr, 1, + wl_total_round_trip_delay_arr)); + /* calculate the round trip delay for Read Leveling */ + CHECK_STATUS(ddr3_tip_static_round_trip_arr_build + (dev_num, table_ptr, 0, + rl_total_round_trip_delay_arr)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + /* check if the interface is enabled */ + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + /* + * Static frequency is defined according to init-frequency + * (not target) + */ + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Static IF %d freq %d\n", + if_id, freq)); + CHECK_STATUS(ddr3_tip_write_leveling_static_config + (dev_num, if_id, freq, + wl_total_round_trip_delay_arr)); + CHECK_STATUS(ddr3_tip_read_leveling_static_config + (dev_num, if_id, freq, + rl_total_round_trip_delay_arr)); + } + + return MV_OK; +} + +/* + * Init controller for static flow + */ +int ddr3_tip_static_init_controller(u32 dev_num) +{ + u32 index_cnt = 0; + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("ddr3_tip_static_init_controller\n")); + while (static_init_controller_config[dev_num][index_cnt].reg_addr != + 0) { + CHECK_STATUS(ddr3_tip_if_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + static_init_controller_config[dev_num][index_cnt]. + reg_addr, + static_init_controller_config[dev_num][index_cnt]. + reg_data, + static_init_controller_config[dev_num][index_cnt]. + reg_mask)); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Init_controller index_cnt %d\n", + index_cnt)); + index_cnt++; + } + + return MV_OK; +} + +int ddr3_tip_static_phy_init_controller(u32 dev_num) +{ + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Phy Init Controller 2\n")); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0xa4, + 0x3dfe)); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Phy Init Controller 3\n")); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0xa6, + 0xcb2)); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Phy Init Controller 4\n")); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0xa9, + 0)); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Static Receiver Calibration\n")); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0xd0, + 0x1f)); + + DEBUG_TRAINING_STATIC_IP(DEBUG_LEVEL_TRACE, + ("Static V-REF Calibration\n")); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0xa8, + 0x434)); + + return MV_OK; +} +#endif + +/* + * Configure phy (called by static init controller) for static flow + */ +int ddr3_tip_configure_phy(u32 dev_num) +{ + u32 if_id, phy_id; + struct hws_topology_map *tm = ddr3_get_topology_map(); + + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + PAD_ZRI_CALIB_PHY_REG, + ((0x7f & g_zpri_data) << 7 | (0x7f & g_znri_data)))); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL, + PAD_ZRI_CALIB_PHY_REG, + ((0x7f & g_zpri_ctrl) << 7 | (0x7f & g_znri_ctrl)))); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + PAD_ODT_CALIB_PHY_REG, + ((0x3f & g_zpodt_data) << 6 | (0x3f & g_znodt_data)))); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL, + PAD_ODT_CALIB_PHY_REG, + ((0x3f & g_zpodt_ctrl) << 6 | (0x3f & g_znodt_ctrl)))); + + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + PAD_PRE_DISABLE_PHY_REG, 0)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, + CMOS_CONFIG_PHY_REG, 0)); + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_CONTROL, + CMOS_CONFIG_PHY_REG, 0)); + + for (if_id = 0; if_id <= MAX_INTERFACE_NUM - 1; if_id++) { + /* check if the interface is enabled */ + VALIDATE_ACTIVE(tm->if_act_mask, if_id); + + for (phy_id = 0; + phy_id < tm->num_of_bus_per_interface; + phy_id++) { + VALIDATE_ACTIVE(tm->bus_act_mask, phy_id); + /* Vref & clamp */ + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, phy_id, DDR_PHY_DATA, + PAD_CONFIG_PHY_REG, + ((clamp_tbl[if_id] << 4) | vref), + ((0x7 << 4) | 0x7))); + /* clamp not relevant for control */ + CHECK_STATUS(ddr3_tip_bus_read_modify_write + (dev_num, ACCESS_TYPE_UNICAST, + if_id, phy_id, DDR_PHY_CONTROL, + PAD_CONFIG_PHY_REG, 0x4, 0x7)); + } + } + + CHECK_STATUS(ddr3_tip_bus_write + (dev_num, ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, + ACCESS_TYPE_MULTICAST, PARAM_NOT_CARE, DDR_PHY_DATA, 0x90, + 0x6002)); + + return MV_OK; +} diff --git a/drivers/ddr/marvell/a38x/ddr_topology_def.h b/drivers/ddr/marvell/a38x/ddr_topology_def.h new file mode 100644 index 0000000..f8894e8 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr_topology_def.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR_TOPOLOGY_DEF_H +#define _DDR_TOPOLOGY_DEF_H + +#include "ddr3_training_ip_def.h" +#include "ddr3_topology_def.h" + +#if defined(CONFIG_ARMADA_38X) +#include "ddr3_a38x.h" +#endif + +/* bus width in bits */ +enum hws_bus_width { + BUS_WIDTH_4, + BUS_WIDTH_8, + BUS_WIDTH_16, + BUS_WIDTH_32 +}; + +enum hws_temperature { + HWS_TEMP_LOW, + HWS_TEMP_NORMAL, + HWS_TEMP_HIGH +}; + +enum hws_mem_size { + MEM_512M, + MEM_1G, + MEM_2G, + MEM_4G, + MEM_8G, + MEM_SIZE_LAST +}; + +struct bus_params { + /* Chip Select (CS) bitmask (bits 0-CS0, bit 1- CS1 ...) */ + u8 cs_bitmask; + + /* + * mirror enable/disable + * (bits 0-CS0 mirroring, bit 1- CS1 mirroring ...) + */ + int mirror_enable_bitmask; + + /* DQS Swap (polarity) - true if enable */ + int is_dqs_swap; + + /* CK swap (polarity) - true if enable */ + int is_ck_swap; +}; + +struct if_params { + /* bus configuration */ + struct bus_params as_bus_params[MAX_BUS_NUM]; + + /* Speed Bin Table */ + enum hws_speed_bin speed_bin_index; + + /* bus width of memory */ + enum hws_bus_width bus_width; + + /* Bus memory size (MBit) */ + enum hws_mem_size memory_size; + + /* The DDR frequency for each interfaces */ + enum hws_ddr_freq memory_freq; + + /* + * delay CAS Write Latency + * - 0 for using default value (jedec suggested) + */ + u8 cas_wl; + + /* + * delay CAS Latency + * - 0 for using default value (jedec suggested) + */ + u8 cas_l; + + /* operation temperature */ + enum hws_temperature interface_temp; +}; + +struct hws_topology_map { + /* Number of interfaces (default is 12) */ + u8 if_act_mask; + + /* Controller configuration per interface */ + struct if_params interface_params[MAX_INTERFACE_NUM]; + + /* BUS per interface (default is 4) */ + u8 num_of_bus_per_interface; + + /* Bit mask for active buses */ + u8 bus_act_mask; +}; + +/* DDR3 training global configuration parameters */ +struct tune_train_params { + u32 ck_delay; + u32 ck_delay_16; + u32 p_finger; + u32 n_finger; + u32 phy_reg3_val; +}; + +#endif /* _DDR_TOPOLOGY_DEF_H */ diff --git a/drivers/ddr/marvell/a38x/ddr_training_ip_db.h b/drivers/ddr/marvell/a38x/ddr_training_ip_db.h new file mode 100644 index 0000000..ff5f817 --- /dev/null +++ b/drivers/ddr/marvell/a38x/ddr_training_ip_db.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _DDR_TRAINING_IP_DB_H_ +#define _DDR_TRAINING_IP_DB_H_ + +#include "ddr_topology_def.h" +#include "ddr3_training_ip_db.h" + +u32 speed_bin_table(u8 index, enum speed_bin_table_elements element); +u32 pattern_table_get_word(u32 dev_num, enum hws_pattern type, u8 index); + +#endif /* _DDR3_TRAINING_IP_DB_H_ */ diff --git a/drivers/ddr/marvell/a38x/silicon_if.h b/drivers/ddr/marvell/a38x/silicon_if.h new file mode 100644 index 0000000..7fce27d --- /dev/null +++ b/drivers/ddr/marvell/a38x/silicon_if.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __silicon_if_H +#define __silicon_if_H + +/* max number of devices supported by driver */ +#ifdef CO_CPU_RUN +#define HWS_MAX_DEVICE_NUM (1) +#else +#define HWS_MAX_DEVICE_NUM (16) +#endif + +#endif /* __silicon_if_H */ diff --git a/drivers/ddr/marvell/a38x/xor.c b/drivers/ddr/marvell/a38x/xor.c new file mode 100644 index 0000000..9c73c54 --- /dev/null +++ b/drivers/ddr/marvell/a38x/xor.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" +#include "xor_regs.h" + +/* defines */ +#ifdef MV_DEBUG +#define DB(x) x +#else +#define DB(x) +#endif + +static u32 ui_xor_regs_ctrl_backup; +static u32 ui_xor_regs_base_backup[MAX_CS]; +static u32 ui_xor_regs_mask_backup[MAX_CS]; + +void mv_sys_xor_init(u32 num_of_cs, u32 cs_ena, u32 cs_size, u32 base_delta) +{ + u32 reg, ui, base, cs_count; + + ui_xor_regs_ctrl_backup = reg_read(XOR_WINDOW_CTRL_REG(0, 0)); + for (ui = 0; ui < MAX_CS; ui++) + ui_xor_regs_base_backup[ui] = + reg_read(XOR_BASE_ADDR_REG(0, ui)); + for (ui = 0; ui < MAX_CS; ui++) + ui_xor_regs_mask_backup[ui] = + reg_read(XOR_SIZE_MASK_REG(0, ui)); + + reg = 0; + for (ui = 0; ui < (num_of_cs); ui++) { + /* Enable Window x for each CS */ + reg |= (0x1 << (ui)); + /* Enable Window x for each CS */ + reg |= (0x3 << ((ui * 2) + 16)); + } + + reg_write(XOR_WINDOW_CTRL_REG(0, 0), reg); + + cs_count = 0; + for (ui = 0; ui < num_of_cs; ui++) { + if (cs_ena & (1 << ui)) { + /* + * window x - Base - 0x00000000, + * Attribute 0x0e - DRAM + */ + base = cs_size * ui + base_delta; + switch (ui) { + case 0: + base |= 0xe00; + break; + case 1: + base |= 0xd00; + break; + case 2: + base |= 0xb00; + break; + case 3: + base |= 0x700; + break; + } + + reg_write(XOR_BASE_ADDR_REG(0, cs_count), base); + + /* window x - Size */ + reg_write(XOR_SIZE_MASK_REG(0, cs_count), 0x7fff0000); + cs_count++; + } + } + + mv_xor_hal_init(1); + + return; +} + +void mv_sys_xor_finish(void) +{ + u32 ui; + + reg_write(XOR_WINDOW_CTRL_REG(0, 0), ui_xor_regs_ctrl_backup); + for (ui = 0; ui < MAX_CS; ui++) + reg_write(XOR_BASE_ADDR_REG(0, ui), + ui_xor_regs_base_backup[ui]); + for (ui = 0; ui < MAX_CS; ui++) + reg_write(XOR_SIZE_MASK_REG(0, ui), + ui_xor_regs_mask_backup[ui]); + + reg_write(XOR_ADDR_OVRD_REG(0, 0), 0); +} + +/* + * mv_xor_hal_init - Initialize XOR engine + * + * DESCRIPTION: + * This function initialize XOR unit. + * INPUT: + * None. + * + * OUTPUT: + * None. + * + * RETURN: + * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. + */ +void mv_xor_hal_init(u32 xor_chan_num) +{ + u32 i; + + /* Abort any XOR activity & set default configuration */ + for (i = 0; i < xor_chan_num; i++) { + mv_xor_command_set(i, MV_STOP); + mv_xor_ctrl_set(i, (1 << XEXCR_REG_ACC_PROTECT_OFFS) | + (4 << XEXCR_DST_BURST_LIMIT_OFFS) | + (4 << XEXCR_SRC_BURST_LIMIT_OFFS)); + } +} + +/* + * mv_xor_ctrl_set - Set XOR channel control registers + * + * DESCRIPTION: + * + * INPUT: + * + * OUTPUT: + * None. + * + * RETURN: + * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. + * NOTE: + * This function does not modify the Operation_mode field of control register. + */ +int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl) +{ + u32 old_value; + + /* update the XOR Engine [0..1] Configuration Registers (XEx_c_r) */ + old_value = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))) & + XEXCR_OPERATION_MODE_MASK; + xor_ctrl &= ~XEXCR_OPERATION_MODE_MASK; + xor_ctrl |= old_value; + reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), xor_ctrl); + + return MV_OK; +} + +int mv_xor_mem_init(u32 chan, u32 start_ptr, u32 block_size, + u32 init_val_high, u32 init_val_low) +{ + u32 temp; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) + return MV_BAD_PARAM; + + if (MV_ACTIVE == mv_xor_state_get(chan)) + return MV_BUSY; + + if ((block_size < XEXBSR_BLOCK_SIZE_MIN_VALUE) || + (block_size > XEXBSR_BLOCK_SIZE_MAX_VALUE)) + return MV_BAD_PARAM; + + /* set the operation mode to Memory Init */ + temp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))); + temp &= ~XEXCR_OPERATION_MODE_MASK; + temp |= XEXCR_OPERATION_MODE_MEM_INIT; + reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), temp); + + /* + * update the start_ptr field in XOR Engine [0..1] Destination Pointer + * Register + */ + reg_write(XOR_DST_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)), start_ptr); + + /* + * update the Block_size field in the XOR Engine[0..1] Block Size + * Registers + */ + reg_write(XOR_BLOCK_SIZE_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + block_size); + + /* + * update the field Init_val_l in the XOR Engine Initial Value Register + * Low (XEIVRL) + */ + reg_write(XOR_INIT_VAL_LOW_REG(XOR_UNIT(chan)), init_val_low); + + /* + * update the field Init_val_h in the XOR Engine Initial Value Register + * High (XEIVRH) + */ + reg_write(XOR_INIT_VAL_HIGH_REG(XOR_UNIT(chan)), init_val_high); + + /* start transfer */ + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTART_MASK); + + return MV_OK; +} + +/* + * mv_xor_state_get - Get XOR channel state. + * + * DESCRIPTION: + * XOR channel activity state can be active, idle, paused. + * This function retrunes the channel activity state. + * + * INPUT: + * chan - the channel number + * + * OUTPUT: + * None. + * + * RETURN: + * XOR_CHANNEL_IDLE - If the engine is idle. + * XOR_CHANNEL_ACTIVE - If the engine is busy. + * XOR_CHANNEL_PAUSED - If the engine is paused. + * MV_UNDEFINED_STATE - If the engine state is undefind or there is no + * such engine + */ +enum mv_state mv_xor_state_get(u32 chan) +{ + u32 state; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) { + DB(printf("%s: ERR. Invalid chan num %d\n", __func__, chan)); + return MV_UNDEFINED_STATE; + } + + /* read the current state */ + state = reg_read(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan))); + state &= XEXACTR_XESTATUS_MASK; + + /* return the state */ + switch (state) { + case XEXACTR_XESTATUS_IDLE: + return MV_IDLE; + case XEXACTR_XESTATUS_ACTIVE: + return MV_ACTIVE; + case XEXACTR_XESTATUS_PAUSED: + return MV_PAUSED; + } + + return MV_UNDEFINED_STATE; +} + +/* + * mv_xor_command_set - Set command of XOR channel + * + * DESCRIPTION: + * XOR channel can be started, idle, paused and restarted. + * Paused can be set only if channel is active. + * Start can be set only if channel is idle or paused. + * Restart can be set only if channel is paused. + * Stop can be set only if channel is active. + * + * INPUT: + * chan - The channel number + * command - The command type (start, stop, restart, pause) + * + * OUTPUT: + * None. + * + * RETURN: + * MV_OK on success , MV_BAD_PARAM on erroneous parameter, MV_ERROR on + * undefind XOR engine mode + */ +int mv_xor_command_set(u32 chan, enum mv_command command) +{ + enum mv_state state; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) { + DB(printf("%s: ERR. Invalid chan num %d\n", __func__, chan)); + return MV_BAD_PARAM; + } + + /* get the current state */ + state = mv_xor_state_get(chan); + + if ((command == MV_START) && (state == MV_IDLE)) { + /* command is start and current state is idle */ + reg_bit_set(XOR_ACTIVATION_REG + (XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTART_MASK); + return MV_OK; + } else if ((command == MV_STOP) && (state == MV_ACTIVE)) { + /* command is stop and current state is active */ + reg_bit_set(XOR_ACTIVATION_REG + (XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTOP_MASK); + return MV_OK; + } else if (((enum mv_state)command == MV_PAUSED) && + (state == MV_ACTIVE)) { + /* command is paused and current state is active */ + reg_bit_set(XOR_ACTIVATION_REG + (XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XEPAUSE_MASK); + return MV_OK; + } else if ((command == MV_RESTART) && (state == MV_PAUSED)) { + /* command is restart and current state is paused */ + reg_bit_set(XOR_ACTIVATION_REG + (XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XERESTART_MASK); + return MV_OK; + } else if ((command == MV_STOP) && (state == MV_IDLE)) { + /* command is stop and current state is active */ + return MV_OK; + } + + /* illegal command */ + DB(printf("%s: ERR. Illegal command\n", __func__)); + + return MV_BAD_PARAM; +} + +void ddr3_new_tip_ecc_scrub(void) +{ + u32 cs_c, max_cs; + u32 cs_ena = 0; + + printf("DDR3 Training Sequence - Start scrubbing\n"); + + max_cs = hws_ddr3_tip_max_cs_get(); + for (cs_c = 0; cs_c < max_cs; cs_c++) + cs_ena |= 1 << cs_c; + + mv_sys_xor_init(max_cs, cs_ena, 0x80000000, 0); + + mv_xor_mem_init(0, 0x00000000, 0x80000000, 0xdeadbeef, 0xdeadbeef); + /* wait for previous transfer completion */ + while (mv_xor_state_get(0) != MV_IDLE) + ; + + mv_xor_mem_init(0, 0x80000000, 0x40000000, 0xdeadbeef, 0xdeadbeef); + + /* wait for previous transfer completion */ + while (mv_xor_state_get(0) != MV_IDLE) + ; + + /* Return XOR State */ + mv_sys_xor_finish(); + + printf("DDR3 Training Sequence - End scrubbing\n"); +} diff --git a/drivers/ddr/marvell/a38x/xor.h b/drivers/ddr/marvell/a38x/xor.h new file mode 100644 index 0000000..7b1e316 --- /dev/null +++ b/drivers/ddr/marvell/a38x/xor.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _XOR_H +#define _XOR_H + +#define SRAM_BASE 0x40000000 + +#include "ddr3_hws_hw_training_def.h" + +#define MV_XOR_MAX_UNIT 2 /* XOR unit == XOR engine */ +#define MV_XOR_MAX_CHAN 4 /* total channels for all units */ +#define MV_XOR_MAX_CHAN_PER_UNIT 2 /* channels for units */ + +#define MV_IS_POWER_OF_2(num) (((num) != 0) && (((num) & ((num) - 1)) == 0)) + +/* + * This structure describes address space window. Window base can be + * 64 bit, window size up to 4GB + */ +struct addr_win { + u32 base_low; /* 32bit base low */ + u32 base_high; /* 32bit base high */ + u32 size; /* 32bit size */ +}; + +/* This structure describes SoC units address decode window */ +struct unit_win_info { + struct addr_win addr_win; /* An address window */ + int enable; /* Address decode window is enabled/disabled */ + u8 attrib; /* chip select attributes */ + u8 target_id; /* Target Id of this MV_TARGET */ +}; + +/* + * This enumerator describes the type of functionality the XOR channel + * can have while using the same data structures. + */ +enum xor_type { + MV_XOR, /* XOR channel functions as XOR accelerator */ + MV_DMA, /* XOR channel functions as IDMA channel */ + MV_CRC32 /* XOR channel functions as CRC 32 calculator */ +}; + +enum mv_state { + MV_IDLE, + MV_ACTIVE, + MV_PAUSED, + MV_UNDEFINED_STATE +}; + +/* + * This enumerator describes the set of commands that can be applied on + * an engine (e.g. IDMA, XOR). Appling a comman depends on the current + * status (see MV_STATE enumerator) + * + * Start can be applied only when status is IDLE + * Stop can be applied only when status is IDLE, ACTIVE or PAUSED + * Pause can be applied only when status is ACTIVE + * Restart can be applied only when status is PAUSED + */ +enum mv_command { + MV_START, /* Start */ + MV_STOP, /* Stop */ + MV_PAUSE, /* Pause */ + MV_RESTART /* Restart */ +}; + +enum xor_override_target { + SRC_ADDR0, /* Source Address #0 Control */ + SRC_ADDR1, /* Source Address #1 Control */ + SRC_ADDR2, /* Source Address #2 Control */ + SRC_ADDR3, /* Source Address #3 Control */ + SRC_ADDR4, /* Source Address #4 Control */ + SRC_ADDR5, /* Source Address #5 Control */ + SRC_ADDR6, /* Source Address #6 Control */ + SRC_ADDR7, /* Source Address #7 Control */ + XOR_DST_ADDR, /* Destination Address Control */ + XOR_NEXT_DESC /* Next Descriptor Address Control */ +}; + +enum mv_state mv_xor_state_get(u32 chan); +void mv_xor_hal_init(u32 xor_chan_num); +int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl); +int mv_xor_command_set(u32 chan, enum mv_command command); +int mv_xor_override_set(u32 chan, enum xor_override_target target, u32 win_num, + int enable); + +#endif diff --git a/drivers/ddr/marvell/a38x/xor_regs.h b/drivers/ddr/marvell/a38x/xor_regs.h new file mode 100644 index 0000000..cc1546e --- /dev/null +++ b/drivers/ddr/marvell/a38x/xor_regs.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _XOR_REGS_h +#define _XOR_REGS_h + +/* + * For controllers that have two XOR units, then chans 2 & 3 will be + * mapped to channels 0 & 1 of unit 1 + */ +#define XOR_UNIT(chan) ((chan) >> 1) +#define XOR_CHAN(chan) ((chan) & 1) + +#define MV_XOR_REGS_OFFSET(unit) (0x60900) +#define MV_XOR_REGS_BASE(unit) (MV_XOR_REGS_OFFSET(unit)) + +/* XOR Engine Control Register Map */ +#define XOR_CHANNEL_ARBITER_REG(unit) (MV_XOR_REGS_BASE(unit)) +#define XOR_CONFIG_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x10 + ((chan) * 4))) +#define XOR_ACTIVATION_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x20 + ((chan) * 4))) + +/* XOR Engine Interrupt Register Map */ +#define XOR_CAUSE_REG(unit) (MV_XOR_REGS_BASE(unit)+(0x30)) +#define XOR_MASK_REG(unit) (MV_XOR_REGS_BASE(unit)+(0x40)) +#define XOR_ERROR_CAUSE_REG(unit) (MV_XOR_REGS_BASE(unit)+(0x50)) +#define XOR_ERROR_ADDR_REG(unit) (MV_XOR_REGS_BASE(unit)+(0x60)) + +/* XOR Engine Descriptor Register Map */ +#define XOR_NEXT_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x200 + ((chan) * 4))) +#define XOR_CURR_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x210 + ((chan) * 4))) +#define XOR_BYTE_COUNT_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x220 + ((chan) * 4))) + +/* XOR Engine ECC/Mem_init Register Map */ +#define XOR_DST_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x2b0 + ((chan) * 4))) +#define XOR_BLOCK_SIZE_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x2c0 + ((chan) * 4))) +#define XOR_TIMER_MODE_CTRL_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2d0)) +#define XOR_TIMER_MODE_INIT_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2d4)) +#define XOR_TIMER_MODE_CURR_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2d8)) +#define XOR_INIT_VAL_LOW_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2e0)) +#define XOR_INIT_VAL_HIGH_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x2e4)) + +/* XOR Engine Debug Register Map */ +#define XOR_DEBUG_REG(unit) (MV_XOR_REGS_BASE(unit) + (0x70)) + +/* XOR register fileds */ + +/* XOR Engine Channel Arbiter Register */ +#define XECAR_SLICE_OFFS(slice_num) (slice_num) +#define XECAR_SLICE_MASK(slice_num) (1 << (XECAR_SLICE_OFFS(slice_num))) + +/* XOR Engine [0..1] Configuration Registers */ +#define XEXCR_OPERATION_MODE_OFFS (0) +#define XEXCR_OPERATION_MODE_MASK (7 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_XOR (0 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_CRC (1 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_DMA (2 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_ECC (3 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_MEM_INIT (4 << XEXCR_OPERATION_MODE_OFFS) + +#define XEXCR_SRC_BURST_LIMIT_OFFS (4) +#define XEXCR_SRC_BURST_LIMIT_MASK (7 << XEXCR_SRC_BURST_LIMIT_OFFS) +#define XEXCR_DST_BURST_LIMIT_OFFS (8) +#define XEXCR_DST_BURST_LIMIT_MASK (7 << XEXCR_DST_BURST_LIMIT_OFFS) +#define XEXCR_DRD_RES_SWP_OFFS (12) +#define XEXCR_DRD_RES_SWP_MASK (1 << XEXCR_DRD_RES_SWP_OFFS) +#define XEXCR_DWR_REQ_SWP_OFFS (13) +#define XEXCR_DWR_REQ_SWP_MASK (1 << XEXCR_DWR_REQ_SWP_OFFS) +#define XEXCR_DES_SWP_OFFS (14) +#define XEXCR_DES_SWP_MASK (1 << XEXCR_DES_SWP_OFFS) +#define XEXCR_REG_ACC_PROTECT_OFFS (15) +#define XEXCR_REG_ACC_PROTECT_MASK (1 << XEXCR_REG_ACC_PROTECT_OFFS) + +/* XOR Engine [0..1] Activation Registers */ +#define XEXACTR_XESTART_OFFS (0) +#define XEXACTR_XESTART_MASK (1 << XEXACTR_XESTART_OFFS) +#define XEXACTR_XESTOP_OFFS (1) +#define XEXACTR_XESTOP_MASK (1 << XEXACTR_XESTOP_OFFS) +#define XEXACTR_XEPAUSE_OFFS (2) +#define XEXACTR_XEPAUSE_MASK (1 << XEXACTR_XEPAUSE_OFFS) +#define XEXACTR_XERESTART_OFFS (3) +#define XEXACTR_XERESTART_MASK (1 << XEXACTR_XERESTART_OFFS) +#define XEXACTR_XESTATUS_OFFS (4) +#define XEXACTR_XESTATUS_MASK (3 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_IDLE (0 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_ACTIVE (1 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_PAUSED (2 << XEXACTR_XESTATUS_OFFS) + +/* XOR Engine Interrupt Cause Register (XEICR) */ +#define XEICR_CHAN_OFFS 16 +#define XEICR_CAUSE_OFFS(chan) (chan * XEICR_CHAN_OFFS) +#define XEICR_CAUSE_MASK(chan, cause) (1 << (cause + XEICR_CAUSE_OFFS(chan))) +#define XEICR_COMP_MASK_ALL 0x000f000f +#define XEICR_COMP_MASK(chan) (0x000f << XEICR_CAUSE_OFFS(chan)) +#define XEICR_ERR_MASK 0x03800380 + +/* XOR Engine Error Cause Register (XEECR) */ +#define XEECR_ERR_TYPE_OFFS 0 +#define XEECR_ERR_TYPE_MASK (0x1f << XEECR_ERR_TYPE_OFFS) + +/* XOR Engine Error Address Register (XEEAR) */ +#define XEEAR_ERR_ADDR_OFFS (0) +#define XEEAR_ERR_ADDR_MASK (0xffffffff << XEEAR_ERR_ADDR_OFFS) + +/* XOR Engine [0..1] Next Descriptor Pointer Register */ +#define XEXNDPR_NEXT_DESC_PTR_OFFS (0) +#define XEXNDPR_NEXT_DESC_PTR_MASK (0xffffffff << \ + XEXNDPR_NEXT_DESC_PTR_OFFS) + +/* XOR Engine [0..1] Current Descriptor Pointer Register */ +#define XEXCDPR_CURRENT_DESC_PTR_OFFS (0) +#define XEXCDPR_CURRENT_DESC_PTR_MASK (0xffffffff << \ + XEXCDPR_CURRENT_DESC_PTR_OFFS) + +/* XOR Engine [0..1] Byte Count Register */ +#define XEXBCR_BYTE_CNT_OFFS (0) +#define XEXBCR_BYTE_CNT_MASK (0xffffffff << XEXBCR_BYTE_CNT_OFFS) + +/* XOR Engine [0..1] Destination Pointer Register */ +#define XEXDPR_DST_PTR_OFFS (0) +#define XEXDPR_DST_PTR_MASK (0xffffffff << XEXDPR_DST_PTR_OFFS) +#define XEXDPR_DST_PTR_XOR_MASK (0x3f) +#define XEXDPR_DST_PTR_DMA_MASK (0x1f) +#define XEXDPR_DST_PTR_CRC_MASK (0x1f) + +/* XOR Engine[0..1] Block Size Registers */ +#define XEXBSR_BLOCK_SIZE_OFFS (0) +#define XEXBSR_BLOCK_SIZE_MASK (0xffffffff << XEXBSR_BLOCK_SIZE_OFFS) +#define XEXBSR_BLOCK_SIZE_MIN_VALUE (128) +#define XEXBSR_BLOCK_SIZE_MAX_VALUE (0xffffffff) + +/* XOR Engine Timer Mode Control Register (XETMCR) */ +#define XETMCR_TIMER_EN_OFFS (0) +#define XETMCR_TIMER_EN_MASK (1 << XETMCR_TIMER_EN_OFFS) +#define XETMCR_TIMER_EN_ENABLE (1 << XETMCR_TIMER_EN_OFFS) +#define XETMCR_TIMER_EN_DISABLE (0 << XETMCR_TIMER_EN_OFFS) +#define XETMCR_SECTION_SIZE_CTRL_OFFS (8) +#define XETMCR_SECTION_SIZE_CTRL_MASK (0x1f << XETMCR_SECTION_SIZE_CTRL_OFFS) +#define XETMCR_SECTION_SIZE_MIN_VALUE (7) +#define XETMCR_SECTION_SIZE_MAX_VALUE (31) + +/* XOR Engine Timer Mode Initial Value Register (XETMIVR) */ +#define XETMIVR_TIMER_INIT_VAL_OFFS (0) +#define XETMIVR_TIMER_INIT_VAL_MASK (0xffffffff << \ + XETMIVR_TIMER_INIT_VAL_OFFS) + +/* XOR Engine Timer Mode Current Value Register (XETMCVR) */ +#define XETMCVR_TIMER_CRNT_VAL_OFFS (0) +#define XETMCVR_TIMER_CRNT_VAL_MASK (0xffffffff << \ + XETMCVR_TIMER_CRNT_VAL_OFFS) + +/* XOR Engine Initial Value Register Low (XEIVRL) */ +#define XEIVRL_INIT_VAL_L_OFFS (0) +#define XEIVRL_INIT_VAL_L_MASK (0xffffffff << XEIVRL_INIT_VAL_L_OFFS) + +/* XOR Engine Initial Value Register High (XEIVRH) */ +#define XEIVRH_INIT_VAL_H_OFFS (0) +#define XEIVRH_INIT_VAL_H_MASK (0xffffffff << XEIVRH_INIT_VAL_H_OFFS) + +/* XOR Engine Debug Register (XEDBR) */ +#define XEDBR_PARITY_ERR_INSR_OFFS (0) +#define XEDBR_PARITY_ERR_INSR_MASK (1 << XEDBR_PARITY_ERR_INSR_OFFS) +#define XEDBR_XBAR_ERR_INSR_OFFS (1) +#define XEDBR_XBAR_ERR_INSR_MASK (1 << XEDBR_XBAR_ERR_INSR_OFFS) + +/* XOR Engine address decode registers. */ +/* Maximum address decode windows */ +#define XOR_MAX_ADDR_DEC_WIN 8 +/* Maximum address arbiter windows */ +#define XOR_MAX_REMAP_WIN 4 + +/* XOR Engine Address Decoding Register Map */ +#define XOR_WINDOW_CTRL_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + \ + (0x240 + ((chan) * 4))) +#define XOR_BASE_ADDR_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \ + (0x250 + ((win_num) * 4))) +#define XOR_SIZE_MASK_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \ + (0x270 + ((win_num) * 4))) +#define XOR_HIGH_ADDR_REMAP_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \ + (0x290 + ((win_num) * 4))) +#define XOR_ADDR_OVRD_REG(unit, win_num) (MV_XOR_REGS_BASE(unit) + \ + (0x2a0 + ((win_num) * 4))) + +/* XOR Engine [0..1] Window Control Registers */ +#define XEXWCR_WIN_EN_OFFS(win_num) (win_num) +#define XEXWCR_WIN_EN_MASK(win_num) (1 << (XEXWCR_WIN_EN_OFFS(win_num))) +#define XEXWCR_WIN_EN_ENABLE(win_num) (1 << (XEXWCR_WIN_EN_OFFS(win_num))) +#define XEXWCR_WIN_EN_DISABLE(win_num) (0 << (XEXWCR_WIN_EN_OFFS(win_num))) + +#define XEXWCR_WIN_ACC_OFFS(win_num) ((2 * win_num) + 16) +#define XEXWCR_WIN_ACC_MASK(win_num) (3 << (XEXWCR_WIN_ACC_OFFS(win_num))) +#define XEXWCR_WIN_ACC_NO_ACC(win_num) (0 << (XEXWCR_WIN_ACC_OFFS(win_num))) +#define XEXWCR_WIN_ACC_RO(win_num) (1 << (XEXWCR_WIN_ACC_OFFS(win_num))) +#define XEXWCR_WIN_ACC_RW(win_num) (3 << (XEXWCR_WIN_ACC_OFFS(win_num))) + +/* XOR Engine Base Address Registers (XEBARx) */ +#define XEBARX_TARGET_OFFS (0) +#define XEBARX_TARGET_MASK (0xf << XEBARX_TARGET_OFFS) +#define XEBARX_ATTR_OFFS (8) +#define XEBARX_ATTR_MASK (0xff << XEBARX_ATTR_OFFS) +#define XEBARX_BASE_OFFS (16) +#define XEBARX_BASE_MASK (0xffff << XEBARX_BASE_OFFS) + +/* XOR Engine Size Mask Registers (XESMRx) */ +#define XESMRX_SIZE_MASK_OFFS (16) +#define XESMRX_SIZE_MASK_MASK (0xffff << XESMRX_SIZE_MASK_OFFS) +#define XOR_WIN_SIZE_ALIGN _64K + +/* XOR Engine High Address Remap Register (XEHARRx1) */ +#define XEHARRX_REMAP_OFFS (0) +#define XEHARRX_REMAP_MASK (0xffffffff << XEHARRX_REMAP_OFFS) + +#define XOR_OVERRIDE_CTRL_REG(chan) (MV_XOR_REGS_BASE(XOR_UNIT(chan)) + \ + (0x2a0 + ((XOR_CHAN(chan)) * 4))) + +/* XOR Engine [0..1] Address Override Control Register */ +#define XEXAOCR_OVR_EN_OFFS(target) (3 * target) +#define XEXAOCR_OVR_EN_MASK(target) (1 << (XEXAOCR_OVR_EN_OFFS(target))) +#define XEXAOCR_OVR_PTR_OFFS(target) ((3 * target) + 1) +#define XEXAOCR_OVR_PTR_MASK(target) (3 << (XEXAOCR_OVR_PTR_OFFS(target))) +#define XEXAOCR_OVR_BAR(win_num, target) (win_num << \ + (XEXAOCR_OVR_PTR_OFFS(target))) + +/* Maximum address override windows */ +#define XOR_MAX_OVERRIDE_WIN 4 + +#endif /* _XOR_REGS_h */ diff --git a/drivers/ddr/mvebu/Makefile b/drivers/ddr/marvell/axp/Makefile index 50a69ea..50a69ea 100644 --- a/drivers/ddr/mvebu/Makefile +++ b/drivers/ddr/marvell/axp/Makefile diff --git a/drivers/ddr/mvebu/ddr3_axp.h b/drivers/ddr/marvell/axp/ddr3_axp.h index d9e33f7..d9e33f7 100644 --- a/drivers/ddr/mvebu/ddr3_axp.h +++ b/drivers/ddr/marvell/axp/ddr3_axp.h diff --git a/drivers/ddr/mvebu/ddr3_axp_config.h b/drivers/ddr/marvell/axp/ddr3_axp_config.h index 800d2d1..800d2d1 100644 --- a/drivers/ddr/mvebu/ddr3_axp_config.h +++ b/drivers/ddr/marvell/axp/ddr3_axp_config.h diff --git a/drivers/ddr/mvebu/ddr3_axp_mc_static.h b/drivers/ddr/marvell/axp/ddr3_axp_mc_static.h index 2c0e9075..2c0e9075 100644 --- a/drivers/ddr/mvebu/ddr3_axp_mc_static.h +++ b/drivers/ddr/marvell/axp/ddr3_axp_mc_static.h diff --git a/drivers/ddr/mvebu/ddr3_axp_training_static.h b/drivers/ddr/marvell/axp/ddr3_axp_training_static.h index 4e61547..4e61547 100644 --- a/drivers/ddr/mvebu/ddr3_axp_training_static.h +++ b/drivers/ddr/marvell/axp/ddr3_axp_training_static.h diff --git a/drivers/ddr/mvebu/ddr3_axp_vars.h b/drivers/ddr/marvell/axp/ddr3_axp_vars.h index 1b0ab56..1b0ab56 100644 --- a/drivers/ddr/mvebu/ddr3_axp_vars.h +++ b/drivers/ddr/marvell/axp/ddr3_axp_vars.h diff --git a/drivers/ddr/mvebu/ddr3_dfs.c b/drivers/ddr/marvell/axp/ddr3_dfs.c index 9347773..9347773 100644 --- a/drivers/ddr/mvebu/ddr3_dfs.c +++ b/drivers/ddr/marvell/axp/ddr3_dfs.c diff --git a/drivers/ddr/mvebu/ddr3_dqs.c b/drivers/ddr/marvell/axp/ddr3_dqs.c index 71a986d..71a986d 100644 --- a/drivers/ddr/mvebu/ddr3_dqs.c +++ b/drivers/ddr/marvell/axp/ddr3_dqs.c diff --git a/drivers/ddr/mvebu/ddr3_hw_training.c b/drivers/ddr/marvell/axp/ddr3_hw_training.c index a8c5e6a..a8c5e6a 100644 --- a/drivers/ddr/mvebu/ddr3_hw_training.c +++ b/drivers/ddr/marvell/axp/ddr3_hw_training.c diff --git a/drivers/ddr/mvebu/ddr3_hw_training.h b/drivers/ddr/marvell/axp/ddr3_hw_training.h index cffa7c4..cffa7c4 100644 --- a/drivers/ddr/mvebu/ddr3_hw_training.h +++ b/drivers/ddr/marvell/axp/ddr3_hw_training.h diff --git a/drivers/ddr/mvebu/ddr3_init.c b/drivers/ddr/marvell/axp/ddr3_init.c index 11b8591..11b8591 100644 --- a/drivers/ddr/mvebu/ddr3_init.c +++ b/drivers/ddr/marvell/axp/ddr3_init.c diff --git a/drivers/ddr/mvebu/ddr3_init.h b/drivers/ddr/marvell/axp/ddr3_init.h index b259e09..b259e09 100644 --- a/drivers/ddr/mvebu/ddr3_init.h +++ b/drivers/ddr/marvell/axp/ddr3_init.h diff --git a/drivers/ddr/mvebu/ddr3_patterns_64bit.h b/drivers/ddr/marvell/axp/ddr3_patterns_64bit.h index 1b57328..1b57328 100644 --- a/drivers/ddr/mvebu/ddr3_patterns_64bit.h +++ b/drivers/ddr/marvell/axp/ddr3_patterns_64bit.h diff --git a/drivers/ddr/mvebu/ddr3_pbs.c b/drivers/ddr/marvell/axp/ddr3_pbs.c index 00ea3fd..00ea3fd 100644 --- a/drivers/ddr/mvebu/ddr3_pbs.c +++ b/drivers/ddr/marvell/axp/ddr3_pbs.c diff --git a/drivers/ddr/mvebu/ddr3_read_leveling.c b/drivers/ddr/marvell/axp/ddr3_read_leveling.c index 4662bde..4662bde 100644 --- a/drivers/ddr/mvebu/ddr3_read_leveling.c +++ b/drivers/ddr/marvell/axp/ddr3_read_leveling.c diff --git a/drivers/ddr/mvebu/ddr3_sdram.c b/drivers/ddr/marvell/axp/ddr3_sdram.c index 50c1bf8..50c1bf8 100644 --- a/drivers/ddr/mvebu/ddr3_sdram.c +++ b/drivers/ddr/marvell/axp/ddr3_sdram.c diff --git a/drivers/ddr/mvebu/ddr3_spd.c b/drivers/ddr/marvell/axp/ddr3_spd.c index f4f94c5..f4f94c5 100644 --- a/drivers/ddr/mvebu/ddr3_spd.c +++ b/drivers/ddr/marvell/axp/ddr3_spd.c diff --git a/drivers/ddr/mvebu/ddr3_write_leveling.c b/drivers/ddr/marvell/axp/ddr3_write_leveling.c index df3a3df..df3a3df 100644 --- a/drivers/ddr/mvebu/ddr3_write_leveling.c +++ b/drivers/ddr/marvell/axp/ddr3_write_leveling.c diff --git a/drivers/ddr/mvebu/xor.c b/drivers/ddr/marvell/axp/xor.c index 66c96ae..66c96ae 100644 --- a/drivers/ddr/mvebu/xor.c +++ b/drivers/ddr/marvell/axp/xor.c diff --git a/drivers/ddr/mvebu/xor.h b/drivers/ddr/marvell/axp/xor.h index 3536487..3536487 100644 --- a/drivers/ddr/mvebu/xor.h +++ b/drivers/ddr/marvell/axp/xor.h diff --git a/drivers/ddr/mvebu/xor_regs.h b/drivers/ddr/marvell/axp/xor_regs.h index 884aa15..884aa15 100644 --- a/drivers/ddr/mvebu/xor_regs.h +++ b/drivers/ddr/marvell/axp/xor_regs.h diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 5864850..67c6374 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,13 +6,9 @@ # ifndef CONFIG_SPL_BUILD -obj-$(CONFIG_DM_GPIO) += gpio-uclass.o obj-$(CONFIG_AXP_GPIO) += axp_gpio.o endif -/* TODO(sjg@chromium.org): Only tegra supports driver model in SPL */ -ifdef CONFIG_TEGRA_GPIO obj-$(CONFIG_DM_GPIO) += gpio-uclass.o -endif obj-$(CONFIG_AT91_GPIO) += at91_gpio.o obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index bf982b9..4efda31 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -48,8 +48,7 @@ static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc) return ret ? ret : -ENOENT; } -int gpio_lookup_name(const char *name, struct udevice **devp, - unsigned int *offsetp, unsigned int *gpiop) +int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc) { struct gpio_dev_priv *uc_priv = NULL; struct udevice *dev; @@ -57,8 +56,6 @@ int gpio_lookup_name(const char *name, struct udevice **devp, int numeric; int ret; - if (devp) - *devp = NULL; numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1; for (ret = uclass_first_device(UCLASS_GPIO, &dev); dev; @@ -84,12 +81,33 @@ int gpio_lookup_name(const char *name, struct udevice **devp, if (!dev) return ret ? ret : -EINVAL; + desc->dev = dev; + desc->offset = offset; + + return 0; +} + +int gpio_lookup_name(const char *name, struct udevice **devp, + unsigned int *offsetp, unsigned int *gpiop) +{ + struct gpio_desc desc; + int ret; + + if (devp) + *devp = NULL; + ret = dm_gpio_lookup_name(name, &desc); + if (ret) + return ret; + if (devp) - *devp = dev; + *devp = desc.dev; if (offsetp) - *offsetp = offset; - if (gpiop) - *gpiop = uc_priv->gpio_base + offset; + *offsetp = desc.offset; + if (gpiop) { + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc.dev); + + *gpiop = uc_priv->gpio_base + desc.offset; + } return 0; } @@ -109,7 +127,7 @@ static int gpio_find_and_xlate(struct gpio_desc *desc, return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0; } -static int dm_gpio_request(struct gpio_desc *desc, const char *label) +int dm_gpio_request(struct gpio_desc *desc, const char *label) { struct udevice *dev = desc->dev; struct gpio_dev_priv *uc_priv; diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig new file mode 100644 index 0000000..de5feea --- /dev/null +++ b/drivers/led/Kconfig @@ -0,0 +1,26 @@ +config LED + bool "Enable LED support" + depends on DM + help + Many boards have LEDs which can be used to signal status or alerts. + U-Boot provides a uclass API to implement this feature. LED drivers + can provide access to board-specific LEDs. Use of the device tree + for configuration is encouraged. + +config SPL_LED_SUPPORT + bool "Enable LED support in SPL" + depends on LED + help + The LED subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use LEDs in SPL, + enable this option. You will need to enable device tree in SPL + for this to work. + +config LED_GPIO + bool "LED support for GPIO-connected LEDs" + depends on LED && DM_GPIO + help + Enable support for LEDs which are connected to GPIO lines. These + GPIOs may be on the SoC or some other device which provides GPIOs. + The GPIO driver must used driver model. LEDs are configured using + the device tree. diff --git a/drivers/led/Makefile b/drivers/led/Makefile new file mode 100644 index 0000000..990129e --- /dev/null +++ b/drivers/led/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2015 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_LED) += led-uclass.o +obj-$(CONFIG_LED_GPIO) += led_gpio.o diff --git a/drivers/led/led-uclass.c b/drivers/led/led-uclass.c new file mode 100644 index 0000000..784ac87 --- /dev/null +++ b/drivers/led/led-uclass.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +int led_get_by_label(const char *label, struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_LED, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + + /* Ignore the top-level LED node */ + if (uc_plat->label && !strcmp(label, uc_plat->label)) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +int led_set_on(struct udevice *dev, int on) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->set_on) + return -ENOSYS; + + return ops->set_on(dev, on); +} + +UCLASS_DRIVER(led) = { + .id = UCLASS_LED, + .name = "led", + .per_device_platdata_auto_alloc_size = sizeof(struct led_uclass_plat), +}; diff --git a/drivers/led/led_gpio.c b/drivers/led/led_gpio.c new file mode 100644 index 0000000..cb6e996 --- /dev/null +++ b/drivers/led/led_gpio.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/gpio.h> +#include <dm/lists.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct led_gpio_priv { + struct gpio_desc gpio; +}; + +static int gpio_led_set_on(struct udevice *dev, int on) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (!dm_gpio_is_valid(&priv->gpio)) + return -EREMOTEIO; + + return dm_gpio_set_value(&priv->gpio, on); +} + +static int led_gpio_probe(struct udevice *dev) +{ + struct led_uclass_plat *uc_plat = dev_get_uclass_platdata(dev); + struct led_gpio_priv *priv = dev_get_priv(dev); + + /* Ignore the top-level LED node */ + if (!uc_plat->label) + return 0; + return gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); +} + +static int led_gpio_remove(struct udevice *dev) +{ + /* + * The GPIO driver may have already been removed. We will need to + * address this more generally. + */ +#ifndef CONFIG_SANDBOX + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->gpio)) + dm_gpio_free(dev, &priv->gpio); +#endif + + return 0; +} + +static int led_gpio_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + struct udevice *dev; + int node; + int ret; + + for (node = fdt_first_subnode(blob, parent->of_offset); + node > 0; + node = fdt_next_subnode(blob, node)) { + struct led_uclass_plat *uc_plat; + const char *label; + + label = fdt_getprop(blob, node, "label", NULL); + if (!label) { + debug("%s: node %s has no label\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + ret = device_bind_driver_to_node(parent, "gpio_led", + fdt_get_name(blob, node, NULL), + node, &dev); + if (ret) + return ret; + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct led_ops gpio_led_ops = { + .set_on = gpio_led_set_on, +}; + +static const struct udevice_id led_gpio_ids[] = { + { .compatible = "gpio-leds" }, + { } +}; + +U_BOOT_DRIVER(led_gpio) = { + .name = "gpio_led", + .id = UCLASS_LED, + .of_match = led_gpio_ids, + .ops = &gpio_led_ops, + .priv_auto_alloc_size = sizeof(struct led_gpio_priv), + .bind = led_gpio_bind, + .probe = led_gpio_probe, + .remove = led_gpio_remove, +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 64b07a3..3b7f76a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -73,3 +73,12 @@ config PCA9551_I2C_ADDR default 0x60 help The I2C address of the PCA9551 LED controller. + +config RESET + bool "Enable support for reset drivers" + depends on DM + help + Enable reset drivers which can be used to reset the CPU or board. + Each driver can provide a reset method which will be called to + effect a reset. The uclass will try all available drivers when + reset_walk() is called. diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 120babc..5218b91 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -22,13 +22,16 @@ obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NS87308) += ns87308.o obj-$(CONFIG_PDSP188x) += pdsp188x.o +obj-$(CONFIG_SANDBOX) += reset_sandbox.o ifdef CONFIG_DM_I2C obj-$(CONFIG_SANDBOX) += i2c_eeprom_emul.o endif obj-$(CONFIG_SMSC_LPC47M) += smsc_lpc47m.o obj-$(CONFIG_STATUS_LED) += status_led.o obj-$(CONFIG_SANDBOX) += swap_case.o +obj-$(CONFIG_SANDBOX) += syscon_sandbox.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_FSL_SEC_MON) += fsl_sec_mon.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o +obj-$(CONFIG_RESET) += reset-uclass.o diff --git a/drivers/misc/reset-uclass.c b/drivers/misc/reset-uclass.c new file mode 100644 index 0000000..fdb5c6f --- /dev/null +++ b/drivers/misc/reset-uclass.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <reset.h> +#include <dm.h> +#include <errno.h> +#include <regmap.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/err.h> + +int reset_request(struct udevice *dev, enum reset_t type) +{ + struct reset_ops *ops = reset_get_ops(dev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(dev, type); +} + +int reset_walk(enum reset_t type) +{ + struct udevice *dev; + int ret = -ENOSYS; + + while (ret != -EINPROGRESS && type < RESET_COUNT) { + for (uclass_first_device(UCLASS_RESET, &dev); + dev; + uclass_next_device(&dev)) { + ret = reset_request(dev, type); + if (ret == -EINPROGRESS) + break; + } + type++; + } + + return ret; +} + +void reset_walk_halt(enum reset_t type) +{ + int ret; + + ret = reset_walk(type); + + /* Wait for the reset to take effect */ + if (ret == -EINPROGRESS) + mdelay(100); + + /* Still no reset? Give up */ + printf("Reset not supported on this platform\n"); + hang(); +} + +/** + * reset_cpu() - calls reset_walk(RESET_WARM) + */ +void reset_cpu(ulong addr) +{ + reset_walk_halt(RESET_WARM); +} + + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + reset_walk_halt(RESET_WARM); + + return 0; +} + +UCLASS_DRIVER(reset) = { + .id = UCLASS_RESET, + .name = "reset", +}; diff --git a/drivers/misc/reset_sandbox.c b/drivers/misc/reset_sandbox.c new file mode 100644 index 0000000..917121b --- /dev/null +++ b/drivers/misc/reset_sandbox.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <reset.h> +#include <asm/state.h> +#include <asm/test.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int sandbox_warm_reset_request(struct udevice *dev, enum reset_t type) +{ + struct sandbox_state *state = state_get_current(); + + switch (type) { + case RESET_WARM: + state->last_reset = type; + break; + default: + return -ENOSYS; + } + if (!state->reset_allowed[type]) + return -EACCES; + + return -EINPROGRESS; +} + +static int sandbox_reset_request(struct udevice *dev, enum reset_t type) +{ + struct sandbox_state *state = state_get_current(); + + /* + * If we have a device tree, the device we created from platform data + * (see the U_BOOT_DEVICE() declaration below) should not do anything. + * If we are that device, return an error. + */ + if (gd->fdt_blob && dev->of_offset == -1) + return -ENODEV; + + switch (type) { + case RESET_COLD: + state->last_reset = type; + break; + case RESET_POWER: + state->last_reset = type; + if (!state->reset_allowed[type]) + return -EACCES; + sandbox_exit(); + break; + default: + return -ENOSYS; + } + if (!state->reset_allowed[type]) + return -EACCES; + + return -EINPROGRESS; +} + +static struct reset_ops sandbox_reset_ops = { + .request = sandbox_reset_request, +}; + +static const struct udevice_id sandbox_reset_ids[] = { + { .compatible = "sandbox,reset" }, + { } +}; + +U_BOOT_DRIVER(reset_sandbox) = { + .name = "reset_sandbox", + .id = UCLASS_RESET, + .of_match = sandbox_reset_ids, + .ops = &sandbox_reset_ops, +}; + +static struct reset_ops sandbox_warm_reset_ops = { + .request = sandbox_warm_reset_request, +}; + +static const struct udevice_id sandbox_warm_reset_ids[] = { + { .compatible = "sandbox,warm-reset" }, + { } +}; + +U_BOOT_DRIVER(warm_reset_sandbox) = { + .name = "warm_reset_sandbox", + .id = UCLASS_RESET, + .of_match = sandbox_warm_reset_ids, + .ops = &sandbox_warm_reset_ops, +}; + +/* This is here in case we don't have a device tree */ +U_BOOT_DEVICE(reset_sandbox_non_fdt) = { + .name = "reset_sandbox", +}; diff --git a/drivers/misc/syscon_sandbox.c b/drivers/misc/syscon_sandbox.c new file mode 100644 index 0000000..ccfab3e --- /dev/null +++ b/drivers/misc/syscon_sandbox.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <syscon.h> +#include <asm/test.h> +#include <dm/lists.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct udevice_id sandbox_syscon_ids[] = { + { .compatible = "sandbox,syscon0", .data = SYSCON0 }, + { .compatible = "sandbox,syscon1", .data = SYSCON1 }, + { } +}; + +U_BOOT_DRIVER(sandbox_syscon) = { + .name = "sandbox_syscon", + .id = UCLASS_SYSCON, + .of_match = sandbox_syscon_ids, +}; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 7ba85a2..3e835f7 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -1,5 +1,15 @@ menu "MMC Host controller Support" +config DM_MMC + bool "Enable MMC controllers using Driver Model" + depends on DM + help + This enables the MultiMediaCard (MMC) uclass which suports MMC and + Secure Digital I/O (SDIO) cards. Both removable (SD, micro-SD, etc.) + and non-removable (e.g. eMMC chip) devices are supported. These + appear as block devices in U-Boot and can support filesystems such + as EXT4 and FAT. + config SH_SDHI bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" depends on RMOBILE diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index ed73687..286df2f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -5,6 +5,8 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_MMC) += mmc-uclass.o + obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o obj-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o obj-$(CONFIG_BFIN_SDH) += bfin_sdh.o @@ -29,6 +31,7 @@ obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o obj-$(CONFIG_S3C_SDI) += s3c_sdi.o obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o +obj-$(CONFIG_SANDBOX) += sandbox_mmc.o obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c new file mode 100644 index 0000000..777489f --- /dev/null +++ b/drivers/mmc/mmc-uclass.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mmc.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> + +struct mmc *mmc_get_mmc_dev(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv; + + if (!device_active(dev)) + return NULL; + upriv = dev_get_uclass_priv(dev); + return upriv->mmc; +} + +U_BOOT_DRIVER(mmc) = { + .name = "mmc", + .id = UCLASS_MMC, +}; + +UCLASS_DRIVER(mmc) = { + .id = UCLASS_MMC, + .name = "mmc", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .per_device_auto_alloc_size = sizeof(struct mmc_uclass_priv), +}; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 79e6fee..da47037 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -250,14 +250,18 @@ static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) return 0; } - if (mmc_set_blocklen(mmc, mmc->read_bl_len)) + if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { + debug("%s: Failed to set blocklen\n", __func__); return 0; + } do { cur = (blocks_todo > mmc->cfg->b_max) ? mmc->cfg->b_max : blocks_todo; - if(mmc_read_blocks(mmc, dst, start, cur) != cur) + if (mmc_read_blocks(mmc, dst, start, cur) != cur) { + debug("%s: Failed to read blocks\n", __func__); return 0; + } blocks_todo -= cur; start += cur; dst += cur * mmc->read_bl_len; @@ -1761,8 +1765,10 @@ int mmc_initialize(bd_t *bis) INIT_LIST_HEAD (&mmc_devices); cur_dev_num = 0; +#ifndef CONFIG_DM_MMC if (board_mmc_init(bis) < 0) cpu_mmc_init(bis); +#endif #ifndef CONFIG_SPL_BUILD print_mmc_devices(','); diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c new file mode 100644 index 0000000..f4646a8 --- /dev/null +++ b/drivers/mmc/sandbox_mmc.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <mmc.h> +#include <asm/test.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const struct udevice_id sandbox_mmc_ids[] = { + { .compatible = "sandbox,mmc" }, + { } +}; + +U_BOOT_DRIVER(warm_mmc_sandbox) = { + .name = "mmc_sandbox", + .id = UCLASS_MMC, + .of_match = sandbox_mmc_ids, +}; diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 645ca64..bcae842 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -528,7 +528,7 @@ static int designware_eth_send(struct udevice *dev, void *packet, int length) return _dw_eth_send(priv, packet, length); } -static int designware_eth_recv(struct udevice *dev, uchar **packetp) +static int designware_eth_recv(struct udevice *dev, int flags, uchar **packetp) { struct dw_eth_dev *priv = dev_get_priv(dev); diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index 958488c..7b6e20f 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -41,10 +41,13 @@ * Modified to use le32_to_cpu and cpu_to_le32 properly */ #include <common.h> +#include <dm.h> #include <errno.h> #include <malloc.h> #include <net.h> +#ifndef CONFIG_DM_ETH #include <netdev.h> +#endif #include <asm/io.h> #include <pci.h> @@ -281,6 +284,8 @@ struct RxDesc { u32 buf_Haddr; }; +static unsigned char rxdata[RX_BUF_LEN]; + #define RTL8169_DESC_SIZE 16 #if ARCH_DMA_MINALIGN > 256 @@ -299,7 +304,8 @@ struct RxDesc { * the driver to allocate descriptors from a pool of non-cached memory. */ #if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN -#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && !defined(CONFIG_SYS_DCACHE_OFF) +#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && \ + !defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_X86) #warning cache-line size is larger than descriptor size #endif #endif @@ -317,6 +323,7 @@ DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); struct rtl8169_private { + ulong iobase; void *mmio_addr; /* memory map physical address */ int chipset; unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ @@ -338,9 +345,9 @@ static const unsigned int rtl8169_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); static struct pci_device_id supported[] = { - {PCI_VENDOR_ID_REALTEK, 0x8167}, - {PCI_VENDOR_ID_REALTEK, 0x8168}, - {PCI_VENDOR_ID_REALTEK, 0x8169}, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167) }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168) }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169) }, {} }; @@ -380,7 +387,7 @@ int mdio_read(int RegAddr) return value; } -static int rtl8169_init_board(struct eth_device *dev) +static int rtl8169_init_board(unsigned long dev_iobase, const char *name) { int i; u32 tmp; @@ -388,7 +395,7 @@ static int rtl8169_init_board(struct eth_device *dev) #ifdef DEBUG_RTL8169 printf ("%s\n", __FUNCTION__); #endif - ioaddr = dev->iobase; + ioaddr = dev_iobase; /* Soft reset the chip. */ RTL_W8(ChipCmd, CmdReset); @@ -412,7 +419,8 @@ static int rtl8169_init_board(struct eth_device *dev) } /* if unknown chip, assume array element #0, original RTL-8169 in this case */ - printf("PCI device %s: unknown chip version, assuming RTL-8169\n", dev->name); + printf("PCI device %s: unknown chip version, assuming RTL-8169\n", + name); printf("PCI device: TxConfig = 0x%lX\n", (unsigned long) RTL_R32(TxConfig)); tpc->chipset = 0; @@ -504,7 +512,8 @@ static void rtl_flush_buffer(void *buf, size_t size) /************************************************************************** RECV - Receive a frame ***************************************************************************/ -static int rtl_recv(struct eth_device *dev) +static int rtl_recv_common(pci_dev_t bdf, unsigned long dev_iobase, + uchar **packetp) { /* return true if there's an ethernet packet ready to read */ /* nic->packet should contain data on return */ @@ -515,7 +524,7 @@ static int rtl_recv(struct eth_device *dev) #ifdef DEBUG_RTL8169_RX printf ("%s\n", __FUNCTION__); #endif - ioaddr = dev->iobase; + ioaddr = dev_iobase; cur_rx = tpc->cur_rx; @@ -523,7 +532,6 @@ static int rtl_recv(struct eth_device *dev) if ((le32_to_cpu(tpc->RxDescArray[cur_rx].status) & OWNbit) == 0) { if (!(le32_to_cpu(tpc->RxDescArray[cur_rx].status) & RxRES)) { - unsigned char rxdata[RX_BUF_LEN]; length = (int) (le32_to_cpu(tpc->RxDescArray[cur_rx]. status) & 0x00001FFF) - 4; @@ -536,17 +544,22 @@ static int rtl_recv(struct eth_device *dev) else tpc->RxDescArray[cur_rx].status = cpu_to_le32(OWNbit + RX_BUF_SIZE); - tpc->RxDescArray[cur_rx].buf_addr = - cpu_to_le32(bus_to_phys(tpc->RxBufferRing[cur_rx])); + tpc->RxDescArray[cur_rx].buf_addr = cpu_to_le32( + pci_mem_to_phys(bdf, (pci_addr_t)(unsigned long) + tpc->RxBufferRing[cur_rx])); rtl_flush_rx_desc(&tpc->RxDescArray[cur_rx]); - +#ifdef CONFIG_DM_ETH + *packetp = rxdata; +#else net_process_received_packet(rxdata, length); +#endif } else { puts("Error Rx"); + length = -EIO; } cur_rx = (cur_rx + 1) % NUM_RX_DESC; tpc->cur_rx = cur_rx; - return 1; + return length; } else { ushort sts = RTL_R8(IntrStatus); @@ -557,11 +570,26 @@ static int rtl_recv(struct eth_device *dev) return (0); /* initially as this is called to flush the input */ } +#ifdef CONFIG_DM_ETH +int rtl8169_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct rtl8169_private *priv = dev_get_priv(dev); + + return rtl_recv_common(pci_get_bdf(dev), priv->iobase, packetp); +} +#else +static int rtl_recv(struct eth_device *dev) +{ + return rtl_recv_common((pci_dev_t)dev->priv, dev->iobase, NULL); +} +#endif /* nCONFIG_DM_ETH */ + #define HZ 1000 /************************************************************************** SEND - Transmit a frame ***************************************************************************/ -static int rtl_send(struct eth_device *dev, void *packet, int length) +static int rtl_send_common(pci_dev_t bdf, unsigned long dev_iobase, + void *packet, int length) { /* send the packet to destination */ @@ -577,7 +605,7 @@ static int rtl_send(struct eth_device *dev, void *packet, int length) printf("sending %d bytes\n", len); #endif - ioaddr = dev->iobase; + ioaddr = dev_iobase; /* point to the current txb incase multiple tx_rings are used */ ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE]; @@ -588,7 +616,8 @@ static int rtl_send(struct eth_device *dev, void *packet, int length) ptxb[len++] = '\0'; tpc->TxDescArray[entry].buf_Haddr = 0; - tpc->TxDescArray[entry].buf_addr = cpu_to_le32(bus_to_phys(ptxb)); + tpc->TxDescArray[entry].buf_addr = cpu_to_le32( + pci_mem_to_phys(bdf, (pci_addr_t)(unsigned long)ptxb)); if (entry != (NUM_TX_DESC - 1)) { tpc->TxDescArray[entry].status = cpu_to_le32((OWNbit | FSbit | LSbit) | @@ -625,7 +654,23 @@ static int rtl_send(struct eth_device *dev, void *packet, int length) return ret; } -static void rtl8169_set_rx_mode(struct eth_device *dev) +#ifdef CONFIG_DM_ETH +int rtl8169_eth_send(struct udevice *dev, void *packet, int length) +{ + struct rtl8169_private *priv = dev_get_priv(dev); + + return rtl_send_common(pci_get_bdf(dev), priv->iobase, packet, length); +} + +#else +static int rtl_send(struct eth_device *dev, void *packet, int length) +{ + return rtl_send_common((pci_dev_t)dev->priv, dev->iobase, packet, + length); +} +#endif + +static void rtl8169_set_rx_mode(void) { u32 mc_filter[2]; /* Multicast hash filter */ int rx_mode; @@ -648,7 +693,7 @@ static void rtl8169_set_rx_mode(struct eth_device *dev) RTL_W32(MAR0 + 4, mc_filter[1]); } -static void rtl8169_hw_start(struct eth_device *dev) +static void rtl8169_hw_start(pci_dev_t bdf) { u32 i; @@ -693,9 +738,11 @@ static void rtl8169_hw_start(struct eth_device *dev) tpc->cur_rx = 0; - RTL_W32(TxDescStartAddrLow, bus_to_phys(tpc->TxDescArray)); + RTL_W32(TxDescStartAddrLow, pci_mem_to_phys(bdf, + (pci_addr_t)(unsigned long)tpc->TxDescArray)); RTL_W32(TxDescStartAddrHigh, (unsigned long)0); - RTL_W32(RxDescStartAddrLow, bus_to_phys(tpc->RxDescArray)); + RTL_W32(RxDescStartAddrLow, pci_mem_to_phys( + bdf, (pci_addr_t)(unsigned long)tpc->RxDescArray)); RTL_W32(RxDescStartAddrHigh, (unsigned long)0); /* RTL-8169sc/8110sc or later version */ @@ -707,7 +754,7 @@ static void rtl8169_hw_start(struct eth_device *dev) RTL_W32(RxMissed, 0); - rtl8169_set_rx_mode(dev); + rtl8169_set_rx_mode(); /* no early-rx interrupts */ RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); @@ -717,7 +764,7 @@ static void rtl8169_hw_start(struct eth_device *dev) #endif } -static void rtl8169_init_ring(struct eth_device *dev) +static void rtl8169_init_ring(pci_dev_t bdf) { int i; @@ -745,8 +792,8 @@ static void rtl8169_init_ring(struct eth_device *dev) cpu_to_le32(OWNbit + RX_BUF_SIZE); tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE]; - tpc->RxDescArray[i].buf_addr = - cpu_to_le32(bus_to_phys(tpc->RxBufferRing[i])); + tpc->RxDescArray[i].buf_addr = cpu_to_le32(pci_mem_to_phys( + bdf, (pci_addr_t)(unsigned long)tpc->RxBufferRing[i])); rtl_flush_rx_desc(&tpc->RxDescArray[i]); } @@ -755,10 +802,7 @@ static void rtl8169_init_ring(struct eth_device *dev) #endif } -/************************************************************************** -RESET - Finish setting up the ethernet interface -***************************************************************************/ -static int rtl_reset(struct eth_device *dev, bd_t *bis) +static void rtl8169_common_start(pci_dev_t bdf, unsigned char *enetaddr) { int i; @@ -767,30 +811,47 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif - rtl8169_init_ring(dev); - rtl8169_hw_start(dev); + rtl8169_init_ring(bdf); + rtl8169_hw_start(bdf); /* Construct a perfect filter frame with the mac address as first match * and broadcast for all others */ for (i = 0; i < 192; i++) txb[i] = 0xFF; - txb[0] = dev->enetaddr[0]; - txb[1] = dev->enetaddr[1]; - txb[2] = dev->enetaddr[2]; - txb[3] = dev->enetaddr[3]; - txb[4] = dev->enetaddr[4]; - txb[5] = dev->enetaddr[5]; + txb[0] = enetaddr[0]; + txb[1] = enetaddr[1]; + txb[2] = enetaddr[2]; + txb[3] = enetaddr[3]; + txb[4] = enetaddr[4]; + txb[5] = enetaddr[5]; #ifdef DEBUG_RTL8169 printf("%s elapsed time : %lu\n", __func__, currticks()-stime); #endif - return 0; } +#ifdef CONFIG_DM_ETH +static int rtl8169_eth_start(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_platdata(dev); + + rtl8169_common_start(pci_get_bdf(dev), plat->enetaddr); + + return 0; +} +#else /************************************************************************** -HALT - Turn off ethernet interface +RESET - Finish setting up the ethernet interface ***************************************************************************/ -static void rtl_halt(struct eth_device *dev) +static int rtl_reset(struct eth_device *dev, bd_t *bis) +{ + rtl8169_common_start((pci_dev_t)dev->priv, dev->enetaddr); + + return 0; +} +#endif /* nCONFIG_DM_ETH */ + +static void rtl_halt_common(unsigned long dev_iobase) { int i; @@ -798,7 +859,7 @@ static void rtl_halt(struct eth_device *dev) printf ("%s\n", __FUNCTION__); #endif - ioaddr = dev->iobase; + ioaddr = dev_iobase; /* Stop the chip's Tx and Rx DMA processes. */ RTL_W8(ChipCmd, 0x00); @@ -813,13 +874,31 @@ static void rtl_halt(struct eth_device *dev) } } +#ifdef CONFIG_DM_ETH +void rtl8169_eth_stop(struct udevice *dev) +{ + struct rtl8169_private *priv = dev_get_priv(dev); + + rtl_halt_common(priv->iobase); +} +#else +/************************************************************************** +HALT - Turn off ethernet interface +***************************************************************************/ +static void rtl_halt(struct eth_device *dev) +{ + rtl_halt_common(dev->iobase); +} +#endif + /************************************************************************** INIT - Look for an adapter, this routine's visible to the outside ***************************************************************************/ #define board_found 1 #define valid_link 0 -static int rtl_init(struct eth_device *dev, bd_t *bis) +static int rtl_init(unsigned long dev_ioaddr, const char *name, + unsigned char *enetaddr) { static int board_idx = -1; int i, rc; @@ -828,33 +907,32 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #ifdef DEBUG_RTL8169 printf ("%s\n", __FUNCTION__); #endif - - ioaddr = dev->iobase; + ioaddr = dev_ioaddr; board_idx++; /* point to private storage */ tpc = &tpx; - rc = rtl8169_init_board(dev); + rc = rtl8169_init_board(ioaddr, name); if (rc) return rc; /* Get MAC address. FIXME: read EEPROM */ for (i = 0; i < MAC_ADDR_LEN; i++) - dev->enetaddr[i] = RTL_R8(MAC0 + i); + enetaddr[i] = RTL_R8(MAC0 + i); #ifdef DEBUG_RTL8169 printf("chipset = %d\n", tpc->chipset); printf("MAC Address"); for (i = 0; i < MAC_ADDR_LEN; i++) - printf(":%02x", dev->enetaddr[i]); + printf(":%02x", enetaddr[i]); putc('\n'); #endif #ifdef DEBUG_RTL8169 /* Print out some hardware info */ - printf("%s: at ioaddr 0x%lx\n", dev->name, ioaddr); + printf("%s: at ioaddr 0x%lx\n", name, ioaddr); #endif /* if TBI is not endbled */ @@ -964,6 +1042,7 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) return 0; } +#ifndef CONFIG_DM_ETH int rtl8169_initialize(bd_t *bis) { pci_dev_t devno; @@ -1014,7 +1093,7 @@ int rtl8169_initialize(bd_t *bis) dev->send = rtl_send; dev->recv = rtl_recv; - err = rtl_init(dev, bis); + err = rtl_init(dev->iobase, dev->name, dev->enetaddr); if (err < 0) { printf(pr_fmt("failed to initialize card: %d\n"), err); free(dev); @@ -1027,3 +1106,62 @@ int rtl8169_initialize(bd_t *bis) } return card_number; } +#endif + +#ifdef CONFIG_DM_ETH +static int rtl8169_eth_probe(struct udevice *dev) +{ + struct pci_child_platdata *pplat = dev_get_parent_platdata(dev); + struct rtl8169_private *priv = dev_get_priv(dev); + struct eth_pdata *plat = dev_get_platdata(dev); + u32 iobase; + int region; + int ret; + + debug("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); + switch (pplat->device) { + case 0x8168: + region = 2; + break; + default: + region = 1; + break; + } + pci_read_config32(pci_get_bdf(dev), PCI_BASE_ADDRESS_0 + region * 4, + &iobase); + iobase &= ~0xf; + priv->iobase = (int)pci_mem_to_phys(pci_get_bdf(dev), iobase); + + ret = rtl_init(priv->iobase, dev->name, plat->enetaddr); + if (ret < 0) { + printf(pr_fmt("failed to initialize card: %d\n"), ret); + return ret; + } + + return 0; +} + +static const struct eth_ops rtl8169_eth_ops = { + .start = rtl8169_eth_start, + .send = rtl8169_eth_send, + .recv = rtl8169_eth_recv, + .stop = rtl8169_eth_stop, +}; + +static const struct udevice_id rtl8169_eth_ids[] = { + { .compatible = "realtek,rtl8169" }, + { } +}; + +U_BOOT_DRIVER(eth_rtl8169) = { + .name = "eth_rtl8169", + .id = UCLASS_ETH, + .of_match = rtl8169_eth_ids, + .probe = rtl8169_eth_probe, + .ops = &rtl8169_eth_ops, + .priv_auto_alloc_size = sizeof(struct rtl8169_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +U_BOOT_PCI_DEVICE(eth_rtl8169, supported); +#endif diff --git a/drivers/net/sandbox-raw.c b/drivers/net/sandbox-raw.c index 45c3b18..5912427 100644 --- a/drivers/net/sandbox-raw.c +++ b/drivers/net/sandbox-raw.c @@ -65,7 +65,7 @@ static int sb_eth_raw_send(struct udevice *dev, void *packet, int length) return sandbox_eth_raw_os_send(packet, length, priv); } -static int sb_eth_raw_recv(struct udevice *dev, uchar **packetp) +static int sb_eth_raw_recv(struct udevice *dev, int flags, uchar **packetp) { struct eth_pdata *pdata = dev_get_platdata(dev); struct eth_sandbox_raw_priv *priv = dev_get_priv(dev); diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c index 4e083d3..6763a24 100644 --- a/drivers/net/sandbox.c +++ b/drivers/net/sandbox.c @@ -152,7 +152,7 @@ static int sb_eth_send(struct udevice *dev, void *packet, int length) return 0; } -static int sb_eth_recv(struct udevice *dev, uchar **packetp) +static int sb_eth_recv(struct udevice *dev, int flags, uchar **packetp) { struct eth_sandbox_priv *priv = dev_get_priv(dev); diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index e939bf2..11cd0ea 100644 --- a/drivers/net/sunxi_emac.c +++ b/drivers/net/sunxi_emac.c @@ -527,7 +527,7 @@ static int sunxi_emac_eth_send(struct udevice *dev, void *packet, int length) return _sunxi_emac_eth_send(priv, packet, length); } -static int sunxi_emac_eth_recv(struct udevice *dev, uchar **packetp) +static int sunxi_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) { struct emac_eth_dev *priv = dev_get_priv(dev); int rx_len; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 5b91fe3..3be76c9 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -30,6 +30,14 @@ struct pci_controller *pci_bus_to_hose(int busnum) return dev_get_uclass_priv(bus); } +pci_dev_t pci_get_bdf(struct udevice *dev) +{ + struct pci_child_platdata *pplat = dev_get_parent_platdata(dev); + struct udevice *bus = dev->parent; + + return PCI_ADD_BUS(bus->seq, pplat->devfn); +} + /** * pci_get_bus_max() - returns the bus number of the last active bus * @@ -295,19 +303,14 @@ int pci_auto_config_devices(struct udevice *bus) for (ret = device_find_first_child(bus, &dev); !ret && dev; ret = device_find_next_child(&dev)) { - struct pci_child_platdata *pplat; struct pci_controller *ctlr_hose; - - pplat = dev_get_parent_platdata(dev); unsigned int max_bus; - pci_dev_t bdf; - bdf = PCI_ADD_BUS(bus->seq, pplat->devfn); debug("%s: device %s\n", __func__, dev->name); /* The root controller has the region information */ ctlr_hose = hose->ctlr->uclass_priv; - max_bus = pciauto_config_device(ctlr_hose, bdf); + max_bus = pciauto_config_device(ctlr_hose, pci_get_bdf(dev)); sub_bus = max(sub_bus, max_bus); } debug("%s: done\n", __func__); @@ -353,6 +356,101 @@ int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf) return sub_bus; } +/** + * pci_match_one_device - Tell if a PCI device structure has a matching + * PCI device id structure + * @id: single PCI device id structure to match + * @dev: the PCI device structure to match against + * + * Returns the matching pci_device_id structure or %NULL if there is no match. + */ +static bool pci_match_one_id(const struct pci_device_id *id, + const struct pci_device_id *find) +{ + if ((id->vendor == PCI_ANY_ID || id->vendor == find->vendor) && + (id->device == PCI_ANY_ID || id->device == find->device) && + (id->subvendor == PCI_ANY_ID || id->subvendor == find->subvendor) && + (id->subdevice == PCI_ANY_ID || id->subdevice == find->subdevice) && + !((id->class ^ find->class) & id->class_mask)) + return true; + + return false; +} + +/** + * pci_find_and_bind_driver() - Find and bind the right PCI driver + * + * This only looks at certain fields in the descriptor. + */ +static int pci_find_and_bind_driver(struct udevice *parent, + struct pci_device_id *find_id, int devfn, + struct udevice **devp) +{ + struct pci_driver_entry *start, *entry; + const char *drv; + int n_ents; + int ret; + char name[30], *str; + + *devp = NULL; + + debug("%s: Searching for driver: vendor=%x, device=%x\n", __func__, + find_id->vendor, find_id->device); + start = ll_entry_start(struct pci_driver_entry, pci_driver_entry); + n_ents = ll_entry_count(struct pci_driver_entry, pci_driver_entry); + for (entry = start; entry != start + n_ents; entry++) { + const struct pci_device_id *id; + struct udevice *dev; + const struct driver *drv; + + for (id = entry->match; + id->vendor || id->subvendor || id->class_mask; + id++) { + if (!pci_match_one_id(id, find_id)) + continue; + + drv = entry->driver; + /* + * We could pass the descriptor to the driver as + * platdata (instead of NULL) and allow its bind() + * method to return -ENOENT if it doesn't support this + * device. That way we could continue the search to + * find another driver. For now this doesn't seem + * necesssary, so just bind the first match. + */ + ret = device_bind(parent, drv, drv->name, NULL, -1, + &dev); + if (ret) + goto error; + debug("%s: Match found: %s\n", __func__, drv->name); + dev->driver_data = find_id->driver_data; + *devp = dev; + return 0; + } + } + + /* Bind a generic driver so that the device can be used */ + sprintf(name, "pci_%x:%x.%x", parent->seq, PCI_DEV(devfn), + PCI_FUNC(devfn)); + str = strdup(name); + if (!str) + return -ENOMEM; + drv = (find_id->class >> 8) == PCI_CLASS_BRIDGE_PCI ? "pci_bridge_drv" : + "pci_generic_drv"; + ret = device_bind_driver(parent, drv, str, devp); + if (ret) { + debug("%s: Failed to bind generic driver: %d", __func__, ret); + return ret; + } + debug("%s: No match found: bound generic driver instead\n", __func__); + + return 0; + +error: + debug("%s: No match found: error %d\n", __func__, ret); + return ret; +} + int pci_bind_bus_devices(struct udevice *bus) { ulong vendor, device; @@ -387,25 +485,33 @@ int pci_bind_bus_devices(struct udevice *bus) bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn)); pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device, PCI_SIZE_16); - pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class, - PCI_SIZE_16); + pci_bus_read_config(bus, devfn, PCI_CLASS_REVISION, &class, + PCI_SIZE_32); + class >>= 8; /* Find this device in the device tree */ ret = pci_bus_find_devfn(bus, devfn, &dev); + /* Search for a driver */ + /* If nothing in the device tree, bind a generic device */ if (ret == -ENODEV) { - char name[30], *str; - const char *drv; - - sprintf(name, "pci_%x:%x.%x", bus->seq, - PCI_DEV(devfn), PCI_FUNC(devfn)); - str = strdup(name); - if (!str) - return -ENOMEM; - drv = class == PCI_CLASS_BRIDGE_PCI ? - "pci_bridge_drv" : "pci_generic_drv"; - ret = device_bind_driver(bus, drv, str, &dev); + struct pci_device_id find_id; + ulong val; + + memset(&find_id, '\0', sizeof(find_id)); + find_id.vendor = vendor; + find_id.device = device; + find_id.class = class; + if ((header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL) { + pci_bus_read_config(bus, devfn, + PCI_SUBSYSTEM_VENDOR_ID, + &val, PCI_SIZE_32); + find_id.subvendor = val & 0xffff; + find_id.subdevice = val >> 16; + } + ret = pci_find_and_bind_driver(bus, &find_id, devfn, + &dev); } if (ret) return ret; diff --git a/drivers/pci/pci_compat.c b/drivers/pci/pci_compat.c index d6938c1..05c3510 100644 --- a/drivers/pci/pci_compat.c +++ b/drivers/pci/pci_compat.c @@ -31,13 +31,9 @@ PCI_HOSE_OP(write, dword, 32, u32) pci_dev_t pci_find_devices(struct pci_device_id *ids, int index) { - struct pci_child_platdata *pplat; - struct udevice *bus, *dev; + struct udevice *dev; if (pci_find_device_id(ids, index, &dev)) return -1; - bus = dev->parent; - pplat = dev_get_parent_platdata(dev); - - return PCI_ADD_BUS(bus->seq, pplat->devfn); + return pci_get_bdf(dev); } diff --git a/drivers/power/pmic/pmic-uclass.c b/drivers/power/pmic/pmic-uclass.c index 812ac13..d99cb9a 100644 --- a/drivers/power/pmic/pmic-uclass.c +++ b/drivers/power/pmic/pmic-uclass.c @@ -9,6 +9,7 @@ #include <fdtdec.h> #include <errno.h> #include <dm.h> +#include <vsprintf.h> #include <dm/lists.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -17,16 +18,6 @@ DECLARE_GLOBAL_DATA_PTR; -static ulong str_get_num(const char *ptr, const char *maxptr) -{ - if (!ptr || !maxptr) - return 0; - - while (!isdigit(*ptr) && ptr++ < maxptr); - - return simple_strtoul(ptr, NULL, 0); -} - int pmic_bind_children(struct udevice *pmic, int offset, const struct pmic_child_info *child_info) { @@ -35,7 +26,6 @@ int pmic_bind_children(struct udevice *pmic, int offset, struct driver *drv; struct udevice *child; const char *node_name; - int node_name_len; int bind_count = 0; int node; int prefix_len; @@ -47,19 +37,19 @@ int pmic_bind_children(struct udevice *pmic, int offset, for (node = fdt_first_subnode(blob, offset); node > 0; node = fdt_next_subnode(blob, node)) { - node_name = fdt_get_name(blob, node, &node_name_len); + node_name = fdt_get_name(blob, node, NULL); debug("* Found child node: '%s' at offset:%d\n", node_name, node); child = NULL; for (info = child_info; info->prefix && info->driver; info++) { + debug(" - compatible prefix: '%s'\n", info->prefix); + prefix_len = strlen(info->prefix); - if (strncasecmp(info->prefix, node_name, prefix_len)) + if (strncmp(info->prefix, node_name, prefix_len)) continue; - debug(" - compatible prefix: '%s'\n", info->prefix); - drv = lists_driver_lookup_name(info->driver); if (!drv) { debug(" - driver: '%s' not found!\n", @@ -78,10 +68,7 @@ int pmic_bind_children(struct udevice *pmic, int offset, debug(" - bound child device: '%s'\n", child->name); - child->driver_data = str_get_num(node_name + - prefix_len, - node_name + - node_name_len); + child->driver_data = trailing_strtol(node_name); debug(" - set 'child->driver_data': %lu\n", child->driver_data); @@ -139,6 +126,38 @@ int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len) return ops->write(dev, reg, buffer, len); } +int pmic_reg_read(struct udevice *dev, uint reg) +{ + u8 byte; + int ret; + + ret = pmic_read(dev, reg, &byte, 1); + debug("%s: reg=%x, value=%x\n", __func__, reg, byte); + + return ret ? ret : byte; +} + +int pmic_reg_write(struct udevice *dev, uint reg, uint value) +{ + u8 byte = value; + + debug("%s: reg=%x, value=%x\n", __func__, reg, value); + return pmic_read(dev, reg, &byte, 1); +} + +int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set) +{ + u8 byte; + int ret; + + ret = pmic_reg_read(dev, reg); + if (ret < 0) + return ret; + byte = (ret & ~clr) | set; + + return pmic_reg_write(dev, reg, byte); +} + UCLASS_DRIVER(pmic) = { .id = UCLASS_PMIC, .name = "pmic", diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c index 31ffd44..12e141b 100644 --- a/drivers/power/regulator/regulator-uclass.c +++ b/drivers/power/regulator/regulator-uclass.c @@ -138,87 +138,57 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp) return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); } -static int failed(int ret, bool verbose, const char *fmt, ...) +int regulator_autoset(struct udevice *dev) { - va_list args; - char buf[64]; - - if (verbose == false) - return ret; + struct dm_regulator_uclass_platdata *uc_pdata; + int ret = 0; - va_start(args, fmt); - vscnprintf(buf, sizeof(buf), fmt, args); - va_end(args); + uc_pdata = dev_get_uclass_platdata(dev); + if (!uc_pdata->always_on && !uc_pdata->boot_on) + return -EMEDIUMTYPE; - printf(buf); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + ret = regulator_set_value(dev, uc_pdata->min_uV); + if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) + ret = regulator_set_current(dev, uc_pdata->min_uA); if (!ret) - return 0; - - printf(" (ret: %d)", ret); + ret = regulator_set_enable(dev, true); return ret; } -int regulator_autoset(const char *platname, - struct udevice **devp, - bool verbose) +static void regulator_show(struct udevice *dev, int ret) { struct dm_regulator_uclass_platdata *uc_pdata; - struct udevice *dev; - int ret; - - if (devp) - *devp = NULL; - - ret = regulator_get_by_platname(platname, &dev); - if (ret) { - error("Can get the regulator: %s!", platname); - return ret; - } uc_pdata = dev_get_uclass_platdata(dev); - if (!uc_pdata) { - error("Can get the regulator %s uclass platdata!", platname); - return -ENXIO; - } - if (!uc_pdata->always_on && !uc_pdata->boot_on) - goto retdev; - - if (verbose) - printf("%s@%s: ", dev->name, uc_pdata->name); - - /* Those values are optional (-ENODATA if unset) */ - if ((uc_pdata->min_uV != -ENODATA) && - (uc_pdata->max_uV != -ENODATA) && - (uc_pdata->min_uV == uc_pdata->max_uV)) { - ret = regulator_set_value(dev, uc_pdata->min_uV); - if (failed(ret, verbose, "set %d uV", uc_pdata->min_uV)) - goto exit; - } - - /* Those values are optional (-ENODATA if unset) */ - if ((uc_pdata->min_uA != -ENODATA) && - (uc_pdata->max_uA != -ENODATA) && - (uc_pdata->min_uA == uc_pdata->max_uA)) { - ret = regulator_set_current(dev, uc_pdata->min_uA); - if (failed(ret, verbose, "; set %d uA", uc_pdata->min_uA)) - goto exit; - } + printf("%s@%s: ", dev->name, uc_pdata->name); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) + printf("set %d uV", uc_pdata->min_uV); + if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) + printf("; set %d uA", uc_pdata->min_uA); + printf("; enabling"); + if (ret) + printf(" (ret: %d)\n", ret); + printf("\n"); +} - ret = regulator_set_enable(dev, true); - if (failed(ret, verbose, "; enabling", uc_pdata->min_uA)) - goto exit; +int regulator_autoset_by_name(const char *platname, struct udevice **devp) +{ + struct udevice *dev; + int ret; -retdev: + ret = regulator_get_by_platname(platname, &dev); if (devp) *devp = dev; -exit: - if (verbose) - printf("\n"); + if (ret) { + debug("Can get the regulator: %s!", platname); + return ret; + } - return ret; + return regulator_autoset(dev); } int regulator_list_autoset(const char *list_platname[], @@ -229,7 +199,9 @@ int regulator_list_autoset(const char *list_platname[], int error = 0, i = 0, ret; while (list_platname[i]) { - ret = regulator_autoset(list_platname[i], &dev, verbose); + ret = regulator_autoset_by_name(list_platname[i], &dev); + if (ret != -EMEDIUMTYPE && verbose) + regulator_show(dev, ret); if (ret & !error) error = ret; @@ -290,7 +262,7 @@ static int regulator_post_bind(struct udevice *dev) if (regulator_name_is_unique(dev, uc_pdata->name)) return 0; - error("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"", + debug("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"", property, dev->name, uc_pdata->name); return -EINVAL; @@ -319,9 +291,43 @@ static int regulator_pre_probe(struct udevice *dev) uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, "regulator-boot-on"); + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uV != -ENODATA) && + (uc_pdata->max_uV != -ENODATA) && + (uc_pdata->min_uV == uc_pdata->max_uV)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; + + /* Those values are optional (-ENODATA if unset) */ + if ((uc_pdata->min_uA != -ENODATA) && + (uc_pdata->max_uA != -ENODATA) && + (uc_pdata->min_uA == uc_pdata->max_uA)) + uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; + return 0; } +int regulators_enable_boot_on(bool verbose) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_REGULATOR, &uc); + if (ret) + return ret; + for (uclass_first_device(UCLASS_REGULATOR, &dev); + dev && !ret; + uclass_next_device(&dev)) { + ret = regulator_autoset(dev); + if (ret == -EMEDIUMTYPE) + continue; + if (verbose) + regulator_show(dev, ret); + } + + return ret; +} + UCLASS_DRIVER(regulator) = { .id = UCLASS_REGULATOR, .name = "regulator", diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig new file mode 100644 index 0000000..642a2d8 --- /dev/null +++ b/drivers/ram/Kconfig @@ -0,0 +1,18 @@ +config RAM + bool "Enable RAM drivers using Driver Model" + depends on DM + help + This allows drivers to be provided for SDRAM and other RAM + controllers and their type to be specified in the board's device + tree. Generally some parameters are required to set up the RAM and + the RAM size can either be statically defined or dynamically + detected. + +config SPL_RAM_SUPPORT + bool "Enable RAM support in SPL" + depends on RAM + help + The RAM subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use RAM drivers in + SPL, enable this option. It might provide a cleaner interface to + setting up RAM (e.g. SDRAM / DDR) within SPL. diff --git a/drivers/ram/Makefile b/drivers/ram/Makefile new file mode 100644 index 0000000..0e10249 --- /dev/null +++ b/drivers/ram/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_RAM) += ram-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox_ram.o diff --git a/drivers/ram/ram-uclass.c b/drivers/ram/ram-uclass.c new file mode 100644 index 0000000..2f1fbe7 --- /dev/null +++ b/drivers/ram/ram-uclass.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <ram.h> +#include <dm.h> +#include <errno.h> +#include <dm/lists.h> +#include <dm/root.h> + +int ram_get_info(struct udevice *dev, struct ram_info *info) +{ + struct ram_ops *ops = ram_get_ops(dev); + + if (!ops->get_info) + return -ENOSYS; + + return ops->get_info(dev, info); +} + +UCLASS_DRIVER(ram) = { + .id = UCLASS_RAM, + .name = "ram", +}; diff --git a/drivers/ram/sandbox_ram.c b/drivers/ram/sandbox_ram.c new file mode 100644 index 0000000..06bf3ec --- /dev/null +++ b/drivers/ram/sandbox_ram.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <ram.h> +#include <asm/test.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int sandbox_get_info(struct udevice *dev, struct ram_info *info) +{ + info->base = 0; + info->size = gd->ram_size; + + return 0; +} + +static const struct ram_ops sandbox_ram_ops = { + .get_info = sandbox_get_info, +}; + +static const struct udevice_id sandbox_ram_ids[] = { + { .compatible = "sandbox,ram" }, + { } +}; + +U_BOOT_DRIVER(warm_ram_sandbox) = { + .name = "ram_sandbox", + .id = UCLASS_RAM, + .of_match = sandbox_ram_ids, + .ops = &sandbox_ram_ops, +}; diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 9b044a3..c8a77e2 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -246,6 +246,17 @@ int NS16550_tstc(NS16550_t com_port) #include <debug_uart.h> +#define serial_dout(reg, value) \ + serial_out_shift((char *)com_port + \ + ((char *)reg - (char *)com_port) * \ + (1 << CONFIG_DEBUG_UART_SHIFT), \ + CONFIG_DEBUG_UART_SHIFT, value) +#define serial_din(reg) \ + serial_in_shift((char *)com_port + \ + ((char *)reg - (char *)com_port) * \ + (1 << CONFIG_DEBUG_UART_SHIFT), \ + CONFIG_DEBUG_UART_SHIFT) + void debug_uart_init(void) { struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; @@ -259,28 +270,23 @@ void debug_uart_init(void) */ baud_divisor = calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); - serial_out_shift(&com_port->ier, CONFIG_DEBUG_UART_SHIFT, - CONFIG_SYS_NS16550_IER); - serial_out_shift(&com_port->mcr, CONFIG_DEBUG_UART_SHIFT, UART_MCRVAL); - serial_out_shift(&com_port->fcr, CONFIG_DEBUG_UART_SHIFT, UART_FCRVAL); - - serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT, - UART_LCR_BKSE | UART_LCRVAL); - serial_out_shift(&com_port->dll, CONFIG_DEBUG_UART_SHIFT, - baud_divisor & 0xff); - serial_out_shift(&com_port->dlm, CONFIG_DEBUG_UART_SHIFT, - (baud_divisor >> 8) & 0xff); - serial_out_shift(&com_port->lcr, CONFIG_DEBUG_UART_SHIFT, - UART_LCRVAL); + serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER); + serial_dout(&com_port->mcr, UART_MCRVAL); + serial_dout(&com_port->fcr, UART_FCRVAL); + + serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL); + serial_dout(&com_port->dll, baud_divisor & 0xff); + serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff); + serial_dout(&com_port->lcr, UART_LCRVAL); } static inline void _debug_uart_putc(int ch) { struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE; - while (!(serial_in_shift(&com_port->lsr, 0) & UART_LSR_THRE)) + while (!(serial_din(&com_port->lsr) & UART_LSR_THRE)) ; - serial_out_shift(&com_port->thr, CONFIG_DEBUG_UART_SHIFT, ch); + serial_dout(&com_port->thr, ch); } DEBUG_UART_FUNCS diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index 737ae64..d666272 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -95,13 +95,13 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags); } -int spi_post_bind(struct udevice *dev) +static int spi_post_bind(struct udevice *dev) { /* Scan the bus for devices */ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int spi_child_post_bind(struct udevice *dev) +static int spi_child_post_bind(struct udevice *dev) { struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); @@ -111,7 +111,7 @@ int spi_child_post_bind(struct udevice *dev) return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat); } -int spi_post_probe(struct udevice *bus) +static int spi_post_probe(struct udevice *bus) { struct dm_spi_bus *spi = dev_get_uclass_priv(bus); @@ -121,7 +121,7 @@ int spi_post_probe(struct udevice *bus) return 0; } -int spi_child_pre_probe(struct udevice *dev) +static int spi_child_pre_probe(struct udevice *dev) { struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev); struct spi_slave *slave = dev_get_parentdata(dev); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 637ef3d..3fa5b2e 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -46,8 +46,8 @@ config DM_USB Much of the code is shared but with this option enabled the USB uclass takes care of device enumeration. USB devices can be - declared with the USB_DEVICE() macro and will be automatically - probed when found on the bus. + declared with the U_BOOT_USB_DEVICE() macro and will be + automatically probed when found on the bus. source "drivers/usb/host/Kconfig" diff --git a/drivers/usb/eth/asix.c b/drivers/usb/eth/asix.c index c8697ae..72ec41e 100644 --- a/drivers/usb/eth/asix.c +++ b/drivers/usb/eth/asix.c @@ -5,11 +5,11 @@ */ #include <common.h> +#include <dm.h> #include <usb.h> +#include <malloc.h> #include <linux/mii.h> #include "usb_ether.h" -#include <malloc.h> - /* ASIX AX8817X based USB 2.0 Ethernet Devices */ @@ -92,14 +92,20 @@ #define FLAG_TYPE_AX88772B (1U << 2) #define FLAG_EEPROM_MAC (1U << 3) /* initial mac address in eeprom */ -/* local vars */ -static int curr_eth_dev; /* index for name of next device detected */ /* driver private */ struct asix_private { int flags; +#ifdef CONFIG_DM_ETH + struct ueth_data ueth; +#endif }; +#ifndef CONFIG_DM_ETH +/* local vars */ +static int curr_eth_dev; /* index for name of next device detected */ +#endif + /* * Asix infrastructure commands */ @@ -284,13 +290,12 @@ static int asix_write_gpio(struct ueth_data *dev, u16 value, int sleep) return ret; } -static int asix_write_hwaddr(struct eth_device *eth) +static int asix_write_hwaddr_common(struct ueth_data *dev, uint8_t *enetaddr) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int ret; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); - memcpy(buf, eth->enetaddr, ETH_ALEN); + memcpy(buf, enetaddr, ETH_ALEN); ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN, buf); if (ret < 0) @@ -325,12 +330,11 @@ static int mii_nway_restart(struct ueth_data *dev) return r; } -static int asix_read_mac(struct eth_device *eth) +static int asix_read_mac_common(struct ueth_data *dev, + struct asix_private *priv, uint8_t *enetaddr) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; - struct asix_private *priv = (struct asix_private *)dev->dev_priv; - int i; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, ETH_ALEN); + int i; if (priv->flags & FLAG_EEPROM_MAC) { for (i = 0; i < (ETH_ALEN >> 1); i++) { @@ -339,7 +343,7 @@ static int asix_read_mac(struct eth_device *eth) debug("Failed to read SROM address 04h.\n"); return -1; } - memcpy((eth->enetaddr + i * 2), buf, 2); + memcpy(enetaddr + i * 2, buf, 2); } } else { if (asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf) @@ -347,7 +351,7 @@ static int asix_read_mac(struct eth_device *eth) debug("Failed to read MAC address.\n"); return -1; } - memcpy(eth->enetaddr, buf, ETH_ALEN); + memcpy(enetaddr, buf, ETH_ALEN); } return 0; @@ -414,12 +418,8 @@ static int asix_basic_reset(struct ueth_data *dev) return 0; } -/* - * Asix callbacks - */ -static int asix_init(struct eth_device *eth, bd_t *bd) +static int asix_init_common(struct ueth_data *dev) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int timeout = 0; #define TIMEOUT_RESOLUTION 50 /* ms */ int link_detected; @@ -452,9 +452,8 @@ out_err: return -1; } -static int asix_send(struct eth_device *eth, void *packet, int length) +static int asix_send_common(struct ueth_data *dev, void *packet, int length) { - struct ueth_data *dev = (struct ueth_data *)eth->priv; int err; u32 packet_len; int actual_len; @@ -481,6 +480,24 @@ static int asix_send(struct eth_device *eth, void *packet, int length) return err; } +#ifndef CONFIG_DM_ETH +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_init_common(dev); +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_send_common(dev, packet, length); +} + static int asix_recv(struct eth_device *eth) { struct ueth_data *dev = (struct ueth_data *)eth->priv; @@ -552,6 +569,13 @@ static void asix_halt(struct eth_device *eth) debug("** %s()\n", __func__); } +static int asix_write_hwaddr(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + + return asix_write_hwaddr_common(dev, eth->enetaddr); +} + /* * Asix probing functions */ @@ -694,9 +718,180 @@ int asix_eth_get_info(struct usb_device *dev, struct ueth_data *ss, return 0; /* Get the MAC address */ - if (asix_read_mac(eth)) + if (asix_read_mac_common(ss, priv, eth->enetaddr)) return 0; debug("MAC %pM\n", eth->enetaddr); return 1; } +#endif + +#ifdef CONFIG_DM_ETH +static int asix_eth_start(struct udevice *dev) +{ + struct asix_private *priv = dev_get_priv(dev); + + return asix_init_common(&priv->ueth); +} + +void asix_eth_stop(struct udevice *dev) +{ + debug("** %s()\n", __func__); +} + +int asix_eth_send(struct udevice *dev, void *packet, int length) +{ + struct asix_private *priv = dev_get_priv(dev); + + return asix_send_common(&priv->ueth, packet, length); +} + +int asix_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ueth = &priv->ueth; + uint8_t *ptr; + int ret, len; + u32 packet_len; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: first try, len=%d\n", __func__, len); + if (!len) { + if (!(flags & ETH_RECV_CHECK_DEVICE)) + return -EAGAIN; + ret = usb_ether_receive(ueth, AX_RX_URB_SIZE); + if (ret == -EAGAIN) + return ret; + + len = usb_ether_get_rx_bytes(ueth, &ptr); + debug("%s: second try, len=%d\n", __func__, len); + } + + /* + * 1st 4 bytes contain the length of the actual data as two + * complementary 16-bit words. Extract the length of the data. + */ + if (len < sizeof(packet_len)) { + debug("Rx: incomplete packet length\n"); + goto err; + } + memcpy(&packet_len, ptr, sizeof(packet_len)); + le32_to_cpus(&packet_len); + if (((~packet_len >> 16) & 0x7ff) != (packet_len & 0x7ff)) { + debug("Rx: malformed packet length: %#x (%#x:%#x)\n", + packet_len, (~packet_len >> 16) & 0x7ff, + packet_len & 0x7ff); + goto err; + } + packet_len = packet_len & 0x7ff; + if (packet_len > len - sizeof(packet_len)) { + debug("Rx: too large packet: %d\n", packet_len); + goto err; + } + + *packetp = ptr + sizeof(packet_len); + return packet_len; + +err: + usb_ether_advance_rxbuf(ueth, -1); + return -EINVAL; +} + +static int asix_free_pkt(struct udevice *dev, uchar *packet, int packet_len) +{ + struct asix_private *priv = dev_get_priv(dev); + + if (packet_len & 1) + packet_len++; + usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len); + + return 0; +} + +int asix_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct asix_private *priv = dev_get_priv(dev); + + if (priv->flags & FLAG_TYPE_AX88172) + return -ENOSYS; + + return asix_write_hwaddr_common(&priv->ueth, pdata->enetaddr); +} + +static int asix_eth_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct asix_private *priv = dev_get_priv(dev); + struct ueth_data *ss = &priv->ueth; + int ret; + + priv->flags = dev->driver_data; + ret = usb_ether_register(dev, ss, AX_RX_URB_SIZE); + if (ret) + return ret; + + ret = asix_basic_reset(ss); + if (ret) + goto err; + + /* Get the MAC address */ + ret = asix_read_mac_common(ss, priv, pdata->enetaddr); + if (ret) + goto err; + debug("MAC %pM\n", pdata->enetaddr); + + return 0; + +err: + return usb_ether_deregister(ss); +} + +static const struct eth_ops asix_eth_ops = { + .start = asix_eth_start, + .send = asix_eth_send, + .recv = asix_eth_recv, + .free_pkt = asix_free_pkt, + .stop = asix_eth_stop, + .write_hwaddr = asix_write_hwaddr, +}; + +U_BOOT_DRIVER(asix_eth) = { + .name = "asix_eth", + .id = UCLASS_ETH, + .probe = asix_eth_probe, + .ops = &asix_eth_ops, + .priv_auto_alloc_size = sizeof(struct asix_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static const struct usb_device_id asix_eth_id_table[] = { + /* Apple USB Ethernet Adapter */ + { USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 }, + /* D-Link DUB-E100 H/W Ver B1 */ + { USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, + /* D-Link DUB-E100 H/W Ver C1 */ + { USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 }, + /* Cables-to-Go USB Ethernet Adapter */ + { USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 }, + /* Trendnet TU2-ET100 V3.0R */ + { USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, + /* SMC */ + { USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 }, + /* MSI - ASIX 88772a */ + { USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 }, + /* Linksys 200M v2.1 */ + { USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 }, + /* 0Q0 cable ethernet */ + { USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 }, + /* DLink DUB-E100 H/W Ver B1 Alternate */ + { USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 }, + /* ASIX 88772B */ + { USB_DEVICE(0x0b95, 0x772b), + .driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC }, + { USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table); +#endif diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index c72b7e4..63785a9 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -6,11 +6,137 @@ #include <common.h> #include <dm.h> +#include <malloc.h> #include <usb.h> #include <dm/device-internal.h> #include "usb_ether.h" +#ifdef CONFIG_DM_ETH + +#define USB_BULK_RECV_TIMEOUT 500 + +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize) +{ + struct usb_device *udev = dev_get_parentdata(dev); + struct usb_interface_descriptor *iface_desc; + bool ep_in_found = false, ep_out_found = false; + struct usb_interface *iface; + const int ifnum = 0; /* Always use interface 0 */ + int ret, i; + + iface = &udev->config.if_desc[ifnum]; + iface_desc = &udev->config.if_desc[ifnum].desc; + + /* Initialize the ueth_data structure with some useful info */ + ueth->ifnum = ifnum; + ueth->subclass = iface_desc->bInterfaceSubClass; + ueth->protocol = iface_desc->bInterfaceProtocol; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and int. + * We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + int ep_addr = iface->ep_desc[i].bEndpointAddress; + + /* is it an BULK endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + if (ep_addr & USB_DIR_IN && !ep_in_found) { + ueth->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = true; + } else if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ueth->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_out_found = true; + } + } + + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ueth->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ueth->irqinterval = iface->ep_desc[i].bInterval; + } + } + debug("Endpoints In %d Out %d Int %d\n", ueth->ep_in, ueth->ep_out, + ueth->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ueth->ep_in || !ueth->ep_out || !ueth->ep_int) { + debug("%s: %s: Cannot find endpoints\n", __func__, dev->name); + return -ENXIO; + } + + ueth->rxsize = rxsize; + ueth->rxbuf = memalign(rxsize, ARCH_DMA_MINALIGN); + if (!ueth->rxbuf) + return -ENOMEM; + + ret = usb_set_interface(udev, iface_desc->bInterfaceNumber, ifnum); + if (ret) { + debug("%s: %s: Cannot set interface: %d\n", __func__, dev->name, + ret); + return ret; + } + ueth->pusb_dev = udev; + + return 0; +} + +int usb_ether_deregister(struct ueth_data *ueth) +{ + return 0; +} + +int usb_ether_receive(struct ueth_data *ueth, int rxsize) +{ + int actual_len; + int ret; + + if (rxsize > ueth->rxsize) + return -EINVAL; + ret = usb_bulk_msg(ueth->pusb_dev, + usb_rcvbulkpipe(ueth->pusb_dev, ueth->ep_in), + ueth->rxbuf, rxsize, &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", rxsize, actual_len, ret); + if (ret) { + printf("Rx: failed to receive: %d\n", ret); + return ret; + } + if (actual_len > rxsize) { + debug("Rx: received too many bytes %d\n", actual_len); + return -ENOSPC; + } + ueth->rxlen = actual_len; + ueth->rxptr = 0; + + return actual_len ? 0 : -EAGAIN; +} + +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes) +{ + ueth->rxptr += num_bytes; + if (num_bytes < 0 || ueth->rxptr >= ueth->rxlen) + ueth->rxlen = 0; +} + +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp) +{ + if (!ueth->rxlen) + return 0; + + *ptrp = &ueth->rxbuf[ueth->rxptr]; + + return ueth->rxlen - ueth->rxptr; +} + +#else + typedef void (*usb_eth_before_probe)(void); typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); @@ -140,8 +266,8 @@ int usb_host_eth_scan(int mode) usb_max_eth_dev = 0; #ifdef CONFIG_DM_USB /* - * TODO: We should add USB_DEVICE() declarations to each USB ethernet - * driver and then most of this file can be removed. + * TODO: We should add U_BOOT_USB_DEVICE() declarations to each USB + * Ethernet driver and then most of this file can be removed. */ struct udevice *bus; struct uclass *uc; @@ -197,3 +323,4 @@ int usb_host_eth_scan(int mode) return 0; return -1; } +#endif diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index bf02221..3a0d32e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -5,20 +5,7 @@ * * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0 */ #include <common.h> #include <dm.h> @@ -321,7 +308,7 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev, struct udevice *dev = parent; if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) { - printf("ehci: Error cannot find high speed parent of usb-1 device\n"); + printf("ehci: Error cannot find high-speed parent of usb-1 device\n"); return; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index b9eabc5..0cb9fcc 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -2,29 +2,49 @@ * Copyright (c) 2007-2008, Juniper Networks, Inc. * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0 */ #include <common.h> +#include <dm.h> #include <errno.h> #include <pci.h> #include <usb.h> #include "ehci.h" +/* Information about a USB port */ +struct ehci_pci_priv { + struct ehci_ctrl ehci; +}; + +static void ehci_pci_common_init(pci_dev_t pdev, struct ehci_hccr **ret_hccr, + struct ehci_hcor **ret_hcor) +{ + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + uint32_t cmd; + + hccr = (struct ehci_hccr *)pci_map_bar(pdev, + PCI_BASE_ADDRESS_0, PCI_REGION_MEM); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + *ret_hccr = hccr; + *ret_hcor = hcor; + + /* enable busmaster */ + pci_read_config_dword(pdev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + pci_write_config_dword(pdev, PCI_COMMAND, cmd); +} + +#ifndef CONFIG_DM_USB + #ifdef CONFIG_PCI_EHCI_DEVICE static struct pci_device_id ehci_pci_ids[] = { /* Please add supported PCI EHCI controller ids here */ @@ -33,7 +53,6 @@ static struct pci_device_id ehci_pci_ids[] = { {0x12D8, 0x400F}, /* Pericom */ {0, 0} }; -#else #endif /* @@ -44,9 +63,6 @@ int ehci_hcd_init(int index, enum usb_init_type init, struct ehci_hccr **ret_hccr, struct ehci_hcor **ret_hcor) { pci_dev_t pdev; - uint32_t cmd; - struct ehci_hccr *hccr; - struct ehci_hcor *hcor; #ifdef CONFIG_PCI_EHCI_DEVICE pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); @@ -57,23 +73,8 @@ int ehci_hcd_init(int index, enum usb_init_type init, printf("EHCI host controller not found\n"); return -1; } + ehci_pci_common_init(pdev, ret_hccr, ret_hcor); - hccr = (struct ehci_hccr *)pci_map_bar(pdev, - PCI_BASE_ADDRESS_0, PCI_REGION_MEM); - hcor = (struct ehci_hcor *)((uint32_t) hccr + - HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - - debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", - (uint32_t)hccr, (uint32_t)hcor, - (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); - - *ret_hccr = hccr; - *ret_hcor = hcor; - - /* enable busmaster */ - pci_read_config_dword(pdev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_dword(pdev, PCI_COMMAND, cmd); return 0; } @@ -85,3 +86,46 @@ int ehci_hcd_stop(int index) { return 0; } +#endif /* nCONFIG_DM_USB */ + +#ifdef CONFIG_DM_USB +static int ehci_pci_probe(struct udevice *dev) +{ + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + + ehci_pci_common_init(pci_get_bdf(dev), &hccr, &hcor); + + return ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); +} + +static int ehci_pci_remove(struct udevice *dev) +{ + int ret; + + ret = ehci_deregister(dev); + if (ret) + return ret; + + return 0; +} + +U_BOOT_DRIVER(ehci_pci) = { + .name = "ehci_pci", + .id = UCLASS_USB, + .probe = ehci_pci_probe, + .remove = ehci_pci_remove, + .ops = &ehci_usb_ops, + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct ehci_pci_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; + +static struct pci_device_id ehci_pci_supported[] = { + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0) }, + {}, +}; + +U_BOOT_PCI_DEVICE(ehci_pci, ehci_pci_supported); + +#endif /* CONFIG_DM_USB */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 774282d..3379c29 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -3,20 +3,7 @@ * Copyright (c) 2008, Michael Trimarchi <trimarchimichael@yahoo.it> * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0 */ #ifndef USB_EHCI_H diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 6f33456..373e04c 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -3,19 +3,7 @@ * * Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * + * SPDX-License-Identifier: GPL-2.0 */ #include <common.h> diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index ca1b671..67dc3c4 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -3,19 +3,7 @@ * * Copyright (C) 2008 Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * + * SPDX-License-Identifier: GPL-2.0 */ #ifndef __R8A66597_H__ diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 6e86f4a..c5d1e7f 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -128,6 +128,17 @@ int usb_alloc_device(struct usb_device *udev) return ops->alloc_device(bus, udev); } +int usb_reset_root_port(struct usb_device *udev) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->reset_root_port) + return -ENOSYS; + + return ops->reset_root_port(bus, udev); +} + int usb_stop(void) { struct udevice *bus; @@ -146,6 +157,9 @@ int usb_stop(void) ret = device_remove(bus); if (ret && !err) err = ret; + ret = device_unbind_children(bus); + if (ret && !err) + err = ret; } #ifdef CONFIG_SANDBOX @@ -265,11 +279,6 @@ int usb_init(void) return usb_started ? 0 : -1; } -int usb_reset_root_port(void) -{ - return -ENOSYS; -} - static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) { struct usb_device *udev; @@ -294,14 +303,14 @@ static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) struct usb_device *usb_get_dev_index(struct udevice *bus, int index) { - struct udevice *hub; + struct udevice *dev; int devnum = index + 1; /* Addresses are allocated from 1 on USB */ - device_find_first_child(bus, &hub); - if (device_get_uclass_id(hub) == UCLASS_USB_HUB) - return find_child_devnum(hub, devnum); + device_find_first_child(bus, &dev); + if (!dev) + return NULL; - return NULL; + return find_child_devnum(dev, devnum); } int usb_post_bind(struct udevice *dev) @@ -310,35 +319,6 @@ int usb_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int usb_port_reset(struct usb_device *parent, int portnr) -{ - unsigned short portstatus; - int ret; - - debug("%s: start\n", __func__); - - if (parent) { - /* reset the port for the second time */ - assert(portnr > 0); - debug("%s: reset %d\n", __func__, portnr - 1); - ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus); - if (ret < 0) { - printf("\n Couldn't reset port %i\n", portnr); - return ret; - } - } else { - debug("%s: reset root\n", __func__); - usb_reset_root_port(); - } - - return 0; -} - -int usb_legacy_port_reset(struct usb_device *parent, int portnr) -{ - return usb_port_reset(parent, portnr); -} - int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) { struct usb_platdata *plat; @@ -511,15 +491,14 @@ error: } /** - * usb_find_child() - Find an existing device which matches our needs - * - * + * usb_find_emul_child() - Find an existing device for emulated devices */ -static int usb_find_child(struct udevice *parent, - struct usb_device_descriptor *desc, - struct usb_interface_descriptor *iface, - struct udevice **devp) +static int usb_find_emul_child(struct udevice *parent, + struct usb_device_descriptor *desc, + struct usb_interface_descriptor *iface, + struct udevice **devp) { +#ifdef CONFIG_SANDBOX struct udevice *dev; *devp = NULL; @@ -538,7 +517,7 @@ static int usb_find_child(struct udevice *parent, return 0; } } - +#endif return -ENOENT; } @@ -594,12 +573,12 @@ int usb_scan_device(struct udevice *parent, int port, debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr); parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ? dev_get_parentdata(parent) : NULL; - ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port); + ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev); debug("read_descriptor for '%s': ret=%d\n", parent->name, ret); if (ret) return ret; - ret = usb_find_child(parent, &udev->descriptor, iface, &dev); - debug("** usb_find_child returns %d\n", ret); + ret = usb_find_emul_child(parent, &udev->descriptor, iface, &dev); + debug("** usb_find_emul_child returns %d\n", ret); if (ret) { if (ret != -ENOENT) return ret; diff --git a/drivers/usb/musb-new/am35x.c b/drivers/usb/musb-new/am35x.c index 857d7eb..d158454 100644 --- a/drivers/usb/musb-new/am35x.c +++ b/drivers/usb/musb-new/am35x.c @@ -100,7 +100,11 @@ struct am35x_glue { /* * am35x_musb_enable - enable interrupts */ +#ifndef __UBOOT__ static void am35x_musb_enable(struct musb *musb) +#else +static int am35x_musb_enable(struct musb *musb) +#endif { void __iomem *reg_base = musb->ctrl_base; u32 epmask; @@ -116,6 +120,9 @@ static void am35x_musb_enable(struct musb *musb) if (is_otg_enabled(musb)) musb_writel(reg_base, CORE_INTR_SRC_SET_REG, AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT); +#ifdef __UBOOT__ + return 0; +#endif } /* diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index 242cc30..f530af4 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/drivers/usb/musb-new/musb_core.c @@ -926,10 +926,17 @@ b_host: /* * Program the HDRC to start (enable interrupts, dma, etc.). */ +#ifndef __UBOOT__ void musb_start(struct musb *musb) +#else +int musb_start(struct musb *musb) +#endif { void __iomem *regs = musb->mregs; u8 devctl = musb_readb(regs, MUSB_DEVCTL); +#ifdef __UBOOT__ + int ret; +#endif dev_dbg(musb->controller, "<== devctl %02x\n", devctl); @@ -972,8 +979,21 @@ void musb_start(struct musb *musb) if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) musb->is_active = 1; } + +#ifndef __UBOOT__ musb_platform_enable(musb); +#else + ret = musb_platform_enable(musb); + if (ret) { + musb->is_active = 0; + return ret; + } +#endif musb_writeb(regs, MUSB_DEVCTL, devctl); + +#ifdef __UBOOT__ + return 0; +#endif } diff --git a/drivers/usb/musb-new/musb_core.h b/drivers/usb/musb-new/musb_core.h index 2695742..8727f64 100644 --- a/drivers/usb/musb-new/musb_core.h +++ b/drivers/usb/musb-new/musb_core.h @@ -231,7 +231,11 @@ struct musb_platform_ops { int (*init)(struct musb *musb); int (*exit)(struct musb *musb); +#ifndef __UBOOT__ void (*enable)(struct musb *musb); +#else + int (*enable)(struct musb *musb); +#endif void (*disable)(struct musb *musb); int (*set_mode)(struct musb *musb, u8 mode); @@ -546,7 +550,11 @@ static inline void musb_configure_ep0(struct musb *musb) extern const char musb_driver_name[]; +#ifndef __UBOOT__ extern void musb_start(struct musb *musb); +#else +extern int musb_start(struct musb *musb); +#endif extern void musb_stop(struct musb *musb); extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src); @@ -564,11 +572,21 @@ static inline void musb_platform_set_vbus(struct musb *musb, int is_on) musb->ops->set_vbus(musb, is_on); } +#ifndef __UBOOT__ static inline void musb_platform_enable(struct musb *musb) { if (musb->ops->enable) musb->ops->enable(musb); } +#else +static inline int musb_platform_enable(struct musb *musb) +{ + if (!musb->ops->enable) + return 0; + + return musb->ops->enable(musb); +} +#endif static inline void musb_platform_disable(struct musb *musb) { diff --git a/drivers/usb/musb-new/musb_dsps.c b/drivers/usb/musb-new/musb_dsps.c index 17ed224..8959397 100644 --- a/drivers/usb/musb-new/musb_dsps.c +++ b/drivers/usb/musb-new/musb_dsps.c @@ -156,7 +156,11 @@ struct dsps_glue { /** * dsps_musb_enable - enable interrupts */ +#ifndef __UBOOT__ static void dsps_musb_enable(struct musb *musb) +#else +static int dsps_musb_enable(struct musb *musb) +#endif { #ifndef __UBOOT__ struct device *dev = musb->controller; @@ -181,6 +185,8 @@ static void dsps_musb_enable(struct musb *musb) if (is_otg_enabled(musb)) dsps_writel(reg_base, wrp->coreintr_set, (1 << wrp->drvvbus) << wrp->usb_shift); +#else + return 0; #endif } diff --git a/drivers/usb/musb-new/musb_gadget_ep0.c b/drivers/usb/musb-new/musb_gadget_ep0.c index 5a71501..415a9f2 100644 --- a/drivers/usb/musb-new/musb_gadget_ep0.c +++ b/drivers/usb/musb-new/musb_gadget_ep0.c @@ -43,6 +43,7 @@ #else #include <common.h> #include "linux-compat.h" +#include <asm/processor.h> #endif #include "musb_core.h" diff --git a/drivers/usb/musb-new/musb_host.c b/drivers/usb/musb-new/musb_host.c index 437309c..40b9c66 100644 --- a/drivers/usb/musb-new/musb_host.c +++ b/drivers/usb/musb-new/musb_host.c @@ -2067,7 +2067,11 @@ int musb_urb_enqueue( /* precompute addressing for external hub/tt ports */ if (musb->is_multipoint) { +#ifndef __UBOOT__ struct usb_device *parent = urb->dev->parent; +#else + struct usb_device *parent = usb_dev_get_parent(urb->dev); +#endif #ifndef __UBOOT__ if (parent != hcd->self.root_hub) { diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index d1ee5f8..9b56e90 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -13,6 +13,7 @@ #include "musb_core.h" #include "musb_host.h" #include "musb_gadget.h" +#include "musb_uboot.h" #ifdef CONFIG_MUSB_HOST struct int_queue { @@ -20,9 +21,9 @@ struct int_queue { struct urb urb; }; -static struct musb *host; -static struct usb_hcd hcd; -static enum usb_device_speed host_speed; +#ifndef CONFIG_DM_USB +struct musb_host_data musb_host; +#endif static void musb_host_complete_urb(struct urb *urb) { @@ -30,9 +31,6 @@ static void musb_host_complete_urb(struct urb *urb) urb->dev->act_len = urb->actual_length; } -static struct usb_host_endpoint hep; -static struct urb urb; - static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep, struct usb_device *dev, int endpoint_type, unsigned long pipe, void *buffer, int len, @@ -90,38 +88,40 @@ static int submit_urb(struct usb_hcd *hcd, struct urb *urb) return urb->status; } -int submit_control_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int len, struct devrequest *setup) +static int _musb_submit_control_msg(struct musb_host_data *host, + struct usb_device *dev, unsigned long pipe, + void *buffer, int len, struct devrequest *setup) { - construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, - buffer, len, setup, 0); + construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_CONTROL, + pipe, buffer, len, setup, 0); /* Fix speed for non hub-attached devices */ - if (!dev->parent) - dev->speed = host_speed; + if (!usb_dev_get_parent(dev)) + dev->speed = host->host_speed; - return submit_urb(&hcd, &urb); + return submit_urb(&host->hcd, &host->urb); } - -int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int len) +static int _musb_submit_bulk_msg(struct musb_host_data *host, + struct usb_device *dev, unsigned long pipe, void *buffer, int len) { - construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_BULK, pipe, - buffer, len, NULL, 0); - return submit_urb(&hcd, &urb); + construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_BULK, + pipe, buffer, len, NULL, 0); + return submit_urb(&host->hcd, &host->urb); } -int submit_int_msg(struct usb_device *dev, unsigned long pipe, - void *buffer, int len, int interval) +static int _musb_submit_int_msg(struct musb_host_data *host, + struct usb_device *dev, unsigned long pipe, + void *buffer, int len, int interval) { - construct_urb(&urb, &hep, dev, USB_ENDPOINT_XFER_INT, pipe, + construct_urb(&host->urb, &host->hep, dev, USB_ENDPOINT_XFER_INT, pipe, buffer, len, NULL, interval); - return submit_urb(&hcd, &urb); + return submit_urb(&host->hcd, &host->urb); } -struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, - int queuesize, int elementsize, void *buffer, int interval) +static struct int_queue *_musb_create_int_queue(struct musb_host_data *host, + struct usb_device *dev, unsigned long pipe, int queuesize, + int elementsize, void *buffer, int interval) { struct int_queue *queue; int ret, index = usb_pipein(pipe) * 16 + usb_pipeendpoint(pipe); @@ -143,7 +143,7 @@ struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, construct_urb(&queue->urb, &queue->hep, dev, USB_ENDPOINT_XFER_INT, pipe, buffer, elementsize, NULL, interval); - ret = musb_urb_enqueue(&hcd, &queue->urb, 0); + ret = musb_urb_enqueue(&host->hcd, &queue->urb, 0); if (ret < 0) { printf("Failed to enqueue URB to controller\n"); free(queue); @@ -154,25 +154,27 @@ struct int_queue *create_int_queue(struct usb_device *dev, unsigned long pipe, return queue; } -int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +static int _musb_destroy_int_queue(struct musb_host_data *host, + struct usb_device *dev, struct int_queue *queue) { int index = usb_pipein(queue->urb.pipe) * 16 + usb_pipeendpoint(queue->urb.pipe); if (queue->urb.status == -EINPROGRESS) - musb_urb_dequeue(&hcd, &queue->urb, -ETIME); + musb_urb_dequeue(&host->hcd, &queue->urb, -ETIME); dev->int_pending &= ~(1 << index); free(queue); return 0; } -void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +static void *_musb_poll_int_queue(struct musb_host_data *host, + struct usb_device *dev, struct int_queue *queue) { if (queue->urb.status != -EINPROGRESS) return NULL; /* URB has already completed in a prev. poll */ - host->isr(0, host); + host->host->isr(0, host->host); if (queue->urb.status != -EINPROGRESS) return queue->urb.transfer_buffer; /* Done */ @@ -180,9 +182,10 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) return NULL; /* URB still pending */ } -int usb_reset_root_port(void) +static int _musb_reset_root_port(struct musb_host_data *host, + struct usb_device *dev) { - void *mbase = host->mregs; + void *mbase = host->host->mregs; u8 power; power = musb_readb(mbase, MUSB_POWER); @@ -202,29 +205,33 @@ int usb_reset_root_port(void) #ifdef CONFIG_ARCH_SUNXI sunxi_usb_phy_enable_squelch_detect(0, 1); #endif - host->isr(0, host); - host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? + host->host->isr(0, host->host); + host->host_speed = (musb_readb(mbase, MUSB_POWER) & MUSB_POWER_HSMODE) ? USB_SPEED_HIGH : (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_FSDEV) ? USB_SPEED_FULL : USB_SPEED_LOW; - mdelay((host_speed == USB_SPEED_LOW) ? 200 : 50); + mdelay((host->host_speed == USB_SPEED_LOW) ? 200 : 50); return 0; } -int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +int musb_lowlevel_init(struct musb_host_data *host) { void *mbase; /* USB spec says it may take up to 1 second for a device to connect */ unsigned long timeout = get_timer(0) + 1000; + int ret; - if (!host) { + if (!host->host) { printf("MUSB host is not registered\n"); return -ENODEV; } - musb_start(host); - mbase = host->mregs; + ret = musb_start(host->host); + if (ret) + return ret; + + mbase = host->host->mregs; do { if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) break; @@ -232,23 +239,135 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) if (get_timer(0) >= timeout) return -ENODEV; - usb_reset_root_port(); - host->is_active = 1; - hcd.hcd_priv = host; + _musb_reset_root_port(host, NULL); + host->host->is_active = 1; + host->hcd.hcd_priv = host->host; return 0; } +#ifndef CONFIG_DM_USB int usb_lowlevel_stop(int index) { - if (!host) { + if (!musb_host.host) { printf("MUSB host is not registered\n"); return -ENODEV; } - musb_stop(host); + musb_stop(musb_host.host); return 0; } + +int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length) +{ + return _musb_submit_bulk_msg(&musb_host, dev, pipe, buffer, length); +} + +int submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, struct devrequest *setup) +{ + return _musb_submit_control_msg(&musb_host, dev, pipe, buffer, length, setup); +} + +int submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, int interval) +{ + return _musb_submit_int_msg(&musb_host, dev, pipe, buffer, length, interval); +} + +struct int_queue *create_int_queue(struct usb_device *dev, + unsigned long pipe, int queuesize, int elementsize, + void *buffer, int interval) +{ + return _musb_create_int_queue(&musb_host, dev, pipe, queuesize, elementsize, + buffer, interval); +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _musb_poll_int_queue(&musb_host, dev, queue); +} + +int destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ + return _musb_destroy_int_queue(&musb_host, dev, queue); +} + +int usb_reset_root_port(struct usb_device *dev) +{ + return _musb_reset_root_port(&musb_host, dev); +} + +int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) +{ + return musb_lowlevel_init(&musb_host); +} +#endif /* !CONFIG_DM_USB */ + +#ifdef CONFIG_DM_USB +static int musb_submit_control_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + struct devrequest *setup) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_submit_control_msg(host, udev, pipe, buffer, length, setup); +} + +static int musb_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_submit_bulk_msg(host, udev, pipe, buffer, length); +} + +static int musb_submit_int_msg(struct udevice *dev, struct usb_device *udev, + unsigned long pipe, void *buffer, int length, + int interval) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_submit_int_msg(host, udev, pipe, buffer, length, interval); +} + +static struct int_queue *musb_create_int_queue(struct udevice *dev, + struct usb_device *udev, unsigned long pipe, int queuesize, + int elementsize, void *buffer, int interval) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_create_int_queue(host, udev, pipe, queuesize, elementsize, + buffer, interval); +} + +static void *musb_poll_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_poll_int_queue(host, udev, queue); +} + +static int musb_destroy_int_queue(struct udevice *dev, struct usb_device *udev, + struct int_queue *queue) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_destroy_int_queue(host, udev, queue); +} + +static int musb_reset_root_port(struct udevice *dev, struct usb_device *udev) +{ + struct musb_host_data *host = dev_get_priv(dev); + return _musb_reset_root_port(host, udev); +} + +struct dm_usb_ops musb_usb_ops = { + .control = musb_submit_control_msg, + .bulk = musb_submit_bulk_msg, + .interrupt = musb_submit_int_msg, + .create_int_queue = musb_create_int_queue, + .poll_int_queue = musb_poll_int_queue, + .destroy_int_queue = musb_destroy_int_queue, + .reset_root_port = musb_reset_root_port, +}; +#endif /* CONFIG_DM_USB */ #endif /* CONFIG_MUSB_HOST */ #ifdef CONFIG_MUSB_GADGET @@ -309,9 +428,9 @@ int musb_register(struct musb_hdrc_platform_data *plat, void *bdata, struct musb **musbp; switch (plat->mode) { -#ifdef CONFIG_MUSB_HOST +#if defined(CONFIG_MUSB_HOST) && !defined(CONFIG_DM_USB) case MUSB_HOST: - musbp = &host; + musbp = &musb_host.host; break; #endif #ifdef CONFIG_MUSB_GADGET diff --git a/drivers/usb/musb-new/musb_uboot.h b/drivers/usb/musb-new/musb_uboot.h new file mode 100644 index 0000000..6312cd2 --- /dev/null +++ b/drivers/usb/musb-new/musb_uboot.h @@ -0,0 +1,28 @@ +/* + * MUSB OTG driver u-boot specific functions + * + * Copyright © 2015 Hans de Goede <hdegoede@redhat.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef __MUSB_UBOOT_H__ +#define __MUSB_UBOOT_H__ + +#include <usb.h> +#include "linux-compat.h" +#include "usb-compat.h" +#include "musb_core.h" + +struct musb_host_data { + struct musb *host; + struct usb_hcd hcd; + enum usb_device_speed host_speed; + struct usb_host_endpoint hep; + struct urb urb; +}; + +extern struct dm_usb_ops musb_usb_ops; + +int musb_lowlevel_init(struct musb_host_data *host); + +#endif diff --git a/drivers/usb/musb-new/omap2430.c b/drivers/usb/musb-new/omap2430.c index 31a280e..77273a4 100644 --- a/drivers/usb/musb-new/omap2430.c +++ b/drivers/usb/musb-new/omap2430.c @@ -400,7 +400,11 @@ err1: return status; } +#ifndef __UBOOT__ static void omap2430_musb_enable(struct musb *musb) +#else +static int omap2430_musb_enable(struct musb *musb) +#endif { #ifndef __UBOOT__ u8 devctl; @@ -445,6 +449,7 @@ static void omap2430_musb_enable(struct musb *musb) __PRETTY_FUNCTION__); } #endif + return 0; #endif } diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index 052e065..c123d61 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -199,12 +199,12 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) /* musb_core does not call enable / disable in a balanced manner <sigh> */ static bool enabled = false; -static void sunxi_musb_enable(struct musb *musb) +static int sunxi_musb_enable(struct musb *musb) { pr_debug("%s():\n", __func__); if (enabled) - return; + return 0; /* select PIO mode */ musb_writeb(musb->mregs, USBC_REG_o_VEND0, 0); @@ -215,6 +215,7 @@ static void sunxi_musb_enable(struct musb *musb) USBC_ForceVbusValidToHigh(musb->mregs); enabled = true; + return 0; } static void sunxi_musb_disable(struct musb *musb) diff --git a/drivers/usb/musb-new/usb-compat.h b/drivers/usb/musb-new/usb-compat.h index 50bad37..53fe4ff 100644 --- a/drivers/usb/musb-new/usb-compat.h +++ b/drivers/usb/musb-new/usb-compat.h @@ -1,6 +1,7 @@ #ifndef __USB_COMPAT_H__ #define __USB_COMPAT_H__ +#include <dm.h> #include "usb.h" struct usb_hcd { @@ -66,6 +67,68 @@ static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, return 0; } +#ifdef CONFIG_DM_USB +static inline u16 find_tt(struct usb_device *udev) +{ + struct udevice *parent; + struct usb_device *uparent, *ttdev; + + /* + * When called from usb-uclass.c: usb_scan_device() udev->dev points + * to the parent udevice, not the actual udevice belonging to the + * udev as the device is not instantiated yet. So when searching + * for the first usb-2 parent start with udev->dev not + * udev->dev->parent . + */ + ttdev = udev; + parent = udev->dev; + uparent = dev_get_parentdata(parent); + + while (uparent->speed != USB_SPEED_HIGH) { + struct udevice *dev = parent; + + if (device_get_uclass_id(dev->parent) != UCLASS_USB_HUB) { + printf("musb: Error cannot find high speed parent of usb-1 device\n"); + return 0; + } + + ttdev = dev_get_parentdata(dev); + parent = dev->parent; + uparent = dev_get_parentdata(parent); + } + + return (uparent->devnum << 8) | (ttdev->portnr - 1); +} + +static inline struct usb_device *usb_dev_get_parent(struct usb_device *udev) +{ + struct udevice *parent = udev->dev->parent; + + /* + * When called from usb-uclass.c: usb_scan_device() udev->dev points + * to the parent udevice, not the actual udevice belonging to the + * udev as the device is not instantiated yet. + * + * If dev is an usb-bus, then we are called from usb_scan_device() for + * an usb-device plugged directly into the root port, return NULL. + */ + if (device_get_uclass_id(udev->dev) == UCLASS_USB) + return NULL; + + /* + * If these 2 are not the same we are being called from + * usb_scan_device() and udev itself is the parent. + */ + if (dev_get_parentdata(udev->dev) != udev) + return udev; + + /* We are being called normally, use the parent pointer */ + if (device_get_uclass_id(parent) == UCLASS_USB_HUB) + return dev_get_parentdata(parent); + + return NULL; +} +#else static inline u16 find_tt(struct usb_device *dev) { u8 chid; @@ -86,4 +149,11 @@ static inline u16 find_tt(struct usb_device *dev) return (hub << 8) | chid; } + +static inline struct usb_device *usb_dev_get_parent(struct usb_device *dev) +{ + return dev->parent; +} +#endif + #endif /* __USB_COMPAT_H__ */ diff --git a/dts/Kconfig b/dts/Kconfig index 957f5c7..09cfefb 100644 --- a/dts/Kconfig +++ b/dts/Kconfig @@ -56,4 +56,16 @@ config DEFAULT_DEVICE_TREE It can be overridden from the command line: $ make DEVICE_TREE=<device-tree-name> +config OF_SPL_REMOVE_PROPS + string "List of device tree properties to drop for SPL" + depends on OF_CONTROL && SPL + default "pinctrl-0 pinctrl-names clocks clock-names interrupt-parent" + help + Since SPL normally runs in a reduced memory space, the device tree + is cut down to only what is needed to load and start U-Boot. Only + nodes marked with the property "u-boot,dm-pre-reloc" will be + included. In addition, some properties are not used by U-Boot and + can be discarded. This option defines the list of properties to + discard. + endmenu diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index db0550b..7ef3e25 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -116,5 +116,6 @@ typedef struct global_data { #define GD_FLG_ENV_READY 0x00080 /* Env. imported into hash table */ #define GD_FLG_SERIAL_READY 0x00100 /* Pre-reloc serial console ready */ #define GD_FLG_FULL_MALLOC_INIT 0x00200 /* Full malloc() is ready */ +#define GD_FLG_SPL_INIT 0x00400 /* spl_init() has been called */ #endif /* __ASM_GENERIC_GBL_DATA_H */ diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index de91e57..0af599f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -322,6 +322,19 @@ struct gpio_dev_priv { const char *gpio_get_bank_info(struct udevice *dev, int *offset_count); /** + * dm_gpio_lookup_name() - Look up a named GPIO and return its description + * + * The name of a GPIO is typically its bank name followed by a number from 0. + * For example A0 is the first GPIO in bank A. Each bank is a separate driver + * model device. + * + * @name: Name to look up + * @desc: Returns description, on success + * @return 0 if OK, -ve on error + */ +int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc); + +/** * gpio_lookup_name - Look up a GPIO name and return its details * * This is used to convert a named GPIO into a device, offset and GPIO @@ -421,6 +434,18 @@ int gpio_request_list_by_name(struct udevice *dev, const char *list_name, int flags); /** + * dm_gpio_request() - manually request a GPIO + * + * Note: This function should only be used for testing / debugging. Instead. + * use gpio_request_by_name() to pull GPIOs from the device tree. + * + * @desc: GPIO description of GPIO to request (see dm_gpio_lookup_name()) + * @label: Label to attach to the GPIO while claimed + * @return 0 if OK, -ve on error + */ +int dm_gpio_request(struct gpio_desc *desc, const char *label); + +/** * gpio_get_list_count() - Returns the number of GPIOs in a list * * Counts the GPIOs in a list. See gpio_request_by_name() for additional diff --git a/include/clk.h b/include/clk.h index df4570c..254ad2b 100644 --- a/include/clk.h +++ b/include/clk.h @@ -1,6 +1,86 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + #ifndef _CLK_H_ #define _CLK_H_ int soc_clk_dump(void); +struct clk_ops { + /** + * get_rate() - Get current clock rate + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, or -ve error code + */ + ulong (*get_rate)(struct udevice *dev); + + /** + * set_rate() - Set current clock rate + * + * @dev: Device to adjust + * @rate: New clock rate in Hz + * @return new rate, or -ve error code + */ + ulong (*set_rate)(struct udevice *dev, ulong rate); + + /** + * clk_set_periph_rate() - Set clock rate for a peripheral + * + * @dev: Device to adjust (UCLASS_CLK) + * @rate: New clock rate in Hz + * @return new clock rate in Hz, or -ve error code + */ + ulong (*get_periph_rate)(struct udevice *dev, int periph); + + /** + * clk_set_periph_rate() - Set current clock rate for a peripheral + * + * @dev: Device to update (UCLASS_CLK) + * @periph: Peripheral ID to cupdate + * @return new clock rate in Hz, or -ve error code + */ + ulong (*set_periph_rate)(struct udevice *dev, int periph, ulong rate); +}; + +#define clk_get_ops(dev) ((struct clk_ops *)(dev)->driver->ops) + +/** + * clk_get_rate() - Get current clock rate + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, or -ve error code + */ +ulong clk_get_rate(struct udevice *dev); + +/** + * set_rate() - Set current clock rate + * + * @dev: Device to adjust + * @rate: New clock rate in Hz + * @return new rate, or -ve error code + */ +ulong clk_set_rate(struct udevice *dev, ulong rate); + +/** + * clk_get_periph_rate() - Get current clock rate for a peripheral + * + * @dev: Device to check (UCLASS_CLK) + * @return clock rate in Hz, -ve error code + */ +ulong clk_get_periph_rate(struct udevice *dev, int periph); + +/** + * clk_set_periph_rate() - Set current clock rate for a peripheral + * + * @dev: Device to update (UCLASS_CLK) + * @periph: Peripheral ID to cupdate + * @return new clock rate in Hz, or -ve error code + */ +ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate); + #endif /* _CLK_H_ */ diff --git a/include/common.h b/include/common.h index 8f4b2ec..4566bd1 100644 --- a/include/common.h +++ b/include/common.h @@ -1010,6 +1010,17 @@ int cpu_release(int nr, int argc, char * const argv[]); #define DEFINE_CACHE_ALIGN_BUFFER(type, name, size) \ DEFINE_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN) +/* + * check_member() - Check the offset of a structure member + * + * @structure: Name of structure (e.g. global_data) + * @member: Name of member (e.g. baudrate) + * @offset: Expected offset in bytes + */ +#define check_member(structure, member, offset) _Static_assert( \ + offsetof(struct structure, member) == offset, \ + "`struct " #structure "` offset for `" #member "` is not " #offset) + /* Pull in stuff for the build system */ #ifdef DO_DEPS_ONLY # include <environment.h> diff --git a/include/configs/db-88f6820-gp.h b/include/configs/db-88f6820-gp.h index a429107..73b3236 100644 --- a/include/configs/db-88f6820-gp.h +++ b/include/configs/db-88f6820-gp.h @@ -11,6 +11,7 @@ * High Level Configuration Options (easy to change) */ #define CONFIG_ARMADA_XP /* SOC Family Name */ +#define CONFIG_ARMADA_38X #define CONFIG_DB_88F6820_GP /* Board target name for DDR training */ #define CONFIG_SYS_L2_PL310 @@ -108,6 +109,40 @@ "fdt_high=0x10000000\0" \ "initrd_high=0x10000000\0" +/* SPL */ +/* Defines for SPL */ +#define CONFIG_SPL_FRAMEWORK +#define CONFIG_SPL_SIZE (140 << 10) +#define CONFIG_SPL_TEXT_BASE 0x40000030 +#define CONFIG_SPL_MAX_SIZE (CONFIG_SPL_SIZE - 0x0030) + +#define CONFIG_SPL_BSS_START_ADDR (0x40000000 + CONFIG_SPL_SIZE) +#define CONFIG_SPL_BSS_MAX_SIZE (16 << 10) + +#define CONFIG_SYS_SPL_MALLOC_START (CONFIG_SPL_BSS_START_ADDR + \ + CONFIG_SPL_BSS_MAX_SIZE) +#define CONFIG_SYS_SPL_MALLOC_SIZE (16 << 10) + +#define CONFIG_SPL_STACK (0x40000000 + ((192 - 16) << 10)) +#define CONFIG_SPL_BOOTROM_SAVE (CONFIG_SPL_STACK + 4) + +#define CONFIG_SPL_LIBCOMMON_SUPPORT +#define CONFIG_SPL_LIBGENERIC_SUPPORT +#define CONFIG_SPL_SERIAL_SUPPORT +#define CONFIG_SPL_I2C_SUPPORT + +/* SPL related SPI defines */ +#define CONFIG_SPL_SPI_SUPPORT +#define CONFIG_SPL_SPI_FLASH_SUPPORT +#define CONFIG_SPL_SPI_LOAD +#define CONFIG_SPL_SPI_BUS 0 +#define CONFIG_SPL_SPI_CS 0 +#define CONFIG_SYS_SPI_U_BOOT_OFFS 0x20000 + +/* Enable DDR support in SPL (DDR3 training from Marvell bin_hdr) */ +#define CONFIG_SYS_MVEBU_DDR_A38X +#define CONFIG_DDR3 + /* * mv-common.h should be defined after CMD configs since it used them * to enable certain macros diff --git a/include/configs/db-mv784mp-gp.h b/include/configs/db-mv784mp-gp.h index c33a588..41e6fdc 100644 --- a/include/configs/db-mv784mp-gp.h +++ b/include/configs/db-mv784mp-gp.h @@ -27,6 +27,7 @@ #define CONFIG_CMD_DHCP #define CONFIG_CMD_ENV #define CONFIG_CMD_I2C +#define CONFIG_CMD_IDE #define CONFIG_CMD_PING #define CONFIG_CMD_SF #define CONFIG_CMD_SPI @@ -60,6 +61,34 @@ #define CONFIG_SYS_CONSOLE_INFO_QUIET /* don't print console @ startup */ #define CONFIG_SYS_ALT_MEMTEST +/* SATA support */ +#ifdef CONFIG_CMD_IDE +#define __io +#define CONFIG_IDE_PREINIT +#define CONFIG_MVSATA_IDE + +/* Needs byte-swapping for ATA data register */ +#define CONFIG_IDE_SWAP_IO + +#define CONFIG_SYS_ATA_REG_OFFSET 0x0100 /* Offset for register access */ +#define CONFIG_SYS_ATA_DATA_OFFSET 0x0100 /* Offset for data I/O */ +#define CONFIG_SYS_ATA_ALT_OFFSET 0x0100 + +/* Each 8-bit ATA register is aligned to a 4-bytes address */ +#define CONFIG_SYS_ATA_STRIDE 4 + +/* CONFIG_CMD_IDE requires some #defines for ATA registers */ +#define CONFIG_SYS_IDE_MAXBUS 2 +#define CONFIG_SYS_IDE_MAXDEVICE CONFIG_SYS_IDE_MAXBUS + +/* ATA registers base is at SATA controller base */ +#define CONFIG_SYS_ATA_BASE_ADDR MVEBU_AXP_SATA_BASE +#define CONFIG_SYS_ATA_IDE0_OFFSET 0x2000 +#define CONFIG_SYS_ATA_IDE1_OFFSET 0x4000 + +#define CONFIG_DOS_PARTITION +#endif /* CONFIG_CMD_IDE */ + /* * mv-common.h should be defined after CMD configs since it used them * to enable certain macros @@ -109,7 +138,7 @@ #define CONFIG_SYS_SPI_U_BOOT_OFFS 0x20000 /* Enable DDR support in SPL (DDR3 training from Marvell bin_hdr) */ -#define CONFIG_SYS_MVEBU_DDR +#define CONFIG_SYS_MVEBU_DDR_AXP #define CONFIG_SPD_EEPROM 0x4e #endif /* _CONFIG_DB_MV7846MP_GP_H */ diff --git a/include/configs/maxbcm.h b/include/configs/maxbcm.h index 4826044..0fb117f 100644 --- a/include/configs/maxbcm.h +++ b/include/configs/maxbcm.h @@ -108,7 +108,7 @@ #define CONFIG_SYS_SPI_U_BOOT_OFFS 0x20000 /* Enable DDR support in SPL (DDR3 training from Marvell bin_hdr) */ -#define CONFIG_SYS_MVEBU_DDR +#define CONFIG_SYS_MVEBU_DDR_AXP #define CONFIG_DDR_FIXED_SIZE (1 << 20) /* 1GiB */ #endif /* _CONFIG_DB_MV7846MP_GP_H */ diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h index af36ac5..4781e79 100644 --- a/include/configs/minnowmax.h +++ b/include/configs/minnowmax.h @@ -60,9 +60,6 @@ #define CONFIG_FIT_SIGNATURE #define CONFIG_RSA -/* Avoid a warning in the Realtek Ethernet driver */ -#define CONFIG_SYS_CACHELINE_SIZE 16 - #define CONFIG_ENV_SECT_SIZE 0x1000 #define CONFIG_ENV_OFFSET 0x007fe000 diff --git a/include/debug_uart.h b/include/debug_uart.h index f56797b..a75e377 100644 --- a/include/debug_uart.h +++ b/include/debug_uart.h @@ -10,8 +10,6 @@ #ifndef _DEBUG_UART_H #define _DEBUG_UART_H -#include <linux/linkage.h> - /* * The debug UART is intended for use very early in U-Boot to debug problems * when an ICE or other debug mechanism is not available. @@ -64,46 +62,46 @@ void debug_uart_init(void); * * @ch: Character to output */ -asmlinkage void printch(int ch); +void printch(int ch); /** * printascii() - Output an ASCII string to the debug UART * * @str: String to output */ -asmlinkage void printascii(const char *str); +void printascii(const char *str); /** * printhex2() - Output a 2-digit hex value * * @value: Value to output */ -asmlinkage void printhex2(uint value); +void printhex2(uint value); /** * printhex4() - Output a 4-digit hex value * * @value: Value to output */ -asmlinkage void printhex4(uint value); +void printhex4(uint value); /** * printhex8() - Output a 8-digit hex value * * @value: Value to output */ -asmlinkage void printhex8(uint value); +void printhex8(uint value); /* * Now define some functions - this should be inserted into the serial driver */ #define DEBUG_UART_FUNCS \ - asmlinkage void printch(int ch) \ + void printch(int ch) \ { \ _debug_uart_putc(ch); \ } \ \ - asmlinkage void printascii(const char *str) \ + void printascii(const char *str) \ { \ while (*str) \ _debug_uart_putc(*str++); \ @@ -121,17 +119,17 @@ asmlinkage void printhex8(uint value); printhex1(value >> (4 * digits)); \ } \ \ - asmlinkage void printhex2(uint value) \ + void printhex2(uint value) \ { \ printhex(value, 2); \ } \ \ - asmlinkage void printhex4(uint value) \ + void printhex4(uint value) \ { \ printhex(value, 4); \ } \ \ - asmlinkage void printhex8(uint value) \ + void printhex8(uint value) \ { \ printhex(value, 8); \ } diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h index 687462b..402304f 100644 --- a/include/dm/device-internal.h +++ b/include/dm/device-internal.h @@ -107,6 +107,32 @@ int device_unbind(struct udevice *dev); static inline int device_unbind(struct udevice *dev) { return 0; } #endif +/** + * device_remove_children() - Stop all device's children + * @dev: The device whose children are to be removed + * @return 0 on success, -ve on error + */ +#ifdef CONFIG_DM_DEVICE_REMOVE +int device_remove_children(struct udevice *dev); +#else +static inline int device_remove_children(struct udevice *dev) { return 0; } +#endif + +/** + * device_unbind_children() - Unbind all device's children from the device + * + * On error, the function continues to unbind all children, and reports the + * first error. + * + * @dev: The device that is to be stripped of its children + * @return 0 on success, -ve on error + */ +#ifdef CONFIG_DM_DEVICE_REMOVE +int device_unbind_children(struct udevice *dev); +#else +static inline int device_unbind_children(struct udevice *dev) { return 0; } +#endif + #ifdef CONFIG_DM_DEVICE_REMOVE void device_free(struct udevice *dev); #else diff --git a/include/dm/device.h b/include/dm/device.h index 18296bb..9fa0048 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -386,10 +386,24 @@ int device_find_child_by_of_offset(struct udevice *parent, int of_offset, * @devp: Returns pointer to device if found, otherwise this is set to NULL * @return 0 if OK, -ve on error */ -int device_get_child_by_of_offset(struct udevice *parent, int seq, +int device_get_child_by_of_offset(struct udevice *parent, int of_offset, struct udevice **devp); /** + * device_get_global_by_of_offset() - Get a device based on FDT offset + * + * Locates a device by its device tree offset, searching globally throughout + * the all driver model devices. + * + * The device is probed to activate it ready for use. + * + * @of_offset: Device tree offset to find + * @devp: Returns pointer to device if found, otherwise this is set to NULL + * @return 0 if OK, -ve on error + */ +int device_get_global_by_of_offset(int of_offset, struct udevice **devp); + +/** * device_find_first_child() - Find the first child of a device * * @parent: Parent device to search diff --git a/include/dm/platdata.h b/include/dm/platdata.h index fbc8a6b..6f4f001 100644 --- a/include/dm/platdata.h +++ b/include/dm/platdata.h @@ -16,6 +16,10 @@ /** * struct driver_info - Information required to instantiate a device * + * NOTE: Avoid using this except in extreme circumstances, where device tree + * is not feasible (e.g. serial driver in SPL where <8KB of SRAM is + * available). U-Boot's driver model uses device tree for configuration. + * * @name: Driver name * @platdata: Driver-specific platform data */ @@ -24,6 +28,11 @@ struct driver_info { const void *platdata; }; +/** + * NOTE: Avoid using these except in extreme circumstances, where device tree + * is not feasible (e.g. serial driver in SPL where <8KB of SRAM is + * available). U-Boot's driver model uses device tree for configuration. + */ #define U_BOOT_DEVICE(__name) \ ll_entry_declare(struct driver_info, __name, driver_info) diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c7310d7..bc057d7 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -25,27 +25,33 @@ enum uclass_id { UCLASS_SIMPLE_BUS, /* bus with child devices */ /* U-Boot uclasses start here - in alphabetical order */ + UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ UCLASS_DISPLAY_PORT, /* Display port video */ + UCLASS_RAM, /* RAM controller */ UCLASS_ETH, /* Ethernet device */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ UCLASS_I2C, /* I2C bus */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ + UCLASS_LED, /* Light-emitting diode (LED) */ UCLASS_LPC, /* x86 'low pin count' interface */ UCLASS_MASS_STORAGE, /* Mass storage device */ + UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ UCLASS_PCH, /* x86 platform controller hub */ UCLASS_PCI, /* PCI bus */ UCLASS_PCI_GENERIC, /* Generic PCI bus device */ UCLASS_PMIC, /* PMIC I/O device */ UCLASS_REGULATOR, /* Regulator device */ + UCLASS_RESET, /* Reset device */ UCLASS_RTC, /* Real time clock device */ UCLASS_SERIAL, /* Serial UART */ UCLASS_SPI, /* SPI bus */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ + UCLASS_SYSCON, /* System configuration device */ UCLASS_THERMAL, /* Thermal sensor */ UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ diff --git a/include/dm/util.h b/include/dm/util.h index 0cec17b..7dbed67 100644 --- a/include/dm/util.h +++ b/include/dm/util.h @@ -33,4 +33,10 @@ struct list_head; */ int list_count_items(struct list_head *head); +/* Dump out a tree of all devices */ +void dm_dump_all(void); + +/* Dump out a list of uclasses and their devices */ +void dm_dump_uclass(void); + #endif diff --git a/include/dwmmc.h b/include/dwmmc.h index 86a5491..7a7555a 100644 --- a/include/dwmmc.h +++ b/include/dwmmc.h @@ -129,8 +129,24 @@ /* quirks */ #define DWMCI_QUIRK_DISABLE_SMU (1 << 0) +/** + * struct dwmci_host - Information about a designware MMC host + * + * @name: Device name + * @ioaddr: Base I/O address of controller + * @quirks: Quick flags - see DWMCI_QUIRK_... + * @caps: Capabilities - see MMC_MODE_... + * @bus_hz: Bus speed in Hz, if @get_mmc_clk() is NULL + * @div: Arbitrary clock divider value for use by controller + * @dev_index: Arbitrary device index for use by controller + * @dev_id: Arbitrary device ID for use by controller + * @buswidth: Bus width in bits (8 or 4) + * @fifoth_val: Value for FIFOTH register (or 0 to leave unset) + * @mmc: Pointer to generic MMC structure for this device + * @priv: Private pointer for use by controller + */ struct dwmci_host { - char *name; + const char *name; void *ioaddr; unsigned int quirks; unsigned int caps; diff --git a/include/image.h b/include/image.h index b6eb57e..63c3d37 100644 --- a/include/image.h +++ b/include/image.h @@ -246,6 +246,8 @@ struct lmb; #define IH_TYPE_LPC32XXIMAGE 21 /* x86 setup.bin Image */ #define IH_TYPE_LOADABLE 22 /* A list of typeless images */ +#define IH_TYPE_COUNT 23 /* Number of image types */ + /* * Compression Types */ @@ -411,6 +413,15 @@ char *get_table_entry_name(const table_entry_t *table, char *msg, int id); const char *genimg_get_os_name(uint8_t os); const char *genimg_get_arch_name(uint8_t arch); const char *genimg_get_type_name(uint8_t type); + +/** + * genimg_get_type_short_name() - get the short name for an image type + * + * @param type Image type (IH_TYPE_...) + * @return image short name, or "unknown" if unknown + */ +const char *genimg_get_type_short_name(uint8_t type); + const char *genimg_get_comp_name(uint8_t comp); int genimg_get_os_id(const char *name); int genimg_get_arch_id(const char *name); diff --git a/include/led.h b/include/led.h new file mode 100644 index 0000000..b929d0c --- /dev/null +++ b/include/led.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __LED_H +#define __LED_H + +/** + * struct led_uclass_plat - Platform data the uclass stores about each device + * + * @label: LED label + */ +struct led_uclass_plat { + const char *label; +}; + +struct led_ops { + /** + * set_on() - set the state of an LED + * + * @dev: LED device to change + * @on: 1 to turn the LED on, 0 to turn it off + * @return 0 if OK, -ve on error + */ + int (*set_on)(struct udevice *dev, int on); +}; + +#define led_get_ops(dev) ((struct led_ops *)(dev)->driver->ops) + +/** + * led_get_by_label() - Find an LED device by label + * + * @label: LED label to look up + * @devp: Returns the associated device, if found + * @return 0 if found, -ENODEV if not found, other -ve on error + */ +int led_get_by_label(const char *label, struct udevice **devp); + +/** + * led_set_on() - set the state of an LED + * + * @dev: LED device to change + * @on: 1 to turn the LED on, 0 to turn it off + * @return 0 if OK, -ve on error + */ +int led_set_on(struct udevice *dev, int on); + +#endif diff --git a/include/libfdt.h b/include/libfdt.h index 421d64f..e48c21a 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -121,7 +121,12 @@ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells * or similar property with a bad format or value */ -#define FDT_ERR_MAX 14 +#define FDT_ERR_TOODEEP 15 + /* FDT_ERR_TOODEEP: The depth of a node has exceeded the internal + * libfdt limit. This can happen if you have more than + * FDT_MAX_DEPTH nested nodes. */ + +#define FDT_ERR_MAX 15 /**********************************************************************/ /* Low-level functions (you probably don't need these) */ @@ -1646,11 +1651,99 @@ int fdt_del_node(void *fdt, int nodeoffset); const char *fdt_strerror(int errval); +/** + * fdt_remove_unused_strings() - Remove any unused strings from an FDT + * + * This creates a new device tree in @new with unused strings removed. The + * called can then use fdt_pack() to minimise the space consumed. + * + * @old: Old device tree blog + * @new: Place to put new device tree blob, which must be as large as + * @old + * @return + * 0, on success + * -FDT_ERR_BADOFFSET, corrupt device tree + * -FDT_ERR_NOSPACE, out of space, which should not happen unless there + * is something very wrong with the device tree input + */ +int fdt_remove_unused_strings(const void *old, void *new); + struct fdt_region { int offset; int size; }; +/* + * Flags for fdt_find_regions() + * + * Add a region for the string table (always the last region) + */ +#define FDT_REG_ADD_STRING_TAB (1 << 0) + +/* + * Add all supernodes of a matching node/property, useful for creating a + * valid subset tree + */ +#define FDT_REG_SUPERNODES (1 << 1) + +/* Add the FDT_BEGIN_NODE tags of subnodes, including their names */ +#define FDT_REG_DIRECT_SUBNODES (1 << 2) + +/* Add all subnodes of a matching node */ +#define FDT_REG_ALL_SUBNODES (1 << 3) + +/* Add a region for the mem_rsvmap table (always the first region) */ +#define FDT_REG_ADD_MEM_RSVMAP (1 << 4) + +/* Indicates what an fdt part is (node, property, value) */ +#define FDT_IS_NODE (1 << 0) +#define FDT_IS_PROP (1 << 1) +#define FDT_IS_VALUE (1 << 2) /* not supported */ +#define FDT_IS_COMPAT (1 << 3) /* used internally */ +#define FDT_NODE_HAS_PROP (1 << 4) /* node contains prop */ + +#define FDT_ANY_GLOBAL (FDT_IS_NODE | FDT_IS_PROP | FDT_IS_VALUE | \ + FDT_IS_COMPAT) +#define FDT_IS_ANY 0x1f /* all the above */ + +/* We set a reasonable limit on the number of nested nodes */ +#define FDT_MAX_DEPTH 32 + +/* Decribes what we want to include from the current tag */ +enum want_t { + WANT_NOTHING, + WANT_NODES_ONLY, /* No properties */ + WANT_NODES_AND_PROPS, /* Everything for one level */ + WANT_ALL_NODES_AND_PROPS /* Everything for all levels */ +}; + +/* Keeps track of the state at parent nodes */ +struct fdt_subnode_stack { + int offset; /* Offset of node */ + enum want_t want; /* The 'want' value here */ + int included; /* 1 if we included this node, 0 if not */ +}; + +struct fdt_region_ptrs { + int depth; /* Current tree depth */ + int done; /* What we have completed scanning */ + enum want_t want; /* What we are currently including */ + char *end; /* Pointer to end of full node path */ + int nextoffset; /* Next node offset to check */ +}; + +/* The state of our finding algortihm */ +struct fdt_region_state { + struct fdt_subnode_stack stack[FDT_MAX_DEPTH]; /* node stack */ + struct fdt_region *region; /* Contains list of regions found */ + int count; /* Numnber of regions found */ + const void *fdt; /* FDT blob */ + int max_regions; /* Maximum regions to find */ + int can_merge; /* 1 if we can merge with previous region */ + int start; /* Start position of current region */ + struct fdt_region_ptrs ptrs; /* Pointers for what we are up to */ +}; + /** * fdt_find_regions() - find regions in device tree * @@ -1710,4 +1803,165 @@ int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, struct fdt_region region[], int max_regions, char *path, int path_len, int add_string_tab); +/** + * fdt_first_region() - find regions in device tree + * + * Given a nodes and properties to include and properties to exclude, find + * the regions of the device tree which describe those included parts. + * + * The use for this function is twofold. Firstly it provides a convenient + * way of performing a structure-aware grep of the tree. For example it is + * possible to grep for a node and get all the properties associated with + * that node. Trees can be subsetted easily, by specifying the nodes that + * are required, and then writing out the regions returned by this function. + * This is useful for small resource-constrained systems, such as boot + * loaders, which want to use an FDT but do not need to know about all of + * it. + * + * Secondly it makes it easy to hash parts of the tree and detect changes. + * The intent is to get a list of regions which will be invariant provided + * those parts are invariant. For example, if you request a list of regions + * for all nodes but exclude the property "data", then you will get the + * same region contents regardless of any change to "data" properties. + * + * This function can be used to produce a byte-stream to send to a hashing + * function to verify that critical parts of the FDT have not changed. + * Note that semantically null changes in order could still cause false + * hash misses. Such reordering might happen if the tree is regenerated + * from source, and nodes are reordered (the bytes-stream will be emitted + * in a different order and mnay hash functions will detect this). However + * if an existing tree is modified using libfdt functions, such as + * fdt_add_subnode() and fdt_setprop(), then this problem is avoided. + * + * The nodes/properties to include/exclude are defined by a function + * provided by the caller. This function is called for each node and + * property, and must return: + * + * 0 - to exclude this part + * 1 - to include this part + * -1 - for FDT_IS_PROP only: no information is available, so include + * if its containing node is included + * + * The last case is only used to deal with properties. Often a property is + * included if its containing node is included - this is the case where + * -1 is returned.. However if the property is specifically required to be + * included/excluded, then 0 or 1 can be returned. Note that including a + * property when the FDT_REG_SUPERNODES flag is given will force its + * containing node to be included since it is not valid to have a property + * that is not in a node. + * + * Using the information provided, the inclusion of a node can be controlled + * either by a node name or its compatible string, or any other property + * that the function can determine. + * + * As an example, including node "/" means to include the root node and all + * root properties. A flag provides a way of also including supernodes (of + * which there is none for the root node), and another flag includes + * immediate subnodes, so in this case we would get the FDT_BEGIN_NODE and + * FDT_END_NODE of all subnodes of /. + * + * The subnode feature helps in a hashing situation since it prevents the + * root node from changing at all. Any change to non-excluded properties, + * names of subnodes or number of subnodes would be detected. + * + * When used with FITs this provides the ability to hash and sign parts of + * the FIT based on different configurations in the FIT. Then it is + * impossible to change anything about that configuration (include images + * attached to the configuration), but it may be possible to add new + * configurations, new images or new signatures within the existing + * framework. + * + * Adding new properties to a device tree may result in the string table + * being extended (if the new property names are different from those + * already added). This function can optionally include a region for + * the string table so that this can be part of the hash too. This is always + * the last region. + * + * The FDT also has a mem_rsvmap table which can also be included, and is + * always the first region if so. + * + * The device tree header is not included in the region list. Since the + * contents of the FDT are changing (shrinking, often), the caller will need + * to regenerate the header anyway. + * + * @fdt: Device tree to check + * @h_include: Function to call to determine whether to include a part or + * not: + * + * @priv: Private pointer as passed to fdt_find_regions() + * @fdt: Pointer to FDT blob + * @offset: Offset of this node / property + * @type: Type of this part, FDT_IS_... + * @data: Pointer to data (node name, property name, compatible + * string, value (not yet supported) + * @size: Size of data, or 0 if none + * @return 0 to exclude, 1 to include, -1 if no information is + * available + * @priv: Private pointer passed to h_include + * @region: Returns list of regions, sorted by offset + * @max_regions: Maximum length of region list + * @path: Pointer to a temporary string for the function to use for + * building path names + * @path_len: Length of path, must be large enough to hold the longest + * path in the tree + * @flags: Various flags that control the region algortihm, see + * FDT_REG_... + * @return number of regions in list. If this is >max_regions then the + * region array was exhausted. You should increase max_regions and try + * the call again. Only the first max_regions elements are available in the + * array. + * + * On error a -ve value is return, which can be: + * + * -FDT_ERR_BADSTRUCTURE (too deep or more END tags than BEGIN tags + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_NOSPACE (path area is too small) + */ +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info); + +/** fdt_next_region() - find next region + * + * See fdt_first_region() for full description. This function finds the + * next region according to the provided parameters, which must be the same + * as passed to fdt_first_region(). + * + * This function can additionally return -FDT_ERR_NOTFOUND when there are no + * more regions + */ +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info); + +/** + * fdt_add_alias_regions() - find aliases that point to existing regions + * + * Once a device tree grep is complete some of the nodes will be present + * and some will have been dropped. This function checks all the alias nodes + * to figure out which points point to nodes which are still present. These + * aliases need to be kept, along with the nodes they reference. + * + * Given a list of regions function finds the aliases that still apply and + * adds more regions to the list for these. This function is called after + * fdt_next_region() has finished returning regions and requires the same + * state. + * + * @fdt: Device tree file to reference + * @region: List of regions that will be kept + * @count: Number of regions + * @max_regions: Number of entries that can fit in @region + * @info: Region state as returned from fdt_next_region() + * @return new number of regions in @region (i.e. count + the number added) + * or -FDT_ERR_NOSPACE if there was not enough space. + */ +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info); + #endif /* _LIBFDT_H */ diff --git a/include/linux/compat.h b/include/linux/compat.h index 6ff3915..fbebf91 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -36,10 +36,25 @@ extern struct p_current *current; #define KERN_INFO #define KERN_DEBUG +#define GFP_ATOMIC ((gfp_t) 0) +#define GFP_KERNEL ((gfp_t) 0) +#define GFP_NOFS ((gfp_t) 0) +#define GFP_USER ((gfp_t) 0) +#define __GFP_NOWARN ((gfp_t) 0) +#define __GFP_ZERO ((__force gfp_t)0x8000u) /* Return zeroed page on success */ + void *kmalloc(size_t size, int flags); -void *kzalloc(size_t size, int flags); + +static inline void *kzalloc(size_t size, gfp_t flags) +{ + return kmalloc(size, flags | __GFP_ZERO); +} #define vmalloc(size) kmalloc(size, 0) #define __vmalloc(size, flags, pgsz) kmalloc(size, flags) +static inline void *vzalloc(unsigned long size) +{ + return kzalloc(size, 0); +} #define kfree(ptr) free(ptr) #define vfree(ptr) free(ptr) @@ -73,13 +88,6 @@ void *kmem_cache_alloc(struct kmem_cache *obj, int flag); /* drivers/char/random.c */ #define get_random_bytes(...) -/* idr.c */ -#define GFP_ATOMIC ((gfp_t) 0) -#define GFP_KERNEL ((gfp_t) 0) -#define GFP_NOFS ((gfp_t) 0) -#define GFP_USER ((gfp_t) 0) -#define __GFP_NOWARN ((gfp_t) 0) - /* include/linux/leds.h */ struct led_trigger {}; @@ -189,8 +197,6 @@ struct work_struct {}; unsigned long copy_from_user(void *dest, const void *src, unsigned long count); -void *vzalloc(unsigned long size); - typedef unused_t spinlock_t; typedef int wait_queue_head_t; @@ -315,8 +321,6 @@ struct notifier_block {}; typedef unsigned long dmaaddr_t; -#define cpu_relax() do {} while (0) - #define pm_runtime_get_sync(dev) do {} while (0) #define pm_runtime_put(dev) do {} while (0) #define pm_runtime_put_sync(dev) do {} while (0) diff --git a/include/mmc.h b/include/mmc.h index dd98b3b..cda9a19 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -266,6 +266,28 @@ #define MMC_NUM_BOOT_PARTITION 2 #define MMC_PART_RPMB 3 /* RPMB partition number */ +/* Driver model support */ + +/** + * struct mmc_uclass_priv - Holds information about a device used by the uclass + */ +struct mmc_uclass_priv { + struct mmc *mmc; +}; + +/** + * mmc_get_mmc_dev() - get the MMC struct pointer for a device + * + * Provided that the device is already probed and ready for use, this value + * will be available. + * + * @dev: Device + * @return associated mmc struct pointer if available, else NULL + */ +struct mmc *mmc_get_mmc_dev(struct udevice *dev); + +/* End of driver model support */ + struct mmc_cid { unsigned long psn; unsigned short oid; diff --git a/include/net.h b/include/net.h index d17173d..d09bec9 100644 --- a/include/net.h +++ b/include/net.h @@ -93,6 +93,14 @@ struct eth_pdata { int phy_interface; }; +enum eth_recv_flags { + /* + * Check hardware device for new packets (otherwise only return those + * which are already in the memory buffer ready to process) + */ + ETH_RECV_CHECK_DEVICE = 1 << 0, +}; + /** * struct eth_ops - functions of Ethernet MAC controllers * @@ -111,7 +119,9 @@ struct eth_pdata { * mcast: Join or leave a multicast group (for TFTP) - optional * write_hwaddr: Write a MAC address to the hardware (used to pass it to Linux * on some platforms like ARM). This function expects the - * eth_pdata::enetaddr field to be populated - optional + * eth_pdata::enetaddr field to be populated. The method can + * return -ENOSYS to indicate that this is not implemented for + this hardware - optional. * read_rom_hwaddr: Some devices have a backup of the MAC address stored in a * ROM on the board. This is how the driver should expose it * to the network stack. This function should fill in the @@ -120,7 +130,7 @@ struct eth_pdata { struct eth_ops { int (*start)(struct udevice *dev); int (*send)(struct udevice *dev, void *packet, int length); - int (*recv)(struct udevice *dev, uchar **packetp); + int (*recv)(struct udevice *dev, int flags, uchar **packetp); int (*free_pkt)(struct udevice *dev, uchar *packet, int length); void (*stop)(struct udevice *dev); #ifdef CONFIG_MCAST_TFTP diff --git a/include/pci.h b/include/pci.h index 542e68b..94bca97 100644 --- a/include/pci.h +++ b/include/pci.h @@ -468,7 +468,10 @@ typedef int pci_dev_t; #define PCI_ANY_ID (~0) struct pci_device_id { - unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */ + unsigned long driver_data; /* Data private to the driver */ }; struct pci_controller; @@ -805,6 +808,14 @@ struct dm_pci_ops { #define pci_get_ops(dev) ((struct dm_pci_ops *)(dev)->driver->ops) /** + * pci_get_bdf() - Get the BDF value for a device + * + * @dev: Device to check + * @return bus/device/function value (see PCI_BDF()) + */ +pci_dev_t pci_get_bdf(struct udevice *dev); + +/** * pci_bind_bus_devices() - scan a PCI bus and bind devices * * Scan a PCI bus looking for devices. Bind each one that is found. If @@ -1101,7 +1112,79 @@ struct dm_pci_emul_ops { int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, struct udevice **emulp); -#endif +#endif /* CONFIG_DM_PCI */ + +/** + * PCI_DEVICE - macro used to describe a specific pci device + * @vend: the 16 bit PCI Vendor ID + * @dev: the 16 bit PCI Device ID + * + * This macro is used to create a struct pci_device_id that matches a + * specific device. The subvendor and subdevice fields will be set to + * PCI_ANY_ID. + */ +#define PCI_DEVICE(vend, dev) \ + .vendor = (vend), .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID + +/** + * PCI_DEVICE_SUB - macro used to describe a specific pci device with subsystem + * @vend: the 16 bit PCI Vendor ID + * @dev: the 16 bit PCI Device ID + * @subvend: the 16 bit PCI Subvendor ID + * @subdev: the 16 bit PCI Subdevice ID + * + * This macro is used to create a struct pci_device_id that matches a + * specific device with subsystem information. + */ +#define PCI_DEVICE_SUB(vend, dev, subvend, subdev) \ + .vendor = (vend), .device = (dev), \ + .subvendor = (subvend), .subdevice = (subdev) + +/** + * PCI_DEVICE_CLASS - macro used to describe a specific pci device class + * @dev_class: the class, subclass, prog-if triple for this device + * @dev_class_mask: the class mask for this device + * + * This macro is used to create a struct pci_device_id that matches a + * specific PCI class. The vendor, device, subvendor, and subdevice + * fields will be set to PCI_ANY_ID. + */ +#define PCI_DEVICE_CLASS(dev_class, dev_class_mask) \ + .class = (dev_class), .class_mask = (dev_class_mask), \ + .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID + +/** + * PCI_VDEVICE - macro used to describe a specific pci device in short form + * @vend: the vendor name + * @dev: the 16 bit PCI Device ID + * + * This macro is used to create a struct pci_device_id that matches a + * specific PCI device. The subvendor, and subdevice fields will be set + * to PCI_ANY_ID. The macro allows the next field to follow as the device + * private data. + */ + +#define PCI_VDEVICE(vend, dev) \ + .vendor = PCI_VENDOR_ID_##vend, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, 0, 0 + +/** + * struct pci_driver_entry - Matches a driver to its pci_device_id list + * @driver: Driver to use + * @match: List of match records for this driver, terminated by {} + */ +struct pci_driver_entry { + struct driver *driver; + const struct pci_device_id *match; +}; + +#define U_BOOT_PCI_DEVICE(__name, __match) \ + ll_entry_declare(struct pci_driver_entry, __name, pci_driver_entry) = {\ + .driver = llsym(struct driver, __name, driver), \ + .match = __match, \ + } #endif /* __ASSEMBLY__ */ #endif /* _PCI_H */ diff --git a/include/power/pmic.h b/include/power/pmic.h index eb152ef..6ba4b6e 100644 --- a/include/power/pmic.h +++ b/include/power/pmic.h @@ -264,6 +264,40 @@ int pmic_reg_count(struct udevice *dev); */ int pmic_read(struct udevice *dev, uint reg, uint8_t *buffer, int len); int pmic_write(struct udevice *dev, uint reg, const uint8_t *buffer, int len); + +/** + * pmic_reg_read() - read a PMIC register value + * + * @dev: PMIC device to read + * @reg: Register to read + * @return value read on success or negative value of errno. + */ +int pmic_reg_read(struct udevice *dev, uint reg); + +/** + * pmic_reg_write() - write a PMIC register value + * + * @dev: PMIC device to write + * @reg: Register to write + * @value: Value to write + * @return 0 on success or negative value of errno. + */ +int pmic_reg_write(struct udevice *dev, uint reg, uint value); + +/** + * pmic_clrsetbits() - clear and set bits in a PMIC register + * + * This reads a register, optionally clears some bits, optionally sets some + * bits, then writes the register. + * + * @dev: PMIC device to update + * @reg: Register to update + * @clr: Bit mask to clear (set those bits that you want cleared) + * @set: Bit mask to set (set those bits that you want set) + * @return 0 on success or negative value of errno. + */ +int pmic_clrsetbits(struct udevice *dev, uint reg, uint clr, uint set); + #endif /* CONFIG_DM_PMIC */ #ifdef CONFIG_POWER diff --git a/include/power/regulator.h b/include/power/regulator.h index 03a2cef..0152290 100644 --- a/include/power/regulator.h +++ b/include/power/regulator.h @@ -128,6 +128,11 @@ struct dm_regulator_mode { const char *name; }; +enum regulator_flag { + REGULATOR_FLAG_AUTOSET_UV = 1 << 0, + REGULATOR_FLAG_AUTOSET_UA = 1 << 1, +}; + /** * struct dm_regulator_uclass_platdata - pointed by dev->uclass_platdata, and * allocated on each regulator bind. This structure holds an information @@ -143,6 +148,8 @@ struct dm_regulator_mode { * @max_uA* - maximum amperage (micro Amps) * @always_on* - bool type, true or false * @boot_on* - bool type, true or false + * TODO(sjg@chromium.org): Consider putting the above two into @flags + * @flags: - flags value (see REGULATOR_FLAG_...) * @name** - fdt regulator name - should be taken from the device tree * * Note: @@ -162,6 +169,7 @@ struct dm_regulator_uclass_platdata { bool always_on; bool boot_on; const char *name; + int flags; }; /* Regulator device operations */ @@ -308,9 +316,39 @@ int regulator_get_mode(struct udevice *dev); int regulator_set_mode(struct udevice *dev, int mode_id); /** - * regulator_autoset: setup the regulator given by its uclass's platform data - * name field. The setup depends on constraints found in device's uclass's - * platform data (struct dm_regulator_uclass_platdata): + * regulators_enable_boot_on() - enable regulators needed for boot + * + * This enables all regulators which are marked to be on at boot time. This + * only works for regulators which don't have a range for voltage/current, + * since in that case it is not possible to know which value to use. + * + * This effectively calls regulator_autoset() for every regulator. + */ +int regulators_enable_boot_on(bool verbose); + +/** + * regulator_autoset: setup the voltage/current on a regulator + * + * The setup depends on constraints found in device's uclass's platform data + * (struct dm_regulator_uclass_platdata): + * + * - Enable - will set - if any of: 'always_on' or 'boot_on' is set to true, + * or if both are unset, then the function returns + * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal + * - Current limit - will set - if '.min_uA' and '.max_uA' values are equal + * + * The function returns on the first-encountered error. + * + * @platname - expected string for dm_regulator_uclass_platdata .name field + * @devp - returned pointer to the regulator device - if non-NULL passed + * @return: 0 on success or negative value of errno. + */ +int regulator_autoset(struct udevice *dev); + +/** + * regulator_autoset_by_name: setup the regulator given by its uclass's + * platform data name field. The setup depends on constraints found in device's + * uclass's platform data (struct dm_regulator_uclass_platdata): * - Enable - will set - if any of: 'always_on' or 'boot_on' is set to true, * or if both are unset, then the function returns * - Voltage value - will set - if '.min_uV' and '.max_uV' values are equal @@ -320,21 +358,18 @@ int regulator_set_mode(struct udevice *dev, int mode_id); * * @platname - expected string for dm_regulator_uclass_platdata .name field * @devp - returned pointer to the regulator device - if non-NULL passed - * @verbose - (true/false) print regulator setup info, or be quiet * @return: 0 on success or negative value of errno. * * The returned 'regulator' device can be used with: * - regulator_get/set_* */ -int regulator_autoset(const char *platname, - struct udevice **devp, - bool verbose); +int regulator_autoset_by_name(const char *platname, struct udevice **devp); /** * regulator_list_autoset: setup the regulators given by list of their uclass's * platform data name field. The setup depends on constraints found in device's * uclass's platform data. The function loops with calls to: - * regulator_autoset() for each name from the list. + * regulator_autoset_by_name() for each name from the list. * * @list_platname - an array of expected strings for .name field of each * regulator's uclass platdata @@ -375,7 +410,7 @@ int regulator_get_by_devname(const char *devname, struct udevice **devp); * Search by name, found in regulator uclass platdata. * * @platname - expected string for uc_pdata->name of regulator uclass platdata - * @devp - returned pointer to the regulator device + * @devp - returns pointer to the regulator device or NULL on error * @return 0 on success or negative value of errno. * * The returned 'regulator' device is probed and can be used with: diff --git a/include/power/sandbox_pmic.h b/include/power/sandbox_pmic.h index ae14292..8547674 100644 --- a/include/power/sandbox_pmic.h +++ b/include/power/sandbox_pmic.h @@ -117,11 +117,11 @@ enum { /* * Expected regulators setup after call of: - * - regulator_autoset() + * - regulator_autoset_by_name() * - regulator_list_autoset() */ -/* BUCK1: for testing regulator_autoset() */ +/* BUCK1: for testing regulator_autoset_by_name() */ #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UV 1200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_UA 200000 #define SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE true diff --git a/include/ram.h b/include/ram.h new file mode 100644 index 0000000..e2172a8 --- /dev/null +++ b/include/ram.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RAM_H +#define __RAM_H + +struct ram_info { + phys_addr_t base; + size_t size; +}; + +struct ram_ops { + /** + * get_info() - Get basic memory info + * + * @dev: Device to check (UCLASS_RAM) + * @info: Place to put info + * @return 0 if OK, -ve on error + */ + int (*get_info)(struct udevice *dev, struct ram_info *info); +}; + +#define ram_get_ops(dev) ((struct ram_ops *)(dev)->driver->ops) + +/** + * ram_get_info() - Get information about a RAM device + * + * @dev: Device to check (UCLASS_RAM) + * @info: Returns RAM info + * @return 0 if OK, -ve on error + */ +int ram_get_info(struct udevice *dev, struct ram_info *info); + +#endif diff --git a/include/rc4.h b/include/rc4.h new file mode 100644 index 0000000..ea409c2 --- /dev/null +++ b/include/rc4.h @@ -0,0 +1,21 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RC4_H +#define __RC4_H + +/** + * rc4_encode() - encode a buf with the RC4 cipher + * + * @buf: Buffer to encode (it is overwrite in the process + * @len: Length of buffer in bytes + * @key: 16-byte key to use + */ +void rc4_encode(unsigned char *buf, unsigned int len, unsigned char key[16]); + +#endif diff --git a/include/regmap.h b/include/regmap.h new file mode 100644 index 0000000..eccf770 --- /dev/null +++ b/include/regmap.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __REGMAP_H +#define __REGMAP_H + +/** + * struct regmap_range - a register map range + * + * @start: Start address + * @size: Size in bytes + */ +struct regmap_range { + ulong start; + ulong size; +}; + +/** + * struct regmap - a way of accessing hardware/bus registers + * + * @base: Base address of register map + * @range_count: Number of ranges available within the map + * @range: Pointer to the list of ranges, allocated if @range_count > 1 + * @base_range: If @range_count is <= 1, @range points here + */ +struct regmap { + phys_addr_t base; + int range_count; + struct regmap_range *range, base_range; +}; + +/* + * Interface to provide access to registers either through a direct memory + * bus or through a peripheral bus like I2C, SPI. + */ +int regmap_write(struct regmap *map, uint offset, uint val); +int regmap_read(struct regmap *map, uint offset, uint *valp); + +#define regmap_write32(map, ptr, member, val) \ + regmap_write(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), val) + +#define regmap_read32(map, ptr, member, valp) \ + regmap_read(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), valp) + +/** + * regmap_init_mem() - Set up a new register map that uses memory access + * + * Use regmap_uninit() to free it. + * + * @dev: Device that uses this map + * @mapp: Returns allocated map + */ +int regmap_init_mem(struct udevice *dev, struct regmap **mapp); + +/** + * regmap_get_range() - Obtain the base memory address of a regmap range + * + * @map: Regmap to query + * @range_num: Range to look up + */ +void *regmap_get_range(struct regmap *map, unsigned int range_num); + +/** + * regmap_uninit() - free a previously inited regmap + */ +int regmap_uninit(struct regmap *map); + +#endif diff --git a/include/reset.h b/include/reset.h new file mode 100644 index 0000000..383761e --- /dev/null +++ b/include/reset.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RESET_H +#define __RESET_H + +enum reset_t { + RESET_WARM, /* Reset CPU, keep GPIOs active */ + RESET_COLD, /* Reset CPU and GPIOs */ + RESET_POWER, /* Reset PMIC (remove and restore power) */ + + RESET_COUNT, +}; + +struct reset_ops { + /** + * request() - request a reset of the given type + * + * Note that this function may return before the reset takes effect. + * + * @type: Reset type to request + * @return -EINPROGRESS if the reset has been started and + * will complete soon, -EPROTONOSUPPORT if not supported + * by this device, 0 if the reset has already happened + * (in which case this method will not actually return) + */ + int (*request)(struct udevice *dev, enum reset_t type); +}; + +#define reset_get_ops(dev) ((struct reset_ops *)(dev)->driver->ops) + +/** + * reset_request() - request a reset + * + * @type: Reset type to request + * @return 0 if OK, -EPROTONOSUPPORT if not supported by this device + */ +int reset_request(struct udevice *dev, enum reset_t type); + +/** + * reset_walk() - cause a reset + * + * This works through the available reset devices until it finds one that can + * perform a reset. If the provided reset type is not available, the next one + * will be tried. + * + * If this function fails to reset, it will display a message and halt + * + * @type: Reset type to request + * @return -EINPROGRESS if a reset is in progress, -ENOSYS if not available + */ +int reset_walk(enum reset_t type); + +/** + * reset_walk_halt() - try to reset, otherwise halt + * + * This calls reset_walk(). If it returns, indicating that reset is not + * supported, it prints a message and halts. + */ +void reset_walk_halt(enum reset_t type); + +/** + * reset_cpu() - calls reset_walk(RESET_WARM) + */ +void reset_cpu(ulong addr); + +#endif diff --git a/include/spl.h b/include/spl.h index d19940f..8e53426 100644 --- a/include/spl.h +++ b/include/spl.h @@ -81,6 +81,18 @@ void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image); int spl_load_image_ext(block_dev_desc_t *block_dev, int partition, const char *filename); int spl_load_image_ext_os(block_dev_desc_t *block_dev, int partition); +/** + * spl_init() - Set up device tree and driver model in SPL if enabled + * + * Call this function in board_init_f() if you want to use device tree and + * driver model early, before board_init_r() is called. This function will + * be called from board_init_r() if not called earlier. + * + * If this is not called, then driver model will be inactive in SPL's + * board_init_f(), and no device tree will be available. + */ +int spl_init(void); + #ifdef CONFIG_SPL_BOARD_INIT void spl_board_init(void); #endif diff --git a/include/syscon.h b/include/syscon.h new file mode 100644 index 0000000..c62ccd6 --- /dev/null +++ b/include/syscon.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SYSCON_H +#define __SYSCON_H + +/** + * struct syscon_uc_info - Information stored by the syscon UCLASS_UCLASS + * + * @regmap: Register map for this controller + */ +struct syscon_uc_info { + struct regmap *regmap; +}; + +/* So far there are no ops so this is a placeholder */ +struct syscon_ops { +}; + +#define syscon_get_ops(dev) ((struct syscon_ops *)(dev)->driver->ops) + +/** + * syscon_get_regmap() - Get access to a register map + * + * @dev: Device to check (UCLASS_SCON) + * @info: Returns regmap for the device + * @return 0 if OK, -ve on error + */ +struct regmap *syscon_get_regmap(struct udevice *dev); + +/** + * syscon_get_regmap_by_driver_data() - Look up a controller by its ID + * + * Each system controller can be accessed by its driver data, which is + * assumed to be unique through the scope of all system controllers that + * are in use. This function looks up the regmap given this driver data. + * + * @driver_data: Driver data value to look up + * @return register map correponding to @driver_data, or -ve error code + */ +struct regmap *syscon_get_regmap_by_driver_data(ulong driver_data); + +/** + * syscon_get_first_range() - get the first memory range from a syscon regmap + * + * @driver_data: Driver data value to look up + * @return first region of register map correponding to @driver_data, or + * -ve error code + */ +void *syscon_get_first_range(ulong driver_data); + +#endif diff --git a/include/test/ut.h b/include/test/ut.h index 5e5aa6c..da7c1a9 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -9,6 +9,8 @@ #ifndef __TEST_UT_H #define __TEST_UT_H +#include <linux/err.h> + struct unit_test_state; /** @@ -101,6 +103,19 @@ void ut_failf(struct unit_test_state *uts, const char *fname, int line, } \ } +/* Assert that a pointer is not an error pointer */ +#define ut_assertok_ptr(expr) { \ + const void *val = (expr); \ + \ + if (IS_ERR(val)) { \ + ut_failf(uts, __FILE__, __LINE__, __func__, \ + #expr " = NULL", \ + "Expected pointer, got error %ld", \ + PTR_ERR(val)); \ + return CMD_RET_FAILURE; \ + } \ +} + /* Assert that an operation succeeds (returns 0) */ #define ut_assertok(cond) ut_asserteq(0, cond) diff --git a/include/usb.h b/include/usb.h index dca512d..cf00ffd 100644 --- a/include/usb.h +++ b/include/usb.h @@ -175,9 +175,9 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller); int usb_lowlevel_stop(int index); #if defined(CONFIG_MUSB_HOST) || defined(CONFIG_DM_USB) -int usb_reset_root_port(void); +int usb_reset_root_port(struct usb_device *dev); #else -#define usb_reset_root_port() +#define usb_reset_root_port(dev) #endif int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, @@ -493,15 +493,31 @@ struct usb_device_id { /** * struct usb_driver_entry - Matches a driver to its usb_device_ids - * @compatible: Compatible string - * @data: Data for this compatible string + * @driver: Driver to use + * @match: List of match records for this driver, terminated by {} */ struct usb_driver_entry { struct driver *driver; const struct usb_device_id *match; }; -#define USB_DEVICE(__name, __match) \ +#define USB_DEVICE_ID_MATCH_DEVICE \ + (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT) + +/** + * USB_DEVICE - macro used to describe a specific usb device + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * + * This macro is used to create a struct usb_device_id that matches a + * specific device. + */ +#define USB_DEVICE(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \ + .idVendor = (vend), \ + .idProduct = (prod) + +#define U_BOOT_USB_DEVICE(__name, __match) \ ll_entry_declare(struct usb_driver_entry, __name, usb_driver_entry) = {\ .driver = llsym(struct driver, __name, driver), \ .match = __match, \ @@ -705,15 +721,16 @@ struct dm_usb_ops { * is read). This should be NULL for EHCI, which does not need this. */ int (*alloc_device)(struct udevice *bus, struct usb_device *udev); + + /** + * reset_root_port() - Reset usb root port + */ + int (*reset_root_port)(struct udevice *bus, struct usb_device *udev); }; #define usb_get_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) #define usb_get_emul_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops) -#ifdef CONFIG_MUSB_HOST -int usb_reset_root_port(void); -#endif - /** * usb_get_dev_index() - look up a device index number * @@ -730,26 +747,18 @@ int usb_reset_root_port(void); struct usb_device *usb_get_dev_index(struct udevice *bus, int index); /** - * usb_legacy_port_reset() - Legacy function to reset a hub port - * - * @hub: Hub device - * @portnr: Port number (1=first) - */ -int usb_legacy_port_reset(struct usb_device *hub, int portnr); - -/** * usb_setup_device() - set up a device ready for use * * @dev: USB device pointer. This need not be a real device - it is * common for it to just be a local variable with its ->dev - * member (i.e. @dev->dev) set to the parent device + * member (i.e. @dev->dev) set to the parent device and + * dev->portnr set to the port number on the hub (1=first) * @do_read: true to read the device descriptor before an address is set * (should be false for XHCI buses, true otherwise) * @parent: Parent device (either UCLASS_USB or UCLASS_USB_HUB) - * @portnr: Port number on hub (1=first) or 0 for none * @return 0 if OK, -ve on error */ int usb_setup_device(struct usb_device *dev, bool do_read, - struct usb_device *parent, int portnr); + struct usb_device *parent); /** * usb_hub_scan() - Scan a hub and find its devices diff --git a/include/usb_ether.h b/include/usb_ether.h index 23507e1..c6d1416 100644 --- a/include/usb_ether.h +++ b/include/usb_ether.h @@ -19,25 +19,91 @@ #define ETH_DATA_LEN 1500 /* Max. octets in payload */ #define ETH_FRAME_LEN PKTSIZE_ALIGN /* Max. octets in frame sans FCS */ +/* TODO(sjg@chromium.org): Remove @pusb_dev when all boards use CONFIG_DM_ETH */ struct ueth_data { /* eth info */ - struct eth_device eth_dev; /* used with eth_register */ - int phy_id; /* mii phy id */ +#ifdef CONFIG_DM_ETH + uint8_t *rxbuf; + int rxsize; + int rxlen; /* Total bytes available in rxbuf */ + int rxptr; /* Current position in rxbuf */ +#else + struct eth_device eth_dev; /* used with eth_register */ + /* driver private */ + void *dev_priv; +#endif + int phy_id; /* mii phy id */ /* usb info */ struct usb_device *pusb_dev; /* this usb_device */ - unsigned char ifnum; /* interface number */ - unsigned char ep_in; /* in endpoint */ - unsigned char ep_out; /* out ....... */ - unsigned char ep_int; /* interrupt . */ - unsigned char subclass; /* as in overview */ - unsigned char protocol; /* .............. */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ unsigned char irqinterval; /* Intervall for IRQ Pipe */ - - /* driver private */ - void *dev_priv; }; +#ifdef CONFIG_DM_ETH +/** + * usb_ether_register() - register a new USB ethernet device + * + * This selects the correct USB interface and figures out the endpoints to use. + * + * @dev: USB device + * @ss: Place to put USB ethernet data + * @rxsize: Maximum size to allocate for the receive buffer + * @return 0 if OK, -ve on error + */ +int usb_ether_register(struct udevice *dev, struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_deregister() - deregister a USB ethernet device + * + * @ueth: USB Ethernet device + * @return 0 + */ +int usb_ether_deregister(struct ueth_data *ueth); + +/** + * usb_ether_receive() - recieve a packet from the bulk in endpoint + * + * The packet is stored in the internal buffer ready for processing. + * + * @ueth: USB Ethernet device + * @rxsize: Maximum size to receive + * @return 0 if a packet was received, -EAGAIN if not, -ENOSPC if @rxsize is + * larger than the size passed ot usb_ether_register(), other -ve on error + */ +int usb_ether_receive(struct ueth_data *ueth, int rxsize); + +/** + * usb_ether_get_rx_bytes() - obtain bytes from the internal packet buffer + * + * This should be called repeatedly to obtain packet data until it returns 0. + * After each packet is processed, call usb_ether_advance_rxbuf() to move to + * the next one. + * + * @ueth: USB Ethernet device + * @ptrp: Returns a pointer to the start of the next packet if there is + * one available + * @return number of bytes available, or 0 if none + */ +int usb_ether_get_rx_bytes(struct ueth_data *ueth, uint8_t **ptrp); + +/** + * usb_ether_advance_rxbuf() - Advance to the next packet in the internal buffer + * + * After processing the data returned by usb_ether_get_rx_bytes(), call this + * function to move to the next packet. You must specify the number of bytes + * you have processed in @num_bytes. + * + * @ueth: USB Ethernet device + * @num_bytes: Number of bytes to skip, or -1 to skip all bytes + */ +void usb_ether_advance_rxbuf(struct ueth_data *ueth, int num_bytes); +#else /* * Function definitions for each USB ethernet driver go here * (declaration is unconditional, compilation is conditional) @@ -65,5 +131,6 @@ int smsc95xx_eth_probe(struct usb_device *dev, unsigned int ifnum, struct ueth_data *ss); int smsc95xx_eth_get_info(struct usb_device *dev, struct ueth_data *ss, struct eth_device *eth); +#endif #endif /* __USB_ETHER_H__ */ diff --git a/include/vsprintf.h b/include/vsprintf.h index d2fcca3..b5bc9c1 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -41,6 +41,32 @@ unsigned long long simple_strtoull(const char *cp, char **endp, long simple_strtol(const char *cp, char **endp, unsigned int base); /** + * trailing_strtol() - extract a trailing integer from a string + * + * Given a string this finds a trailing number on the string and returns it. + * For example, "abc123" would return 123. + * + * @str: String to exxamine + * @return training number if found, else -1 + */ +long trailing_strtol(const char *str); + +/** + * trailing_strtoln() - extract a trailing integer from a fixed-length string + * + * Given a fixed-length string this finds a trailing number on the string + * and returns it. For example, "abc123" would return 123. Only the + * characters between @str and @end - 1 are examined. If @end is NULL, it is + * set to str + strlen(str). + * + * @str: String to exxamine + * @end: Pointer to end of string to examine, or NULL to use the + * whole string + * @return training number if found, else -1 + */ +long trailing_strtoln(const char *str, const char *end); + +/** * panic() - Print a message and reset/hang * * Prints a message on the console(s) and then resets. If CONFIG_PANIC_HANG is diff --git a/lib/Kconfig b/lib/Kconfig index c98d399..972ac17 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -50,6 +50,8 @@ config LIB_RAND help This library provides pseudo-random number generator functions. +source lib/dhry/Kconfig + source lib/rsa/Kconfig menu "Hashing Support" diff --git a/lib/Makefile b/lib/Makefile index 97ed398..fd106b9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -15,12 +15,15 @@ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ obj-$(CONFIG_OF_LIBFDT) += libfdt/ obj-$(CONFIG_FIT) += libfdt/ +obj-$(CONFIG_FIT) += libfdt/ +obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_AES) += aes.o obj-$(CONFIG_USB_TTY) += circbuf.o obj-y += crc7.o obj-y += crc8.o obj-y += crc16.o +obj-$(CONFIG_ERRNO_STR) += errno_str.o obj-$(CONFIG_FIT) += fdtdec_common.o obj-$(CONFIG_OF_CONTROL) += fdtdec_common.o obj-$(CONFIG_OF_CONTROL) += fdtdec.o @@ -34,6 +37,7 @@ obj-$(CONFIG_MD5) += md5.o obj-y += net_utils.o obj-$(CONFIG_PHYSMEM) += physmem.o obj-y += qsort.o +obj-y += rc4.o obj-$(CONFIG_SHA1) += sha1.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o obj-$(CONFIG_SHA256) += sha256.o @@ -57,7 +61,6 @@ endif obj-$(CONFIG_ADDR_MAP) += addr_map.o obj-y += hashtable.o obj-y += errno.o -obj-$(CONFIG_ERRNO_STR) += errno_str.o obj-y += display_options.o obj-$(CONFIG_BCH) += bch.o obj-y += crc32.o diff --git a/lib/dhry/Kconfig b/lib/dhry/Kconfig new file mode 100644 index 0000000..641b806 --- /dev/null +++ b/lib/dhry/Kconfig @@ -0,0 +1,7 @@ +config CMD_DHRYSTONE + bool "Support the 'dhry' command to run the dhrystone benchmark" + help + Dhrystone is an old benchmark in the public domain that gives a + rough idea of CPU performance. This enables a 'dhry' command + which runs this benchmark within U-Boot and reports the performance. + The number of 'Dhrystone MIPS' is also reported. diff --git a/lib/dhry/Makefile b/lib/dhry/Makefile new file mode 100644 index 0000000..926c0d6 --- /dev/null +++ b/lib/dhry/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += cmd_dhry.o dhry_1.o dhry_2.o diff --git a/lib/dhry/cmd_dhry.c b/lib/dhry/cmd_dhry.c new file mode 100644 index 0000000..5dc191e --- /dev/null +++ b/lib/dhry/cmd_dhry.c @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include "dhry.h" + +static int do_dhry(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + ulong start, duration, dhry_per_sec, vax_mips; + int iterations = 1000000; + + if (argc > 1) + iterations = simple_strtoul(argv[1], NULL, 10); + + start = get_timer(0); + dhry(iterations); + duration = get_timer(start); + dhry_per_sec = iterations * 1000 / duration; + vax_mips = dhry_per_sec / 1757; + printf("%d iterations in %lu ms: %lu/s, %lu DMIPS\n", iterations, + duration, dhry_per_sec, vax_mips); + + return 0; +} + +U_BOOT_CMD( + dhry, 2, 1, do_dhry, + "[iterations] - run dhrystone benchmark", + "\n - run the Dhrystone 2.1 benchmark, a rough measure of CPU speed\n" +); diff --git a/lib/dhry/dhry.h b/lib/dhry/dhry.h new file mode 100644 index 0000000..49d4223 --- /dev/null +++ b/lib/dhry/dhry.h @@ -0,0 +1,442 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry.h SID: 3.4 5/15/91 19:30:21 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * Modification Log: + * addapted from: + * + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry.h (part 1 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * Siemens AG, AUT E 51 + * Postfach 3220 + * 8520 Erlangen + * Germany (West) + * Phone: [+49]-9131-7-20330 + * (8-17 Central European Time) + * Usenet: ..!mcvax!unido!estevax!weicker + * + * Original Version (in Ada) published in + * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984), + * pp. 1013 - 1030, together with the statistics + * on which the distribution of statements etc. is based. + * + * In this C version, the following C library functions are used: + * - strcpy, strcmp (inside the measurement loop) + * - printf, scanf (outside the measurement loop) + * In addition, Berkeley UNIX system calls "times ()" or "time ()" + * are used for execution time measurement. For measurements + * on other systems, these calls have to be changed. + * + * Collection of Results: + * Reinhold Weicker (address see above) and + * + * Rick Richardson + * PC Research. Inc. + * 94 Apple Orchard Drive + * Tinton Falls, NJ 07724 + * Phone: (201) 834-1378 (9-17 EST) + * Usenet: ...!seismo!uunet!pcrat!rick + * + * Please send results to Rick Richardson and/or Reinhold Weicker. + * Complete information should be given on hardware and software used. + * Hardware information includes: Machine type, CPU, type and size + * of caches; for microprocessors: clock frequency, memory speed + * (number of wait states). + * Software information includes: Compiler (and runtime library) + * manufacturer and version, compilation switches, OS version. + * The Operating System version may give an indication about the + * compiler; Dhrystone itself performs no OS calls in the measurement loop. + * + * The complete output generated by the program should be mailed + * such that at least some checks for correctness can be made. + * + *************************************************************************** + * + * History: This version C/2.1 has been made for two reasons: + * + * 1) There is an obvious need for a common C version of + * Dhrystone, since C is at present the most popular system + * programming language for the class of processors + * (microcomputers, minicomputers) where Dhrystone is used most. + * There should be, as far as possible, only one C version of + * Dhrystone such that results can be compared without + * restrictions. In the past, the C versions distributed + * by Rick Richardson (Version 1.1) and by Reinhold Weicker + * had small (though not significant) differences. + * + * 2) As far as it is possible without changes to the Dhrystone + * statistics, optimizing compilers should be prevented from + * removing significant statements. + * + * This C version has been developed in cooperation with + * Rick Richardson (Tinton Falls, NJ), it incorporates many + * ideas from the "Version 1.1" distributed previously by + * him over the UNIX network Usenet. + * I also thank Chaim Benedelac (National Semiconductor), + * David Ditzel (SUN), Earl Killian and John Mashey (MIPS), + * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley) + * for their help with comments on earlier versions of the + * benchmark. + * + * Changes: In the initialization part, this version follows mostly + * Rick Richardson's version distributed via Usenet, not the + * version distributed earlier via floppy disk by Reinhold Weicker. + * As a concession to older compilers, names have been made + * unique within the first 8 characters. + * Inside the measurement loop, this version follows the + * version previously distributed by Reinhold Weicker. + * + * At several places in the benchmark, code has been added, + * but within the measurement loop only in branches that + * are not executed. The intention is that optimizing compilers + * should be prevented from moving code out of the measurement + * loop, or from removing code altogether. Since the statements + * that are executed within the measurement loop have NOT been + * changed, the numbers defining the "Dhrystone distribution" + * (distribution of statements, operand types and locality) + * still hold. Except for sophisticated optimizing compilers, + * execution times for this version should be the same as + * for previous versions. + * + * Since it has proven difficult to subtract the time for the + * measurement loop overhead in a correct way, the loop check + * has been made a part of the benchmark. This does have + * an impact - though a very minor one - on the distribution + * statistics which have been updated for this version. + * + * All changes within the measurement loop are described + * and discussed in the companion paper "Rationale for + * Dhrystone version 2". + * + * Because of the self-imposed limitation that the order and + * distribution of the executed statements should not be + * changed, there are still cases where optimizing compilers + * may not generate code for some statements. To a certain + * degree, this is unavoidable for small synthetic benchmarks. + * Users of the benchmark are advised to check code listings + * whether code is generated for all statements of Dhrystone. + * + * Version 2.1 is identical to version 2.0 distributed via + * the UNIX network Usenet in March 1988 except that it corrects + * some minor deficiencies that were found by users of version 2.0. + * The only change within the measurement loop is that a + * non-executed "else" part was added to the "if" statement in + * Func_3, and a non-executed "else" part removed from Proc_3. + * + *************************************************************************** + * + * Defines: The following "Defines" are possible: + * -DREG=register (default: Not defined) + * As an approximation to what an average C programmer + * might do, the "register" storage class is applied + * (if enabled by -DREG=register) + * - for local variables, if they are used (dynamically) + * five or more times + * - for parameters if they are used (dynamically) + * six or more times + * Note that an optimal "register" strategy is + * compiler-dependent, and that "register" declarations + * do not necessarily lead to faster execution. + * -DNOSTRUCTASSIGN (default: Not defined) + * Define if the C compiler does not support + * assignment of structures. + * -DNOENUMS (default: Not defined) + * Define if the C compiler does not support + * enumeration types. + * -DTIMES (default) + * -DTIME + * The "times" function of UNIX (returning process times) + * or the "time" function (returning wallclock time) + * is used for measurement. + * For single user machines, "time ()" is adequate. For + * multi-user machines where you cannot get single-user + * access, use the "times ()" function. If you have + * neither, use a stopwatch in the dead of night. + * "printf"s are provided marking the points "Start Timer" + * and "Stop Timer". DO NOT use the UNIX "time(1)" + * command, as this will measure the total time to + * run this program, which will (erroneously) include + * the time to allocate storage (malloc) and to perform + * the initialization. + * -DHZ=nnn + * In Berkeley UNIX, the function "times" returns process + * time in 1/HZ seconds, with HZ = 60 for most systems. + * CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY + * A VALUE. + * + *************************************************************************** + * + * Compilation model and measurement (IMPORTANT): + * + * This C version of Dhrystone consists of three files: + * - dhry.h (this file, containing global definitions and comments) + * - dhry_1.c (containing the code corresponding to Ada package Pack_1) + * - dhry_2.c (containing the code corresponding to Ada package Pack_2) + * + * The following "ground rules" apply for measurements: + * - Separate compilation + * - No procedure merging + * - Otherwise, compiler optimizations are allowed but should be indicated + * - Default results are those without register declarations + * See the companion paper "Rationale for Dhrystone Version 2" for a more + * detailed discussion of these ground rules. + * + * For 16-Bit processors (e.g. 80186, 80286), times for all compilation + * models ("small", "medium", "large" etc.) should be given if possible, + * together with a definition of these models for the compiler system used. + * + ************************************************************************** + * + * Dhrystone (C version) statistics: + * + * [Comment from the first distribution, updated for version 2. + * Note that because of language differences, the numbers are slightly + * different from the Ada version.] + * + * The following program contains statements of a high level programming + * language (here: C) in a distribution considered representative: + * + * assignments 52 (51.0 %) + * control statements 33 (32.4 %) + * procedure, function calls 17 (16.7 %) + * + * 103 statements are dynamically executed. The program is balanced with + * respect to the three aspects: + * + * - statement type + * - operand type + * - operand locality + * operand global, local, parameter, or constant. + * + * The combination of these three aspects is balanced only approximately. + * + * 1. Statement Type: + * ----------------- number + * + * V1 = V2 9 + * (incl. V1 = F(..) + * V = Constant 12 + * Assignment, 7 + * with array element + * Assignment, 6 + * with record component + * -- + * 34 34 + * + * X = Y +|-|"&&"|"|" Z 5 + * X = Y +|-|"==" Constant 6 + * X = X +|- 1 3 + * X = Y *|/ Z 2 + * X = Expression, 1 + * two operators + * X = Expression, 1 + * three operators + * -- + * 18 18 + * + * if .... 14 + * with "else" 7 + * without "else" 7 + * executed 3 + * not executed 4 + * for ... 7 | counted every time + * while ... 4 | the loop condition + * do ... while 1 | is evaluated + * switch ... 1 + * break 1 + * declaration with 1 + * initialization + * -- + * 34 34 + * + * P (...) procedure call 11 + * user procedure 10 + * library procedure 1 + * X = F (...) + * function call 6 + * user function 5 + * library function 1 + * -- + * 17 17 + * --- + * 103 + * + * The average number of parameters in procedure or function calls + * is 1.82 (not counting the function values as implicit parameters). + * + * + * 2. Operators + * ------------ + * number approximate + * percentage + * + * Arithmetic 32 50.8 + * + * + 21 33.3 + * - 7 11.1 + * * 3 4.8 + * / (int div) 1 1.6 + * + * Comparison 27 42.8 + * + * == 9 14.3 + * /= 4 6.3 + * > 1 1.6 + * < 3 4.8 + * >= 1 1.6 + * <= 9 14.3 + * + * Logic 4 6.3 + * + * && (AND-THEN) 1 1.6 + * | (OR) 1 1.6 + * ! (NOT) 2 3.2 + * + * -- ----- + * 63 100.1 + * + * + * 3. Operand Type (counted once per operand reference): + * --------------- + * number approximate + * percentage + * + * Integer 175 72.3 % + * Character 45 18.6 % + * Pointer 12 5.0 % + * String30 6 2.5 % + * Array 2 0.8 % + * Record 2 0.8 % + * --- ------- + * 242 100.0 % + * + * When there is an access path leading to the final operand (e.g. a record + * component), only the final data type on the access path is counted. + * + * + * 4. Operand Locality: + * ------------------- + * number approximate + * percentage + * + * local variable 114 47.1 % + * global variable 22 9.1 % + * parameter 45 18.6 % + * value 23 9.5 % + * reference 22 9.1 % + * function result 6 2.5 % + * constant 55 22.7 % + * --- ------- + * 242 100.0 % + * + * + * The program does not compute anything meaningful, but it is syntactically + * and semantically correct. All variables have a value assigned to them + * before they are used as a source operand. + * + * There has been no explicit effort to account for the effects of a + * cache, or to balance the use of long or short displacements for code or + * data. + * + *************************************************************************** + */ + + +/* Compiler and system dependent definitions: */ + +#ifndef TIME +#define TIMES +#endif + /* Use times(2) time function unless */ + /* explicitly defined otherwise */ + +#define Mic_secs_Per_Second 1000000.0 + /* Berkeley UNIX C returns process times in seconds/HZ */ + +#ifdef NOSTRUCTASSIGN +#define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) +#else +#define structassign(d, s) d = s +#endif + +#ifdef NOENUM +#define Ident_1 0 +#define Ident_2 1 +#define Ident_3 2 +#define Ident_4 3 +#define Ident_5 4 + typedef int Enumeration; +#else + typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5} + Enumeration; +#endif + /* for boolean and enumeration types in Ada, Pascal */ + +/* General definitions: */ + +#define Null 0 + /* Value of a Null pointer */ +#define true 1 +#define false 0 + +typedef int One_Thirty; +typedef int One_Fifty; +typedef char Capital_Letter; +typedef int Boolean; +typedef char Str_30 [31]; +typedef int Arr_1_Dim [50]; +typedef int Arr_2_Dim [50] [50]; + +typedef struct record + { + struct record *Ptr_Comp; + Enumeration Discr; + union { + struct { + Enumeration Enum_Comp; + int Int_Comp; + char Str_Comp [31]; + } var_1; + struct { + Enumeration E_Comp_2; + char Str_2_Comp [31]; + } var_2; + struct { + char Ch_1_Comp; + char Ch_2_Comp; + } var_3; + } variant; + } Rec_Type, *Rec_Pointer; + + +/* + * dhry() - run dhrystone for a given number of iterations + * + * @iterations: Number of iterations to run + */ +void dhry(int iterations); diff --git a/lib/dhry/dhry_1.c b/lib/dhry/dhry_1.c new file mode 100644 index 0000000..be63710 --- /dev/null +++ b/lib/dhry/dhry_1.c @@ -0,0 +1,421 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry_1.c SID: 3.4 5/15/91 19:30:21 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * + * *** WARNING **** With BYTE's modifications applied, results obtained with + * ******* this version of the Dhrystone program may not be applicable + * to other versions. + * + * Modification Log: + * 10/22/97 - code cleanup to remove ANSI C compiler warnings + * Andy Kahn <kahn@zk3.dec.com> + * + * Adapted from: + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry_1.c (part 2 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + ***************************************************************************/ +char SCCSid[] = "@(#) @(#)dhry_1.c:3.4 -- 5/15/91 19:30:21"; + +#include <common.h> +#include <malloc.h> + +#include "dhry.h" + +unsigned long Run_Index; + +void report(void) +{ + printf("%ld loops\n", Run_Index); +} + +/* Global Variables: */ + +Rec_Pointer Ptr_Glob, + Next_Ptr_Glob; +int Int_Glob; +Boolean Bool_Glob; +char Ch_1_Glob, + Ch_2_Glob; +int Arr_1_Glob [50]; +int Arr_2_Glob [50] [50]; + +Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val); + /* forward declaration necessary since Enumeration may not simply be int */ + +#ifndef REG + Boolean Reg = false; +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else + Boolean Reg = true; +#endif + +/* variables for time measurement: */ + +#ifdef TIMES +#define Too_Small_Time 120 + /* Measurements should last at least about 2 seconds */ +#endif +#ifdef TIME +extern long time(); + /* see library function "time" */ +#define Too_Small_Time 2 + /* Measurements should last at least 2 seconds */ +#endif + +long Begin_Time, + End_Time, + User_Time; + +/* end of variables for time measurement */ + +void Proc_1 (REG Rec_Pointer Ptr_Val_Par); +void Proc_2 (One_Fifty *Int_Par_Ref); +void Proc_3 (Rec_Pointer *Ptr_Ref_Par); +void Proc_4 (void); +void Proc_5 (void); + + +extern Boolean Func_2(Str_30, Str_30); +extern void Proc_6(Enumeration, Enumeration *); +extern void Proc_7(One_Fifty, One_Fifty, One_Fifty *); +extern void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int); + +void dhry(int Number_Of_Runs) + /* main program, corresponds to procedures */ + /* Main and Proc_0 in the Ada version */ +{ + One_Fifty Int_1_Loc; + REG One_Fifty Int_2_Loc; + One_Fifty Int_3_Loc; + REG char Ch_Index; + Enumeration Enum_Loc; + Str_30 Str_1_Loc; + Str_30 Str_2_Loc; + + /* Initializations */ + + Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + + Ptr_Glob->Ptr_Comp = Next_Ptr_Glob; + Ptr_Glob->Discr = Ident_1; + Ptr_Glob->variant.var_1.Enum_Comp = Ident_3; + Ptr_Glob->variant.var_1.Int_Comp = 40; + strcpy (Ptr_Glob->variant.var_1.Str_Comp, + "DHRYSTONE PROGRAM, SOME STRING"); + strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); + + Arr_2_Glob [8][7] = 10; + /* Was missing in published program. Without this statement, */ + /* Arr_2_Glob [8][7] would have an undefined value. */ + /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */ + /* overflow may occur for this array element. */ + +#ifdef PRATTLE + printf ("\n"); + printf ("Dhrystone Benchmark, Version 2.1 (Language: C)\n"); + printf ("\n"); + if (Reg) + { + printf ("Program compiled with 'register' attribute\n"); + printf ("\n"); + } + else + { + printf ("Program compiled without 'register' attribute\n"); + printf ("\n"); + } + printf ("Please give the number of runs through the benchmark: "); + { + int n; + scanf ("%d", &n); + Number_Of_Runs = n; + } + printf ("\n"); + + printf ("Execution starts, %d runs through Dhrystone\n", Number_Of_Runs); +#endif /* PRATTLE */ + + Run_Index = 0; + + /***************/ + /* Start timer */ + /***************/ + +#ifdef SELF_TIMED +#ifdef TIMES + times (&time_info); + Begin_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + Begin_Time = time ( (long *) 0); +#endif +#endif /* SELF_TIMED */ + + for (Run_Index = 1; Run_Index < Number_Of_Runs; ++Run_Index) + { + + Proc_5(); + Proc_4(); + /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */ + Int_1_Loc = 2; + Int_2_Loc = 3; + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + Enum_Loc = Ident_2; + Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc); + /* Bool_Glob == 1 */ + while (Int_1_Loc < Int_2_Loc) /* loop body executed once */ + { + Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc; + /* Int_3_Loc == 7 */ + Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc); + /* Int_3_Loc == 7 */ + Int_1_Loc += 1; + } /* while */ + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc); + /* Int_Glob == 5 */ + Proc_1 (Ptr_Glob); + for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index) + /* loop body executed twice */ + { + if (Enum_Loc == Func_1 (Ch_Index, 'C')) + /* then, not executed */ + { + Proc_6 (Ident_1, &Enum_Loc); + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING"); + Int_2_Loc = Run_Index; + Int_Glob = Run_Index; + } + } + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Int_2_Loc = Int_2_Loc * Int_1_Loc; + Int_1_Loc = Int_2_Loc / Int_3_Loc; + Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc; + /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */ + Proc_2 (&Int_1_Loc); + /* Int_1_Loc == 5 */ + + } /* loop "for Run_Index" */ + + /**************/ + /* Stop timer */ + /**************/ +#ifdef SELF_TIMED +#ifdef TIMES + times (&time_info); + End_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + End_Time = time ( (long *) 0); +#endif +#endif /* SELF_TIMED */ + + /* BYTE version never executes this stuff */ +#ifdef SELF_TIMED + printf ("Execution ends\n"); + printf ("\n"); + printf ("Final values of the variables used in the benchmark:\n"); + printf ("\n"); + printf ("Int_Glob: %d\n", Int_Glob); + printf (" should be: %d\n", 5); + printf ("Bool_Glob: %d\n", Bool_Glob); + printf (" should be: %d\n", 1); + printf ("Ch_1_Glob: %c\n", Ch_1_Glob); + printf (" should be: %c\n", 'A'); + printf ("Ch_2_Glob: %c\n", Ch_2_Glob); + printf (" should be: %c\n", 'B'); + printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]); + printf (" should be: %d\n", 7); + printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]); + printf (" should be: Number_Of_Runs + 10\n"); + printf ("Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent)\n"); + printf (" Discr: %d\n", Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 2); + printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 17); + printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Next_Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent), same as above\n"); + printf (" Discr: %d\n", Next_Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 1); + printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 18); + printf (" Str_Comp: %s\n", + Next_Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Int_1_Loc: %d\n", Int_1_Loc); + printf (" should be: %d\n", 5); + printf ("Int_2_Loc: %d\n", Int_2_Loc); + printf (" should be: %d\n", 13); + printf ("Int_3_Loc: %d\n", Int_3_Loc); + printf (" should be: %d\n", 7); + printf ("Enum_Loc: %d\n", Enum_Loc); + printf (" should be: %d\n", 1); + printf ("Str_1_Loc: %s\n", Str_1_Loc); + printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n"); + printf ("Str_2_Loc: %s\n", Str_2_Loc); + printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n"); + printf ("\n"); + + User_Time = End_Time - Begin_Time; + + if (User_Time < Too_Small_Time) + { + printf ("Measured time too small to obtain meaningful results\n"); + printf ("Please increase number of runs\n"); + printf ("\n"); + } + else + { +#ifdef TIME + Microseconds = (float) User_Time * Mic_secs_Per_Second + / (float) Number_Of_Runs; + Dhrystones_Per_Second = (float) Number_Of_Runs / (float) User_Time; +#else + Microseconds = (float) User_Time * Mic_secs_Per_Second + / ((float) HZ * ((float) Number_Of_Runs)); + Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs) + / (float) User_Time; +#endif + printf ("Microseconds for one run through Dhrystone: "); + printf ("%6.1f \n", Microseconds); + printf ("Dhrystones per Second: "); + printf ("%6.1f \n", Dhrystones_Per_Second); + printf ("\n"); + } +#endif /* SELF_TIMED */ +} + + +void Proc_1 (REG Rec_Pointer Ptr_Val_Par) + /* executed once */ +{ + REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp; + /* == Ptr_Glob_Next */ + /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */ + /* corresponds to "rename" in Ada, "with" in Pascal */ + + structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob); + Ptr_Val_Par->variant.var_1.Int_Comp = 5; + Next_Record->variant.var_1.Int_Comp + = Ptr_Val_Par->variant.var_1.Int_Comp; + Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp; + Proc_3 (&Next_Record->Ptr_Comp); + /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp + == Ptr_Glob->Ptr_Comp */ + if (Next_Record->Discr == Ident_1) + /* then, executed */ + { + Next_Record->variant.var_1.Int_Comp = 6; + Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp, + &Next_Record->variant.var_1.Enum_Comp); + Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp; + Proc_7 (Next_Record->variant.var_1.Int_Comp, 10, + &Next_Record->variant.var_1.Int_Comp); + } + else /* not executed */ + structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp); +} /* Proc_1 */ + + +void Proc_2 (One_Fifty *Int_Par_Ref) + /* executed once */ + /* *Int_Par_Ref == 1, becomes 4 */ +{ + One_Fifty Int_Loc; + Enumeration Enum_Loc; + + Enum_Loc = 0; + + Int_Loc = *Int_Par_Ref + 10; + do /* executed once */ + if (Ch_1_Glob == 'A') + /* then, executed */ + { + Int_Loc -= 1; + *Int_Par_Ref = Int_Loc - Int_Glob; + Enum_Loc = Ident_1; + } /* if */ + while (Enum_Loc != Ident_1); /* true */ +} /* Proc_2 */ + + +void Proc_3 (Rec_Pointer *Ptr_Ref_Par) + /* executed once */ + /* Ptr_Ref_Par becomes Ptr_Glob */ +{ + if (Ptr_Glob != Null) + /* then, executed */ + *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp; + Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp); +} /* Proc_3 */ + + +void Proc_4 (void) /* without parameters */ + /* executed once */ +{ + Boolean Bool_Loc; + + Bool_Loc = Ch_1_Glob == 'A'; + Bool_Glob = Bool_Loc | Bool_Glob; + Ch_2_Glob = 'B'; +} /* Proc_4 */ + +void Proc_5 (void) /* without parameters */ +/*******/ + /* executed once */ +{ + Ch_1_Glob = 'A'; + Bool_Glob = false; +} /* Proc_5 */ + + + /* Procedure for the assignment of structures, */ + /* if the C compiler doesn't support this feature */ +#ifdef NOSTRUCTASSIGN +memcpy (d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} +#endif diff --git a/lib/dhry/dhry_2.c b/lib/dhry/dhry_2.c new file mode 100644 index 0000000..59aa458 --- /dev/null +++ b/lib/dhry/dhry_2.c @@ -0,0 +1,217 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Dhrystone is widely available in the public domain. A GPL license is + * chosen for U-Boot. + */ + +/***************************************************************************** + * The BYTE UNIX Benchmarks - Release 3 + * Module: dhry_2.c SID: 3.4 5/15/91 19:30:22 + * + ***************************************************************************** + * Bug reports, patches, comments, suggestions should be sent to: + * + * Ben Smith, Rick Grehan or Tom Yager + * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com + * + ***************************************************************************** + * Modification Log: + * 10/22/97 - code cleanup to remove ANSI C compiler warnings + * Andy Kahn <kahn@zk3.dec.com> + * + * Adapted from: + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * **** WARNING **** See warning in n.dhry_1.c + * + * Version: C, Version 2.1 + * + * File: dhry_2.c (part 3 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + ****************************************************************************/ +/* SCCSid is defined in dhry_1.c */ + +#include <common.h> +#include "dhry.h" + +#ifndef REG +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#endif + +extern int Int_Glob; +extern char Ch_1_Glob; + +void Proc_6(Enumeration, Enumeration *); +void Proc_7(One_Fifty, One_Fifty, One_Fifty *); +void Proc_8(Arr_1_Dim, Arr_2_Dim, int, int); +Enumeration Func_1(Capital_Letter, Capital_Letter); +Boolean Func_2(Str_30, Str_30); +Boolean Func_3(Enumeration); + +void Proc_6 (Enumeration Enum_Val_Par, Enumeration *Enum_Ref_Par) + /* executed once */ + /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */ +{ + *Enum_Ref_Par = Enum_Val_Par; + if (! Func_3 (Enum_Val_Par)) + /* then, not executed */ + *Enum_Ref_Par = Ident_4; + switch (Enum_Val_Par) + { + case Ident_1: + *Enum_Ref_Par = Ident_1; + break; + case Ident_2: + if (Int_Glob > 100) + /* then */ + *Enum_Ref_Par = Ident_1; + else *Enum_Ref_Par = Ident_4; + break; + case Ident_3: /* executed */ + *Enum_Ref_Par = Ident_2; + break; + case Ident_4: break; + case Ident_5: + *Enum_Ref_Par = Ident_3; + break; + } /* switch */ +} /* Proc_6 */ + +void Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref) +One_Fifty Int_1_Par_Val; +One_Fifty Int_2_Par_Val; +One_Fifty *Int_Par_Ref; +/**********************************************/ + /* executed three times */ + /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */ + /* Int_Par_Ref becomes 7 */ + /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */ + /* Int_Par_Ref becomes 17 */ + /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */ + /* Int_Par_Ref becomes 18 */ +{ + One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 2; + *Int_Par_Ref = Int_2_Par_Val + Int_Loc; +} /* Proc_7 */ + + +void Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val) +/*********************************************************************/ + /* executed once */ + /* Int_Par_Val_1 == 3 */ + /* Int_Par_Val_2 == 7 */ +Arr_1_Dim Arr_1_Par_Ref; +Arr_2_Dim Arr_2_Par_Ref; +int Int_1_Par_Val; +int Int_2_Par_Val; +{ + REG One_Fifty Int_Index; + REG One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 5; + Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val; + Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc]; + Arr_1_Par_Ref [Int_Loc+30] = Int_Loc; + for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index) + Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc; + Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1; + Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc]; + Int_Glob = 5; +} /* Proc_8 */ + + +Enumeration Func_1 (Capital_Letter Ch_1_Par_Val, Capital_Letter Ch_2_Par_Val) +/*************************************************/ + /* executed three times */ + /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */ + /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */ + /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */ +{ + Capital_Letter Ch_1_Loc; + Capital_Letter Ch_2_Loc; + + Ch_1_Loc = Ch_1_Par_Val; + Ch_2_Loc = Ch_1_Loc; + if (Ch_2_Loc != Ch_2_Par_Val) + /* then, executed */ + return (Ident_1); + else /* not executed */ + { + Ch_1_Glob = Ch_1_Loc; + return (Ident_2); + } +} /* Func_1 */ + + + +Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref) +/*************************************************/ + /* executed once */ + /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */ + /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */ + +Str_30 Str_1_Par_Ref; +Str_30 Str_2_Par_Ref; +{ + REG One_Thirty Int_Loc; + Capital_Letter Ch_Loc; + + Ch_Loc = 'A'; + Int_Loc = 2; + while (Int_Loc <= 2) /* loop body executed once */ + if (Func_1 (Str_1_Par_Ref[Int_Loc], + Str_2_Par_Ref[Int_Loc+1]) == Ident_1) + /* then, executed */ + { + Ch_Loc = 'A'; + Int_Loc += 1; + } /* if, while */ + if (Ch_Loc >= 'W' && Ch_Loc < 'Z') + /* then, not executed */ + Int_Loc = 7; + if (Ch_Loc == 'R') + /* then, not executed */ + return (true); + else /* executed */ + { + if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0) + /* then, not executed */ + { + Int_Loc += 7; + Int_Glob = Int_Loc; + return (true); + } + else /* executed */ + return (false); + } /* if Ch_Loc */ +} /* Func_2 */ + + +Boolean Func_3 (Enum_Par_Val) +/***************************/ + /* executed once */ + /* Enum_Par_Val == Ident_3 */ +Enumeration Enum_Par_Val; +{ + Enumeration Enum_Loc; + + Enum_Loc = Enum_Par_Val; + if (Enum_Loc == Ident_3) + /* then, executed */ + return (true); + else /* not executed */ + return (false); +} /* Func_3 */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 9c6b361..232ca74 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -505,8 +505,7 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, const char *prop; const char *name; const char *slash; - const char *p; - int len; + int len, val; prop = fdt_getprop_by_offset(blob, prop_offset, &name, &len); debug(" - %s, %s\n", name, prop); @@ -517,12 +516,11 @@ int fdtdec_get_alias_seq(const void *blob, const char *base, int offset, slash = strrchr(prop, '/'); if (strcmp(slash + 1, find_name)) continue; - for (p = name + strlen(name) - 1; p > name; p--) { - if (!isdigit(*p)) { - *seqp = simple_strtoul(p + 1, NULL, 10); - debug("Found seq %d\n", *seqp); - return 0; - } + val = trailing_strtol(name); + if (val != -1) { + *seqp = val; + debug("Found seq %d\n", *seqp); + return 0; } } @@ -570,6 +568,13 @@ int fdtdec_prepare_fdt(void) puts("Missing DTB\n"); #else puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d <file.dtb>\n"); +# ifdef DEBUG + if (gd->fdt_blob) { + printf("fdt_blob=%p\n", gd->fdt_blob); + print_buffer((ulong)gd->fdt_blob, gd->fdt_blob, 4, + 32, 0); + } +# endif #endif return -1; } diff --git a/lib/libfdt/Makefile b/lib/libfdt/Makefile index 2f5413f..934d614 100644 --- a/lib/libfdt/Makefile +++ b/lib/libfdt/Makefile @@ -6,4 +6,4 @@ # obj-y += fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o \ - fdt_empty_tree.o fdt_addresses.o + fdt_empty_tree.o fdt_addresses.o fdt_region.o diff --git a/lib/libfdt/fdt_region.c b/lib/libfdt/fdt_region.c new file mode 100644 index 0000000..9fea775 --- /dev/null +++ b/lib/libfdt/fdt_region.c @@ -0,0 +1,492 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2013 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause + */ + +#include "libfdt_env.h" + +#ifndef USE_HOSTCC +#include <fdt.h> +#include <libfdt.h> +#else +#include "fdt_host.h" +#endif + +#include "libfdt_internal.h" + +/** + * fdt_add_region() - Add a new region to our list + * + * The region is added if there is space, but in any case we increment the + * count. If permitted, and the new region overlaps the last one, we merge + * them. + * + * @info: State information + * @offset: Start offset of region + * @size: Size of region + */ +static int fdt_add_region(struct fdt_region_state *info, int offset, int size) +{ + struct fdt_region *reg; + + reg = info->region ? &info->region[info->count - 1] : NULL; + if (info->can_merge && info->count && + info->count <= info->max_regions && + reg && offset <= reg->offset + reg->size) { + reg->size = offset + size - reg->offset; + } else if (info->count++ < info->max_regions) { + if (reg) { + reg++; + reg->offset = offset; + reg->size = size; + } + } else { + return -1; + } + + return 0; +} + +static int region_list_contains_offset(struct fdt_region_state *info, + const void *fdt, int target) +{ + struct fdt_region *reg; + int num; + + target += fdt_off_dt_struct(fdt); + for (reg = info->region, num = 0; num < info->count; reg++, num++) { + if (target >= reg->offset && target < reg->offset + reg->size) + return 1; + } + + return 0; +} + +int fdt_add_alias_regions(const void *fdt, struct fdt_region *region, int count, + int max_regions, struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int node, node_end, offset; + int did_alias_header; + + node = fdt_subnode_offset(fdt, 0, "aliases"); + if (node < 0) + return -FDT_ERR_NOTFOUND; + + /* The aliases node must come before the others */ + node_end = fdt_next_subnode(fdt, node); + if (node_end <= 0) + return -FDT_ERR_BADLAYOUT; + node_end -= sizeof(fdt32_t); + + did_alias_header = 0; + info->region = region; + info->count = count; + info->can_merge = 0; + info->max_regions = max_regions; + + for (offset = fdt_first_property_offset(fdt, node); + offset >= 0; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *name; + int target, next; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + target = fdt_path_offset(fdt, name); + if (!region_list_contains_offset(info, fdt, target)) + continue; + next = fdt_next_property_offset(fdt, offset); + if (next < 0) + next = node_end - sizeof(fdt32_t); + + if (!did_alias_header) { + fdt_add_region(info, base + node, 12); + did_alias_header = 1; + } + fdt_add_region(info, base + offset, next - offset); + } + + /* Add the 'end' tag */ + if (did_alias_header) + fdt_add_region(info, base + node_end, sizeof(fdt32_t)); + + return info->count < max_regions ? info->count : -FDT_ERR_NOSPACE; +} + +/** + * fdt_include_supernodes() - Include supernodes required by this node + * + * When we decided to include a node or property which is not at the top + * level, this function forces the inclusion of higher level nodes. For + * example, given this tree: + * + * / { + * testing { + * } + * } + * + * If we decide to include testing then we need the root node to have a valid + * tree. This function adds those regions. + * + * @info: State information + * @depth: Current stack depth + */ +static int fdt_include_supernodes(struct fdt_region_state *info, int depth) +{ + int base = fdt_off_dt_struct(info->fdt); + int start, stop_at; + int i; + + /* + * Work down the stack looking for supernodes that we didn't include. + * The algortihm here is actually pretty simple, since we know that + * no previous subnode had to include these nodes, or if it did, we + * marked them as included (on the stack) already. + */ + for (i = 0; i <= depth; i++) { + if (!info->stack[i].included) { + start = info->stack[i].offset; + + /* Add the FDT_BEGIN_NODE tag of this supernode */ + fdt_next_tag(info->fdt, start, &stop_at); + if (fdt_add_region(info, base + start, stop_at - start)) + return -1; + + /* Remember that this supernode is now included */ + info->stack[i].included = 1; + info->can_merge = 1; + } + + /* Force (later) generation of the FDT_END_NODE tag */ + if (!info->stack[i].want) + info->stack[i].want = WANT_NODES_ONLY; + } + + return 0; +} + +enum { + FDT_DONE_NOTHING, + FDT_DONE_MEM_RSVMAP, + FDT_DONE_STRUCT, + FDT_DONE_END, + FDT_DONE_STRINGS, + FDT_DONE_ALL, +}; + +int fdt_first_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + struct fdt_region_ptrs *p = &info->ptrs; + + /* Set up our state */ + info->fdt = fdt; + info->can_merge = 1; + info->max_regions = 1; + info->start = -1; + p->want = WANT_NOTHING; + p->end = path; + *p->end = '\0'; + p->nextoffset = 0; + p->depth = -1; + p->done = FDT_DONE_NOTHING; + + return fdt_next_region(fdt, h_include, priv, region, + path, path_len, flags, info); +} + +/* + * Theory of operation + * + * + * + +Note: in this description 'included' means that a node (or other part of +the tree) should be included in the region list, i.e. it will have a region +which covers its part of the tree. + +This function maintains some state from the last time it is called. It +checks the next part of the tree that it is supposed to look at +(p.nextoffset) to see if that should be included or not. When it finds +something to include, it sets info->start to its offset. This marks the +start of the region we want to include. + +Once info->start is set to the start (i.e. not -1), we continue scanning +until we find something that we don't want included. This will be the end +of a region. At this point we can close off the region and add it to the +list. So we do so, and reset info->start to -1. + +One complication here is that we want to merge regions. So when we come to +add another region later, we may in fact merge it with the previous one if +one ends where the other starts. + +The function fdt_add_region() will return -1 if it fails to add the region, +because we already have a region ready to be returned, and the new one +cannot be merged in with it. In this case, we must return the region we +found, and wait for another call to this function. When it comes, we will +repeat the processing of the tag and again try to add a region. This time it +will succeed. + +The current state of the pointers (stack, offset, etc.) is maintained in +a ptrs member. At the start of every loop iteration we make a copy of it. +The copy is then updated as the tag is processed. Only if we get to the end +of the loop iteration (and successfully call fdt_add_region() if we need +to) can we commit the changes we have made to these pointers. For example, +if we see an FDT_END_NODE tag we will decrement the depth value. But if we +need to add a region for this tag (let's say because the previous tag is +included and this FDT_END_NODE tag is not included) then we will only commit +the result if we were able to add the region. That allows us to retry again +next time. + +We keep track of a variable called 'want' which tells us what we want to +include when there is no specific information provided by the h_include +function for a particular property. This basically handles the inclusion of +properties which are pulled in by virtue of the node they are in. So if you +include a node, its properties are also included. In this case 'want' will +be WANT_NODES_AND_PROPS. The FDT_REG_DIRECT_SUBNODES feature also makes use +of 'want'. While we are inside the subnode, 'want' will be set to +WANT_NODES_ONLY, so that only the subnode's FDT_BEGIN_NODE and FDT_END_NODE +tags will be included, and properties will be skipped. If WANT_NOTHING is +selected, then we will just rely on what the h_include() function tells us. + +Using 'want' we work out 'include', which tells us whether this current tag +should be included or not. As you can imagine, if the value of 'include' +changes, that means we are on a boundary between nodes to include and nodes +to exclude. At this point we either close off a previous region and add it +to the list, or mark the start of a new region. + +Apart from the nodes, we have mem_rsvmap, the FDT_END tag and the string +list. Each of these dealt with as a whole (i.e. we create a region for each +if it is to be included). For mem_rsvmap we don't allow it to merge with +the first struct region. For the stringlist we don't allow it to merge with +the last struct region (which contains at minimum the FDT_END tag). +*/ +int fdt_next_region(const void *fdt, + int (*h_include)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + void *priv, struct fdt_region *region, + char *path, int path_len, int flags, + struct fdt_region_state *info) +{ + int base = fdt_off_dt_struct(fdt); + int last_node = 0; + const char *str; + + info->region = region; + info->count = 0; + if (info->ptrs.done < FDT_DONE_MEM_RSVMAP && + (flags & FDT_REG_ADD_MEM_RSVMAP)) { + /* Add the memory reserve map into its own region */ + if (fdt_add_region(info, fdt_off_mem_rsvmap(fdt), + fdt_off_dt_struct(fdt) - + fdt_off_mem_rsvmap(fdt))) + return 0; + info->can_merge = 0; /* Don't allow merging with this */ + info->ptrs.done = FDT_DONE_MEM_RSVMAP; + } + + /* + * Work through the tags one by one, deciding whether each needs to + * be included or not. We set the variable 'include' to indicate our + * decision. 'want' is used to track what we want to include - it + * allows us to pick up all the properties (and/or subnode tags) of + * a node. + */ + while (info->ptrs.done < FDT_DONE_STRUCT) { + const struct fdt_property *prop; + struct fdt_region_ptrs p; + const char *name; + int include = 0; + int stop_at = 0; + uint32_t tag; + int offset; + int val; + int len; + + /* + * Make a copy of our pointers. If we make it to the end of + * this block then we will commit them back to info->ptrs. + * Otherwise we can try again from the same starting state + * next time we are called. + */ + p = info->ptrs; + + /* + * Find the tag, and the offset of the next one. If we need to + * stop including tags, then by default we stop *after* + * including the current tag + */ + offset = p.nextoffset; + tag = fdt_next_tag(fdt, offset, &p.nextoffset); + stop_at = p.nextoffset; + + switch (tag) { + case FDT_PROP: + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + val = h_include(priv, fdt, last_node, FDT_IS_PROP, str, + strlen(str) + 1); + if (val == -1) { + include = p.want >= WANT_NODES_AND_PROPS; + } else { + include = val; + /* + * Make sure we include the } for this block. + * It might be more correct to have this done + * by the call to fdt_include_supernodes() in + * the case where it adds the node we are + * currently in, but this is equivalent. + */ + if ((flags & FDT_REG_SUPERNODES) && val && + !p.want) + p.want = WANT_NODES_ONLY; + } + + /* Value grepping is not yet supported */ + break; + + case FDT_NOP: + include = p.want >= WANT_NODES_AND_PROPS; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + last_node = offset; + p.depth++; + if (p.depth == FDT_MAX_DEPTH) + return -FDT_ERR_TOODEEP; + name = fdt_get_name(fdt, offset, &len); + if (p.end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + + /* Build the full path of this node */ + if (p.end != path + 1) + *p.end++ = '/'; + strcpy(p.end, name); + p.end += len; + info->stack[p.depth].want = p.want; + info->stack[p.depth].offset = offset; + + /* + * If we are not intending to include this node unless + * it matches, make sure we stop *before* its tag. + */ + if (p.want == WANT_NODES_ONLY || + !(flags & (FDT_REG_DIRECT_SUBNODES | + FDT_REG_ALL_SUBNODES))) { + stop_at = offset; + p.want = WANT_NOTHING; + } + val = h_include(priv, fdt, offset, FDT_IS_NODE, path, + p.end - path + 1); + + /* Include this if requested */ + if (val) { + p.want = (flags & FDT_REG_ALL_SUBNODES) ? + WANT_ALL_NODES_AND_PROPS : + WANT_NODES_AND_PROPS; + } + + /* If not requested, decay our 'p.want' value */ + else if (p.want) { + if (p.want != WANT_ALL_NODES_AND_PROPS) + p.want--; + + /* Not including this tag, so stop now */ + } else { + stop_at = offset; + } + + /* + * Decide whether to include this tag, and update our + * stack with the state for this node + */ + include = p.want; + info->stack[p.depth].included = include; + break; + + case FDT_END_NODE: + include = p.want; + if (p.depth < 0) + return -FDT_ERR_BADSTRUCTURE; + + /* + * If we don't want this node, stop right away, unless + * we are including subnodes + */ + if (!p.want && !(flags & FDT_REG_DIRECT_SUBNODES)) + stop_at = offset; + p.want = info->stack[p.depth].want; + p.depth--; + while (p.end > path && *--p.end != '/') + ; + *p.end = '\0'; + break; + + case FDT_END: + /* We always include the end tag */ + include = 1; + p.done = FDT_DONE_STRUCT; + break; + } + + /* If this tag is to be included, mark it as region start */ + if (include && info->start == -1) { + /* Include any supernodes required by this one */ + if (flags & FDT_REG_SUPERNODES) { + if (fdt_include_supernodes(info, p.depth)) + return 0; + } + info->start = offset; + } + + /* + * If this tag is not to be included, finish up the current + * region. + */ + if (!include && info->start != -1) { + if (fdt_add_region(info, base + info->start, + stop_at - info->start)) + return 0; + info->start = -1; + info->can_merge = 1; + } + + /* If we have made it this far, we can commit our pointers */ + info->ptrs = p; + } + + /* Add a region for the END tag and a separate one for string table */ + if (info->ptrs.done < FDT_DONE_END) { + if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTRUCTURE; + + if (fdt_add_region(info, base + info->start, + info->ptrs.nextoffset - info->start)) + return 0; + info->ptrs.done++; + } + if (info->ptrs.done < FDT_DONE_STRINGS) { + if (flags & FDT_REG_ADD_STRING_TAB) { + info->can_merge = 0; + if (fdt_off_dt_strings(fdt) < + base + info->ptrs.nextoffset) + return -FDT_ERR_BADLAYOUT; + if (fdt_add_region(info, fdt_off_dt_strings(fdt), + fdt_size_dt_strings(fdt))) + return 0; + } + info->ptrs.done++; + } + + return info->count > 0 ? 0 : -FDT_ERR_NOTFOUND; +} diff --git a/lib/libfdt/fdt_rw.c b/lib/libfdt/fdt_rw.c index bec8b8a..1a358a8 100644 --- a/lib/libfdt/fdt_rw.c +++ b/lib/libfdt/fdt_rw.c @@ -449,3 +449,35 @@ int fdt_pack(void *fdt) return 0; } + +int fdt_remove_unused_strings(const void *old, void *new) +{ + const struct fdt_property *old_prop; + struct fdt_property *new_prop; + int size = fdt_totalsize(old); + int next_offset, offset; + const char *str; + int ret; + int tag = FDT_PROP; + + /* Make a copy and remove the strings */ + memcpy(new, old, size); + fdt_set_size_dt_strings(new, 0); + + /* Add every property name back into the new string table */ + for (offset = 0; tag != FDT_END; offset = next_offset) { + tag = fdt_next_tag(old, offset, &next_offset); + if (tag != FDT_PROP) + continue; + old_prop = fdt_get_property_by_offset(old, offset, NULL); + new_prop = (struct fdt_property *)(unsigned long) + fdt_get_property_by_offset(new, offset, NULL); + str = fdt_string(old, fdt32_to_cpu(old_prop->nameoff)); + ret = _fdt_find_add_string(new, str); + if (ret < 0) + return ret; + new_prop->nameoff = cpu_to_fdt32(ret); + } + + return 0; +} diff --git a/lib/linux_compat.c b/lib/linux_compat.c index a3d4675..a936a7e 100644 --- a/lib/linux_compat.c +++ b/lib/linux_compat.c @@ -16,19 +16,13 @@ unsigned long copy_from_user(void *dest, const void *src, void *kmalloc(size_t size, int flags) { - return memalign(ARCH_DMA_MINALIGN, size); -} + void *p; -void *kzalloc(size_t size, int flags) -{ - void *ptr = kmalloc(size, flags); - memset(ptr, 0, size); - return ptr; -} + p = memalign(ARCH_DMA_MINALIGN, size); + if (flags & __GFP_ZERO) + memset(p, 0, size); -void *vzalloc(unsigned long size) -{ - return kzalloc(size, 0); + return p; } struct kmem_cache *get_mem(int element_sz) diff --git a/lib/rc4.c b/lib/rc4.c new file mode 100644 index 0000000..89d15f3 --- /dev/null +++ b/lib/rc4.c @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * + * Rivest Cipher 4 (RC4) implementation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef USE_HOSTCC +#include <common.h> +#endif +#include <rc4.h> + +void rc4_encode(unsigned char *buf, unsigned int len, unsigned char key[16]) +{ + unsigned char s[256], k[256], temp; + unsigned short i, j, t; + int ptr; + + j = 0; + for (i = 0; i < 256; i++) { + s[i] = (unsigned char)i; + j &= 0x0f; + k[i] = key[j]; + j++; + } + + j = 0; + for (i = 0; i < 256; i++) { + j = (j + s[i] + k[i]) % 256; + temp = s[i]; + s[i] = s[j]; + s[j] = temp; + } + + i = 0; + j = 0; + for (ptr = 0; ptr < len; ptr++) { + i = (i + 1) % 256; + j = (j + s[i]) % 256; + temp = s[i]; + s[i] = s[j]; + s[j] = temp; + t = (s[i] + (s[j] % 256)) % 256; + buf[ptr] = buf[ptr] ^ s[t]; + } +} diff --git a/lib/vsprintf.c b/lib/vsprintf.c index a9b8a3a..4c82837 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -166,6 +166,25 @@ unsigned long long simple_strtoull(const char *cp, char **endp, return result; } +long trailing_strtoln(const char *str, const char *end) +{ + const char *p; + + if (!end) + end = str + strlen(str); + for (p = end - 1; p > str; p--) { + if (!isdigit(*p)) + return simple_strtoul(p + 1, NULL, 10); + } + + return -1; +} + +long trailing_strtol(const char *str) +{ + return trailing_strtoln(str, NULL); +} + /* we use this so that we can do without the ctype library */ #define is_digit(c) ((c) >= '0' && (c) <= '9') @@ -287,7 +287,13 @@ static int eth_write_hwaddr(struct udevice *dev) return -EINVAL; } + /* + * Drivers are allowed to decide not to implement this at + * run-time. E.g. Some devices may use it and some may not. + */ ret = eth_get_ops(dev)->write_hwaddr(dev); + if (ret == -ENOSYS) + ret = 0; if (ret) printf("\nWarning: %s failed to set MAC address\n", dev->name); @@ -404,6 +410,7 @@ int eth_rx(void) { struct udevice *current; uchar *packet; + int flags; int ret; int i; @@ -415,8 +422,10 @@ int eth_rx(void) return -EINVAL; /* Process up to 32 packets at one time */ + flags = ETH_RECV_CHECK_DEVICE; for (i = 0; i < 32; i++) { - ret = eth_get_ops(current)->recv(current, &packet); + ret = eth_get_ops(current)->recv(current, flags, &packet); + flags = 0; if (ret > 0) net_process_received_packet(packet, ret); if (ret >= 0 && eth_get_ops(current)->free_pkt) diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl index fd572f4..b1047b5 100644 --- a/scripts/Makefile.spl +++ b/scripts/Makefile.spl @@ -54,18 +54,22 @@ libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/ libs-$(CONFIG_SPL_FRAMEWORK) += common/spl/ libs-$(CONFIG_SPL_LIBCOMMON_SUPPORT) += common/ libs-$(CONFIG_SPL_LIBDISK_SUPPORT) += disk/ +libs-$(CONFIG_SPL_CLK_SUPPORT) += drivers/clk/ libs-$(CONFIG_SPL_DM) += drivers/core/ libs-$(CONFIG_SPL_I2C_SUPPORT) += drivers/i2c/ libs-$(CONFIG_SPL_GPIO_SUPPORT) += drivers/gpio/ libs-$(CONFIG_SPL_MMC_SUPPORT) += drivers/mmc/ libs-$(CONFIG_SPL_MPC8XXX_INIT_DDR_SUPPORT) += drivers/ddr/fsl/ -libs-$(CONFIG_SYS_MVEBU_DDR) += drivers/ddr/mvebu/ +libs-$(CONFIG_SYS_MVEBU_DDR_A38X) += drivers/ddr/marvell/a38x/ +libs-$(CONFIG_SYS_MVEBU_DDR_AXP) += drivers/ddr/marvell/axp/ libs-$(CONFIG_SPL_SERIAL_SUPPORT) += drivers/serial/ libs-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/ libs-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/ libs-y += fs/ +libs-$(CONFIG_SPL_LED_SUPPORT) += drivers/led/ libs-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/ libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/ drivers/power/pmic/ +libs-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/regulator/ libs-$(CONFIG_SPL_MTD_SUPPORT) += drivers/mtd/ libs-$(CONFIG_SPL_NAND_SUPPORT) += drivers/mtd/nand/ libs-$(CONFIG_SPL_DRIVERS_MISC_SUPPORT) += drivers/misc/ @@ -76,6 +80,7 @@ libs-$(CONFIG_SPL_NET_SUPPORT) += net/ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/ libs-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/phy/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/net/phy/ +libs-$(CONFIG_SPL_RAM_SUPPORT) += drivers/ram/ libs-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/ libs-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/ libs-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/ @@ -151,6 +156,8 @@ boot.bin: $(obj)/u-boot-spl.bin ALL-y += $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN).cfg +ALL-$(CONFIG_OF_SEPARATE) += $(obj)/$(SPL_BIN)-pad.bin $(obj)/$(SPL_BIN)-dtb.bin + ifdef CONFIG_SAMSUNG ALL-y += $(obj)/$(BOARD)-spl.bin endif @@ -165,6 +172,32 @@ endif all: $(ALL-y) +quiet_cmd_cat = CAT $@ +cmd_cat = cat $(filter-out $(PHONY), $^) > $@ + +$(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN).bin $(obj)/$(SPL_BIN)-pad.bin \ + $(obj)/$(SPL_BIN).dtb FORCE + $(call if_changed,cat) + +# Create a file that pads from the end of u-boot-spl.bin to bss_end +$(obj)/$(SPL_BIN)-pad.bin: $(obj)/$(SPL_BIN) + @bss_size_str=$(shell $(NM) $< | awk 'BEGIN {size = 0} /__bss_size/ {size = $$1} END {print "ibase=16; " toupper(size)}' | bc); \ + dd if=/dev/zero of=$@ bs=1 count=$${bss_size_str} 2>/dev/null; + +# Pass the original device tree file through fdtgrep twice. The first pass +# removes any unwanted nodes (i.e. those which don't have the +# 'u-boot,dm-pre-reloc' property and thus are not needed by SPL. The second +# pass removes various unused properties from the remaining nodes. +# The output is typically a much smaller device tree file. +quiet_cmd_fdtgrep = FDTGREP $@ + cmd_fdtgrep = $(objtree)/tools/fdtgrep -b u-boot,dm-pre-reloc -RT $< \ + -n /chosen -O dtb | \ + $(objtree)/tools/fdtgrep -r -O dtb - -o $@ \ + $(addprefix -P ,$(subst $\",,$(CONFIG_OF_SPL_REMOVE_PROPS))) + +$(obj)/$(SPL_BIN).dtb: dts/dt.dtb + $(call cmd,fdtgrep) + quiet_cmd_cpp_cfg = CFG $@ cmd_cpp_cfg = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \ -DDO_DEPS_ONLY -D__ASSEMBLY__ -x assembler-with-cpp -P -dM -E -o $@ $< diff --git a/test/dm/Makefile b/test/dm/Makefile index 19ad2fb..eda9643 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -15,13 +15,20 @@ obj-$(CONFIG_UT_DM) += test-uclass.o # subsystem you must add sandbox tests here. obj-$(CONFIG_UT_DM) += core.o ifneq ($(CONFIG_SANDBOX),) +obj-$(CONFIG_CLK) += clk.o obj-$(CONFIG_DM_ETH) += eth.o obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_I2C) += i2c.o +obj-$(CONFIG_LED) += led.o +obj-$(CONFIG_DM_MMC) += mmc.o obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_RAM) += ram.o +obj-y += regmap.o +obj-$(CONFIG_RESET) += reset.o obj-$(CONFIG_DM_RTC) += rtc.o obj-$(CONFIG_DM_SPI_FLASH) += sf.o obj-$(CONFIG_DM_SPI) += spi.o +obj-y += syscon.o obj-$(CONFIG_DM_USB) += usb.o obj-$(CONFIG_DM_PMIC) += pmic.o obj-$(CONFIG_DM_REGULATOR) += regulator.o diff --git a/test/dm/clk.c b/test/dm/clk.c new file mode 100644 index 0000000..9ff6d95 --- /dev/null +++ b/test/dm/clk.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <linux/err.h> +#include <test/ut.h> + +/* Test that we can find and adjust clocks */ +static int dm_test_clk_base(struct unit_test_state *uts) +{ + struct udevice *clk; + ulong rate; + + ut_assertok(uclass_get_device(UCLASS_CLK, 0, &clk)); + rate = clk_get_rate(clk); + ut_asserteq(SANDBOX_CLK_RATE, rate); + ut_asserteq(-EINVAL, clk_set_rate(clk, 0)); + ut_assertok(clk_set_rate(clk, rate * 2)); + ut_asserteq(SANDBOX_CLK_RATE * 2, clk_get_rate(clk)); + + return 0; +} +DM_TEST(dm_test_clk_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that peripheral clocks work as expected */ +static int dm_test_clk_periph(struct unit_test_state *uts) +{ + struct udevice *clk; + ulong rate; + + ut_assertok(uclass_get_device(UCLASS_CLK, 0, &clk)); + rate = clk_set_periph_rate(clk, PERIPH_ID_COUNT, 123); + ut_asserteq(-EINVAL, rate); + ut_asserteq(1, IS_ERR_VALUE(rate)); + + rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 123); + ut_asserteq(0, rate); + ut_asserteq(123, clk_get_periph_rate(clk, PERIPH_ID_SPI)); + + rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 1234); + ut_asserteq(123, rate); + + rate = clk_set_periph_rate(clk, PERIPH_ID_I2C, 567); + + rate = clk_set_periph_rate(clk, PERIPH_ID_SPI, 1234); + ut_asserteq(1234, rate); + + ut_asserteq(567, clk_get_periph_rate(clk, PERIPH_ID_I2C)); + + return 0; +} +DM_TEST(dm_test_clk_periph, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c index 5bb2a99..5c501ec 100644 --- a/test/dm/cmd_dm.c +++ b/test/dm/cmd_dm.c @@ -14,96 +14,20 @@ #include <errno.h> #include <asm/io.h> #include <dm/root.h> -#include <dm/uclass-internal.h> - -static void show_devices(struct udevice *dev, int depth, int last_flag) -{ - int i, is_last; - struct udevice *child; - char class_name[12]; - - /* print the first 11 characters to not break the tree-format. */ - strlcpy(class_name, dev->uclass->uc_drv->name, sizeof(class_name)); - printf(" %-11s [ %c ] ", class_name, - dev->flags & DM_FLAG_ACTIVATED ? '+' : ' '); - - for (i = depth; i >= 0; i--) { - is_last = (last_flag >> i) & 1; - if (i) { - if (is_last) - printf(" "); - else - printf("| "); - } else { - if (is_last) - printf("`-- "); - else - printf("|-- "); - } - } - - printf("%s\n", dev->name); - - list_for_each_entry(child, &dev->child_head, sibling_node) { - is_last = list_is_last(&child->sibling_node, &dev->child_head); - show_devices(child, depth + 1, (last_flag << 1) | is_last); - } -} +#include <dm/util.h> static int do_dm_dump_all(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - struct udevice *root; - - root = dm_root(); - if (root) { - printf(" Class Probed Name\n"); - printf("----------------------------------------\n"); - show_devices(root, -1, 0); - } + dm_dump_all(); return 0; } -/** - * dm_display_line() - Display information about a single device - * - * Displays a single line of information with an option prefix - * - * @dev: Device to display - */ -static void dm_display_line(struct udevice *dev) -{ - printf("- %c %s @ %08lx", - dev->flags & DM_FLAG_ACTIVATED ? '*' : ' ', - dev->name, (ulong)map_to_sysmem(dev)); - if (dev->seq != -1 || dev->req_seq != -1) - printf(", seq %d, (req %d)", dev->seq, dev->req_seq); - puts("\n"); -} - static int do_dm_dump_uclass(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - struct uclass *uc; - int ret; - int id; - - for (id = 0; id < UCLASS_COUNT; id++) { - struct udevice *dev; - - ret = uclass_get(id, &uc); - if (ret) - continue; - - printf("uclass %d: %s\n", id, uc->uc_drv->name); - if (list_empty(&uc->dev_head)) - continue; - list_for_each_entry(dev, &uc->dev_head, uclass_node) { - dm_display_line(dev); - } - puts("\n"); - } + dm_dump_uclass(); return 0; } diff --git a/test/dm/led.c b/test/dm/led.c new file mode 100644 index 0000000..8ee075c --- /dev/null +++ b/test/dm/led.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <led.h> +#include <asm/gpio.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Base test of the led uclass */ +static int dm_test_led_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Get the top-level device */ + ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 2, &dev)); + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_LED, 3, &dev)); + + return 0; +} +DM_TEST(dm_test_led_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test of the led uclass using the led_gpio driver */ +static int dm_test_led_gpio(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate an LED. LED 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + led_set_on(dev, 1); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + led_set_on(dev, 0); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + + return 0; +} +DM_TEST(dm_test_led_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test obtaining an LED by label */ +static int dm_test_led_label(struct unit_test_state *uts) +{ + struct udevice *dev, *cmp; + + ut_assertok(led_get_by_label("sandbox:red", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 1, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_assertok(led_get_by_label("sandbox:green", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 2, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_asserteq(-ENODEV, led_get_by_label("sandbox:blue", &dev)); + + return 0; +} +DM_TEST(dm_test_led_label, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/mmc.c b/test/dm/mmc.c new file mode 100644 index 0000000..0461423 --- /dev/null +++ b/test/dm/mmc.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mmc.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Basic test of the mmc uclass. We could expand this by implementing an MMC + * stack for sandbox, or at least implementing the basic operation. + */ +static int dm_test_mmc_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev)); + + return 0; +} +DM_TEST(dm_test_mmc_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/ram.c b/test/dm/ram.c new file mode 100644 index 0000000..3a7c5ff --- /dev/null +++ b/test/dm/ram.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <ram.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Basic test of the ram uclass */ +static int dm_test_ram_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct ram_info info; + + ut_assertok(uclass_get_device(UCLASS_RAM, 0, &dev)); + ut_assertok(ram_get_info(dev, &info)); + ut_asserteq(0, info.base); + ut_asserteq(gd->ram_size, info.size); + + return 0; +} +DM_TEST(dm_test_ram_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/regmap.c b/test/dm/regmap.c new file mode 100644 index 0000000..7f66058 --- /dev/null +++ b/test/dm/regmap.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Google, Inc +2 * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Base test of register maps */ +static int dm_test_regmap_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + int i; + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + ut_asserteq(1, map->range_count); + ut_asserteq(0x10, map->base); + ut_asserteq(0x10, map->range->start); + ut_asserteq(4, map->range->size); + ut_asserteq_ptr(&map->base_range, map->range); + ut_asserteq(0x10, map_to_sysmem(regmap_get_range(map, 0))); + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + ut_asserteq(4, map->range_count); + ut_asserteq(0x20, map->base); + ut_assert(&map->base_range != map->range); + for (i = 0; i < 4; i++) { + const unsigned long addr = 0x20 + 8 * i; + + ut_asserteq(addr, map->range[i].start); + ut_asserteq(5 + i, map->range[i].size); + ut_asserteq(addr, map_to_sysmem(regmap_get_range(map, i))); + } + + /* Check that we can't pretend a different device is a syscon */ + ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev)); + map = syscon_get_regmap(dev); + ut_asserteq_ptr(ERR_PTR(-ENOEXEC), map); + + return 0; +} +DM_TEST(dm_test_regmap_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test we can access a regmap through syscon */ +static int dm_test_regmap_syscon(struct unit_test_state *uts) +{ + struct regmap *map; + + map = syscon_get_regmap_by_driver_data(SYSCON0); + ut_assertok_ptr(map); + ut_asserteq(1, map->range_count); + + map = syscon_get_regmap_by_driver_data(SYSCON1); + ut_assertok_ptr(map); + ut_asserteq(4, map->range_count); + + map = syscon_get_regmap_by_driver_data(SYSCON_COUNT); + ut_asserteq_ptr(ERR_PTR(-ENODEV), map); + + ut_asserteq(0x10, map_to_sysmem(syscon_get_first_range(SYSCON0))); + ut_asserteq(0x20, map_to_sysmem(syscon_get_first_range(SYSCON1))); + ut_asserteq_ptr(ERR_PTR(-ENODEV), + syscon_get_first_range(SYSCON_COUNT)); + + return 0; +} + +DM_TEST(dm_test_regmap_syscon, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/regulator.c b/test/dm/regulator.c index d279c04..3d0056f 100644 --- a/test/dm/regulator.c +++ b/test/dm/regulator.c @@ -210,7 +210,7 @@ static int dm_test_power_regulator_autoset(struct unit_test_state *uts) * Expected output state: uV=1200000; uA=200000; output enabled */ platname = regulator_names[BUCK1][PLATNAME]; - ut_assertok(regulator_autoset(platname, &dev_autoset, false)); + ut_assertok(regulator_autoset_by_name(platname, &dev_autoset)); /* Check, that the returned device is proper */ ut_assertok(regulator_get_by_platname(platname, &dev)); diff --git a/test/dm/reset.c b/test/dm/reset.c new file mode 100644 index 0000000..5d53f25 --- /dev/null +++ b/test/dm/reset.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <reset.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Test that we can use particular reset devices */ +static int dm_test_reset_base(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + struct udevice *dev; + + /* Device 0 is the platform data device - it should never respond */ + ut_assertok(uclass_get_device(UCLASS_RESET, 0, &dev)); + ut_asserteq(-ENODEV, reset_request(dev, RESET_WARM)); + ut_asserteq(-ENODEV, reset_request(dev, RESET_COLD)); + ut_asserteq(-ENODEV, reset_request(dev, RESET_POWER)); + + /* Device 1 is the warm reset device */ + ut_assertok(uclass_get_device(UCLASS_RESET, 1, &dev)); + ut_asserteq(-EACCES, reset_request(dev, RESET_WARM)); + ut_asserteq(-ENOSYS, reset_request(dev, RESET_COLD)); + ut_asserteq(-ENOSYS, reset_request(dev, RESET_POWER)); + + state->reset_allowed[RESET_WARM] = true; + ut_asserteq(-EINPROGRESS, reset_request(dev, RESET_WARM)); + state->reset_allowed[RESET_WARM] = false; + + /* Device 2 is the cold reset device */ + ut_assertok(uclass_get_device(UCLASS_RESET, 2, &dev)); + ut_asserteq(-ENOSYS, reset_request(dev, RESET_WARM)); + ut_asserteq(-EACCES, reset_request(dev, RESET_COLD)); + state->reset_allowed[RESET_POWER] = false; + ut_asserteq(-EACCES, reset_request(dev, RESET_POWER)); + state->reset_allowed[RESET_POWER] = true; + + return 0; +} +DM_TEST(dm_test_reset_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that we can walk through the reset devices */ +static int dm_test_reset_walk(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + + /* If we generate a power reset, we will exit sandbox! */ + state->reset_allowed[RESET_POWER] = false; + ut_asserteq(-EACCES, reset_walk(RESET_WARM)); + ut_asserteq(-EACCES, reset_walk(RESET_COLD)); + ut_asserteq(-EACCES, reset_walk(RESET_POWER)); + + /* + * Enable cold reset - this should make cold reset work, plus a warm + * reset should be promoted to cold, since this is the next step + * along. + */ + state->reset_allowed[RESET_COLD] = true; + ut_asserteq(-EINPROGRESS, reset_walk(RESET_WARM)); + ut_asserteq(-EINPROGRESS, reset_walk(RESET_COLD)); + ut_asserteq(-EACCES, reset_walk(RESET_POWER)); + state->reset_allowed[RESET_COLD] = false; + state->reset_allowed[RESET_POWER] = true; + + return 0; +} +DM_TEST(dm_test_reset_walk, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/syscon.c b/test/dm/syscon.c new file mode 100644 index 0000000..3642481 --- /dev/null +++ b/test/dm/syscon.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <syscon.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Base test of system controllers */ +static int dm_test_syscon_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + ut_asserteq(SYSCON0, dev->driver_data); + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev)); + ut_asserteq(SYSCON1, dev->driver_data); + + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_SYSCON, 2, &dev)); + + return 0; +} +DM_TEST(dm_test_syscon_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test-main.c b/test/dm/test-main.c index 0477d2f..0e43ab9 100644 --- a/test/dm/test-main.c +++ b/test/dm/test-main.c @@ -76,6 +76,7 @@ static int dm_test_main(const char *test_name) struct unit_test_state *uts = &global_dm_test_state; uts->priv = &_global_priv_dm_test_state; struct unit_test *test; + int run_count; /* * If we have no device tree, or it only has a root node, then these @@ -90,10 +91,17 @@ static int dm_test_main(const char *test_name) if (!test_name) printf("Running %d driver model tests\n", n_ents); + run_count = 0; for (test = tests; test < tests + n_ents; test++) { - if (test_name && strcmp(test_name, test->name)) + const char *name = test->name; + + /* All tests have this prefix */ + if (!strncmp(name, "dm_test_", 8)) + name += 8; + if (test_name && strcmp(test_name, name)) continue; printf("Test: %s\n", test->name); + run_count++; ut_assertok(dm_test_init(uts)); uts->start = mallinfo(); @@ -109,7 +117,10 @@ static int dm_test_main(const char *test_name) ut_assertok(dm_test_destroy(uts)); } - printf("Failures: %d\n", uts->fail_count); + if (test_name && !run_count) + printf("Test '%s' not found\n", test_name); + else + printf("Failures: %d\n", uts->fail_count); gd->dm_root = NULL; ut_assertok(dm_init()); diff --git a/tools/Makefile b/tools/Makefile index 8ff9c2e..98414f7 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -58,7 +58,8 @@ hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o # Flattened device tree objects LIBFDT_OBJS := $(addprefix lib/libfdt/, \ - fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_wip.o) + fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_wip.o \ + fdt_region.o) RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ rsa-sign.o rsa-verify.o rsa-checksum.o \ rsa-mod-exp.o) @@ -155,6 +156,9 @@ hostprogs-$(CONFIG_ARMADA_XP) += kwboot hostprogs-y += proftool hostprogs-$(CONFIG_STATIC_RELA) += relocate-rela +hostprogs-y += fdtgrep +fdtgrep-objs += $(LIBFDT_OBJS) fdtgrep.o + # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/fdtgrep.c b/tools/fdtgrep.c new file mode 100644 index 0000000..caaf600 --- /dev/null +++ b/tools/fdtgrep.c @@ -0,0 +1,1234 @@ +/* + * Copyright (c) 2013, Google Inc. + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Perform a grep of an FDT either displaying the source subset or producing + * a new .dtb subset which can be used as required. + */ + +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <../include/libfdt.h> +#include <libfdt_internal.h> + +/* Define DEBUG to get some debugging output on stderr */ +#ifdef DEBUG +#define debug(a, b...) fprintf(stderr, a, ## b) +#else +#define debug(a, b...) +#endif + +/* A linked list of values we are grepping for */ +struct value_node { + int type; /* Types this value matches (FDT_IS... mask) */ + int include; /* 1 to include matches, 0 to exclude */ + const char *string; /* String to match */ + struct value_node *next; /* Pointer to next node, or NULL */ +}; + +/* Output formats we support */ +enum output_t { + OUT_DTS, /* Device tree source */ + OUT_DTB, /* Valid device tree binary */ + OUT_BIN, /* Fragment of .dtb, for hashing */ +}; + +/* Holds information which controls our output and options */ +struct display_info { + enum output_t output; /* Output format */ + int add_aliases; /* Add aliases node to output */ + int all; /* Display all properties/nodes */ + int colour; /* Display output in ANSI colour */ + int region_list; /* Output a region list */ + int flags; /* Flags (FDT_REG_...) */ + int list_strings; /* List strings in string table */ + int show_offset; /* Show offset */ + int show_addr; /* Show address */ + int header; /* Output an FDT header */ + int diff; /* Show +/- diff markers */ + int include_root; /* Include the root node and all properties */ + int remove_strings; /* Remove unused strings */ + int show_dts_version; /* Put '/dts-v1/;' on the first line */ + int types_inc; /* Mask of types that we include (FDT_IS...) */ + int types_exc; /* Mask of types that we exclude (FDT_IS...) */ + int invert; /* Invert polarity of match */ + struct value_node *value_head; /* List of values to match */ + const char *output_fname; /* Output filename */ + FILE *fout; /* File to write dts/dtb output */ +}; + +static void report_error(const char *where, int err) +{ + fprintf(stderr, "Error at '%s': %s\n", where, fdt_strerror(err)); +} + +/* Supported ANSI colours */ +enum { + COL_BLACK, + COL_RED, + COL_GREEN, + COL_YELLOW, + COL_BLUE, + COL_MAGENTA, + COL_CYAN, + COL_WHITE, + + COL_NONE = -1, +}; + +/** + * print_ansi_colour() - Print out the ANSI sequence for a colour + * + * @fout: Output file + * @col: Colour to output (COL_...), or COL_NONE to reset colour + */ +static void print_ansi_colour(FILE *fout, int col) +{ + if (col == COL_NONE) + fprintf(fout, "\033[0m"); + else + fprintf(fout, "\033[1;%dm", col + 30); +} + + +/** + * value_add() - Add a new value to our list of things to grep for + * + * @disp: Display structure, holding info about our options + * @headp: Pointer to header pointer of list + * @type: Type of this value (FDT_IS_...) + * @include: 1 if we want to include matches, 0 to exclude + * @str: String value to match + */ +static int value_add(struct display_info *disp, struct value_node **headp, + int type, int include, const char *str) +{ + struct value_node *node; + + /* + * Keep track of which types we are excluding/including. We don't + * allow both including and excluding things, because it doesn't make + * sense. 'Including' means that everything not mentioned is + * excluded. 'Excluding' means that everything not mentioned is + * included. So using the two together would be meaningless. + */ + if (include) + disp->types_inc |= type; + else + disp->types_exc |= type; + if (disp->types_inc & disp->types_exc & type) { + fprintf(stderr, + "Cannot use both include and exclude for '%s'\n", str); + return -1; + } + + str = strdup(str); + node = malloc(sizeof(*node)); + if (!str || !node) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + node->next = *headp; + node->type = type; + node->include = include; + node->string = str; + *headp = node; + + return 0; +} + +static bool util_is_printable_string(const void *data, int len) +{ + const char *s = data; + const char *ss, *se; + + /* zero length is not */ + if (len == 0) + return 0; + + /* must terminate with zero */ + if (s[len - 1] != '\0') + return 0; + + se = s + len; + + while (s < se) { + ss = s; + while (s < se && *s && isprint((unsigned char)*s)) + s++; + + /* not zero, or not done yet */ + if (*s != '\0' || s == ss) + return 0; + + s++; + } + + return 1; +} + +static void utilfdt_print_data(const char *data, int len) +{ + int i; + const char *p = data; + const char *s; + + /* no data, don't print */ + if (len == 0) + return; + + if (util_is_printable_string(data, len)) { + printf(" = "); + + s = data; + do { + printf("\"%s\"", s); + s += strlen(s) + 1; + if (s < data + len) + printf(", "); + } while (s < data + len); + + } else if ((len % 4) == 0) { + const uint32_t *cell = (const uint32_t *)data; + + printf(" = <"); + for (i = 0, len /= 4; i < len; i++) + printf("0x%08x%s", fdt32_to_cpu(cell[i]), + i < (len - 1) ? " " : ""); + printf(">"); + } else { + printf(" = ["); + for (i = 0; i < len; i++) + printf("%02x%s", *p++, i < len - 1 ? " " : ""); + printf("]"); + } +} + +/** + * display_fdt_by_regions() - Display regions of an FDT source + * + * This dumps an FDT as source, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to display, + * and this function displays them. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + */ +static int display_fdt_by_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count) +{ + struct fdt_region *reg = region, *reg_end = region + count; + uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(blob); + int base = fdt_off_dt_struct(blob); + int version = fdt_version(blob); + int offset, nextoffset; + int tag, depth, shift; + FILE *f = disp->fout; + uint64_t addr, size; + int in_region; + int file_ofs; + int i; + + if (disp->show_dts_version) + fprintf(f, "/dts-v1/;\n"); + + if (disp->header) { + fprintf(f, "// magic:\t\t0x%x\n", fdt_magic(blob)); + fprintf(f, "// totalsize:\t\t0x%x (%d)\n", fdt_totalsize(blob), + fdt_totalsize(blob)); + fprintf(f, "// off_dt_struct:\t0x%x\n", + fdt_off_dt_struct(blob)); + fprintf(f, "// off_dt_strings:\t0x%x\n", + fdt_off_dt_strings(blob)); + fprintf(f, "// off_mem_rsvmap:\t0x%x\n", off_mem_rsvmap); + fprintf(f, "// version:\t\t%d\n", version); + fprintf(f, "// last_comp_version:\t%d\n", + fdt_last_comp_version(blob)); + if (version >= 2) { + fprintf(f, "// boot_cpuid_phys:\t0x%x\n", + fdt_boot_cpuid_phys(blob)); + } + if (version >= 3) { + fprintf(f, "// size_dt_strings:\t0x%x\n", + fdt_size_dt_strings(blob)); + } + if (version >= 17) { + fprintf(f, "// size_dt_struct:\t0x%x\n", + fdt_size_dt_struct(blob)); + } + fprintf(f, "\n"); + } + + if (disp->flags & FDT_REG_ADD_MEM_RSVMAP) { + const struct fdt_reserve_entry *p_rsvmap; + + p_rsvmap = (const struct fdt_reserve_entry *) + ((const char *)blob + off_mem_rsvmap); + for (i = 0; ; i++) { + addr = fdt64_to_cpu(p_rsvmap[i].address); + size = fdt64_to_cpu(p_rsvmap[i].size); + if (addr == 0 && size == 0) + break; + + fprintf(f, "/memreserve/ %llx %llx;\n", + (unsigned long long)addr, + (unsigned long long)size); + } + } + + depth = 0; + nextoffset = 0; + shift = 4; /* 4 spaces per indent */ + do { + const struct fdt_property *prop; + const char *name; + int show; + int len; + + offset = nextoffset; + + /* + * Work out the file offset of this offset, and decide + * whether it is in the region list or not + */ + file_ofs = base + offset; + if (reg < reg_end && file_ofs >= reg->offset + reg->size) + reg++; + in_region = reg < reg_end && file_ofs >= reg->offset && + file_ofs < reg->offset + reg->size; + tag = fdt_next_tag(blob, offset, &nextoffset); + + if (tag == FDT_END) + break; + show = in_region || disp->all; + if (show && disp->diff) + fprintf(f, "%c", in_region ? '+' : '-'); + + if (!show) { + /* Do this here to avoid 'if (show)' in every 'case' */ + if (tag == FDT_BEGIN_NODE) + depth++; + else if (tag == FDT_END_NODE) + depth--; + continue; + } + if (tag != FDT_END) { + if (disp->show_addr) + fprintf(f, "%4x: ", file_ofs); + if (disp->show_offset) + fprintf(f, "%4x: ", file_ofs - base); + } + + /* Green means included, red means excluded */ + if (disp->colour) + print_ansi_colour(f, in_region ? COL_GREEN : COL_RED); + + switch (tag) { + case FDT_PROP: + prop = fdt_get_property_by_offset(blob, offset, NULL); + name = fdt_string(blob, fdt32_to_cpu(prop->nameoff)); + fprintf(f, "%*s%s", depth * shift, "", name); + utilfdt_print_data(prop->data, + fdt32_to_cpu(prop->len)); + fprintf(f, ";"); + break; + + case FDT_NOP: + fprintf(f, "%*s// [NOP]", depth * shift, ""); + break; + + case FDT_BEGIN_NODE: + name = fdt_get_name(blob, offset, &len); + fprintf(f, "%*s%s {", depth++ * shift, "", + *name ? name : "/"); + break; + + case FDT_END_NODE: + fprintf(f, "%*s};", --depth * shift, ""); + break; + } + + /* Reset colour back to normal before end of line */ + if (disp->colour) + print_ansi_colour(f, COL_NONE); + fprintf(f, "\n"); + } while (1); + + /* Print a list of strings if requested */ + if (disp->list_strings) { + const char *str; + int str_base = fdt_off_dt_strings(blob); + + for (offset = 0; offset < fdt_size_dt_strings(blob); + offset += strlen(str) + 1) { + str = fdt_string(blob, offset); + int len = strlen(str) + 1; + int show; + + /* Only print strings that are in the region */ + file_ofs = str_base + offset; + in_region = reg < reg_end && + file_ofs >= reg->offset && + file_ofs + len < reg->offset + + reg->size; + show = in_region || disp->all; + if (show && disp->diff) + printf("%c", in_region ? '+' : '-'); + if (disp->show_addr) + printf("%4x: ", file_ofs); + if (disp->show_offset) + printf("%4x: ", offset); + printf("%s\n", str); + } + } + + return 0; +} + +/** + * dump_fdt_regions() - Dump regions of an FDT as binary data + * + * This dumps an FDT as binary, but only certain regions of it. This is the + * final stage of the grep - we have a list of regions we want to dump, + * and this function dumps them. + * + * The output of this function may or may not be a valid FDT. To ensure it + * is, these disp->flags must be set: + * + * FDT_REG_SUPERNODES: ensures that subnodes are preceeded by their + * parents. Without this option, fragments of subnode data may be + * output without the supernodes above them. This is useful for + * hashing but cannot produce a valid FDT. + * FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT. + * Without this none of the properties will have names + * FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid + * without this. + * + * @disp: Display structure, holding info about our options + * @blob: FDT blob to display + * @region: List of regions to display + * @count: Number of regions + * @out: Output destination + */ +static int dump_fdt_regions(struct display_info *disp, const void *blob, + struct fdt_region region[], int count, char *out) +{ + struct fdt_header *fdt; + int size, struct_start; + int ptr; + int i; + + /* Set up a basic header (even if we don't actually write it) */ + fdt = (struct fdt_header *)out; + memset(fdt, '\0', sizeof(*fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + struct_start = FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry)); + fdt_set_off_mem_rsvmap(fdt, struct_start); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + + /* + * Calculate the total size of the regions we are writing out. The + * first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag + * is set. The last will be the string table if FDT_REG_ADD_STRING_TAB + * is set. + */ + for (i = size = 0; i < count; i++) + size += region[i].size; + + /* Bring in the mem_rsvmap section from the old file if requested */ + if (count > 0 && (disp->flags & FDT_REG_ADD_MEM_RSVMAP)) { + struct_start += region[0].size; + size -= region[0].size; + } + fdt_set_off_dt_struct(fdt, struct_start); + + /* Update the header to have the correct offsets/sizes */ + if (count >= 2 && (disp->flags & FDT_REG_ADD_STRING_TAB)) { + int str_size; + + str_size = region[count - 1].size; + fdt_set_size_dt_struct(fdt, size - str_size); + fdt_set_off_dt_strings(fdt, struct_start + size - str_size); + fdt_set_size_dt_strings(fdt, str_size); + fdt_set_totalsize(fdt, struct_start + size); + } + + /* Write the header if required */ + ptr = 0; + if (disp->header) { + ptr = sizeof(*fdt); + while (ptr < fdt_off_mem_rsvmap(fdt)) + out[ptr++] = '\0'; + } + + /* Output all the nodes including any mem_rsvmap/string table */ + for (i = 0; i < count; i++) { + struct fdt_region *reg = ®ion[i]; + + memcpy(out + ptr, (const char *)blob + reg->offset, reg->size); + ptr += reg->size; + } + + return ptr; +} + +/** + * show_region_list() - Print out a list of regions + * + * The list includes the region offset (absolute offset from start of FDT + * blob in bytes) and size + * + * @reg: List of regions to print + * @count: Number of regions + */ +static void show_region_list(struct fdt_region *reg, int count) +{ + int i; + + printf("Regions: %d\n", count); + for (i = 0; i < count; i++, reg++) { + printf("%d: %-10x %-10x\n", i, reg->offset, + reg->offset + reg->size); + } +} + +static int check_type_include(void *priv, int type, const char *data, int size) +{ + struct display_info *disp = priv; + struct value_node *val; + int match, none_match = FDT_IS_ANY; + + /* If none of our conditions mention this type, we know nothing */ + debug("type=%x, data=%s\n", type, data ? data : "(null)"); + if (!((disp->types_inc | disp->types_exc) & type)) { + debug(" - not in any condition\n"); + return -1; + } + + /* + * Go through the list of conditions. For inclusive conditions, we + * return 1 at the first match. For exclusive conditions, we must + * check that there are no matches. + */ + for (val = disp->value_head; val; val = val->next) { + if (!(type & val->type)) + continue; + match = fdt_stringlist_contains(data, size, val->string); + debug(" - val->type=%x, str='%s', match=%d\n", + val->type, val->string, match); + if (match && val->include) { + debug(" - match inc %s\n", val->string); + return 1; + } + if (match) + none_match &= ~val->type; + } + + /* + * If this is an exclusive condition, and nothing matches, then we + * should return 1. + */ + if ((type & disp->types_exc) && (none_match & type)) { + debug(" - match exc\n"); + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type + */ + if (type == FDT_IS_NODE && disp->types_exc == FDT_ANY_GLOBAL) { + debug(" - supressed exc node\n"); + return -1; + } + return 1; + } + + /* + * Allow FDT_IS_COMPAT to make the final decision in the + * case where there is no specific type (inclusive) + */ + if (type == FDT_IS_NODE && disp->types_inc == FDT_ANY_GLOBAL) + return -1; + + debug(" - no match, types_inc=%x, types_exc=%x, none_match=%x\n", + disp->types_inc, disp->types_exc, none_match); + + return 0; +} + +/** + * h_include() - Include handler function for fdt_find_regions() + * + * This function decides whether to include or exclude a node, property or + * compatible string. The function is defined by fdt_find_regions(). + * + * The algorithm is documented in the code - disp->invert is 0 for normal + * operation, and 1 to invert the sense of all matches. + * + * See + */ +static int h_include(void *priv, const void *fdt, int offset, int type, + const char *data, int size) +{ + struct display_info *disp = priv; + int inc, len; + + inc = check_type_include(priv, type, data, size); + if (disp->include_root && type == FDT_IS_PROP && offset == 0 && inc) + return 1; + + /* + * If the node name does not tell us anything, check the + * compatible string + */ + if (inc == -1 && type == FDT_IS_NODE) { + debug(" - checking compatible2\n"); + data = fdt_getprop(fdt, offset, "compatible", &len); + inc = check_type_include(priv, FDT_IS_COMPAT, data, len); + } + + /* If we still have no idea, check for properties in the node */ + if (inc != 1 && type == FDT_IS_NODE && + (disp->types_inc & FDT_NODE_HAS_PROP)) { + debug(" - checking node '%s'\n", + fdt_get_name(fdt, offset, NULL)); + for (offset = fdt_first_property_offset(fdt, offset); + offset > 0 && inc != 1; + offset = fdt_next_property_offset(fdt, offset)) { + const struct fdt_property *prop; + const char *str; + + prop = fdt_get_property_by_offset(fdt, offset, NULL); + if (!prop) + continue; + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + inc = check_type_include(priv, FDT_NODE_HAS_PROP, str, + strlen(str)); + } + if (inc == -1) + inc = 0; + } + + switch (inc) { + case 1: + inc = !disp->invert; + break; + case 0: + inc = disp->invert; + break; + } + debug(" - returning %d\n", inc); + + return inc; +} + +static int h_cmp_region(const void *v1, const void *v2) +{ + const struct fdt_region *region1 = v1, *region2 = v2; + + return region1->offset - region2->offset; +} + +static int fdtgrep_find_regions(const void *fdt, + int (*include_func)(void *priv, const void *fdt, int offset, + int type, const char *data, int size), + struct display_info *disp, struct fdt_region *region, + int max_regions, char *path, int path_len, int flags) +{ + struct fdt_region_state state; + int count; + int ret; + + count = 0; + ret = fdt_first_region(fdt, include_func, disp, + ®ion[count++], path, path_len, + disp->flags, &state); + while (ret == 0) { + ret = fdt_next_region(fdt, include_func, disp, + count < max_regions ? ®ion[count] : NULL, + path, path_len, disp->flags, &state); + if (!ret) + count++; + } + + /* Find all the aliases and add those regions back in */ + if (disp->add_aliases && count < max_regions) { + int new_count; + + new_count = fdt_add_alias_regions(fdt, region, count, + max_regions, &state); + if (new_count > max_regions) { + region = malloc(new_count * sizeof(struct fdt_region)); + if (!region) { + fprintf(stderr, + "Out of memory for %d regions\n", + count); + return -1; + } + memcpy(region, state.region, + count * sizeof(struct fdt_region)); + free(state.region); + new_count = fdt_add_alias_regions(fdt, region, count, + max_regions, &state); + } + + /* + * The alias regions will now be at the end of the list. Sort + * the regions by offset to get things into the right order + */ + qsort(region, new_count, sizeof(struct fdt_region), + h_cmp_region); + count = new_count; + } + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + + return count; +} + +int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len) +{ + int fd = 0; /* assume stdin */ + char *buf = NULL; + off_t bufsize = 1024, offset = 0; + int ret = 0; + + *buffp = NULL; + if (strcmp(filename, "-") != 0) { + fd = open(filename, O_RDONLY); + if (fd < 0) + return errno; + } + + /* Loop until we have read everything */ + buf = malloc(bufsize); + if (!buf) + return -ENOMEM; + do { + /* Expand the buffer to hold the next chunk */ + if (offset == bufsize) { + bufsize *= 2; + buf = realloc(buf, bufsize); + if (!buf) + return -ENOMEM; + } + + ret = read(fd, &buf[offset], bufsize - offset); + if (ret < 0) { + ret = errno; + break; + } + offset += ret; + } while (ret != 0); + + /* Clean up, including closing stdin; return errno on error */ + close(fd); + if (ret) + free(buf); + else + *buffp = buf; + *len = bufsize; + return ret; +} + +int utilfdt_read_err(const char *filename, char **buffp) +{ + off_t len; + return utilfdt_read_err_len(filename, buffp, &len); +} + +char *utilfdt_read_len(const char *filename, off_t *len) +{ + char *buff; + int ret = utilfdt_read_err_len(filename, &buff, len); + + if (ret) { + fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, + strerror(ret)); + return NULL; + } + /* Successful read */ + return buff; +} + +char *utilfdt_read(const char *filename) +{ + off_t len; + return utilfdt_read_len(filename, &len); +} + +/** + * Run the main fdtgrep operation, given a filename and valid arguments + * + * @param disp Display information / options + * @param filename Filename of blob file + * @param return 0 if ok, -ve on error + */ +static int do_fdtgrep(struct display_info *disp, const char *filename) +{ + struct fdt_region *region; + int max_regions; + int count = 100; + char path[1024]; + char *blob; + int i, ret; + + blob = utilfdt_read(filename); + if (!blob) + return -1; + ret = fdt_check_header(blob); + if (ret) { + fprintf(stderr, "Error: %s\n", fdt_strerror(ret)); + return ret; + } + + /* Allow old files, but they are untested */ + if (fdt_version(blob) < 17 && disp->value_head) { + fprintf(stderr, + "Warning: fdtgrep does not fully support version %d files\n", + fdt_version(blob)); + } + + /* + * We do two passes, since we don't know how many regions we need. + * The first pass will count the regions, but if it is too many, + * we do another pass to actually record them. + */ + for (i = 0; i < 2; i++) { + region = malloc(count * sizeof(struct fdt_region)); + if (!region) { + fprintf(stderr, "Out of memory for %d regions\n", + count); + return -1; + } + max_regions = count; + count = fdtgrep_find_regions(blob, + h_include, disp, + region, max_regions, path, sizeof(path), + disp->flags); + if (count < 0) { + report_error("fdt_find_regions", count); + return -1; + } + if (count <= max_regions) + break; + free(region); + } + + /* Optionally print a list of regions */ + if (disp->region_list) + show_region_list(region, count); + + /* Output either source .dts or binary .dtb */ + if (disp->output == OUT_DTS) { + ret = display_fdt_by_regions(disp, blob, region, count); + } else { + void *fdt; + /* Allow reserved memory section to expand slightly */ + int size = fdt_totalsize(blob) + 16; + + fdt = malloc(size); + if (!fdt) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + size = dump_fdt_regions(disp, blob, region, count, fdt); + if (disp->remove_strings) { + void *out; + + out = malloc(size); + if (!out) { + fprintf(stderr, "Out_of_memory\n"); + ret = -1; + goto err; + } + ret = fdt_remove_unused_strings(fdt, out); + if (ret < 0) { + fprintf(stderr, + "Failed to remove unused strings: err=%d\n", + ret); + goto err; + } + free(fdt); + fdt = out; + ret = fdt_pack(fdt); + if (ret < 0) { + fprintf(stderr, "Failed to pack: err=%d\n", + ret); + goto err; + } + size = fdt_totalsize(fdt); + } + + if (size != fwrite(fdt, 1, size, disp->fout)) { + fprintf(stderr, "Write failure, %d bytes\n", size); + free(fdt); + ret = 1; + goto err; + } + free(fdt); + } +err: + free(blob); + free(region); + + return ret; +} + +static const char usage_synopsis[] = + "fdtgrep - extract portions from device tree\n" + "\n" + "Usage:\n" + " fdtgrep <options> <dt file>|-\n\n" + "Output formats are:\n" + "\tdts - device tree soure text\n" + "\tdtb - device tree blob (sets -Hmt automatically)\n" + "\tbin - device tree fragment (may not be a valid .dtb)"; + +/* Helper for usage_short_opts string constant */ +#define USAGE_COMMON_SHORT_OPTS "hV" + +/* Helper for aligning long_opts array */ +#define a_argument required_argument + +/* Helper for usage_long_opts option array */ +#define USAGE_COMMON_LONG_OPTS \ + {"help", no_argument, NULL, 'h'}, \ + {"version", no_argument, NULL, 'V'}, \ + {NULL, no_argument, NULL, 0x0} + +/* Helper for usage_opts_help array */ +#define USAGE_COMMON_OPTS_HELP \ + "Print this help and exit", \ + "Print version and exit", \ + NULL + +/* Helper for getopt case statements */ +#define case_USAGE_COMMON_FLAGS \ + case 'h': usage(NULL); \ + case 'V': util_version(); \ + case '?': usage("unknown option"); + +static const char usage_short_opts[] = + "haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv" + USAGE_COMMON_SHORT_OPTS; +static struct option const usage_long_opts[] = { + {"show-address", no_argument, NULL, 'a'}, + {"colour", no_argument, NULL, 'A'}, + {"include-node-with-prop", a_argument, NULL, 'b'}, + {"include-compat", a_argument, NULL, 'c'}, + {"exclude-compat", a_argument, NULL, 'C'}, + {"diff", no_argument, NULL, 'd'}, + {"enter-node", no_argument, NULL, 'e'}, + {"show-offset", no_argument, NULL, 'f'}, + {"include-match", a_argument, NULL, 'g'}, + {"exclude-match", a_argument, NULL, 'G'}, + {"show-header", no_argument, NULL, 'H'}, + {"show-version", no_argument, NULL, 'I'}, + {"list-regions", no_argument, NULL, 'l'}, + {"list-strings", no_argument, NULL, 'L'}, + {"include-mem", no_argument, NULL, 'm'}, + {"include-node", a_argument, NULL, 'n'}, + {"exclude-node", a_argument, NULL, 'N'}, + {"include-prop", a_argument, NULL, 'p'}, + {"exclude-prop", a_argument, NULL, 'P'}, + {"remove-strings", no_argument, NULL, 'r'}, + {"include-root", no_argument, NULL, 'R'}, + {"show-subnodes", no_argument, NULL, 's'}, + {"skip-supernodes", no_argument, NULL, 'S'}, + {"show-stringtab", no_argument, NULL, 't'}, + {"show-aliases", no_argument, NULL, 'T'}, + {"out", a_argument, NULL, 'o'}, + {"out-format", a_argument, NULL, 'O'}, + {"invert-match", no_argument, NULL, 'v'}, + USAGE_COMMON_LONG_OPTS, +}; +static const char * const usage_opts_help[] = { + "Display address", + "Show all nodes/tags, colour those that match", + "Include contains containing property", + "Compatible nodes to include in grep", + "Compatible nodes to exclude in grep", + "Diff: Mark matching nodes with +, others with -", + "Enter direct subnode names of matching nodes", + "Display offset", + "Node/property/compatible string to include in grep", + "Node/property/compatible string to exclude in grep", + "Output a header", + "Put \"/dts-v1/;\" on first line of dts output", + "Output a region list", + "List strings in string table", + "Include mem_rsvmap section in binary output", + "Node to include in grep", + "Node to exclude in grep", + "Property to include in grep", + "Property to exclude in grep", + "Remove unused strings from string table", + "Include root node and all properties", + "Show all subnodes matching nodes", + "Don't include supernodes of matching nodes", + "Include string table in binary output", + "Include matching aliases in output", + "-o <output file>", + "-O <output format>", + "Invert the sense of matching (select non-matching lines)", + USAGE_COMMON_OPTS_HELP +}; + +/** + * Call getopt_long() with standard options + * + * Since all util code runs getopt in the same way, provide a helper. + */ +#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \ + usage_long_opts, NULL) + +void util_usage(const char *errmsg, const char *synopsis, + const char *short_opts, struct option const long_opts[], + const char * const opts_help[]) +{ + FILE *fp = errmsg ? stderr : stdout; + const char a_arg[] = "<arg>"; + size_t a_arg_len = strlen(a_arg) + 1; + size_t i; + int optlen; + + fprintf(fp, + "Usage: %s\n" + "\n" + "Options: -[%s]\n", synopsis, short_opts); + + /* prescan the --long opt length to auto-align */ + optlen = 0; + for (i = 0; long_opts[i].name; ++i) { + /* +1 is for space between --opt and help text */ + int l = strlen(long_opts[i].name) + 1; + if (long_opts[i].has_arg == a_argument) + l += a_arg_len; + if (optlen < l) + optlen = l; + } + + for (i = 0; long_opts[i].name; ++i) { + /* helps when adding new applets or options */ + assert(opts_help[i] != NULL); + + /* first output the short flag if it has one */ + if (long_opts[i].val > '~') + fprintf(fp, " "); + else + fprintf(fp, " -%c, ", long_opts[i].val); + + /* then the long flag */ + if (long_opts[i].has_arg == no_argument) { + fprintf(fp, "--%-*s", optlen, long_opts[i].name); + } else { + fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg, + (int)(optlen - strlen(long_opts[i].name) - + a_arg_len), ""); + } + + /* finally the help text */ + fprintf(fp, "%s\n", opts_help[i]); + } + + if (errmsg) { + fprintf(fp, "\nError: %s\n", errmsg); + exit(EXIT_FAILURE); + } else { + exit(EXIT_SUCCESS); + } +} + +/** + * Show usage and exit + * + * If you name all your usage variables with usage_xxx, then you can call this + * help macro rather than expanding all arguments yourself. + * + * @param errmsg If non-NULL, an error message to display + */ +#define usage(errmsg) \ + util_usage(errmsg, usage_synopsis, usage_short_opts, \ + usage_long_opts, usage_opts_help) + +void util_version(void) +{ + printf("Version: %s\n", "(U-Boot)"); + exit(0); +} + +static void scan_args(struct display_info *disp, int argc, char *argv[]) +{ + int opt; + + while ((opt = util_getopt_long()) != EOF) { + int type = 0; + int inc = 1; + + switch (opt) { + case_USAGE_COMMON_FLAGS + case 'a': + disp->show_addr = 1; + break; + case 'A': + disp->all = 1; + break; + case 'b': + type = FDT_NODE_HAS_PROP; + break; + case 'C': + inc = 0; + /* no break */ + case 'c': + type = FDT_IS_COMPAT; + break; + case 'd': + disp->diff = 1; + break; + case 'e': + disp->flags |= FDT_REG_DIRECT_SUBNODES; + break; + case 'f': + disp->show_offset = 1; + break; + case 'G': + inc = 0; + /* no break */ + case 'g': + type = FDT_ANY_GLOBAL; + break; + case 'H': + disp->header = 1; + break; + case 'l': + disp->region_list = 1; + break; + case 'L': + disp->list_strings = 1; + break; + case 'm': + disp->flags |= FDT_REG_ADD_MEM_RSVMAP; + break; + case 'N': + inc = 0; + /* no break */ + case 'n': + type = FDT_IS_NODE; + break; + case 'o': + disp->output_fname = optarg; + break; + case 'O': + if (!strcmp(optarg, "dtb")) + disp->output = OUT_DTB; + else if (!strcmp(optarg, "dts")) + disp->output = OUT_DTS; + else if (!strcmp(optarg, "bin")) + disp->output = OUT_BIN; + else + usage("Unknown output format"); + break; + case 'P': + inc = 0; + /* no break */ + case 'p': + type = FDT_IS_PROP; + break; + case 'r': + disp->remove_strings = 1; + break; + case 'R': + disp->include_root = 1; + break; + case 's': + disp->flags |= FDT_REG_ALL_SUBNODES; + break; + case 'S': + disp->flags &= ~FDT_REG_SUPERNODES; + break; + case 't': + disp->flags |= FDT_REG_ADD_STRING_TAB; + break; + case 'T': + disp->add_aliases = 1; + break; + case 'v': + disp->invert = 1; + break; + case 'I': + disp->show_dts_version = 1; + break; + } + + if (type && value_add(disp, &disp->value_head, type, inc, + optarg)) + usage("Cannot add value"); + } + + if (disp->invert && disp->types_exc) + usage("-v has no meaning when used with 'exclude' conditions"); +} + +int main(int argc, char *argv[]) +{ + char *filename = NULL; + struct display_info disp; + int ret; + + /* set defaults */ + memset(&disp, '\0', sizeof(disp)); + disp.flags = FDT_REG_SUPERNODES; /* Default flags */ + + scan_args(&disp, argc, argv); + + /* Show matched lines in colour if we can */ + disp.colour = disp.all && isatty(0); + + /* Any additional arguments can match anything, just like -g */ + while (optind < argc - 1) { + if (value_add(&disp, &disp.value_head, FDT_IS_ANY, 1, + argv[optind++])) + usage("Cannot add value"); + } + + if (optind < argc) + filename = argv[optind++]; + if (!filename) + usage("Missing filename"); + + /* If a valid .dtb is required, set flags to ensure we get one */ + if (disp.output == OUT_DTB) { + disp.header = 1; + disp.flags |= FDT_REG_ADD_MEM_RSVMAP | FDT_REG_ADD_STRING_TAB; + } + + if (disp.output_fname) { + disp.fout = fopen(disp.output_fname, "w"); + if (!disp.fout) + usage("Cannot open output file"); + } else { + disp.fout = stdout; + } + + /* Run the grep and output the results */ + ret = do_fdtgrep(&disp, filename); + if (disp.output_fname) + fclose(disp.fout); + if (ret) + return 1; + + return 0; +} diff --git a/tools/imagetool.h b/tools/imagetool.h index b7874f4..99bbf2f 100644 --- a/tools/imagetool.h +++ b/tools/imagetool.h @@ -59,6 +59,7 @@ struct image_tool_params { const char *keydest; /* Destination .dtb for public key */ const char *comment; /* Comment to add to signature node */ int require_keys; /* 1 to mark signing keys as 'required' */ + int file_size; /* Total size of output file */ }; /* diff --git a/tools/mkimage.c b/tools/mkimage.c index 5ccd951..e81d455 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -26,8 +26,48 @@ struct image_tool_params params = { .imagename2 = "", }; -int -main (int argc, char **argv) +static int h_compare_image_name(const void *vtype1, const void *vtype2) +{ + const int *type1 = vtype1; + const int *type2 = vtype2; + const char *name1 = genimg_get_type_short_name(*type1); + const char *name2 = genimg_get_type_short_name(*type2); + + return strcmp(name1, name2); +} + +/* Show all image types supported by mkimage */ +static void show_image_types(void) +{ + struct image_type_params *tparams; + int order[IH_TYPE_COUNT]; + int count; + int type; + int i; + + /* Sort the names in order of short name for easier reading */ + memset(order, '\0', sizeof(order)); + for (count = 0, type = 0; type < IH_TYPE_COUNT; type++) { + tparams = imagetool_get_type(type); + if (tparams) + order[count++] = type; + } + qsort(order, count, sizeof(int), h_compare_image_name); + + fprintf(stderr, "\nInvalid image type. Supported image types:\n"); + for (i = 0; i < count; i++) { + type = order[i]; + tparams = imagetool_get_type(type); + if (tparams) { + fprintf(stderr, "\t%-15s %s\n", + genimg_get_type_short_name(type), + genimg_get_type_name(type)); + } + } + fprintf(stderr, "\n"); +} + +int main(int argc, char **argv) { int ifd = -1; struct stat sbuf; @@ -35,6 +75,7 @@ main (int argc, char **argv) int retval = 0; struct image_type_params *tparams = NULL; int pad_len = 0; + int dfd; params.cmdname = *argv; params.addr = params.ep = 0; @@ -75,12 +116,16 @@ main (int argc, char **argv) usage (); goto NXTARG; case 'T': - if ((--argc <= 0) || - (params.type = - genimg_get_type_id (*++argv)) < 0) - usage (); + params.type = -1; + if (--argc >= 0 && argv[1]) { + params.type = + genimg_get_type_id(*++argv); + } + if (params.type < 0) { + show_image_types(); + usage(); + } goto NXTARG; - case 'a': if (--argc <= 0) usage (); @@ -266,6 +311,22 @@ NXTARG: ; exit (retval); } + dfd = open(params.datafile, O_RDONLY | O_BINARY); + if (dfd < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + params.cmdname, params.datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (fstat(dfd, &sbuf) < 0) { + fprintf(stderr, "%s: Can't stat %s: %s\n", + params.cmdname, params.datafile, strerror(errno)); + exit(EXIT_FAILURE); + } + + params.file_size = sbuf.st_size + tparams->header_size; + close(dfd); + /* * In case there an header with a variable * length will be added, the corresponding @@ -365,6 +426,7 @@ NXTARG: ; params.cmdname, params.imagefile, strerror(errno)); exit (EXIT_FAILURE); } + params.file_size = sbuf.st_size; ptr = mmap(0, sbuf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, ifd, 0); if (ptr == MAP_FAILED) { @@ -546,6 +608,7 @@ static void usage(void) #endif fprintf (stderr, " %s -V ==> print version information and exit\n", params.cmdname); + fprintf(stderr, "Use -T to see a list of available image types\n"); exit (EXIT_FAILURE); } |