162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <soc/tegra/mc.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "tegra210-emc.h" 1562306a36Sopenharmony_ci#include "tegra210-mc.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * Enable flags for specifying verbosity. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci#define INFO (1 << 0) 2162306a36Sopenharmony_ci#define STEPS (1 << 1) 2262306a36Sopenharmony_ci#define SUB_STEPS (1 << 2) 2362306a36Sopenharmony_ci#define PRELOCK (1 << 3) 2462306a36Sopenharmony_ci#define PRELOCK_STEPS (1 << 4) 2562306a36Sopenharmony_ci#define ACTIVE_EN (1 << 5) 2662306a36Sopenharmony_ci#define PRAMP_UP (1 << 6) 2762306a36Sopenharmony_ci#define PRAMP_DN (1 << 7) 2862306a36Sopenharmony_ci#define EMA_WRITES (1 << 10) 2962306a36Sopenharmony_ci#define EMA_UPDATES (1 << 11) 3062306a36Sopenharmony_ci#define PER_TRAIN (1 << 16) 3162306a36Sopenharmony_ci#define CC_PRINT (1 << 17) 3262306a36Sopenharmony_ci#define CCFIFO (1 << 29) 3362306a36Sopenharmony_ci#define REGS (1 << 30) 3462306a36Sopenharmony_ci#define REG_LISTS (1 << 31) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define emc_dbg(emc, flags, ...) dev_dbg(emc->dev, __VA_ARGS__) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DVFS_CLOCK_CHANGE_VERSION 21021 3962306a36Sopenharmony_ci#define EMC_PRELOCK_VERSION 2101 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cienum { 4262306a36Sopenharmony_ci DVFS_SEQUENCE = 1, 4362306a36Sopenharmony_ci WRITE_TRAINING_SEQUENCE = 2, 4462306a36Sopenharmony_ci PERIODIC_TRAINING_SEQUENCE = 3, 4562306a36Sopenharmony_ci DVFS_PT1 = 10, 4662306a36Sopenharmony_ci DVFS_UPDATE = 11, 4762306a36Sopenharmony_ci TRAINING_PT1 = 12, 4862306a36Sopenharmony_ci TRAINING_UPDATE = 13, 4962306a36Sopenharmony_ci PERIODIC_TRAINING_UPDATE = 14 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * PTFV defines - basically just indexes into the per table PTFV array. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C0D0U0_INDEX 0 5662306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C0D0U1_INDEX 1 5762306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C0D1U0_INDEX 2 5862306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C0D1U1_INDEX 3 5962306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C1D0U0_INDEX 4 6062306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C1D0U1_INDEX 5 6162306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C1D1U0_INDEX 6 6262306a36Sopenharmony_ci#define PTFV_DQSOSC_MOVAVG_C1D1U1_INDEX 7 6362306a36Sopenharmony_ci#define PTFV_DVFS_SAMPLES_INDEX 9 6462306a36Sopenharmony_ci#define PTFV_MOVAVG_WEIGHT_INDEX 10 6562306a36Sopenharmony_ci#define PTFV_CONFIG_CTRL_INDEX 11 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA (1 << 0) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Do arithmetic in fixed point. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci#define MOVAVG_PRECISION_FACTOR 100 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * The division portion of the average operation. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci#define __AVERAGE_PTFV(dev) \ 7862306a36Sopenharmony_ci ({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \ 7962306a36Sopenharmony_ci next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \ 8062306a36Sopenharmony_ci next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; }) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * Convert val to fixed point and add it to the temporary average. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define __INCREMENT_PTFV(dev, val) \ 8662306a36Sopenharmony_ci ({ next->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \ 8762306a36Sopenharmony_ci ((val) * MOVAVG_PRECISION_FACTOR); }) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Convert a moving average back to integral form and return the value. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci#define __MOVAVG_AC(timing, dev) \ 9362306a36Sopenharmony_ci ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \ 9462306a36Sopenharmony_ci MOVAVG_PRECISION_FACTOR) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Weighted update. */ 9762306a36Sopenharmony_ci#define __WEIGHTED_UPDATE_PTFV(dev, nval) \ 9862306a36Sopenharmony_ci do { \ 9962306a36Sopenharmony_ci int w = PTFV_MOVAVG_WEIGHT_INDEX; \ 10062306a36Sopenharmony_ci int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX; \ 10162306a36Sopenharmony_ci \ 10262306a36Sopenharmony_ci next->ptfv_list[dqs] = \ 10362306a36Sopenharmony_ci ((nval * MOVAVG_PRECISION_FACTOR) + \ 10462306a36Sopenharmony_ci (next->ptfv_list[dqs] * \ 10562306a36Sopenharmony_ci next->ptfv_list[w])) / \ 10662306a36Sopenharmony_ci (next->ptfv_list[w] + 1); \ 10762306a36Sopenharmony_ci \ 10862306a36Sopenharmony_ci emc_dbg(emc, EMA_UPDATES, "%s: (s=%lu) EMA: %u\n", \ 10962306a36Sopenharmony_ci __stringify(dev), nval, next->ptfv_list[dqs]); \ 11062306a36Sopenharmony_ci } while (0) 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* Access a particular average. */ 11362306a36Sopenharmony_ci#define __MOVAVG(timing, dev) \ 11462306a36Sopenharmony_ci ((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX]) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic u32 update_clock_tree_delay(struct tegra210_emc *emc, int type) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci bool periodic_training_update = type == PERIODIC_TRAINING_UPDATE; 11962306a36Sopenharmony_ci struct tegra210_emc_timing *last = emc->last; 12062306a36Sopenharmony_ci struct tegra210_emc_timing *next = emc->next; 12162306a36Sopenharmony_ci u32 last_timing_rate_mhz = last->rate / 1000; 12262306a36Sopenharmony_ci u32 next_timing_rate_mhz = next->rate / 1000; 12362306a36Sopenharmony_ci bool dvfs_update = type == DVFS_UPDATE; 12462306a36Sopenharmony_ci s32 tdel = 0, tmdel = 0, adel = 0; 12562306a36Sopenharmony_ci bool dvfs_pt1 = type == DVFS_PT1; 12662306a36Sopenharmony_ci unsigned long cval = 0; 12762306a36Sopenharmony_ci u32 temp[2][2], value; 12862306a36Sopenharmony_ci unsigned int i; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Dev0 MSB. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 13462306a36Sopenharmony_ci value = tegra210_emc_mrr_read(emc, 2, 19); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) { 13762306a36Sopenharmony_ci temp[i][0] = (value & 0x00ff) << 8; 13862306a36Sopenharmony_ci temp[i][1] = (value & 0xff00) << 0; 13962306a36Sopenharmony_ci value >>= 16; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Dev0 LSB. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci value = tegra210_emc_mrr_read(emc, 2, 18); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) { 14862306a36Sopenharmony_ci temp[i][0] |= (value & 0x00ff) >> 0; 14962306a36Sopenharmony_ci temp[i][1] |= (value & 0xff00) >> 8; 15062306a36Sopenharmony_ci value >>= 16; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 15562306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 15662306a36Sopenharmony_ci cval *= 1000000; 15762306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[0][0]; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (dvfs_pt1) 16162306a36Sopenharmony_ci __INCREMENT_PTFV(C0D0U0, cval); 16262306a36Sopenharmony_ci else if (dvfs_update) 16362306a36Sopenharmony_ci __AVERAGE_PTFV(C0D0U0); 16462306a36Sopenharmony_ci else if (periodic_training_update) 16562306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C0D0U0, cval); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 16862306a36Sopenharmony_ci tdel = next->current_dram_clktree[C0D0U0] - 16962306a36Sopenharmony_ci __MOVAVG_AC(next, C0D0U0); 17062306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 17162306a36Sopenharmony_ci adel = tmdel; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 17462306a36Sopenharmony_ci next->tree_margin) 17562306a36Sopenharmony_ci next->current_dram_clktree[C0D0U0] = 17662306a36Sopenharmony_ci __MOVAVG_AC(next, C0D0U0); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 18062306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 18162306a36Sopenharmony_ci cval *= 1000000; 18262306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[0][1]; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (dvfs_pt1) 18662306a36Sopenharmony_ci __INCREMENT_PTFV(C0D0U1, cval); 18762306a36Sopenharmony_ci else if (dvfs_update) 18862306a36Sopenharmony_ci __AVERAGE_PTFV(C0D0U1); 18962306a36Sopenharmony_ci else if (periodic_training_update) 19062306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C0D0U1, cval); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 19362306a36Sopenharmony_ci tdel = next->current_dram_clktree[C0D0U1] - 19462306a36Sopenharmony_ci __MOVAVG_AC(next, C0D0U1); 19562306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (tmdel > adel) 19862306a36Sopenharmony_ci adel = tmdel; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 20162306a36Sopenharmony_ci next->tree_margin) 20262306a36Sopenharmony_ci next->current_dram_clktree[C0D0U1] = 20362306a36Sopenharmony_ci __MOVAVG_AC(next, C0D0U1); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (emc->num_channels > 1) { 20762306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 20862306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 20962306a36Sopenharmony_ci cval *= 1000000; 21062306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[1][0]; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (dvfs_pt1) 21462306a36Sopenharmony_ci __INCREMENT_PTFV(C1D0U0, cval); 21562306a36Sopenharmony_ci else if (dvfs_update) 21662306a36Sopenharmony_ci __AVERAGE_PTFV(C1D0U0); 21762306a36Sopenharmony_ci else if (periodic_training_update) 21862306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C1D0U0, cval); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 22162306a36Sopenharmony_ci tdel = next->current_dram_clktree[C1D0U0] - 22262306a36Sopenharmony_ci __MOVAVG_AC(next, C1D0U0); 22362306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (tmdel > adel) 22662306a36Sopenharmony_ci adel = tmdel; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 22962306a36Sopenharmony_ci next->tree_margin) 23062306a36Sopenharmony_ci next->current_dram_clktree[C1D0U0] = 23162306a36Sopenharmony_ci __MOVAVG_AC(next, C1D0U0); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 23562306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 23662306a36Sopenharmony_ci cval *= 1000000; 23762306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[1][1]; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (dvfs_pt1) 24162306a36Sopenharmony_ci __INCREMENT_PTFV(C1D0U1, cval); 24262306a36Sopenharmony_ci else if (dvfs_update) 24362306a36Sopenharmony_ci __AVERAGE_PTFV(C1D0U1); 24462306a36Sopenharmony_ci else if (periodic_training_update) 24562306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C1D0U1, cval); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 24862306a36Sopenharmony_ci tdel = next->current_dram_clktree[C1D0U1] - 24962306a36Sopenharmony_ci __MOVAVG_AC(next, C1D0U1); 25062306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (tmdel > adel) 25362306a36Sopenharmony_ci adel = tmdel; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 25662306a36Sopenharmony_ci next->tree_margin) 25762306a36Sopenharmony_ci next->current_dram_clktree[C1D0U1] = 25862306a36Sopenharmony_ci __MOVAVG_AC(next, C1D0U1); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (emc->num_devices < 2) 26362306a36Sopenharmony_ci goto done; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * Dev1 MSB. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 26962306a36Sopenharmony_ci value = tegra210_emc_mrr_read(emc, 1, 19); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) { 27262306a36Sopenharmony_ci temp[i][0] = (value & 0x00ff) << 8; 27362306a36Sopenharmony_ci temp[i][1] = (value & 0xff00) << 0; 27462306a36Sopenharmony_ci value >>= 16; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * Dev1 LSB. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci value = tegra210_emc_mrr_read(emc, 1, 18); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) { 28362306a36Sopenharmony_ci temp[i][0] |= (value & 0x00ff) >> 0; 28462306a36Sopenharmony_ci temp[i][1] |= (value & 0xff00) >> 8; 28562306a36Sopenharmony_ci value >>= 16; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 29062306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 29162306a36Sopenharmony_ci cval *= 1000000; 29262306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[0][0]; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (dvfs_pt1) 29662306a36Sopenharmony_ci __INCREMENT_PTFV(C0D1U0, cval); 29762306a36Sopenharmony_ci else if (dvfs_update) 29862306a36Sopenharmony_ci __AVERAGE_PTFV(C0D1U0); 29962306a36Sopenharmony_ci else if (periodic_training_update) 30062306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C0D1U0, cval); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 30362306a36Sopenharmony_ci tdel = next->current_dram_clktree[C0D1U0] - 30462306a36Sopenharmony_ci __MOVAVG_AC(next, C0D1U0); 30562306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (tmdel > adel) 30862306a36Sopenharmony_ci adel = tmdel; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 31162306a36Sopenharmony_ci next->tree_margin) 31262306a36Sopenharmony_ci next->current_dram_clktree[C0D1U0] = 31362306a36Sopenharmony_ci __MOVAVG_AC(next, C0D1U0); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 31762306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 31862306a36Sopenharmony_ci cval *= 1000000; 31962306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[0][1]; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (dvfs_pt1) 32362306a36Sopenharmony_ci __INCREMENT_PTFV(C0D1U1, cval); 32462306a36Sopenharmony_ci else if (dvfs_update) 32562306a36Sopenharmony_ci __AVERAGE_PTFV(C0D1U1); 32662306a36Sopenharmony_ci else if (periodic_training_update) 32762306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C0D1U1, cval); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 33062306a36Sopenharmony_ci tdel = next->current_dram_clktree[C0D1U1] - 33162306a36Sopenharmony_ci __MOVAVG_AC(next, C0D1U1); 33262306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (tmdel > adel) 33562306a36Sopenharmony_ci adel = tmdel; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 33862306a36Sopenharmony_ci next->tree_margin) 33962306a36Sopenharmony_ci next->current_dram_clktree[C0D1U1] = 34062306a36Sopenharmony_ci __MOVAVG_AC(next, C0D1U1); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (emc->num_channels > 1) { 34462306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 34562306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 34662306a36Sopenharmony_ci cval *= 1000000; 34762306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[1][0]; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (dvfs_pt1) 35162306a36Sopenharmony_ci __INCREMENT_PTFV(C1D1U0, cval); 35262306a36Sopenharmony_ci else if (dvfs_update) 35362306a36Sopenharmony_ci __AVERAGE_PTFV(C1D1U0); 35462306a36Sopenharmony_ci else if (periodic_training_update) 35562306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C1D1U0, cval); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 35862306a36Sopenharmony_ci tdel = next->current_dram_clktree[C1D1U0] - 35962306a36Sopenharmony_ci __MOVAVG_AC(next, C1D1U0); 36062306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (tmdel > adel) 36362306a36Sopenharmony_ci adel = tmdel; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 36662306a36Sopenharmony_ci next->tree_margin) 36762306a36Sopenharmony_ci next->current_dram_clktree[C1D1U0] = 36862306a36Sopenharmony_ci __MOVAVG_AC(next, C1D1U0); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (dvfs_pt1 || periodic_training_update) { 37262306a36Sopenharmony_ci cval = tegra210_emc_actual_osc_clocks(last->run_clocks); 37362306a36Sopenharmony_ci cval *= 1000000; 37462306a36Sopenharmony_ci cval /= last_timing_rate_mhz * 2 * temp[1][1]; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (dvfs_pt1) 37862306a36Sopenharmony_ci __INCREMENT_PTFV(C1D1U1, cval); 37962306a36Sopenharmony_ci else if (dvfs_update) 38062306a36Sopenharmony_ci __AVERAGE_PTFV(C1D1U1); 38162306a36Sopenharmony_ci else if (periodic_training_update) 38262306a36Sopenharmony_ci __WEIGHTED_UPDATE_PTFV(C1D1U1, cval); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (dvfs_update || periodic_training_update) { 38562306a36Sopenharmony_ci tdel = next->current_dram_clktree[C1D1U1] - 38662306a36Sopenharmony_ci __MOVAVG_AC(next, C1D1U1); 38762306a36Sopenharmony_ci tmdel = (tdel < 0) ? -1 * tdel : tdel; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (tmdel > adel) 39062306a36Sopenharmony_ci adel = tmdel; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (tmdel * 128 * next_timing_rate_mhz / 1000000 > 39362306a36Sopenharmony_ci next->tree_margin) 39462306a36Sopenharmony_ci next->current_dram_clktree[C1D1U1] = 39562306a36Sopenharmony_ci __MOVAVG_AC(next, C1D1U1); 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cidone: 40062306a36Sopenharmony_ci return adel; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic u32 periodic_compensation_handler(struct tegra210_emc *emc, u32 type, 40462306a36Sopenharmony_ci struct tegra210_emc_timing *last, 40562306a36Sopenharmony_ci struct tegra210_emc_timing *next) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci#define __COPY_EMA(nt, lt, dev) \ 40862306a36Sopenharmony_ci ({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) * \ 40962306a36Sopenharmony_ci (nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; }) 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci u32 i, adel = 0, samples = next->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; 41262306a36Sopenharmony_ci u32 delay; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci delay = tegra210_emc_actual_osc_clocks(last->run_clocks); 41562306a36Sopenharmony_ci delay *= 1000; 41662306a36Sopenharmony_ci delay = 2 + (delay / last->rate); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!next->periodic_training) 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (type == DVFS_SEQUENCE) { 42262306a36Sopenharmony_ci if (last->periodic_training && 42362306a36Sopenharmony_ci (next->ptfv_list[PTFV_CONFIG_CTRL_INDEX] & 42462306a36Sopenharmony_ci PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA)) { 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * If the previous frequency was using periodic 42762306a36Sopenharmony_ci * calibration then we can reuse the previous 42862306a36Sopenharmony_ci * frequencies EMA data. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci __COPY_EMA(next, last, C0D0U0); 43162306a36Sopenharmony_ci __COPY_EMA(next, last, C0D0U1); 43262306a36Sopenharmony_ci __COPY_EMA(next, last, C1D0U0); 43362306a36Sopenharmony_ci __COPY_EMA(next, last, C1D0U1); 43462306a36Sopenharmony_ci __COPY_EMA(next, last, C0D1U0); 43562306a36Sopenharmony_ci __COPY_EMA(next, last, C0D1U1); 43662306a36Sopenharmony_ci __COPY_EMA(next, last, C1D1U0); 43762306a36Sopenharmony_ci __COPY_EMA(next, last, C1D1U1); 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci /* Reset the EMA.*/ 44062306a36Sopenharmony_ci __MOVAVG(next, C0D0U0) = 0; 44162306a36Sopenharmony_ci __MOVAVG(next, C0D0U1) = 0; 44262306a36Sopenharmony_ci __MOVAVG(next, C1D0U0) = 0; 44362306a36Sopenharmony_ci __MOVAVG(next, C1D0U1) = 0; 44462306a36Sopenharmony_ci __MOVAVG(next, C0D1U0) = 0; 44562306a36Sopenharmony_ci __MOVAVG(next, C0D1U1) = 0; 44662306a36Sopenharmony_ci __MOVAVG(next, C1D1U0) = 0; 44762306a36Sopenharmony_ci __MOVAVG(next, C1D1U1) = 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (i = 0; i < samples; i++) { 45062306a36Sopenharmony_ci tegra210_emc_start_periodic_compensation(emc); 45162306a36Sopenharmony_ci udelay(delay); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * Generate next sample of data. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci adel = update_clock_tree_delay(emc, DVFS_PT1); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Seems like it should be part of the 46262306a36Sopenharmony_ci * 'if (last_timing->periodic_training)' conditional 46362306a36Sopenharmony_ci * since is already done for the else clause. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci adel = update_clock_tree_delay(emc, DVFS_UPDATE); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (type == PERIODIC_TRAINING_SEQUENCE) { 46962306a36Sopenharmony_ci tegra210_emc_start_periodic_compensation(emc); 47062306a36Sopenharmony_ci udelay(delay); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci adel = update_clock_tree_delay(emc, PERIODIC_TRAINING_UPDATE); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return adel; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic u32 tegra210_emc_r21021_periodic_compensation(struct tegra210_emc *emc) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci u32 emc_cfg, emc_cfg_o, emc_cfg_update, del, value; 48162306a36Sopenharmony_ci static const u32 list[] = { 48262306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0, 48362306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1, 48462306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2, 48562306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3, 48662306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0, 48762306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1, 48862306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2, 48962306a36Sopenharmony_ci EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3, 49062306a36Sopenharmony_ci EMC_DATA_BRLSHFT_0, 49162306a36Sopenharmony_ci EMC_DATA_BRLSHFT_1 49262306a36Sopenharmony_ci }; 49362306a36Sopenharmony_ci struct tegra210_emc_timing *last = emc->last; 49462306a36Sopenharmony_ci unsigned int items = ARRAY_SIZE(list), i; 49562306a36Sopenharmony_ci unsigned long delay; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (last->periodic_training) { 49862306a36Sopenharmony_ci emc_dbg(emc, PER_TRAIN, "Periodic training starting\n"); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci value = emc_readl(emc, EMC_DBG); 50162306a36Sopenharmony_ci emc_cfg_o = emc_readl(emc, EMC_CFG); 50262306a36Sopenharmony_ci emc_cfg = emc_cfg_o & ~(EMC_CFG_DYN_SELF_REF | 50362306a36Sopenharmony_ci EMC_CFG_DRAM_ACPD | 50462306a36Sopenharmony_ci EMC_CFG_DRAM_CLKSTOP_PD); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * 1. Power optimizations should be off. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci emc_writel(emc, emc_cfg, EMC_CFG); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Does emc_timing_update() for above changes. */ 51362306a36Sopenharmony_ci tegra210_emc_dll_disable(emc); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) 51662306a36Sopenharmony_ci tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, 51762306a36Sopenharmony_ci EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 51862306a36Sopenharmony_ci 0); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) 52162306a36Sopenharmony_ci tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, 52262306a36Sopenharmony_ci EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 52362306a36Sopenharmony_ci 0); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci emc_cfg_update = value = emc_readl(emc, EMC_CFG_UPDATE); 52662306a36Sopenharmony_ci value &= ~EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK; 52762306a36Sopenharmony_ci value |= (2 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT); 52862306a36Sopenharmony_ci emc_writel(emc, value, EMC_CFG_UPDATE); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * 2. osc kick off - this assumes training and dvfs have set 53262306a36Sopenharmony_ci * correct MR23. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci tegra210_emc_start_periodic_compensation(emc); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * 3. Let dram capture its clock tree delays. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci delay = tegra210_emc_actual_osc_clocks(last->run_clocks); 54062306a36Sopenharmony_ci delay *= 1000; 54162306a36Sopenharmony_ci delay /= last->rate + 1; 54262306a36Sopenharmony_ci udelay(delay); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * 4. Check delta wrt previous values (save value if margin 54662306a36Sopenharmony_ci * exceeds what is set in table). 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci del = periodic_compensation_handler(emc, 54962306a36Sopenharmony_ci PERIODIC_TRAINING_SEQUENCE, 55062306a36Sopenharmony_ci last, last); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * 5. Apply compensation w.r.t. trained values (if clock tree 55462306a36Sopenharmony_ci * has drifted more than the set margin). 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci if (last->tree_margin < ((del * 128 * (last->rate / 1000)) / 1000000)) { 55762306a36Sopenharmony_ci for (i = 0; i < items; i++) { 55862306a36Sopenharmony_ci value = tegra210_emc_compensate(last, list[i]); 55962306a36Sopenharmony_ci emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", 56062306a36Sopenharmony_ci list[i], value); 56162306a36Sopenharmony_ci emc_writel(emc, value, list[i]); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci emc_writel(emc, emc_cfg_o, EMC_CFG); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* 56862306a36Sopenharmony_ci * 6. Timing update actally applies the new trimmers. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci tegra210_emc_timing_update(emc); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* 6.1. Restore the UPDATE_DLL_IN_UPDATE field. */ 57362306a36Sopenharmony_ci emc_writel(emc, emc_cfg_update, EMC_CFG_UPDATE); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 6.2. Restore the DLL. */ 57662306a36Sopenharmony_ci tegra210_emc_dll_enable(emc); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * Do the clock change sequence. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_cistatic void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci /* state variables */ 58862306a36Sopenharmony_ci static bool fsp_for_next_freq; 58962306a36Sopenharmony_ci /* constant configuration parameters */ 59062306a36Sopenharmony_ci const bool save_restore_clkstop_pd = true; 59162306a36Sopenharmony_ci const u32 zqcal_before_cc_cutoff = 2400; 59262306a36Sopenharmony_ci const bool cya_allow_ref_cc = false; 59362306a36Sopenharmony_ci const bool cya_issue_pc_ref = false; 59462306a36Sopenharmony_ci const bool opt_cc_short_zcal = true; 59562306a36Sopenharmony_ci const bool ref_b4_sref_en = false; 59662306a36Sopenharmony_ci const u32 tZQCAL_lpddr4 = 1000000; 59762306a36Sopenharmony_ci const bool opt_short_zcal = true; 59862306a36Sopenharmony_ci const bool opt_do_sw_qrst = true; 59962306a36Sopenharmony_ci const u32 opt_dvfs_mode = MAN_SR; 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * This is the timing table for the source frequency. It does _not_ 60262306a36Sopenharmony_ci * necessarily correspond to the actual timing values in the EMC at the 60362306a36Sopenharmony_ci * moment. If the boot BCT differs from the table then this can happen. 60462306a36Sopenharmony_ci * However, we need it for accessing the dram_timings (which are not 60562306a36Sopenharmony_ci * really registers) array for the current frequency. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci struct tegra210_emc_timing *fake, *last = emc->last, *next = emc->next; 60862306a36Sopenharmony_ci u32 tRTM, RP_war, R2P_war, TRPab_war, deltaTWATM, W2P_war, tRPST; 60962306a36Sopenharmony_ci u32 mr13_flip_fspwr, mr13_flip_fspop, ramp_up_wait, ramp_down_wait; 61062306a36Sopenharmony_ci u32 zq_wait_long, zq_latch_dvfs_wait_time, tZQCAL_lpddr4_fc_adj; 61162306a36Sopenharmony_ci u32 emc_auto_cal_config, auto_cal_en, emc_cfg, emc_sel_dpd_ctrl; 61262306a36Sopenharmony_ci u32 tFC_lpddr4 = 1000 * next->dram_timings[T_FC_LPDDR4]; 61362306a36Sopenharmony_ci u32 bg_reg_mode_change, enable_bglp_reg, enable_bg_reg; 61462306a36Sopenharmony_ci bool opt_zcal_en_cc = false, is_lpddr3 = false; 61562306a36Sopenharmony_ci bool compensate_trimmer_applicable = false; 61662306a36Sopenharmony_ci u32 emc_dbg, emc_cfg_pipe_clk, emc_pin; 61762306a36Sopenharmony_ci u32 src_clk_period, dst_clk_period; /* in picoseconds */ 61862306a36Sopenharmony_ci bool shared_zq_resistor = false; 61962306a36Sopenharmony_ci u32 value, dram_type; 62062306a36Sopenharmony_ci u32 opt_dll_mode = 0; 62162306a36Sopenharmony_ci unsigned long delay; 62262306a36Sopenharmony_ci unsigned int i; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci emc_dbg(emc, INFO, "Running clock change.\n"); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* XXX fake == last */ 62762306a36Sopenharmony_ci fake = tegra210_emc_find_timing(emc, last->rate * 1000UL); 62862306a36Sopenharmony_ci fsp_for_next_freq = !fsp_for_next_freq; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci value = emc_readl(emc, EMC_FBIO_CFG5) & EMC_FBIO_CFG5_DRAM_TYPE_MASK; 63162306a36Sopenharmony_ci dram_type = value >> EMC_FBIO_CFG5_DRAM_TYPE_SHIFT; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (last->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX] & BIT(31)) 63462306a36Sopenharmony_ci shared_zq_resistor = true; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if ((next->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0 && 63762306a36Sopenharmony_ci last->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0) || 63862306a36Sopenharmony_ci dram_type == DRAM_TYPE_LPDDR4) 63962306a36Sopenharmony_ci opt_zcal_en_cc = true; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_DDR3) 64262306a36Sopenharmony_ci opt_dll_mode = tegra210_emc_get_dll_state(next); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if ((next->burst_regs[EMC_FBIO_CFG5_INDEX] & BIT(25)) && 64562306a36Sopenharmony_ci (dram_type == DRAM_TYPE_LPDDR2)) 64662306a36Sopenharmony_ci is_lpddr3 = true; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci emc_readl(emc, EMC_CFG); 64962306a36Sopenharmony_ci emc_readl(emc, EMC_AUTO_CAL_CONFIG); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci src_clk_period = 1000000000 / last->rate; 65262306a36Sopenharmony_ci dst_clk_period = 1000000000 / next->rate; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (dst_clk_period <= zqcal_before_cc_cutoff) 65562306a36Sopenharmony_ci tZQCAL_lpddr4_fc_adj = tZQCAL_lpddr4 - tFC_lpddr4; 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci tZQCAL_lpddr4_fc_adj = tZQCAL_lpddr4; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci tZQCAL_lpddr4_fc_adj /= dst_clk_period; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci emc_dbg = emc_readl(emc, EMC_DBG); 66262306a36Sopenharmony_ci emc_pin = emc_readl(emc, EMC_PIN); 66362306a36Sopenharmony_ci emc_cfg_pipe_clk = emc_readl(emc, EMC_CFG_PIPE_CLK); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci emc_cfg = next->burst_regs[EMC_CFG_INDEX]; 66662306a36Sopenharmony_ci emc_cfg &= ~(EMC_CFG_DYN_SELF_REF | EMC_CFG_DRAM_ACPD | 66762306a36Sopenharmony_ci EMC_CFG_DRAM_CLKSTOP_SR | EMC_CFG_DRAM_CLKSTOP_PD); 66862306a36Sopenharmony_ci emc_sel_dpd_ctrl = next->emc_sel_dpd_ctrl; 66962306a36Sopenharmony_ci emc_sel_dpd_ctrl &= ~(EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN | 67062306a36Sopenharmony_ci EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN | 67162306a36Sopenharmony_ci EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN | 67262306a36Sopenharmony_ci EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN | 67362306a36Sopenharmony_ci EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci emc_dbg(emc, INFO, "Clock change version: %d\n", 67662306a36Sopenharmony_ci DVFS_CLOCK_CHANGE_VERSION); 67762306a36Sopenharmony_ci emc_dbg(emc, INFO, "DRAM type = %d\n", dram_type); 67862306a36Sopenharmony_ci emc_dbg(emc, INFO, "DRAM dev #: %u\n", emc->num_devices); 67962306a36Sopenharmony_ci emc_dbg(emc, INFO, "Next EMC clksrc: 0x%08x\n", clksrc); 68062306a36Sopenharmony_ci emc_dbg(emc, INFO, "DLL clksrc: 0x%08x\n", next->dll_clk_src); 68162306a36Sopenharmony_ci emc_dbg(emc, INFO, "last rate: %u, next rate %u\n", last->rate, 68262306a36Sopenharmony_ci next->rate); 68362306a36Sopenharmony_ci emc_dbg(emc, INFO, "last period: %u, next period: %u\n", 68462306a36Sopenharmony_ci src_clk_period, dst_clk_period); 68562306a36Sopenharmony_ci emc_dbg(emc, INFO, " shared_zq_resistor: %d\n", !!shared_zq_resistor); 68662306a36Sopenharmony_ci emc_dbg(emc, INFO, " num_channels: %u\n", emc->num_channels); 68762306a36Sopenharmony_ci emc_dbg(emc, INFO, " opt_dll_mode: %d\n", opt_dll_mode); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* 69062306a36Sopenharmony_ci * Step 1: 69162306a36Sopenharmony_ci * Pre DVFS SW sequence. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 1\n"); 69462306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 1.1: Disable DLL temporarily.\n"); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci value = emc_readl(emc, EMC_CFG_DIG_DLL); 69762306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; 69862306a36Sopenharmony_ci emc_writel(emc, value, EMC_CFG_DIG_DLL); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci tegra210_emc_timing_update(emc); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) 70362306a36Sopenharmony_ci tegra210_emc_wait_for_update(emc, i, EMC_CFG_DIG_DLL, 70462306a36Sopenharmony_ci EMC_CFG_DIG_DLL_CFG_DLL_EN, 0); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 1.2: Disable AUTOCAL temporarily.\n"); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci emc_auto_cal_config = next->emc_auto_cal_config; 70962306a36Sopenharmony_ci auto_cal_en = emc_auto_cal_config & EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE; 71062306a36Sopenharmony_ci emc_auto_cal_config &= ~EMC_AUTO_CAL_CONFIG_AUTO_CAL_START; 71162306a36Sopenharmony_ci emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL; 71262306a36Sopenharmony_ci emc_auto_cal_config |= EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL; 71362306a36Sopenharmony_ci emc_auto_cal_config |= auto_cal_en; 71462306a36Sopenharmony_ci emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); 71562306a36Sopenharmony_ci emc_readl(emc, EMC_AUTO_CAL_CONFIG); /* Flush write. */ 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 1.3: Disable other power features.\n"); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 72062306a36Sopenharmony_ci emc_writel(emc, emc_cfg, EMC_CFG); 72162306a36Sopenharmony_ci emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); 72262306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (next->periodic_training) { 72562306a36Sopenharmony_ci tegra210_emc_reset_dram_clktree_values(next); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) 72862306a36Sopenharmony_ci tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, 72962306a36Sopenharmony_ci EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 73062306a36Sopenharmony_ci 0); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for (i = 0; i < emc->num_channels; i++) 73362306a36Sopenharmony_ci tegra210_emc_wait_for_update(emc, i, EMC_EMC_STATUS, 73462306a36Sopenharmony_ci EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 73562306a36Sopenharmony_ci 0); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci tegra210_emc_start_periodic_compensation(emc); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci delay = 1000 * tegra210_emc_actual_osc_clocks(last->run_clocks); 74062306a36Sopenharmony_ci udelay((delay / last->rate) + 2); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci value = periodic_compensation_handler(emc, DVFS_SEQUENCE, fake, 74362306a36Sopenharmony_ci next); 74462306a36Sopenharmony_ci value = (value * 128 * next->rate / 1000) / 1000000; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (next->periodic_training && value > next->tree_margin) 74762306a36Sopenharmony_ci compensate_trimmer_applicable = true; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci emc_writel(emc, EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS); 75162306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 75262306a36Sopenharmony_ci emc_writel(emc, emc_cfg, EMC_CFG); 75362306a36Sopenharmony_ci emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); 75462306a36Sopenharmony_ci emc_writel(emc, emc_cfg_pipe_clk | EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON, 75562306a36Sopenharmony_ci EMC_CFG_PIPE_CLK); 75662306a36Sopenharmony_ci emc_writel(emc, next->emc_fdpd_ctrl_cmd_no_ramp & 75762306a36Sopenharmony_ci ~EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE, 75862306a36Sopenharmony_ci EMC_FDPD_CTRL_CMD_NO_RAMP); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci bg_reg_mode_change = 76162306a36Sopenharmony_ci ((next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 76262306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) ^ 76362306a36Sopenharmony_ci (last->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 76462306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD)) || 76562306a36Sopenharmony_ci ((next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 76662306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) ^ 76762306a36Sopenharmony_ci (last->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 76862306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD)); 76962306a36Sopenharmony_ci enable_bglp_reg = 77062306a36Sopenharmony_ci (next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 77162306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) == 0; 77262306a36Sopenharmony_ci enable_bg_reg = 77362306a36Sopenharmony_ci (next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 77462306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) == 0; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (bg_reg_mode_change) { 77762306a36Sopenharmony_ci if (enable_bg_reg) 77862306a36Sopenharmony_ci emc_writel(emc, last->burst_regs 77962306a36Sopenharmony_ci [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 78062306a36Sopenharmony_ci ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD, 78162306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (enable_bglp_reg) 78462306a36Sopenharmony_ci emc_writel(emc, last->burst_regs 78562306a36Sopenharmony_ci [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 78662306a36Sopenharmony_ci ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD, 78762306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* Check if we need to turn on VREF generator. */ 79162306a36Sopenharmony_ci if ((((last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & 79262306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 0) && 79362306a36Sopenharmony_ci ((next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & 79462306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 1)) || 79562306a36Sopenharmony_ci (((last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & 79662306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 0) && 79762306a36Sopenharmony_ci ((next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] & 79862306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) != 0))) { 79962306a36Sopenharmony_ci u32 pad_tx_ctrl = 80062306a36Sopenharmony_ci next->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; 80162306a36Sopenharmony_ci u32 last_pad_tx_ctrl = 80262306a36Sopenharmony_ci last->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX]; 80362306a36Sopenharmony_ci u32 next_dq_e_ivref, next_dqs_e_ivref; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci next_dqs_e_ivref = pad_tx_ctrl & 80662306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF; 80762306a36Sopenharmony_ci next_dq_e_ivref = pad_tx_ctrl & 80862306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF; 80962306a36Sopenharmony_ci value = (last_pad_tx_ctrl & 81062306a36Sopenharmony_ci ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF & 81162306a36Sopenharmony_ci ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) | 81262306a36Sopenharmony_ci next_dq_e_ivref | next_dqs_e_ivref; 81362306a36Sopenharmony_ci emc_writel(emc, value, EMC_PMACRO_DATA_PAD_TX_CTRL); 81462306a36Sopenharmony_ci udelay(1); 81562306a36Sopenharmony_ci } else if (bg_reg_mode_change) { 81662306a36Sopenharmony_ci udelay(1); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* 82262306a36Sopenharmony_ci * Step 2: 82362306a36Sopenharmony_ci * Prelock the DLL. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 2\n"); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (next->burst_regs[EMC_CFG_DIG_DLL_INDEX] & 82862306a36Sopenharmony_ci EMC_CFG_DIG_DLL_CFG_DLL_EN) { 82962306a36Sopenharmony_ci emc_dbg(emc, INFO, "Prelock enabled for target frequency.\n"); 83062306a36Sopenharmony_ci value = tegra210_emc_dll_prelock(emc, clksrc); 83162306a36Sopenharmony_ci emc_dbg(emc, INFO, "DLL out: 0x%03x\n", value); 83262306a36Sopenharmony_ci } else { 83362306a36Sopenharmony_ci emc_dbg(emc, INFO, "Disabling DLL for target frequency.\n"); 83462306a36Sopenharmony_ci tegra210_emc_dll_disable(emc); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * Step 3: 83962306a36Sopenharmony_ci * Prepare autocal for the clock change. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 3\n"); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 84462306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config2, EMC_AUTO_CAL_CONFIG2); 84562306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config3, EMC_AUTO_CAL_CONFIG3); 84662306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config4, EMC_AUTO_CAL_CONFIG4); 84762306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config5, EMC_AUTO_CAL_CONFIG5); 84862306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config6, EMC_AUTO_CAL_CONFIG6); 84962306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config7, EMC_AUTO_CAL_CONFIG7); 85062306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config8, EMC_AUTO_CAL_CONFIG8); 85162306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci emc_auto_cal_config |= (EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START | 85462306a36Sopenharmony_ci auto_cal_en); 85562306a36Sopenharmony_ci emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* 85862306a36Sopenharmony_ci * Step 4: 85962306a36Sopenharmony_ci * Update EMC_CFG. (??) 86062306a36Sopenharmony_ci */ 86162306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 4\n"); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (src_clk_period > 50000 && dram_type == DRAM_TYPE_LPDDR4) 86462306a36Sopenharmony_ci ccfifo_writel(emc, 1, EMC_SELF_REF, 0); 86562306a36Sopenharmony_ci else 86662306a36Sopenharmony_ci emc_writel(emc, next->emc_cfg_2, EMC_CFG_2); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* 86962306a36Sopenharmony_ci * Step 5: 87062306a36Sopenharmony_ci * Prepare reference variables for ZQCAL regs. 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 5\n"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) 87562306a36Sopenharmony_ci zq_wait_long = max((u32)1, div_o3(1000000, dst_clk_period)); 87662306a36Sopenharmony_ci else if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3) 87762306a36Sopenharmony_ci zq_wait_long = max(next->min_mrs_wait, 87862306a36Sopenharmony_ci div_o3(360000, dst_clk_period)) + 4; 87962306a36Sopenharmony_ci else if (dram_type == DRAM_TYPE_DDR3) 88062306a36Sopenharmony_ci zq_wait_long = max((u32)256, 88162306a36Sopenharmony_ci div_o3(320000, dst_clk_period) + 2); 88262306a36Sopenharmony_ci else 88362306a36Sopenharmony_ci zq_wait_long = 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* 88662306a36Sopenharmony_ci * Step 6: 88762306a36Sopenharmony_ci * Training code - removed. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 6\n"); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* 89262306a36Sopenharmony_ci * Step 7: 89362306a36Sopenharmony_ci * Program FSP reference registers and send MRWs to new FSPWR. 89462306a36Sopenharmony_ci */ 89562306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 7\n"); 89662306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Step 7.1: Bug 200024907 - Patch RP R2P"); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* WAR 200024907 */ 89962306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 90062306a36Sopenharmony_ci u32 nRTP = 16; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (src_clk_period >= 1000000 / 1866) /* 535.91 ps */ 90362306a36Sopenharmony_ci nRTP = 14; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (src_clk_period >= 1000000 / 1600) /* 625.00 ps */ 90662306a36Sopenharmony_ci nRTP = 12; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (src_clk_period >= 1000000 / 1333) /* 750.19 ps */ 90962306a36Sopenharmony_ci nRTP = 10; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (src_clk_period >= 1000000 / 1066) /* 938.09 ps */ 91262306a36Sopenharmony_ci nRTP = 8; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci deltaTWATM = max_t(u32, div_o3(7500, src_clk_period), 8); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* 91762306a36Sopenharmony_ci * Originally there was a + .5 in the tRPST calculation. 91862306a36Sopenharmony_ci * However since we can't do FP in the kernel and the tRTM 91962306a36Sopenharmony_ci * computation was in a floating point ceiling function, adding 92062306a36Sopenharmony_ci * one to tRTP should be ok. There is no other source of non 92162306a36Sopenharmony_ci * integer values, so the result was always going to be 92262306a36Sopenharmony_ci * something for the form: f_ceil(N + .5) = N + 1; 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_ci tRPST = (last->emc_mrw & 0x80) >> 7; 92562306a36Sopenharmony_ci tRTM = fake->dram_timings[RL] + div_o3(3600, src_clk_period) + 92662306a36Sopenharmony_ci max_t(u32, div_o3(7500, src_clk_period), 8) + tRPST + 92762306a36Sopenharmony_ci 1 + nRTP; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci emc_dbg(emc, INFO, "tRTM = %u, EMC_RP = %u\n", tRTM, 93062306a36Sopenharmony_ci next->burst_regs[EMC_RP_INDEX]); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (last->burst_regs[EMC_RP_INDEX] < tRTM) { 93362306a36Sopenharmony_ci if (tRTM > (last->burst_regs[EMC_R2P_INDEX] + 93462306a36Sopenharmony_ci last->burst_regs[EMC_RP_INDEX])) { 93562306a36Sopenharmony_ci R2P_war = tRTM - last->burst_regs[EMC_RP_INDEX]; 93662306a36Sopenharmony_ci RP_war = last->burst_regs[EMC_RP_INDEX]; 93762306a36Sopenharmony_ci TRPab_war = last->burst_regs[EMC_TRPAB_INDEX]; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (R2P_war > 63) { 94062306a36Sopenharmony_ci RP_war = R2P_war + 94162306a36Sopenharmony_ci last->burst_regs[EMC_RP_INDEX] - 63; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (TRPab_war < RP_war) 94462306a36Sopenharmony_ci TRPab_war = RP_war; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci R2P_war = 63; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci } else { 94962306a36Sopenharmony_ci R2P_war = last->burst_regs[EMC_R2P_INDEX]; 95062306a36Sopenharmony_ci RP_war = last->burst_regs[EMC_RP_INDEX]; 95162306a36Sopenharmony_ci TRPab_war = last->burst_regs[EMC_TRPAB_INDEX]; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci if (RP_war < deltaTWATM) { 95562306a36Sopenharmony_ci W2P_war = last->burst_regs[EMC_W2P_INDEX] 95662306a36Sopenharmony_ci + deltaTWATM - RP_war; 95762306a36Sopenharmony_ci if (W2P_war > 63) { 95862306a36Sopenharmony_ci RP_war = RP_war + W2P_war - 63; 95962306a36Sopenharmony_ci if (TRPab_war < RP_war) 96062306a36Sopenharmony_ci TRPab_war = RP_war; 96162306a36Sopenharmony_ci W2P_war = 63; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci } else { 96462306a36Sopenharmony_ci W2P_war = last->burst_regs[ 96562306a36Sopenharmony_ci EMC_W2P_INDEX]; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if ((last->burst_regs[EMC_W2P_INDEX] ^ W2P_war) || 96962306a36Sopenharmony_ci (last->burst_regs[EMC_R2P_INDEX] ^ R2P_war) || 97062306a36Sopenharmony_ci (last->burst_regs[EMC_RP_INDEX] ^ RP_war) || 97162306a36Sopenharmony_ci (last->burst_regs[EMC_TRPAB_INDEX] ^ TRPab_war)) { 97262306a36Sopenharmony_ci emc_writel(emc, RP_war, EMC_RP); 97362306a36Sopenharmony_ci emc_writel(emc, R2P_war, EMC_R2P); 97462306a36Sopenharmony_ci emc_writel(emc, W2P_war, EMC_W2P); 97562306a36Sopenharmony_ci emc_writel(emc, TRPab_war, EMC_TRPAB); 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci tegra210_emc_timing_update(emc); 97962306a36Sopenharmony_ci } else { 98062306a36Sopenharmony_ci emc_dbg(emc, INFO, "Skipped WAR\n"); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (!fsp_for_next_freq) { 98562306a36Sopenharmony_ci mr13_flip_fspwr = (next->emc_mrw3 & 0xffffff3f) | 0x80; 98662306a36Sopenharmony_ci mr13_flip_fspop = (next->emc_mrw3 & 0xffffff3f) | 0x00; 98762306a36Sopenharmony_ci } else { 98862306a36Sopenharmony_ci mr13_flip_fspwr = (next->emc_mrw3 & 0xffffff3f) | 0x40; 98962306a36Sopenharmony_ci mr13_flip_fspop = (next->emc_mrw3 & 0xffffff3f) | 0xc0; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 99362306a36Sopenharmony_ci emc_writel(emc, mr13_flip_fspwr, EMC_MRW3); 99462306a36Sopenharmony_ci emc_writel(emc, next->emc_mrw, EMC_MRW); 99562306a36Sopenharmony_ci emc_writel(emc, next->emc_mrw2, EMC_MRW2); 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* 99962306a36Sopenharmony_ci * Step 8: 100062306a36Sopenharmony_ci * Program the shadow registers. 100162306a36Sopenharmony_ci */ 100262306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 8\n"); 100362306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing burst_regs\n"); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci for (i = 0; i < next->num_burst; i++) { 100662306a36Sopenharmony_ci const u16 *offsets = emc->offsets->burst; 100762306a36Sopenharmony_ci u16 offset; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (!offsets[i]) 101062306a36Sopenharmony_ci continue; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci value = next->burst_regs[i]; 101362306a36Sopenharmony_ci offset = offsets[i]; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (dram_type != DRAM_TYPE_LPDDR4 && 101662306a36Sopenharmony_ci (offset == EMC_MRW6 || offset == EMC_MRW7 || 101762306a36Sopenharmony_ci offset == EMC_MRW8 || offset == EMC_MRW9 || 101862306a36Sopenharmony_ci offset == EMC_MRW10 || offset == EMC_MRW11 || 101962306a36Sopenharmony_ci offset == EMC_MRW12 || offset == EMC_MRW13 || 102062306a36Sopenharmony_ci offset == EMC_MRW14 || offset == EMC_MRW15 || 102162306a36Sopenharmony_ci offset == EMC_TRAINING_CTRL)) 102262306a36Sopenharmony_ci continue; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* Pain... And suffering. */ 102562306a36Sopenharmony_ci if (offset == EMC_CFG) { 102662306a36Sopenharmony_ci value &= ~EMC_CFG_DRAM_ACPD; 102762306a36Sopenharmony_ci value &= ~EMC_CFG_DYN_SELF_REF; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 103062306a36Sopenharmony_ci value &= ~EMC_CFG_DRAM_CLKSTOP_SR; 103162306a36Sopenharmony_ci value &= ~EMC_CFG_DRAM_CLKSTOP_PD; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci } else if (offset == EMC_MRS_WAIT_CNT && 103462306a36Sopenharmony_ci dram_type == DRAM_TYPE_LPDDR2 && 103562306a36Sopenharmony_ci opt_zcal_en_cc && !opt_cc_short_zcal && 103662306a36Sopenharmony_ci opt_short_zcal) { 103762306a36Sopenharmony_ci value = (value & ~(EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK << 103862306a36Sopenharmony_ci EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)) | 103962306a36Sopenharmony_ci ((zq_wait_long & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) << 104062306a36Sopenharmony_ci EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT); 104162306a36Sopenharmony_ci } else if (offset == EMC_ZCAL_WAIT_CNT && 104262306a36Sopenharmony_ci dram_type == DRAM_TYPE_DDR3 && opt_zcal_en_cc && 104362306a36Sopenharmony_ci !opt_cc_short_zcal && opt_short_zcal) { 104462306a36Sopenharmony_ci value = (value & ~(EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK << 104562306a36Sopenharmony_ci EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT)) | 104662306a36Sopenharmony_ci ((zq_wait_long & EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK) << 104762306a36Sopenharmony_ci EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT); 104862306a36Sopenharmony_ci } else if (offset == EMC_ZCAL_INTERVAL && opt_zcal_en_cc) { 104962306a36Sopenharmony_ci value = 0; /* EMC_ZCAL_INTERVAL reset value. */ 105062306a36Sopenharmony_ci } else if (offset == EMC_PMACRO_AUTOCAL_CFG_COMMON) { 105162306a36Sopenharmony_ci value |= EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS; 105262306a36Sopenharmony_ci } else if (offset == EMC_PMACRO_DATA_PAD_TX_CTRL) { 105362306a36Sopenharmony_ci value &= ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC | 105462306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC | 105562306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC | 105662306a36Sopenharmony_ci EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC); 105762306a36Sopenharmony_ci } else if (offset == EMC_PMACRO_CMD_PAD_TX_CTRL) { 105862306a36Sopenharmony_ci value |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON; 105962306a36Sopenharmony_ci value &= ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC | 106062306a36Sopenharmony_ci EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC | 106162306a36Sopenharmony_ci EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC | 106262306a36Sopenharmony_ci EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC); 106362306a36Sopenharmony_ci } else if (offset == EMC_PMACRO_BRICK_CTRL_RFU1) { 106462306a36Sopenharmony_ci value &= 0xf800f800; 106562306a36Sopenharmony_ci } else if (offset == EMC_PMACRO_COMMON_PAD_TX_CTRL) { 106662306a36Sopenharmony_ci value &= 0xfffffff0; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci emc_writel(emc, value, offset); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* SW addition: do EMC refresh adjustment here. */ 107362306a36Sopenharmony_ci tegra210_emc_adjust_timing(emc, next); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 107662306a36Sopenharmony_ci value = (23 << EMC_MRW_MRW_MA_SHIFT) | 107762306a36Sopenharmony_ci (next->run_clocks & EMC_MRW_MRW_OP_MASK); 107862306a36Sopenharmony_ci emc_writel(emc, value, EMC_MRW); 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Per channel burst registers. */ 108262306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing burst_regs_per_ch\n"); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci for (i = 0; i < next->num_burst_per_ch; i++) { 108562306a36Sopenharmony_ci const struct tegra210_emc_per_channel_regs *burst = 108662306a36Sopenharmony_ci emc->offsets->burst_per_channel; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (!burst[i].offset) 108962306a36Sopenharmony_ci continue; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (dram_type != DRAM_TYPE_LPDDR4 && 109262306a36Sopenharmony_ci (burst[i].offset == EMC_MRW6 || 109362306a36Sopenharmony_ci burst[i].offset == EMC_MRW7 || 109462306a36Sopenharmony_ci burst[i].offset == EMC_MRW8 || 109562306a36Sopenharmony_ci burst[i].offset == EMC_MRW9 || 109662306a36Sopenharmony_ci burst[i].offset == EMC_MRW10 || 109762306a36Sopenharmony_ci burst[i].offset == EMC_MRW11 || 109862306a36Sopenharmony_ci burst[i].offset == EMC_MRW12 || 109962306a36Sopenharmony_ci burst[i].offset == EMC_MRW13 || 110062306a36Sopenharmony_ci burst[i].offset == EMC_MRW14 || 110162306a36Sopenharmony_ci burst[i].offset == EMC_MRW15)) 110262306a36Sopenharmony_ci continue; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* Filter out second channel if not in DUAL_CHANNEL mode. */ 110562306a36Sopenharmony_ci if (emc->num_channels < 2 && burst[i].bank >= 1) 110662306a36Sopenharmony_ci continue; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 110962306a36Sopenharmony_ci next->burst_reg_per_ch[i], burst[i].offset); 111062306a36Sopenharmony_ci emc_channel_writel(emc, burst[i].bank, 111162306a36Sopenharmony_ci next->burst_reg_per_ch[i], 111262306a36Sopenharmony_ci burst[i].offset); 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* Vref regs. */ 111662306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing vref_regs\n"); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci for (i = 0; i < next->vref_num; i++) { 111962306a36Sopenharmony_ci const struct tegra210_emc_per_channel_regs *vref = 112062306a36Sopenharmony_ci emc->offsets->vref_per_channel; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (!vref[i].offset) 112362306a36Sopenharmony_ci continue; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (emc->num_channels < 2 && vref[i].bank >= 1) 112662306a36Sopenharmony_ci continue; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 112962306a36Sopenharmony_ci next->vref_perch_regs[i], vref[i].offset); 113062306a36Sopenharmony_ci emc_channel_writel(emc, vref[i].bank, next->vref_perch_regs[i], 113162306a36Sopenharmony_ci vref[i].offset); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Trimmers. */ 113562306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing trim_regs\n"); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci for (i = 0; i < next->num_trim; i++) { 113862306a36Sopenharmony_ci const u16 *offsets = emc->offsets->trim; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (!offsets[i]) 114162306a36Sopenharmony_ci continue; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (compensate_trimmer_applicable && 114462306a36Sopenharmony_ci (offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 || 114562306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 || 114662306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 || 114762306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 || 114862306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 || 114962306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 || 115062306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 || 115162306a36Sopenharmony_ci offsets[i] == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 || 115262306a36Sopenharmony_ci offsets[i] == EMC_DATA_BRLSHFT_0 || 115362306a36Sopenharmony_ci offsets[i] == EMC_DATA_BRLSHFT_1)) { 115462306a36Sopenharmony_ci value = tegra210_emc_compensate(next, offsets[i]); 115562306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 115662306a36Sopenharmony_ci value, offsets[i]); 115762306a36Sopenharmony_ci emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", 115862306a36Sopenharmony_ci (u32)(u64)offsets[i], value); 115962306a36Sopenharmony_ci emc_writel(emc, value, offsets[i]); 116062306a36Sopenharmony_ci } else { 116162306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 116262306a36Sopenharmony_ci next->trim_regs[i], offsets[i]); 116362306a36Sopenharmony_ci emc_writel(emc, next->trim_regs[i], offsets[i]); 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* Per channel trimmers. */ 116862306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing trim_regs_per_ch\n"); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci for (i = 0; i < next->num_trim_per_ch; i++) { 117162306a36Sopenharmony_ci const struct tegra210_emc_per_channel_regs *trim = 117262306a36Sopenharmony_ci &emc->offsets->trim_per_channel[0]; 117362306a36Sopenharmony_ci unsigned int offset; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (!trim[i].offset) 117662306a36Sopenharmony_ci continue; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (emc->num_channels < 2 && trim[i].bank >= 1) 117962306a36Sopenharmony_ci continue; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci offset = trim[i].offset; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (compensate_trimmer_applicable && 118462306a36Sopenharmony_ci (offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 || 118562306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 || 118662306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 || 118762306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 || 118862306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 || 118962306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 || 119062306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 || 119162306a36Sopenharmony_ci offset == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 || 119262306a36Sopenharmony_ci offset == EMC_DATA_BRLSHFT_0 || 119362306a36Sopenharmony_ci offset == EMC_DATA_BRLSHFT_1)) { 119462306a36Sopenharmony_ci value = tegra210_emc_compensate(next, offset); 119562306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 119662306a36Sopenharmony_ci value, offset); 119762306a36Sopenharmony_ci emc_dbg(emc, EMA_WRITES, "0x%08x <= 0x%08x\n", offset, 119862306a36Sopenharmony_ci value); 119962306a36Sopenharmony_ci emc_channel_writel(emc, trim[i].bank, value, offset); 120062306a36Sopenharmony_ci } else { 120162306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 120262306a36Sopenharmony_ci next->trim_perch_regs[i], offset); 120362306a36Sopenharmony_ci emc_channel_writel(emc, trim[i].bank, 120462306a36Sopenharmony_ci next->trim_perch_regs[i], offset); 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing burst_mc_regs\n"); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci for (i = 0; i < next->num_mc_regs; i++) { 121162306a36Sopenharmony_ci const u16 *offsets = emc->offsets->burst_mc; 121262306a36Sopenharmony_ci u32 *values = next->burst_mc_regs; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 121562306a36Sopenharmony_ci values[i], offsets[i]); 121662306a36Sopenharmony_ci mc_writel(emc->mc, values[i], offsets[i]); 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Registers to be programmed on the faster clock. */ 122062306a36Sopenharmony_ci if (next->rate < last->rate) { 122162306a36Sopenharmony_ci const u16 *la = emc->offsets->la_scale; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci emc_dbg(emc, SUB_STEPS, "Writing la_scale_regs\n"); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci for (i = 0; i < next->num_up_down; i++) { 122662306a36Sopenharmony_ci emc_dbg(emc, REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, 122762306a36Sopenharmony_ci next->la_scale_regs[i], la[i]); 122862306a36Sopenharmony_ci mc_writel(emc->mc, next->la_scale_regs[i], la[i]); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* Flush all the burst register writes. */ 123362306a36Sopenharmony_ci mc_readl(emc->mc, MC_EMEM_ADR_CFG); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* 123662306a36Sopenharmony_ci * Step 9: 123762306a36Sopenharmony_ci * LPDDR4 section A. 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 9\n"); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci value = next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX]; 124262306a36Sopenharmony_ci value &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 124562306a36Sopenharmony_ci emc_writel(emc, 0, EMC_ZCAL_INTERVAL); 124662306a36Sopenharmony_ci emc_writel(emc, value, EMC_ZCAL_WAIT_CNT); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci value = emc_dbg | (EMC_DBG_WRITE_MUX_ACTIVE | 124962306a36Sopenharmony_ci EMC_DBG_WRITE_ACTIVE_ONLY); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci emc_writel(emc, value, EMC_DBG); 125262306a36Sopenharmony_ci emc_writel(emc, 0, EMC_ZCAL_INTERVAL); 125362306a36Sopenharmony_ci emc_writel(emc, emc_dbg, EMC_DBG); 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* 125762306a36Sopenharmony_ci * Step 10: 125862306a36Sopenharmony_ci * LPDDR4 and DDR3 common section. 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 10\n"); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (opt_dvfs_mode == MAN_SR || dram_type == DRAM_TYPE_LPDDR4) { 126362306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) 126462306a36Sopenharmony_ci ccfifo_writel(emc, 0x101, EMC_SELF_REF, 0); 126562306a36Sopenharmony_ci else 126662306a36Sopenharmony_ci ccfifo_writel(emc, 0x1, EMC_SELF_REF, 0); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4 && 126962306a36Sopenharmony_ci dst_clk_period <= zqcal_before_cc_cutoff) { 127062306a36Sopenharmony_ci ccfifo_writel(emc, mr13_flip_fspwr ^ 0x40, EMC_MRW3, 0); 127162306a36Sopenharmony_ci ccfifo_writel(emc, (next->burst_regs[EMC_MRW6_INDEX] & 127262306a36Sopenharmony_ci 0xFFFF3F3F) | 127362306a36Sopenharmony_ci (last->burst_regs[EMC_MRW6_INDEX] & 127462306a36Sopenharmony_ci 0x0000C0C0), EMC_MRW6, 0); 127562306a36Sopenharmony_ci ccfifo_writel(emc, (next->burst_regs[EMC_MRW14_INDEX] & 127662306a36Sopenharmony_ci 0xFFFF0707) | 127762306a36Sopenharmony_ci (last->burst_regs[EMC_MRW14_INDEX] & 127862306a36Sopenharmony_ci 0x00003838), EMC_MRW14, 0); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (emc->num_devices > 1) { 128162306a36Sopenharmony_ci ccfifo_writel(emc, 128262306a36Sopenharmony_ci (next->burst_regs[EMC_MRW7_INDEX] & 128362306a36Sopenharmony_ci 0xFFFF3F3F) | 128462306a36Sopenharmony_ci (last->burst_regs[EMC_MRW7_INDEX] & 128562306a36Sopenharmony_ci 0x0000C0C0), EMC_MRW7, 0); 128662306a36Sopenharmony_ci ccfifo_writel(emc, 128762306a36Sopenharmony_ci (next->burst_regs[EMC_MRW15_INDEX] & 128862306a36Sopenharmony_ci 0xFFFF0707) | 128962306a36Sopenharmony_ci (last->burst_regs[EMC_MRW15_INDEX] & 129062306a36Sopenharmony_ci 0x00003838), EMC_MRW15, 0); 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci if (opt_zcal_en_cc) { 129462306a36Sopenharmony_ci if (emc->num_devices < 2) 129562306a36Sopenharmony_ci ccfifo_writel(emc, 129662306a36Sopenharmony_ci 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT 129762306a36Sopenharmony_ci | EMC_ZQ_CAL_ZQ_CAL_CMD, 129862306a36Sopenharmony_ci EMC_ZQ_CAL, 0); 129962306a36Sopenharmony_ci else if (shared_zq_resistor) 130062306a36Sopenharmony_ci ccfifo_writel(emc, 130162306a36Sopenharmony_ci 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT 130262306a36Sopenharmony_ci | EMC_ZQ_CAL_ZQ_CAL_CMD, 130362306a36Sopenharmony_ci EMC_ZQ_CAL, 0); 130462306a36Sopenharmony_ci else 130562306a36Sopenharmony_ci ccfifo_writel(emc, 130662306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_CAL_CMD, 130762306a36Sopenharmony_ci EMC_ZQ_CAL, 0); 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 131362306a36Sopenharmony_ci value = (1000 * fake->dram_timings[T_RP]) / src_clk_period; 131462306a36Sopenharmony_ci ccfifo_writel(emc, mr13_flip_fspop | 0x8, EMC_MRW3, value); 131562306a36Sopenharmony_ci ccfifo_writel(emc, 0, 0, tFC_lpddr4 / src_clk_period); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4 || opt_dvfs_mode != MAN_SR) { 131962306a36Sopenharmony_ci delay = 30; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (cya_allow_ref_cc) { 132262306a36Sopenharmony_ci delay += (1000 * fake->dram_timings[T_RP]) / 132362306a36Sopenharmony_ci src_clk_period; 132462306a36Sopenharmony_ci delay += 4000 * fake->dram_timings[T_RFC]; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci ccfifo_writel(emc, emc_pin & ~(EMC_PIN_PIN_CKE_PER_DEV | 132862306a36Sopenharmony_ci EMC_PIN_PIN_CKEB | 132962306a36Sopenharmony_ci EMC_PIN_PIN_CKE), 133062306a36Sopenharmony_ci EMC_PIN, delay); 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* calculate reference delay multiplier */ 133462306a36Sopenharmony_ci value = 1; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (ref_b4_sref_en) 133762306a36Sopenharmony_ci value++; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (cya_allow_ref_cc) 134062306a36Sopenharmony_ci value++; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (cya_issue_pc_ref) 134362306a36Sopenharmony_ci value++; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci if (dram_type != DRAM_TYPE_LPDDR4) { 134662306a36Sopenharmony_ci delay = ((1000 * fake->dram_timings[T_RP] / src_clk_period) + 134762306a36Sopenharmony_ci (1000 * fake->dram_timings[T_RFC] / src_clk_period)); 134862306a36Sopenharmony_ci delay = value * delay + 20; 134962306a36Sopenharmony_ci } else { 135062306a36Sopenharmony_ci delay = 0; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* 135462306a36Sopenharmony_ci * Step 11: 135562306a36Sopenharmony_ci * Ramp down. 135662306a36Sopenharmony_ci */ 135762306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 11\n"); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ccfifo_writel(emc, 0x0, EMC_CFG_SYNC, delay); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci value = emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE | EMC_DBG_WRITE_ACTIVE_ONLY; 136262306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_DBG, 0); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci ramp_down_wait = tegra210_emc_dvfs_power_ramp_down(emc, src_clk_period, 136562306a36Sopenharmony_ci 0); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* 136862306a36Sopenharmony_ci * Step 12: 136962306a36Sopenharmony_ci * And finally - trigger the clock change. 137062306a36Sopenharmony_ci */ 137162306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 12\n"); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci ccfifo_writel(emc, 1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE, 0); 137462306a36Sopenharmony_ci value &= ~EMC_DBG_WRITE_ACTIVE_ONLY; 137562306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_DBG, 0); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* 137862306a36Sopenharmony_ci * Step 13: 137962306a36Sopenharmony_ci * Ramp up. 138062306a36Sopenharmony_ci */ 138162306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 13\n"); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci ramp_up_wait = tegra210_emc_dvfs_power_ramp_up(emc, dst_clk_period, 0); 138462306a36Sopenharmony_ci ccfifo_writel(emc, emc_dbg, EMC_DBG, 0); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci /* 138762306a36Sopenharmony_ci * Step 14: 138862306a36Sopenharmony_ci * Bringup CKE pins. 138962306a36Sopenharmony_ci */ 139062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 14\n"); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 139362306a36Sopenharmony_ci value = emc_pin | EMC_PIN_PIN_CKE; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (emc->num_devices <= 1) 139662306a36Sopenharmony_ci value &= ~(EMC_PIN_PIN_CKEB | EMC_PIN_PIN_CKE_PER_DEV); 139762306a36Sopenharmony_ci else 139862306a36Sopenharmony_ci value |= EMC_PIN_PIN_CKEB | EMC_PIN_PIN_CKE_PER_DEV; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_PIN, 0); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* 140462306a36Sopenharmony_ci * Step 15: (two step 15s ??) 140562306a36Sopenharmony_ci * Calculate zqlatch wait time; has dependency on ramping times. 140662306a36Sopenharmony_ci */ 140762306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 15\n"); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (dst_clk_period <= zqcal_before_cc_cutoff) { 141062306a36Sopenharmony_ci s32 t = (s32)(ramp_up_wait + ramp_down_wait) / 141162306a36Sopenharmony_ci (s32)dst_clk_period; 141262306a36Sopenharmony_ci zq_latch_dvfs_wait_time = (s32)tZQCAL_lpddr4_fc_adj - t; 141362306a36Sopenharmony_ci } else { 141462306a36Sopenharmony_ci zq_latch_dvfs_wait_time = tZQCAL_lpddr4_fc_adj - 141562306a36Sopenharmony_ci div_o3(1000 * next->dram_timings[T_PDEX], 141662306a36Sopenharmony_ci dst_clk_period); 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci emc_dbg(emc, INFO, "tZQCAL_lpddr4_fc_adj = %u\n", tZQCAL_lpddr4_fc_adj); 142062306a36Sopenharmony_ci emc_dbg(emc, INFO, "dst_clk_period = %u\n", 142162306a36Sopenharmony_ci dst_clk_period); 142262306a36Sopenharmony_ci emc_dbg(emc, INFO, "next->dram_timings[T_PDEX] = %u\n", 142362306a36Sopenharmony_ci next->dram_timings[T_PDEX]); 142462306a36Sopenharmony_ci emc_dbg(emc, INFO, "zq_latch_dvfs_wait_time = %d\n", 142562306a36Sopenharmony_ci max_t(s32, 0, zq_latch_dvfs_wait_time)); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4 && opt_zcal_en_cc) { 142862306a36Sopenharmony_ci delay = div_o3(1000 * next->dram_timings[T_PDEX], 142962306a36Sopenharmony_ci dst_clk_period); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (emc->num_devices < 2) { 143262306a36Sopenharmony_ci if (dst_clk_period > zqcal_before_cc_cutoff) 143362306a36Sopenharmony_ci ccfifo_writel(emc, 143462306a36Sopenharmony_ci 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 143562306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, 143662306a36Sopenharmony_ci delay); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; 143962306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_MRW3, delay); 144062306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_SELF_REF, 0); 144162306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_REF, 0); 144262306a36Sopenharmony_ci ccfifo_writel(emc, 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 144362306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_LATCH_CMD, 144462306a36Sopenharmony_ci EMC_ZQ_CAL, 144562306a36Sopenharmony_ci max_t(s32, 0, zq_latch_dvfs_wait_time)); 144662306a36Sopenharmony_ci } else if (shared_zq_resistor) { 144762306a36Sopenharmony_ci if (dst_clk_period > zqcal_before_cc_cutoff) 144862306a36Sopenharmony_ci ccfifo_writel(emc, 144962306a36Sopenharmony_ci 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 145062306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, 145162306a36Sopenharmony_ci delay); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci ccfifo_writel(emc, 2UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 145462306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, 145562306a36Sopenharmony_ci max_t(s32, 0, zq_latch_dvfs_wait_time) + 145662306a36Sopenharmony_ci delay); 145762306a36Sopenharmony_ci ccfifo_writel(emc, 1UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 145862306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_LATCH_CMD, 145962306a36Sopenharmony_ci EMC_ZQ_CAL, 0); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; 146262306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_MRW3, 0); 146362306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_SELF_REF, 0); 146462306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_REF, 0); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci ccfifo_writel(emc, 1UL << EMC_ZQ_CAL_DEV_SEL_SHIFT | 146762306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, 146862306a36Sopenharmony_ci tZQCAL_lpddr4 / dst_clk_period); 146962306a36Sopenharmony_ci } else { 147062306a36Sopenharmony_ci if (dst_clk_period > zqcal_before_cc_cutoff) 147162306a36Sopenharmony_ci ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_CAL_CMD, 147262306a36Sopenharmony_ci EMC_ZQ_CAL, delay); 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci value = (mr13_flip_fspop & 0xfffffff7) | 0x0c000000; 147562306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_MRW3, delay); 147662306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_SELF_REF, 0); 147762306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_REF, 0); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL, 148062306a36Sopenharmony_ci max_t(s32, 0, zq_latch_dvfs_wait_time)); 148162306a36Sopenharmony_ci } 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* WAR: delay for zqlatch */ 148562306a36Sopenharmony_ci ccfifo_writel(emc, 0, 0, 10); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* 148862306a36Sopenharmony_ci * Step 16: 148962306a36Sopenharmony_ci * LPDDR4 Conditional Training Kickoff. Removed. 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* 149362306a36Sopenharmony_ci * Step 17: 149462306a36Sopenharmony_ci * MANSR exit self refresh. 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 17\n"); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (opt_dvfs_mode == MAN_SR && dram_type != DRAM_TYPE_LPDDR4) 149962306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_SELF_REF, 0); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci /* 150262306a36Sopenharmony_ci * Step 18: 150362306a36Sopenharmony_ci * Send MRWs to LPDDR3/DDR3. 150462306a36Sopenharmony_ci */ 150562306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 18\n"); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR2) { 150862306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_mrw2, EMC_MRW2, 0); 150962306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_mrw, EMC_MRW, 0); 151062306a36Sopenharmony_ci if (is_lpddr3) 151162306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_mrw4, EMC_MRW4, 0); 151262306a36Sopenharmony_ci } else if (dram_type == DRAM_TYPE_DDR3) { 151362306a36Sopenharmony_ci if (opt_dll_mode) 151462306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_emrs & 151562306a36Sopenharmony_ci ~EMC_EMRS_USE_EMRS_LONG_CNT, EMC_EMRS, 0); 151662306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_emrs2 & 151762306a36Sopenharmony_ci ~EMC_EMRS2_USE_EMRS2_LONG_CNT, EMC_EMRS2, 0); 151862306a36Sopenharmony_ci ccfifo_writel(emc, next->emc_mrs | 151962306a36Sopenharmony_ci EMC_EMRS_USE_EMRS_LONG_CNT, EMC_MRS, 0); 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* 152362306a36Sopenharmony_ci * Step 19: 152462306a36Sopenharmony_ci * ZQCAL for LPDDR3/DDR3 152562306a36Sopenharmony_ci */ 152662306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 19\n"); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci if (opt_zcal_en_cc) { 152962306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR2) { 153062306a36Sopenharmony_ci value = opt_cc_short_zcal ? 90000 : 360000; 153162306a36Sopenharmony_ci value = div_o3(value, dst_clk_period); 153262306a36Sopenharmony_ci value = value << 153362306a36Sopenharmony_ci EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT | 153462306a36Sopenharmony_ci value << 153562306a36Sopenharmony_ci EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT; 153662306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_MRS_WAIT_CNT2, 0); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci value = opt_cc_short_zcal ? 0x56 : 0xab; 153962306a36Sopenharmony_ci ccfifo_writel(emc, 2 << EMC_MRW_MRW_DEV_SELECTN_SHIFT | 154062306a36Sopenharmony_ci EMC_MRW_USE_MRW_EXT_CNT | 154162306a36Sopenharmony_ci 10 << EMC_MRW_MRW_MA_SHIFT | 154262306a36Sopenharmony_ci value << EMC_MRW_MRW_OP_SHIFT, 154362306a36Sopenharmony_ci EMC_MRW, 0); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (emc->num_devices > 1) { 154662306a36Sopenharmony_ci value = 1 << EMC_MRW_MRW_DEV_SELECTN_SHIFT | 154762306a36Sopenharmony_ci EMC_MRW_USE_MRW_EXT_CNT | 154862306a36Sopenharmony_ci 10 << EMC_MRW_MRW_MA_SHIFT | 154962306a36Sopenharmony_ci value << EMC_MRW_MRW_OP_SHIFT; 155062306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_MRW, 0); 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci } else if (dram_type == DRAM_TYPE_DDR3) { 155362306a36Sopenharmony_ci value = opt_cc_short_zcal ? 0 : EMC_ZQ_CAL_LONG; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci ccfifo_writel(emc, value | 155662306a36Sopenharmony_ci 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT | 155762306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, 155862306a36Sopenharmony_ci 0); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (emc->num_devices > 1) { 156162306a36Sopenharmony_ci value = value | 1 << EMC_ZQ_CAL_DEV_SEL_SHIFT | 156262306a36Sopenharmony_ci EMC_ZQ_CAL_ZQ_CAL_CMD; 156362306a36Sopenharmony_ci ccfifo_writel(emc, value, EMC_ZQ_CAL, 0); 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (bg_reg_mode_change) { 156962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (ramp_up_wait <= 1250000) 157262306a36Sopenharmony_ci delay = (1250000 - ramp_up_wait) / dst_clk_period; 157362306a36Sopenharmony_ci else 157462306a36Sopenharmony_ci delay = 0; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci ccfifo_writel(emc, 157762306a36Sopenharmony_ci next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX], 157862306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0, delay); 157962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci /* 158362306a36Sopenharmony_ci * Step 20: 158462306a36Sopenharmony_ci * Issue ref and optional QRST. 158562306a36Sopenharmony_ci */ 158662306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 20\n"); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci if (dram_type != DRAM_TYPE_LPDDR4) 158962306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_REF, 0); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci if (opt_do_sw_qrst) { 159262306a36Sopenharmony_ci ccfifo_writel(emc, 1, EMC_ISSUE_QRST, 0); 159362306a36Sopenharmony_ci ccfifo_writel(emc, 0, EMC_ISSUE_QRST, 2); 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* 159762306a36Sopenharmony_ci * Step 21: 159862306a36Sopenharmony_ci * Restore ZCAL and ZCAL interval. 159962306a36Sopenharmony_ci */ 160062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 21\n"); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (save_restore_clkstop_pd || opt_zcal_en_cc) { 160362306a36Sopenharmony_ci ccfifo_writel(emc, emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, 160462306a36Sopenharmony_ci EMC_DBG, 0); 160562306a36Sopenharmony_ci if (opt_zcal_en_cc && dram_type != DRAM_TYPE_LPDDR4) 160662306a36Sopenharmony_ci ccfifo_writel(emc, next->burst_regs[EMC_ZCAL_INTERVAL_INDEX], 160762306a36Sopenharmony_ci EMC_ZCAL_INTERVAL, 0); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (save_restore_clkstop_pd) 161062306a36Sopenharmony_ci ccfifo_writel(emc, next->burst_regs[EMC_CFG_INDEX] & 161162306a36Sopenharmony_ci ~EMC_CFG_DYN_SELF_REF, 161262306a36Sopenharmony_ci EMC_CFG, 0); 161362306a36Sopenharmony_ci ccfifo_writel(emc, emc_dbg, EMC_DBG, 0); 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci /* 161762306a36Sopenharmony_ci * Step 22: 161862306a36Sopenharmony_ci * Restore EMC_CFG_PIPE_CLK. 161962306a36Sopenharmony_ci */ 162062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 22\n"); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci ccfifo_writel(emc, emc_cfg_pipe_clk, EMC_CFG_PIPE_CLK, 0); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (bg_reg_mode_change) { 162562306a36Sopenharmony_ci if (enable_bg_reg) 162662306a36Sopenharmony_ci emc_writel(emc, 162762306a36Sopenharmony_ci next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 162862306a36Sopenharmony_ci ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD, 162962306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0); 163062306a36Sopenharmony_ci else 163162306a36Sopenharmony_ci emc_writel(emc, 163262306a36Sopenharmony_ci next->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] & 163362306a36Sopenharmony_ci ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD, 163462306a36Sopenharmony_ci EMC_PMACRO_BG_BIAS_CTRL_0); 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* 163862306a36Sopenharmony_ci * Step 23: 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 23\n"); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci value = emc_readl(emc, EMC_CFG_DIG_DLL); 164362306a36Sopenharmony_ci value |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC; 164462306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK; 164562306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK; 164662306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN; 164762306a36Sopenharmony_ci value = (value & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) | 164862306a36Sopenharmony_ci (2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT); 164962306a36Sopenharmony_ci emc_writel(emc, value, EMC_CFG_DIG_DLL); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci tegra210_emc_do_clock_change(emc, clksrc); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci /* 165462306a36Sopenharmony_ci * Step 24: 165562306a36Sopenharmony_ci * Save training results. Removed. 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* 165962306a36Sopenharmony_ci * Step 25: 166062306a36Sopenharmony_ci * Program MC updown registers. 166162306a36Sopenharmony_ci */ 166262306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 25\n"); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci if (next->rate > last->rate) { 166562306a36Sopenharmony_ci for (i = 0; i < next->num_up_down; i++) 166662306a36Sopenharmony_ci mc_writel(emc->mc, next->la_scale_regs[i], 166762306a36Sopenharmony_ci emc->offsets->la_scale[i]); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci tegra210_emc_timing_update(emc); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci /* 167362306a36Sopenharmony_ci * Step 26: 167462306a36Sopenharmony_ci * Restore ZCAL registers. 167562306a36Sopenharmony_ci */ 167662306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 26\n"); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR4) { 167962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 168062306a36Sopenharmony_ci emc_writel(emc, next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX], 168162306a36Sopenharmony_ci EMC_ZCAL_WAIT_CNT); 168262306a36Sopenharmony_ci emc_writel(emc, next->burst_regs[EMC_ZCAL_INTERVAL_INDEX], 168362306a36Sopenharmony_ci EMC_ZCAL_INTERVAL); 168462306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci if (dram_type != DRAM_TYPE_LPDDR4 && opt_zcal_en_cc && 168862306a36Sopenharmony_ci !opt_short_zcal && opt_cc_short_zcal) { 168962306a36Sopenharmony_ci udelay(2); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 169262306a36Sopenharmony_ci if (dram_type == DRAM_TYPE_LPDDR2) 169362306a36Sopenharmony_ci emc_writel(emc, next->burst_regs[EMC_MRS_WAIT_CNT_INDEX], 169462306a36Sopenharmony_ci EMC_MRS_WAIT_CNT); 169562306a36Sopenharmony_ci else if (dram_type == DRAM_TYPE_DDR3) 169662306a36Sopenharmony_ci emc_writel(emc, next->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX], 169762306a36Sopenharmony_ci EMC_ZCAL_WAIT_CNT); 169862306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci /* 170262306a36Sopenharmony_ci * Step 27: 170362306a36Sopenharmony_ci * Restore EMC_CFG, FDPD registers. 170462306a36Sopenharmony_ci */ 170562306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 27\n"); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 170862306a36Sopenharmony_ci emc_writel(emc, next->burst_regs[EMC_CFG_INDEX], EMC_CFG); 170962306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 171062306a36Sopenharmony_ci emc_writel(emc, next->emc_fdpd_ctrl_cmd_no_ramp, 171162306a36Sopenharmony_ci EMC_FDPD_CTRL_CMD_NO_RAMP); 171262306a36Sopenharmony_ci emc_writel(emc, next->emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* 171562306a36Sopenharmony_ci * Step 28: 171662306a36Sopenharmony_ci * Training recover. Removed. 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 28\n"); 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ACTIVE); 172162306a36Sopenharmony_ci emc_writel(emc, 172262306a36Sopenharmony_ci next->burst_regs[EMC_PMACRO_AUTOCAL_CFG_COMMON_INDEX], 172362306a36Sopenharmony_ci EMC_PMACRO_AUTOCAL_CFG_COMMON); 172462306a36Sopenharmony_ci tegra210_emc_set_shadow_bypass(emc, ASSEMBLY); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* 172762306a36Sopenharmony_ci * Step 29: 172862306a36Sopenharmony_ci * Power fix WAR. 172962306a36Sopenharmony_ci */ 173062306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 29\n"); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci emc_writel(emc, EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0 | 173362306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1 | 173462306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2 | 173562306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3 | 173662306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4 | 173762306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5 | 173862306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6 | 173962306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7, 174062306a36Sopenharmony_ci EMC_PMACRO_CFG_PM_GLOBAL_0); 174162306a36Sopenharmony_ci emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR, 174262306a36Sopenharmony_ci EMC_PMACRO_TRAINING_CTRL_0); 174362306a36Sopenharmony_ci emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR, 174462306a36Sopenharmony_ci EMC_PMACRO_TRAINING_CTRL_1); 174562306a36Sopenharmony_ci emc_writel(emc, 0, EMC_PMACRO_CFG_PM_GLOBAL_0); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci /* 174862306a36Sopenharmony_ci * Step 30: 174962306a36Sopenharmony_ci * Re-enable autocal. 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_ci emc_dbg(emc, STEPS, "Step 30: Re-enable DLL and AUTOCAL\n"); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (next->burst_regs[EMC_CFG_DIG_DLL_INDEX] & EMC_CFG_DIG_DLL_CFG_DLL_EN) { 175462306a36Sopenharmony_ci value = emc_readl(emc, EMC_CFG_DIG_DLL); 175562306a36Sopenharmony_ci value |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC; 175662306a36Sopenharmony_ci value |= EMC_CFG_DIG_DLL_CFG_DLL_EN; 175762306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK; 175862306a36Sopenharmony_ci value &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK; 175962306a36Sopenharmony_ci value = (value & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) | 176062306a36Sopenharmony_ci (2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT); 176162306a36Sopenharmony_ci emc_writel(emc, value, EMC_CFG_DIG_DLL); 176262306a36Sopenharmony_ci tegra210_emc_timing_update(emc); 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci emc_writel(emc, next->emc_auto_cal_config, EMC_AUTO_CAL_CONFIG); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci /* Done! Yay. */ 176862306a36Sopenharmony_ci} 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ciconst struct tegra210_emc_sequence tegra210_emc_r21021 = { 177162306a36Sopenharmony_ci .revision = 0x7, 177262306a36Sopenharmony_ci .set_clock = tegra210_emc_r21021_set_clock, 177362306a36Sopenharmony_ci .periodic_compensation = tegra210_emc_r21021_periodic_compensation, 177462306a36Sopenharmony_ci}; 1775